| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- <?php
- /**
- * @file
- * Support tests for SQL Server.
- */
- class SqlServerSelectTest extends DatabaseTestCase {
- /**
- * {inheritdoc}
- */
- public static function getInfo() {
- return array(
- 'name' => 'Select tests',
- 'description' => 'Extended tests for SELECT handling.',
- 'group' => 'Database (SQL Server)',
- );
- }
- /**
- * Test group by expansion.
- */
- public function testGroupByExpansion() {
- // By ANSI SQL, GROUP BY columns cannot use aliases. Test that the
- // driver expands the aliases properly.
- $query = db_select('test_task', 't');
- $count_field = $query->addExpression('COUNT(task)', 'num');
- $task_field = $query->addExpression('CONCAT(:prefix, t.task)', 'task', array(':prefix' => 'Task: '));
- $query->orderBy($count_field);
- $query->groupBy($task_field);
- $result = $query->execute();
- $num_records = 0;
- $last_count = 0;
- $records = array();
- foreach ($result as $record) {
- $num_records++;
- $this->assertTrue($record->$count_field >= $last_count, t('Results returned in correct order.'));
- $last_count = $record->$count_field;
- $records[$record->$task_field] = $record->$count_field;
- }
- $correct_results = array(
- 'Task: eat' => 1,
- 'Task: sleep' => 2,
- 'Task: code' => 1,
- 'Task: found new band' => 1,
- 'Task: perform at superbowl' => 1,
- );
- foreach ($correct_results as $task => $count) {
- $this->assertEqual($records[$task], $count, t("Correct number of '@task' records found.", array('@task' => $task)));
- }
- $this->assertEqual($num_records, 6, t('Returned the correct number of total rows.'));
-
- // The driver is also verifying that any expressions/columns used in GROUP BY/ORDER BY
- // are added to the select list if they are not because SQL Server will complain
- // about those fields missing in aggregates.
- $query = db_select('test_task', 't')->distinct();
- $query->addField('t', 'task');
- $query->orderBy('t.pid');
-
- try {
- $result = $query->execute();
- }
- catch (\Exception $e) {
- $this->assert('fail', "Driver failed to deal with missing fields in aggregate: {$e->getMessage()}");
- }
-
- $query = db_select('test_task', 't');
- $query->addField('t', 'task');
- $query->orderBy('t.pid');
- $query->groupBy('t.pid');
-
- try {
- $result = $query->execute();
- }
- catch (\Exception $e) {
- $this->assert('fail', "Driver failed to deal with missing fields in aggregate: {$e->getMessage()}");
- }
- }
-
- /**
- * Test cross join.
- */
- public function testCrossJoin() {
- // SelectQuery in SQL Server driver
- // is expanding expressions into a cross
- // join statement. This allows the use
- // of these expressions in the Aggregate
- // or Where part of the query.
- $query = db_select('test_task', 't');
- // Cast the task to an accent insensitive collation in an expression.
- $query->addExpression('(t.task collate Latin1_General_CS_AI)', 'ci_task');
- // Add condition over that expression.
- $query->where('ci_task = :param', array(':param' => 'slëep'));
- $result = $query->execute();
- $num_records = 0;
- foreach ($result as $record) {
- $num_records++;
- }
- $this->assertEqual($num_records, 2, t('Returned the correct number of total rows.'));
-
- // There is a special case, if the query is an aggregate
- // and an expression is used, this expression must be part of the aggregate.
- $query = db_select('test_task', 't');
- // Cast the task to an accent insensitive collation in an expression.
- $query->addExpression('(t.task collate Latin1_General_CS_AI)', 'ci_task');
- // Add condition over that expression.
- $query->where('ci_task = :param', array(':param' => 'slëep'));
- // Add condition over that expression.
- $query->groupBy('t.task');
-
- $result = $query->execute();
- $num_records = 0;
- foreach ($result as $record) {
- $num_records++;
- }
- $this->assertEqual($num_records, 1, t('Returned the correct number of total rows.'));
- }
- /**
- * Test the 2100 parameter limit per query.
- */
- public function testParameterLimit() {
- $values = array();
- for ($x = 0; $x < 2200; $x ++) {
- $values[] = uniqid($x, TRUE);
- }
- $query = db_select('test_task', 't');
- $query->addExpression('COUNT(task)', 'num');
- $query->where('t.task IN (:data)', array(':data' => $values));
- $result = NULL;
- // If > 2100 we can get SQL Exception! The driver must handle that.
- try {
- $result = $query->execute()->fetchField();
- }
- catch (\Exception $err) {
- }
- $this->assertEqual($result, 0, 'Returned the correct number of total rows.');
- }
- /**
- * Although per official documentation you cannot send
- * duplicate placeholders in same query, this works in mySQL
- * and is present in some queries, even in core, wich have not
- * gotten enough attention.
- */
- public function testDuplicatePlaceholders() {
- $query = db_select('test_task', 't');
- $query->addExpression('COUNT(task)', 'num');
- $query->where('t.task IN (:data0, :data0)', array(':data0' => 'sleep'));
- $result = NULL;
- try {
- $result = $query->execute()->fetchField();
- }
- catch (\Exception $err) {
- }
- $this->assertEqual($result, 2, 'Returned the correct number of total rows.');
- }
- /**
- * Test for weird key names
- * in array arguments.
- */
- public function testBadKeysInArrayArguments() {
- $params[':nids'] = array(
- 'uid1' => -9,
- 'What a bad placeholder name, why should we care?' => -6,
- );
- $result = NULL;
- try {
- // The regular expandArguments implementation will fail to
- // properly expand the associative array with weird keys, OH, and actually
- // you can perform some SQL Injection through the array keys.
- $result = db_query('SELECT COUNT(*) FROM USERS WHERE USERS.UID IN (:nids)', $params)->fetchField();
- }
- catch (\Exception $err) {
- // Regular drupal will fail with
- // SQLSTATE[IMSSP]: An error occurred substituting the named parameters.
- // https://www.drupal.org/node/2146839
- }
- // User ID's are negative, so this should return 0 matches.
- $this->assertEqual($result, 0, 'Returned the correct number of total rows.');
- }
-
- /**
- * Test the temporary table functionality.
- */
- public function testTemporaryTables() {
-
- $query = db_select('test_task', 't');
- $query->fields('t');
-
- $table = db_query_temporary((string) $query);
-
- // First assert that the table exists
- $this->assertTRUE(db_table_exists($table), 'The temporary table exists.');
-
- $query2 = db_select($table, 't');
- $query2->fields('t');
-
- // Now make sure that both tables are exactly the same.
- $data1 = $query->execute()->fetchAllAssoc('tid');
- $data2 = $query2->execute()->fetchAllAssoc('tid');
- // User ID's are negative, so this should return 0 matches.
- $this->assertEqual(count($data1), count($data2), 'Temporary table has the same number of rows.');
- // $this->assertEqual(count($data1[0]), count($data2[0]), 'Temporary table has the same number of columns.');
-
- // Drop the table.
- db_drop_table($table);
-
- // The table should not exist now.
- $this->assertFALSE(db_table_exists($table), 'The temporary table does not exists.');
- }
-
- /**
- * Test LIKE statement wildcards are properly escaped.
- */
- public function testEscapeLike() {
- // Test expected escaped characters
- $string = 't[e%s]t_\\';
- $expected = 't[[]e[%]s[]]t[_]\\';
- $actual = db_like($string);
- $this->assertEqual($actual, $expected, 'Properly escaped LIKE statement wildcards.');
- db_insert('test_task')
- ->fields(array(
- 'task' => 'T\\est',
- ))
- ->execute();
- $query = db_select('test_task', 't');
- $query->fields('t');
- $query->condition('t.task', db_like('T\\est'), 'LIKE');
- $result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 1, t('db_select returned the correct number of total rows.'));
- db_insert('test_task')
- ->fields(array(
- 'task' => 'T\'est',
- ))
- ->execute();
- $query = db_select('test_task', 't');
- $query->fields('t');
- $query->condition('t.task', db_like('T\'est'), 'LIKE');
- $result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 1, t('db_select returned the correct number of total rows.'));
- // db_select: Test unescaped wildcard.
- $query = db_select('test_task', 't');
- $query->condition('t.task', '[s]leep', 'LIKE');
- $query->fields('t');
- $result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 2, t('db_select returned the correct number of total rows.'));
- // db_select: Test unescaped wildcard.
- $query = db_select('test_task', 't');
- $query->condition('t.task', '[s]leep', 'LIKE');
- $query->fields('t');
- $result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 2, t('db_select returned the correct number of total rows.'));
- // db_select: Test escaped wildcard.
- $query = db_select('test_task', 't');
- $query->condition('t.task', db_like('[s]leep'), 'LIKE');
- $query->fields('t');
- $result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 0, t('db_select returned the correct number of total rows.'));
- // db_select->where: Test unescaped wildcard.
- $query = db_select('test_task', 't');
- $query->where('t.task LIKE :task', array(':task' => '[s]leep'));
- $query->fields('t');
- $result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 2, t('db_select returned the correct number of total rows.'));
- // db_select->where: Test escaped wildcard.
- $query = db_select('test_task', 't');
- $query->where('t.task LIKE :task', array(':task' => db_like('[s]leep')));
- $query->fields('t');
- $result = $query->execute()->fetchAll();
- $this->assertEqual(count($result), 0, t('db_select returned the correct number of total rows.'));
- // db_query: Test unescaped wildcard.
- $query = db_query('SELECT COUNT(*) FROM {test_task} WHERE task LIKE :task',
- array(':task' => '[s]leep'));
- $result = $query->fetchField();
- $this->assertEqual($result, 2, t('db_query returned the correct number of total rows.'));
- // db_query: Test escaped wildcard.
- $query = db_query('SELECT COUNT(*) FROM {test_task} WHERE task LIKE :task',
- array(':task' => db_like('[s]leep')));
- $result = $query->fetchField();
- $this->assertEqual($result, 0, t('db_query returned the correct number of total rows.'));
- }
- }
|