Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Test case for sqlsrv dml support.
19
 *
20
 * @package    core
21
 * @category   test
22
 * @copyright  2017 John Okely
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
namespace core;
27
 
28
use sqlsrv_native_moodle_database;
29
 
30
defined('MOODLE_INTERNAL') || die();
31
 
32
global $CFG;
33
require_once($CFG->dirroot.'/lib/dml/sqlsrv_native_moodle_database.php');
34
 
35
/**
36
 * Test case for sqlsrv dml support.
37
 *
38
 * @package    core
39
 * @category   test
40
 * @copyright  2017 John Okely
41
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42
 */
1441 ariadna 43
final class sqlsrv_native_moodle_database_test extends \advanced_testcase {
1 efrain 44
    public function setUp(): void {
45
        parent::setUp();
46
        $this->resetAfterTest();
47
    }
48
 
49
    /**
50
     * Dataprovider for test_add_no_lock_to_temp_tables
51
     * @return array Data for test_add_no_lock_to_temp_tables
52
     */
1441 ariadna 53
    public static function add_no_lock_to_temp_tables_provider(): array {
1 efrain 54
        return [
55
            "Basic temp table, nothing following" => [
56
                'input' => 'SELECT * FROM {table_temp}',
57
                'expected' => 'SELECT * FROM {table_temp} WITH (NOLOCK)'
58
            ],
59
            "Basic temp table, with capitalised alias" => [
60
                'input' => 'SELECT * FROM {table_temp} MYTABLE',
61
                'expected' => 'SELECT * FROM {table_temp} MYTABLE WITH (NOLOCK)'
62
            ],
63
            "Temp table with alias, and another non-temp table" => [
64
                'input' => 'SELECT * FROM {table_temp} x WHERE y in (SELECT y from {table2})',
65
                'expected' => 'SELECT * FROM {table_temp} x WITH (NOLOCK) WHERE y in (SELECT y from {table2})'
66
            ],
67
            "Temp table with reserve word following, no alias" => [
68
                'input' => 'SELECT DISTINCT * FROM {table_temp} WHERE y in (SELECT y from {table2} nottemp)',
69
                'expected' => 'SELECT DISTINCT * FROM {table_temp} WITH (NOLOCK) WHERE y in (SELECT y from {table2} nottemp)'
70
            ],
71
            "Temp table with reserve word, lower case" => [
72
                'input' => 'SELECT DISTINCT * FROM {table_temp} where y in (SELECT y from {table2} nottemp)',
73
                'expected' => 'SELECT DISTINCT * FROM {table_temp} WITH (NOLOCK) where y in (SELECT y from {table2} nottemp)'
74
            ],
75
            "Another reserve word test" => [
76
                'input' => 'SELECT DISTINCT * FROM {table_temp} PIVOT y in (SELECT y from {table2} nottemp)',
77
                'expected' => 'SELECT DISTINCT * FROM {table_temp} WITH (NOLOCK) PIVOT y in (SELECT y from {table2} nottemp)'
78
            ],
79
            "Another reserve word test should fail" => [
80
                'input' => 'SELECT DISTINCT * FROM {table_temp} PIVOT y in (SELECT y from {table2} nottemp)',
81
                'expected' => 'SELECT DISTINCT * FROM {table_temp} WITH (NOLOCK) PIVOT y in (SELECT y from {table2} nottemp)'
82
            ],
83
            "Temp table with an alias starting with a keyword" => [
84
                'input' => 'SELECT * FROM {table_temp} asx',
85
                'expected' => 'SELECT * FROM {table_temp} asx WITH (NOLOCK)'
86
            ],
87
            "Keep alias with underscore" => [
88
                'input' => 'SELECT * FROM {table_temp} alias_for_table',
89
                'expected' => 'SELECT * FROM {table_temp} alias_for_table WITH (NOLOCK)'
90
            ],
91
            "Alias with number" => [
92
                'input' => 'SELECT * FROM {table_temp} a5 WHERE y',
93
                'expected' => 'SELECT * FROM {table_temp} a5 WITH (NOLOCK) WHERE y'
94
            ],
95
            "Alias with number and underscore" => [
96
                'input' => 'SELECT * FROM {table_temp} a_5 WHERE y',
97
                'expected' => 'SELECT * FROM {table_temp} a_5 WITH (NOLOCK) WHERE y'
98
            ],
99
            "Temp table in subquery" => [
100
                'input' => 'select * FROM (SELECT DISTINCT * FROM {table_temp})',
101
                'expected' => 'select * FROM (SELECT DISTINCT * FROM {table_temp} WITH (NOLOCK))'
102
            ],
103
            "Temp table in subquery, with following commands" => [
104
                'input' => 'select * FROM (SELECT DISTINCT * FROM {table_temp} ) WHERE y',
105
                'expected' => 'select * FROM (SELECT DISTINCT * FROM {table_temp} WITH (NOLOCK) ) WHERE y'
106
            ],
107
            "Temp table in subquery, with alias" => [
108
                'input' => 'select * FROM (SELECT DISTINCT * FROM {table_temp} x) WHERE y',
109
                'expected' => 'select * FROM (SELECT DISTINCT * FROM {table_temp} x WITH (NOLOCK)) WHERE y'
110
            ],
111
        ];
112
    }
113
 
114
    /**
115
     * Test add_no_lock_to_temp_tables
116
     *
117
     * @param string $input The input SQL query
118
     * @param string $expected The expected resultant query
119
     * @dataProvider add_no_lock_to_temp_tables_provider
120
     */
11 efrain 121
    public function test_add_no_lock_to_temp_tables($input, $expected): void {
1 efrain 122
        $sqlsrv = new sqlsrv_native_moodle_database();
123
 
124
        $reflector = new \ReflectionObject($sqlsrv);
125
 
126
        $method = $reflector->getMethod('add_no_lock_to_temp_tables');
127
 
128
        $temptablesproperty = $reflector->getProperty('temptables');
129
        $temptables = new temptables_tester();
130
 
131
        $temptablesproperty->setValue($sqlsrv, $temptables);
132
 
133
        $result = $method->invoke($sqlsrv, $input);
134
 
135
        $temptablesproperty->setValue($sqlsrv, null);
136
        $this->assertEquals($expected, $result);
137
    }
138
 
139
    /**
140
     * Data provider for test_has_query_order_by
141
     *
142
     * @return array data for test_has_query_order_by
143
     */
1441 ariadna 144
    public static function has_query_order_by_provider(): array {
1 efrain 145
        // Fixtures taken from https://docs.moodle.org/en/ad-hoc_contributed_reports.
146
 
147
        return [
148
            'User with language => FALSE' => [
149
                'sql' => <<<EOT
150
SELECT username, lang
151
  FROM prefix_user
152
EOT
153
                ,
154
                'expectedmainquery' => <<<EOT
155
SELECT username, lang
156
  FROM prefix_user
157
EOT
158
                ,
159
                'expectedresult' => false
160
            ],
161
            'List Users with extra info (email) in current course => FALSE' => [
162
                'sql' => <<<EOT
163
SELECT u.firstname, u.lastname, u.email
164
  FROM prefix_role_assignments AS ra
165
  JOIN prefix_context AS context ON context.id = ra.contextid AND context.contextlevel = 50
166
  JOIN prefix_course AS c ON c.id = context.instanceid AND c.id = %%COURSEID%%
167
  JOIN prefix_user AS u ON u.id = ra.userid
168
EOT
169
                ,
170
                'expectedmainquery' => <<<EOT
171
SELECT u.firstname, u.lastname, u.email
172
  FROM prefix_role_assignments AS ra
173
  JOIN prefix_context AS context ON context.id = ra.contextid AND context.contextlevel = 50
174
  JOIN prefix_course AS c ON c.id = context.instanceid AND c.id = %%COURSEID%%
175
  JOIN prefix_user AS u ON u.id = ra.userid
176
EOT
177
                ,
178
                'expectedresult' => false
179
            ],
180
            'ROW_NUMBER() OVER (ORDER BY ...) => FALSE (https://github.com/jleyva/moodle-block_configurablereports/issues/120)' => [
181
                'sql' => <<<EOT
182
SELECT COUNT(*) AS 'Users who have logged in today'
183
  FROM (
184
         SELECT ROW_NUMBER() OVER(ORDER BY lastaccess DESC) AS Row
185
           FROM mdl_user
186
          WHERE lastaccess > DATEDIFF(s, '1970-01-01 02:00:00', (SELECT Convert(DateTime, DATEDIFF(DAY, 0, GETDATE()))))
187
       ) AS Logins
188
EOT
189
                ,
190
                'expectedmainquery' => <<<EOT
191
SELECT COUNT() AS 'Users who have logged in today'
192
  FROM () AS Logins
193
EOT
194
                ,
195
                'expectedresult' => false
196
            ],
197
            'CONTRIB-7725 workaround) => TRUE' => [
198
                'sql' => <<<EOT
199
SELECT COUNT(*) AS 'Users who have logged in today'
200
  FROM (
201
         SELECT ROW_NUMBER() OVER(ORDER BY lastaccess DESC) AS Row
202
           FROM mdl_user
203
          WHERE lastaccess > DATEDIFF(s, '1970-01-01 02:00:00', (SELECT Convert(DateTime, DATEDIFF(DAY, 0, GETDATE()))))
204
       ) AS Logins ORDER BY 1
205
EOT
206
                ,
207
                'expectedmainquery' => <<<EOT
208
SELECT COUNT() AS 'Users who have logged in today'
209
  FROM () AS Logins ORDER BY 1
210
EOT
211
                ,
212
                'expectedresult' => true
213
            ],
214
            'Enrolment count in each Course => TRUE' => [
215
                'sql' => <<<EOT
216
  SELECT c.fullname, COUNT(ue.id) AS Enroled
217
    FROM prefix_course AS c
218
    JOIN prefix_enrol AS en ON en.courseid = c.id
219
    JOIN prefix_user_enrolments AS ue ON ue.enrolid = en.id
220
GROUP BY c.id
221
ORDER BY c.fullname
222
EOT
223
                ,
224
                'expectedmainquery' => <<<EOT
225
  SELECT c.fullname, COUNT() AS Enroled
226
    FROM prefix_course AS c
227
    JOIN prefix_enrol AS en ON en.courseid = c.id
228
    JOIN prefix_user_enrolments AS ue ON ue.enrolid = en.id
229
GROUP BY c.id
230
ORDER BY c.fullname
231
EOT
232
                ,
233
                'expectedresult' => true
234
            ],
235
        ];
236
    }
237
 
238
    /**
239
     * Test has_query_order_by
240
     *
241
     * @dataProvider has_query_order_by_provider
242
     * @param string $sql the query
243
     * @param string $expectedmainquery the expected main query
244
     * @param bool $expectedresult the expected result
245
     */
11 efrain 246
    public function test_has_query_order_by(string $sql, string $expectedmainquery, bool $expectedresult): void {
1 efrain 247
        $mainquery = preg_replace('/\(((?>[^()]+)|(?R))*\)/', '()', $sql);
248
        $this->assertSame($expectedmainquery, $mainquery);
249
 
250
        // The has_query_order_by static method is protected. Use Reflection to call the method.
251
        $method = new \ReflectionMethod('sqlsrv_native_moodle_database', 'has_query_order_by');
252
        $result = $method->invoke(null, $sql);
253
        $this->assertSame($expectedresult, $result);
254
    }
255
}
256
 
257
/**
258
 * Test class for testing temptables
259
 *
260
 * @copyright  2017 John Okely
261
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
262
 */
263
class temptables_tester {
264
    /**
265
     * Returns if one table, based in the information present in the store, is a temp table
266
     *
267
     * For easy testing, anything with the word 'temp' in it is considered temporary.
268
     *
269
     * @param string $tablename name without prefix of the table we are asking about
270
     * @return bool true if the table is a temp table (based in the store info), false if not
271
     */
272
    public function is_temptable($tablename) {
273
        if (strpos($tablename, 'temp') === false) {
274
            return false;
275
        } else {
276
            return true;
277
        }
278
    }
279
    /**
280
     * Dispose the temptables
281
     *
282
     * @return void
283
     */
284
    public function dispose() {
285
    }
286
}