cms/drupal/modules/simpletest/tests/database_test.test
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * Dummy class for fetching into a class.
       
     5  *
       
     6  * PDO supports using a new instance of an arbitrary class for records
       
     7  * rather than just a stdClass or array. This class is for testing that
       
     8  * functionality. (See testQueryFetchClass() below)
       
     9  */
       
    10 class FakeRecord { }
       
    11 
       
    12 /**
       
    13  * Base test class for databases.
       
    14  *
       
    15  * Because all database tests share the same test data, we can centralize that
       
    16  * here.
       
    17  */
       
    18 class DatabaseTestCase extends DrupalWebTestCase {
       
    19   protected $profile = 'testing';
       
    20 
       
    21   function setUp() {
       
    22     parent::setUp('database_test');
       
    23 
       
    24     $schema['test'] = drupal_get_schema('test');
       
    25     $schema['test_people'] = drupal_get_schema('test_people');
       
    26     $schema['test_people_copy'] = drupal_get_schema('test_people_copy');
       
    27     $schema['test_one_blob'] = drupal_get_schema('test_one_blob');
       
    28     $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs');
       
    29     $schema['test_task'] = drupal_get_schema('test_task');
       
    30 
       
    31     $this->installTables($schema);
       
    32 
       
    33     $this->addSampleData();
       
    34   }
       
    35 
       
    36   /**
       
    37    * Set up several tables needed by a certain test.
       
    38    *
       
    39    * @param $schema
       
    40    *   An array of table definitions to install.
       
    41    */
       
    42   function installTables($schema) {
       
    43     // This ends up being a test for table drop and create, too, which is nice.
       
    44     foreach ($schema as $name => $data) {
       
    45       if (db_table_exists($name)) {
       
    46         db_drop_table($name);
       
    47       }
       
    48       db_create_table($name, $data);
       
    49     }
       
    50 
       
    51     foreach ($schema as $name => $data) {
       
    52       $this->assertTrue(db_table_exists($name), format_string('Table @name created successfully.', array('@name' => $name)));
       
    53     }
       
    54   }
       
    55 
       
    56   /**
       
    57    * Set up tables for NULL handling.
       
    58    */
       
    59   function ensureSampleDataNull() {
       
    60     $schema['test_null'] = drupal_get_schema('test_null');
       
    61     $this->installTables($schema);
       
    62 
       
    63     db_insert('test_null')
       
    64     ->fields(array('name', 'age'))
       
    65     ->values(array(
       
    66       'name' => 'Kermit',
       
    67       'age' => 25,
       
    68     ))
       
    69     ->values(array(
       
    70       'name' => 'Fozzie',
       
    71       'age' => NULL,
       
    72     ))
       
    73     ->values(array(
       
    74       'name' => 'Gonzo',
       
    75       'age' => 27,
       
    76     ))
       
    77     ->execute();
       
    78   }
       
    79 
       
    80   /**
       
    81    * Setup our sample data.
       
    82    *
       
    83    * These are added using db_query(), since we're not trying to test the
       
    84    * INSERT operations here, just populate.
       
    85    */
       
    86   function addSampleData() {
       
    87     // We need the IDs, so we can't use a multi-insert here.
       
    88     $john = db_insert('test')
       
    89       ->fields(array(
       
    90         'name' => 'John',
       
    91         'age' => 25,
       
    92         'job' => 'Singer',
       
    93       ))
       
    94       ->execute();
       
    95 
       
    96     $george = db_insert('test')
       
    97       ->fields(array(
       
    98         'name' => 'George',
       
    99         'age' => 27,
       
   100         'job' => 'Singer',
       
   101       ))
       
   102       ->execute();
       
   103 
       
   104     $ringo = db_insert('test')
       
   105       ->fields(array(
       
   106         'name' => 'Ringo',
       
   107         'age' => 28,
       
   108         'job' => 'Drummer',
       
   109       ))
       
   110       ->execute();
       
   111 
       
   112     $paul = db_insert('test')
       
   113       ->fields(array(
       
   114         'name' => 'Paul',
       
   115         'age' => 26,
       
   116         'job' => 'Songwriter',
       
   117       ))
       
   118       ->execute();
       
   119 
       
   120     db_insert('test_people')
       
   121       ->fields(array(
       
   122         'name' => 'Meredith',
       
   123         'age' => 30,
       
   124         'job' => 'Speaker',
       
   125       ))
       
   126       ->execute();
       
   127 
       
   128     db_insert('test_task')
       
   129       ->fields(array('pid', 'task', 'priority'))
       
   130       ->values(array(
       
   131         'pid' => $john,
       
   132         'task' => 'eat',
       
   133         'priority' => 3,
       
   134       ))
       
   135       ->values(array(
       
   136         'pid' => $john,
       
   137         'task' => 'sleep',
       
   138         'priority' => 4,
       
   139       ))
       
   140       ->values(array(
       
   141         'pid' => $john,
       
   142         'task' => 'code',
       
   143         'priority' => 1,
       
   144       ))
       
   145       ->values(array(
       
   146         'pid' => $george,
       
   147         'task' => 'sing',
       
   148         'priority' => 2,
       
   149       ))
       
   150       ->values(array(
       
   151         'pid' => $george,
       
   152         'task' => 'sleep',
       
   153         'priority' => 2,
       
   154       ))
       
   155       ->values(array(
       
   156         'pid' => $paul,
       
   157         'task' => 'found new band',
       
   158         'priority' => 1,
       
   159       ))
       
   160       ->values(array(
       
   161         'pid' => $paul,
       
   162         'task' => 'perform at superbowl',
       
   163         'priority' => 3,
       
   164       ))
       
   165       ->execute();
       
   166   }
       
   167 }
       
   168 
       
   169 /**
       
   170  * Test connection management.
       
   171  */
       
   172 class DatabaseConnectionTestCase extends DatabaseTestCase {
       
   173 
       
   174   public static function getInfo() {
       
   175     return array(
       
   176       'name' => 'Connection tests',
       
   177       'description' => 'Tests of the core database system.',
       
   178       'group' => 'Database',
       
   179     );
       
   180   }
       
   181 
       
   182   /**
       
   183    * Test that connections return appropriate connection objects.
       
   184    */
       
   185   function testConnectionRouting() {
       
   186     // Clone the master credentials to a slave connection.
       
   187     // Note this will result in two independent connection objects that happen
       
   188     // to point to the same place.
       
   189     $connection_info = Database::getConnectionInfo('default');
       
   190     Database::addConnectionInfo('default', 'slave', $connection_info['default']);
       
   191 
       
   192     $db1 = Database::getConnection('default', 'default');
       
   193     $db2 = Database::getConnection('slave', 'default');
       
   194 
       
   195     $this->assertNotNull($db1, 'default connection is a real connection object.');
       
   196     $this->assertNotNull($db2, 'slave connection is a real connection object.');
       
   197     $this->assertNotIdentical($db1, $db2, 'Each target refers to a different connection.');
       
   198 
       
   199     // Try to open those targets another time, that should return the same objects.
       
   200     $db1b = Database::getConnection('default', 'default');
       
   201     $db2b = Database::getConnection('slave', 'default');
       
   202     $this->assertIdentical($db1, $db1b, 'A second call to getConnection() returns the same object.');
       
   203     $this->assertIdentical($db2, $db2b, 'A second call to getConnection() returns the same object.');
       
   204 
       
   205     // Try to open an unknown target.
       
   206     $unknown_target = $this->randomName();
       
   207     $db3 = Database::getConnection($unknown_target, 'default');
       
   208     $this->assertNotNull($db3, 'Opening an unknown target returns a real connection object.');
       
   209     $this->assertIdentical($db1, $db3, 'An unknown target opens the default connection.');
       
   210 
       
   211     // Try to open that unknown target another time, that should return the same object.
       
   212     $db3b = Database::getConnection($unknown_target, 'default');
       
   213     $this->assertIdentical($db3, $db3b, 'A second call to getConnection() returns the same object.');
       
   214   }
       
   215 
       
   216   /**
       
   217    * Test that connections return appropriate connection objects.
       
   218    */
       
   219   function testConnectionRoutingOverride() {
       
   220     // Clone the master credentials to a slave connection.
       
   221     // Note this will result in two independent connection objects that happen
       
   222     // to point to the same place.
       
   223     $connection_info = Database::getConnectionInfo('default');
       
   224     Database::addConnectionInfo('default', 'slave', $connection_info['default']);
       
   225 
       
   226     Database::ignoreTarget('default', 'slave');
       
   227 
       
   228     $db1 = Database::getConnection('default', 'default');
       
   229     $db2 = Database::getConnection('slave', 'default');
       
   230 
       
   231     $this->assertIdentical($db1, $db2, 'Both targets refer to the same connection.');
       
   232   }
       
   233 
       
   234   /**
       
   235    * Tests the closing of a database connection.
       
   236    */
       
   237   function testConnectionClosing() {
       
   238     // Open the default target so we have an object to compare.
       
   239     $db1 = Database::getConnection('default', 'default');
       
   240 
       
   241     // Try to close the default connection, then open a new one.
       
   242     Database::closeConnection('default', 'default');
       
   243     $db2 = Database::getConnection('default', 'default');
       
   244 
       
   245     // Opening a connection after closing it should yield an object different than the original.
       
   246     $this->assertNotIdentical($db1, $db2, 'Opening the default connection after it is closed returns a new object.');
       
   247   }
       
   248 
       
   249   /**
       
   250    * Tests the connection options of the active database.
       
   251    */
       
   252   function testConnectionOptions() {
       
   253     $connection_info = Database::getConnectionInfo('default');
       
   254 
       
   255     // Be sure we're connected to the default database.
       
   256     $db = Database::getConnection('default', 'default');
       
   257     $connectionOptions = $db->getConnectionOptions();
       
   258 
       
   259     // In the MySQL driver, the port can be different, so check individual
       
   260     // options.
       
   261     $this->assertEqual($connection_info['default']['driver'], $connectionOptions['driver'], 'The default connection info driver matches the current connection options driver.');
       
   262     $this->assertEqual($connection_info['default']['database'], $connectionOptions['database'], 'The default connection info database matches the current connection options database.');
       
   263 
       
   264     // Set up identical slave and confirm connection options are identical.
       
   265     Database::addConnectionInfo('default', 'slave', $connection_info['default']);
       
   266     $db2 = Database::getConnection('slave', 'default');
       
   267     $connectionOptions2 = $db2->getConnectionOptions();
       
   268 
       
   269     // Get a fresh copy of the default connection options.
       
   270     $connectionOptions = $db->getConnectionOptions();
       
   271     $this->assertIdentical($connectionOptions, $connectionOptions2, 'The default and slave connection options are identical.');
       
   272 
       
   273     // Set up a new connection with different connection info.
       
   274     $test = $connection_info['default'];
       
   275     $test['database'] .= 'test';
       
   276     Database::addConnectionInfo('test', 'default', $test);
       
   277     $connection_info = Database::getConnectionInfo('test');
       
   278 
       
   279     // Get a fresh copy of the default connection options.
       
   280     $connectionOptions = $db->getConnectionOptions();
       
   281     $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], 'The test connection info database does not match the current connection options database.');
       
   282   }
       
   283 }
       
   284 
       
   285 /**
       
   286  * Test cloning Select queries.
       
   287  */
       
   288 class DatabaseSelectCloneTest extends DatabaseTestCase {
       
   289 
       
   290   public static function getInfo() {
       
   291     return array(
       
   292       'name' => 'Select tests, cloning',
       
   293       'description' => 'Test cloning Select queries.',
       
   294       'group' => 'Database',
       
   295     );
       
   296   }
       
   297 
       
   298   /**
       
   299    * Test that subqueries as value within conditions are cloned properly.
       
   300    */
       
   301   function testSelectConditionSubQueryCloning() {
       
   302     $subquery = db_select('test', 't');
       
   303     $subquery->addField('t', 'id', 'id');
       
   304     $subquery->condition('age', 28, '<');
       
   305 
       
   306     $query = db_select('test', 't');
       
   307     $query->addField('t', 'name', 'name');
       
   308     $query->condition('id', $subquery, 'IN');
       
   309 
       
   310     $clone = clone $query;
       
   311     // Cloned query should not be altered by the following modification
       
   312     // happening on original query.
       
   313     $subquery->condition('age', 25, '>');
       
   314 
       
   315     $clone_result = $clone->countQuery()->execute()->fetchField();
       
   316     $query_result = $query->countQuery()->execute()->fetchField();
       
   317 
       
   318     // Make sure the cloned query has not been modified
       
   319     $this->assertEqual(3, $clone_result, 'The cloned query returns the expected number of rows');
       
   320     $this->assertEqual(2, $query_result, 'The query returns the expected number of rows');
       
   321   }
       
   322 }
       
   323 
       
   324 /**
       
   325  * Test fetch actions, part 1.
       
   326  *
       
   327  * We get timeout errors if we try to run too many tests at once.
       
   328  */
       
   329 class DatabaseFetchTestCase extends DatabaseTestCase {
       
   330 
       
   331   public static function getInfo() {
       
   332     return array(
       
   333       'name' => 'Fetch tests',
       
   334       'description' => 'Test the Database system\'s various fetch capabilities.',
       
   335       'group' => 'Database',
       
   336     );
       
   337   }
       
   338 
       
   339   /**
       
   340    * Confirm that we can fetch a record properly in default object mode.
       
   341    */
       
   342   function testQueryFetchDefault() {
       
   343     $records = array();
       
   344     $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25));
       
   345     $this->assertTrue($result instanceof DatabaseStatementInterface, 'Result set is a Drupal statement object.');
       
   346     foreach ($result as $record) {
       
   347       $records[] = $record;
       
   348       $this->assertTrue(is_object($record), 'Record is an object.');
       
   349       $this->assertIdentical($record->name, 'John', '25 year old is John.');
       
   350     }
       
   351 
       
   352     $this->assertIdentical(count($records), 1, 'There is only one record.');
       
   353   }
       
   354 
       
   355   /**
       
   356    * Confirm that we can fetch a record to an object explicitly.
       
   357    */
       
   358   function testQueryFetchObject() {
       
   359     $records = array();
       
   360     $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_OBJ));
       
   361     foreach ($result as $record) {
       
   362       $records[] = $record;
       
   363       $this->assertTrue(is_object($record), 'Record is an object.');
       
   364       $this->assertIdentical($record->name, 'John', '25 year old is John.');
       
   365     }
       
   366 
       
   367     $this->assertIdentical(count($records), 1, 'There is only one record.');
       
   368   }
       
   369 
       
   370   /**
       
   371    * Confirm that we can fetch a record to an array associative explicitly.
       
   372    */
       
   373   function testQueryFetchArray() {
       
   374     $records = array();
       
   375     $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_ASSOC));
       
   376     foreach ($result as $record) {
       
   377       $records[] = $record;
       
   378       if ($this->assertTrue(is_array($record), 'Record is an array.')) {
       
   379         $this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.');
       
   380       }
       
   381     }
       
   382 
       
   383     $this->assertIdentical(count($records), 1, 'There is only one record.');
       
   384   }
       
   385 
       
   386   /**
       
   387    * Confirm that we can fetch a record into a new instance of a custom class.
       
   388    *
       
   389    * @see FakeRecord
       
   390    */
       
   391   function testQueryFetchClass() {
       
   392     $records = array();
       
   393     $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => 'FakeRecord'));
       
   394     foreach ($result as $record) {
       
   395       $records[] = $record;
       
   396       if ($this->assertTrue($record instanceof FakeRecord, 'Record is an object of class FakeRecord.')) {
       
   397         $this->assertIdentical($record->name, 'John', '25 year old is John.');
       
   398       }
       
   399     }
       
   400 
       
   401     $this->assertIdentical(count($records), 1, 'There is only one record.');
       
   402   }
       
   403 }
       
   404 
       
   405 /**
       
   406  * Test fetch actions, part 2.
       
   407  *
       
   408  * We get timeout errors if we try to run too many tests at once.
       
   409  */
       
   410 class DatabaseFetch2TestCase extends DatabaseTestCase {
       
   411 
       
   412   public static function getInfo() {
       
   413     return array(
       
   414       'name' => 'Fetch tests, part 2',
       
   415       'description' => 'Test the Database system\'s various fetch capabilities.',
       
   416       'group' => 'Database',
       
   417     );
       
   418   }
       
   419 
       
   420   function setUp() {
       
   421     parent::setUp();
       
   422   }
       
   423 
       
   424   // Confirm that we can fetch a record into an indexed array explicitly.
       
   425   function testQueryFetchNum() {
       
   426     $records = array();
       
   427     $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_NUM));
       
   428     foreach ($result as $record) {
       
   429       $records[] = $record;
       
   430       if ($this->assertTrue(is_array($record), 'Record is an array.')) {
       
   431         $this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.');
       
   432       }
       
   433     }
       
   434 
       
   435     $this->assertIdentical(count($records), 1, 'There is only one record');
       
   436   }
       
   437 
       
   438   /**
       
   439    * Confirm that we can fetch a record into a doubly-keyed array explicitly.
       
   440    */
       
   441   function testQueryFetchBoth() {
       
   442     $records = array();
       
   443     $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_BOTH));
       
   444     foreach ($result as $record) {
       
   445       $records[] = $record;
       
   446       if ($this->assertTrue(is_array($record), 'Record is an array.')) {
       
   447         $this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.');
       
   448         $this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.');
       
   449       }
       
   450     }
       
   451 
       
   452     $this->assertIdentical(count($records), 1, 'There is only one record.');
       
   453   }
       
   454 
       
   455   /**
       
   456    * Confirm that we can fetch an entire column of a result set at once.
       
   457    */
       
   458   function testQueryFetchCol() {
       
   459     $records = array();
       
   460     $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
       
   461     $column = $result->fetchCol();
       
   462     $this->assertIdentical(count($column), 3, 'fetchCol() returns the right number of records.');
       
   463 
       
   464     $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
       
   465     $i = 0;
       
   466     foreach ($result as $record) {
       
   467       $this->assertIdentical($record->name, $column[$i++], 'Column matches direct accesss.');
       
   468     }
       
   469   }
       
   470 }
       
   471 
       
   472 /**
       
   473  * Test the insert builder.
       
   474  */
       
   475 class DatabaseInsertTestCase extends DatabaseTestCase {
       
   476 
       
   477   public static function getInfo() {
       
   478     return array(
       
   479       'name' => 'Insert tests',
       
   480       'description' => 'Test the Insert query builder.',
       
   481       'group' => 'Database',
       
   482     );
       
   483   }
       
   484 
       
   485   /**
       
   486    * Test the very basic insert functionality.
       
   487    */
       
   488   function testSimpleInsert() {
       
   489     $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
   490 
       
   491     $query = db_insert('test');
       
   492     $query->fields(array(
       
   493       'name' => 'Yoko',
       
   494       'age' => '29',
       
   495     ));
       
   496     $query->execute();
       
   497 
       
   498     $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
   499     $this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
       
   500     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Yoko'))->fetchField();
       
   501     $this->assertIdentical($saved_age, '29', 'Can retrieve after inserting.');
       
   502   }
       
   503 
       
   504   /**
       
   505    * Test that we can insert multiple records in one query object.
       
   506    */
       
   507   function testMultiInsert() {
       
   508     $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
   509 
       
   510     $query = db_insert('test');
       
   511     $query->fields(array(
       
   512       'name' => 'Larry',
       
   513       'age' => '30',
       
   514     ));
       
   515 
       
   516     // We should be able to specify values in any order if named.
       
   517     $query->values(array(
       
   518       'age' => '31',
       
   519       'name' => 'Curly',
       
   520     ));
       
   521 
       
   522     // We should be able to say "use the field order".
       
   523     // This is not the recommended mechanism for most cases, but it should work.
       
   524     $query->values(array('Moe', '32'));
       
   525     $query->execute();
       
   526 
       
   527     $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
   528     $this->assertIdentical($num_records_before + 3, $num_records_after, 'Record inserts correctly.');
       
   529     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
       
   530     $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
       
   531     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
       
   532     $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
       
   533     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
       
   534     $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
       
   535   }
       
   536 
       
   537   /**
       
   538    * Test that an insert object can be reused with new data after it executes.
       
   539    */
       
   540   function testRepeatedInsert() {
       
   541     $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
   542 
       
   543     $query = db_insert('test');
       
   544 
       
   545     $query->fields(array(
       
   546       'name' => 'Larry',
       
   547       'age' => '30',
       
   548     ));
       
   549     $query->execute();  // This should run the insert, but leave the fields intact.
       
   550 
       
   551     // We should be able to specify values in any order if named.
       
   552     $query->values(array(
       
   553       'age' => '31',
       
   554       'name' => 'Curly',
       
   555     ));
       
   556     $query->execute();
       
   557 
       
   558     // We should be able to say "use the field order".
       
   559     $query->values(array('Moe', '32'));
       
   560     $query->execute();
       
   561 
       
   562     $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
   563     $this->assertIdentical((int) $num_records_before + 3, (int) $num_records_after, 'Record inserts correctly.');
       
   564     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
       
   565     $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
       
   566     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
       
   567     $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
       
   568     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
       
   569     $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
       
   570   }
       
   571 
       
   572   /**
       
   573    * Test that we can specify fields without values and specify values later.
       
   574    */
       
   575   function testInsertFieldOnlyDefinintion() {
       
   576     // This is useful for importers, when we want to create a query and define
       
   577     // its fields once, then loop over a multi-insert execution.
       
   578     db_insert('test')
       
   579       ->fields(array('name', 'age'))
       
   580       ->values(array('Larry', '30'))
       
   581       ->values(array('Curly', '31'))
       
   582       ->values(array('Moe', '32'))
       
   583       ->execute();
       
   584     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
       
   585     $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
       
   586     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
       
   587     $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
       
   588     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
       
   589     $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
       
   590   }
       
   591 
       
   592   /**
       
   593    * Test that inserts return the proper auto-increment ID.
       
   594    */
       
   595   function testInsertLastInsertID() {
       
   596     $id = db_insert('test')
       
   597       ->fields(array(
       
   598         'name' => 'Larry',
       
   599         'age' => '30',
       
   600       ))
       
   601       ->execute();
       
   602 
       
   603     $this->assertIdentical($id, '5', 'Auto-increment ID returned successfully.');
       
   604   }
       
   605 
       
   606   /**
       
   607    * Test that the INSERT INTO ... SELECT (fields) ... syntax works.
       
   608    */
       
   609   function testInsertSelectFields() {
       
   610     $query = db_select('test_people', 'tp');
       
   611     // The query builder will always append expressions after fields.
       
   612     // Add the expression first to test that the insert fields are correctly
       
   613     // re-ordered.
       
   614     $query->addExpression('tp.age', 'age');
       
   615     $query
       
   616       ->fields('tp', array('name','job'))
       
   617       ->condition('tp.name', 'Meredith');
       
   618 
       
   619     // The resulting query should be equivalent to:
       
   620     // INSERT INTO test (age, name, job)
       
   621     // SELECT tp.age AS age, tp.name AS name, tp.job AS job
       
   622     // FROM test_people tp
       
   623     // WHERE tp.name = 'Meredith'
       
   624     db_insert('test')
       
   625       ->from($query)
       
   626       ->execute();
       
   627 
       
   628     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();
       
   629     $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
       
   630   }
       
   631 
       
   632   /**
       
   633    * Tests that the INSERT INTO ... SELECT * ... syntax works.
       
   634    */
       
   635   function testInsertSelectAll() {
       
   636     $query = db_select('test_people', 'tp')
       
   637       ->fields('tp')
       
   638       ->condition('tp.name', 'Meredith');
       
   639 
       
   640     // The resulting query should be equivalent to:
       
   641     // INSERT INTO test_people_copy
       
   642     // SELECT *
       
   643     // FROM test_people tp
       
   644     // WHERE tp.name = 'Meredith'
       
   645     db_insert('test_people_copy')
       
   646       ->from($query)
       
   647       ->execute();
       
   648 
       
   649     $saved_age = db_query('SELECT age FROM {test_people_copy} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();
       
   650     $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
       
   651   }
       
   652 }
       
   653 
       
   654 /**
       
   655  * Insert tests using LOB fields, which are weird on some databases.
       
   656  */
       
   657 class DatabaseInsertLOBTestCase extends DatabaseTestCase {
       
   658 
       
   659   public static function getInfo() {
       
   660     return array(
       
   661       'name' => 'Insert tests, LOB fields',
       
   662       'description' => 'Test the Insert query builder with LOB fields.',
       
   663       'group' => 'Database',
       
   664     );
       
   665   }
       
   666 
       
   667   /**
       
   668    * Test that we can insert a single blob field successfully.
       
   669    */
       
   670   function testInsertOneBlob() {
       
   671     $data = "This is\000a test.";
       
   672     $this->assertTrue(strlen($data) === 15, 'Test data contains a NULL.');
       
   673     $id = db_insert('test_one_blob')
       
   674       ->fields(array('blob1' => $data))
       
   675       ->execute();
       
   676     $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
       
   677     $this->assertTrue($r['blob1'] === $data, format_string('Can insert a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
       
   678   }
       
   679 
       
   680   /**
       
   681    * Test that we can insert multiple blob fields in the same query.
       
   682    */
       
   683   function testInsertMultipleBlob() {
       
   684     $id = db_insert('test_two_blobs')
       
   685       ->fields(array(
       
   686         'blob1' => 'This is',
       
   687         'blob2' => 'a test',
       
   688       ))
       
   689       ->execute();
       
   690     $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
       
   691     $this->assertTrue($r['blob1'] === 'This is' && $r['blob2'] === 'a test', 'Can insert multiple blobs per row.');
       
   692   }
       
   693 }
       
   694 
       
   695 /**
       
   696  * Insert tests for "database default" values.
       
   697  */
       
   698 class DatabaseInsertDefaultsTestCase extends DatabaseTestCase {
       
   699 
       
   700   public static function getInfo() {
       
   701     return array(
       
   702       'name' => 'Insert tests, default fields',
       
   703       'description' => 'Test the Insert query builder with default values.',
       
   704       'group' => 'Database',
       
   705     );
       
   706   }
       
   707 
       
   708   /**
       
   709    * Test that we can run a query that is "default values for everything".
       
   710    */
       
   711   function testDefaultInsert() {
       
   712     $query = db_insert('test')->useDefaults(array('job'));
       
   713     $id = $query->execute();
       
   714 
       
   715     $schema = drupal_get_schema('test');
       
   716 
       
   717     $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
       
   718     $this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.');
       
   719   }
       
   720 
       
   721   /**
       
   722    * Test that no action will be preformed if no fields are specified.
       
   723    */
       
   724   function testDefaultEmptyInsert() {
       
   725     $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
   726 
       
   727     try {
       
   728       $result = db_insert('test')->execute();
       
   729       // This is only executed if no exception has been thrown.
       
   730       $this->fail('Expected exception NoFieldsException has not been thrown.');
       
   731     } catch (NoFieldsException $e) {
       
   732       $this->pass('Expected exception NoFieldsException has been thrown.');
       
   733     }
       
   734 
       
   735     $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
   736     $this->assertIdentical($num_records_before, $num_records_after, 'Do nothing as no fields are specified.');
       
   737   }
       
   738 
       
   739   /**
       
   740    * Test that we can insert fields with values and defaults in the same query.
       
   741    */
       
   742   function testDefaultInsertWithFields() {
       
   743     $query = db_insert('test')
       
   744       ->fields(array('name' => 'Bob'))
       
   745       ->useDefaults(array('job'));
       
   746     $id = $query->execute();
       
   747 
       
   748     $schema = drupal_get_schema('test');
       
   749 
       
   750     $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
       
   751     $this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.');
       
   752   }
       
   753 }
       
   754 
       
   755 /**
       
   756  * Update builder tests.
       
   757  */
       
   758 class DatabaseUpdateTestCase extends DatabaseTestCase {
       
   759 
       
   760   public static function getInfo() {
       
   761     return array(
       
   762       'name' => 'Update tests',
       
   763       'description' => 'Test the Update query builder.',
       
   764       'group' => 'Database',
       
   765     );
       
   766   }
       
   767 
       
   768   /**
       
   769    * Confirm that we can update a single record successfully.
       
   770    */
       
   771   function testSimpleUpdate() {
       
   772     $num_updated = db_update('test')
       
   773       ->fields(array('name' => 'Tiffany'))
       
   774       ->condition('id', 1)
       
   775       ->execute();
       
   776     $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
       
   777 
       
   778     $saved_name = db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 1))->fetchField();
       
   779     $this->assertIdentical($saved_name, 'Tiffany', 'Updated name successfully.');
       
   780   }
       
   781 
       
   782   /**
       
   783    * Confirm updating to NULL.
       
   784    */
       
   785   function testSimpleNullUpdate() {
       
   786     $this->ensureSampleDataNull();
       
   787     $num_updated = db_update('test_null')
       
   788       ->fields(array('age' => NULL))
       
   789       ->condition('name', 'Kermit')
       
   790       ->execute();
       
   791     $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
       
   792 
       
   793     $saved_age = db_query('SELECT age FROM {test_null} WHERE name = :name', array(':name' => 'Kermit'))->fetchField();
       
   794     $this->assertNull($saved_age, 'Updated name successfully.');
       
   795   }
       
   796 
       
   797   /**
       
   798    * Confirm that we can update a multiple records successfully.
       
   799    */
       
   800   function testMultiUpdate() {
       
   801     $num_updated = db_update('test')
       
   802       ->fields(array('job' => 'Musician'))
       
   803       ->condition('job', 'Singer')
       
   804       ->execute();
       
   805     $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
       
   806 
       
   807     $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
       
   808     $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
       
   809   }
       
   810 
       
   811   /**
       
   812    * Confirm that we can update a multiple records with a non-equality condition.
       
   813    */
       
   814   function testMultiGTUpdate() {
       
   815     $num_updated = db_update('test')
       
   816       ->fields(array('job' => 'Musician'))
       
   817       ->condition('age', 26, '>')
       
   818       ->execute();
       
   819     $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
       
   820 
       
   821     $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
       
   822     $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
       
   823   }
       
   824 
       
   825   /**
       
   826    * Confirm that we can update a multiple records with a where call.
       
   827    */
       
   828   function testWhereUpdate() {
       
   829     $num_updated = db_update('test')
       
   830       ->fields(array('job' => 'Musician'))
       
   831       ->where('age > :age', array(':age' => 26))
       
   832       ->execute();
       
   833     $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
       
   834 
       
   835     $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
       
   836     $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
       
   837   }
       
   838 
       
   839   /**
       
   840    * Confirm that we can stack condition and where calls.
       
   841    */
       
   842   function testWhereAndConditionUpdate() {
       
   843     $update = db_update('test')
       
   844       ->fields(array('job' => 'Musician'))
       
   845       ->where('age > :age', array(':age' => 26))
       
   846       ->condition('name', 'Ringo');
       
   847     $num_updated = $update->execute();
       
   848     $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
       
   849 
       
   850     $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
       
   851     $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
       
   852   }
       
   853 
       
   854   /**
       
   855    * Test updating with expressions.
       
   856    */
       
   857   function testExpressionUpdate() {
       
   858     // Set age = 1 for a single row for this test to work.
       
   859     db_update('test')
       
   860       ->condition('id', 1)
       
   861       ->fields(array('age' => 1))
       
   862       ->execute();
       
   863 
       
   864     // Ensure that expressions are handled properly.  This should set every
       
   865     // record's age to a square of itself, which will change only three of the
       
   866     // four records in the table since 1*1 = 1. That means only three records
       
   867     // are modified, so we should get back 3, not 4, from execute().
       
   868     $num_rows = db_update('test')
       
   869       ->expression('age', 'age * age')
       
   870       ->execute();
       
   871     $this->assertIdentical($num_rows, 3, 'Number of affected rows are returned.');
       
   872   }
       
   873 
       
   874   /**
       
   875    * Confirm that we can update the primary key of a record successfully.
       
   876    */
       
   877   function testPrimaryKeyUpdate() {
       
   878     $num_updated = db_update('test')
       
   879       ->fields(array('id' => 42, 'name' => 'John'))
       
   880       ->condition('id', 1)
       
   881       ->execute();
       
   882     $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
       
   883 
       
   884     $saved_name= db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 42))->fetchField();
       
   885     $this->assertIdentical($saved_name, 'John', 'Updated primary key successfully.');
       
   886   }
       
   887 }
       
   888 
       
   889 /**
       
   890  * Tests for more complex update statements.
       
   891  */
       
   892 class DatabaseUpdateComplexTestCase extends DatabaseTestCase {
       
   893 
       
   894   public static function getInfo() {
       
   895     return array(
       
   896       'name' => 'Update tests, Complex',
       
   897       'description' => 'Test the Update query builder, complex queries.',
       
   898       'group' => 'Database',
       
   899     );
       
   900   }
       
   901 
       
   902   /**
       
   903    * Test updates with OR conditionals.
       
   904    */
       
   905   function testOrConditionUpdate() {
       
   906     $update = db_update('test')
       
   907       ->fields(array('job' => 'Musician'))
       
   908       ->condition(db_or()
       
   909         ->condition('name', 'John')
       
   910         ->condition('name', 'Paul')
       
   911       );
       
   912     $num_updated = $update->execute();
       
   913     $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
       
   914 
       
   915     $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
       
   916     $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
       
   917   }
       
   918 
       
   919   /**
       
   920    * Test WHERE IN clauses.
       
   921    */
       
   922   function testInConditionUpdate() {
       
   923     $num_updated = db_update('test')
       
   924       ->fields(array('job' => 'Musician'))
       
   925       ->condition('name', array('John', 'Paul'), 'IN')
       
   926       ->execute();
       
   927     $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
       
   928 
       
   929     $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
       
   930     $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
       
   931   }
       
   932 
       
   933   /**
       
   934    * Test WHERE NOT IN clauses.
       
   935    */
       
   936   function testNotInConditionUpdate() {
       
   937     // The o is lowercase in the 'NoT IN' operator, to make sure the operators
       
   938     // work in mixed case.
       
   939     $num_updated = db_update('test')
       
   940       ->fields(array('job' => 'Musician'))
       
   941       ->condition('name', array('John', 'Paul', 'George'), 'NoT IN')
       
   942       ->execute();
       
   943     $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
       
   944 
       
   945     $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
       
   946     $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
       
   947   }
       
   948 
       
   949   /**
       
   950    * Test BETWEEN conditional clauses.
       
   951    */
       
   952   function testBetweenConditionUpdate() {
       
   953     $num_updated = db_update('test')
       
   954       ->fields(array('job' => 'Musician'))
       
   955       ->condition('age', array(25, 26), 'BETWEEN')
       
   956       ->execute();
       
   957     $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
       
   958 
       
   959     $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
       
   960     $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
       
   961   }
       
   962 
       
   963   /**
       
   964    * Test LIKE conditionals.
       
   965    */
       
   966   function testLikeConditionUpdate() {
       
   967     $num_updated = db_update('test')
       
   968       ->fields(array('job' => 'Musician'))
       
   969       ->condition('name', '%ge%', 'LIKE')
       
   970       ->execute();
       
   971     $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
       
   972 
       
   973     $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
       
   974     $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
       
   975   }
       
   976 
       
   977   /**
       
   978    * Test update with expression values.
       
   979    */
       
   980   function testUpdateExpression() {
       
   981     $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
       
   982     $GLOBALS['larry_test'] = 1;
       
   983     $num_updated = db_update('test')
       
   984       ->condition('name', 'Ringo')
       
   985       ->fields(array('job' => 'Musician'))
       
   986       ->expression('age', 'age + :age', array(':age' => 4))
       
   987       ->execute();
       
   988     $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
       
   989 
       
   990     $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
       
   991     $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
       
   992 
       
   993     $person = db_query('SELECT * FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetch();
       
   994     $this->assertEqual($person->name, 'Ringo', 'Name set correctly.');
       
   995     $this->assertEqual($person->age, $before_age + 4, 'Age set correctly.');
       
   996     $this->assertEqual($person->job, 'Musician', 'Job set correctly.');
       
   997     $GLOBALS['larry_test'] = 0;
       
   998   }
       
   999 
       
  1000   /**
       
  1001    * Test update with only expression values.
       
  1002    */
       
  1003   function testUpdateOnlyExpression() {
       
  1004     $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
       
  1005     $num_updated = db_update('test')
       
  1006       ->condition('name', 'Ringo')
       
  1007       ->expression('age', 'age + :age', array(':age' => 4))
       
  1008       ->execute();
       
  1009     $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
       
  1010 
       
  1011     $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
       
  1012     $this->assertEqual($before_age + 4, $after_age, 'Age updated correctly');
       
  1013   }
       
  1014 }
       
  1015 
       
  1016 /**
       
  1017  * Test update queries involving LOB values.
       
  1018  */
       
  1019 class DatabaseUpdateLOBTestCase extends DatabaseTestCase {
       
  1020 
       
  1021   public static function getInfo() {
       
  1022     return array(
       
  1023       'name' => 'Update tests, LOB',
       
  1024       'description' => 'Test the Update query builder with LOB fields.',
       
  1025       'group' => 'Database',
       
  1026     );
       
  1027   }
       
  1028 
       
  1029   /**
       
  1030    * Confirm that we can update a blob column.
       
  1031    */
       
  1032   function testUpdateOneBlob() {
       
  1033     $data = "This is\000a test.";
       
  1034     $this->assertTrue(strlen($data) === 15, 'Test data contains a NULL.');
       
  1035     $id = db_insert('test_one_blob')
       
  1036       ->fields(array('blob1' => $data))
       
  1037       ->execute();
       
  1038 
       
  1039     $data .= $data;
       
  1040     db_update('test_one_blob')
       
  1041       ->condition('id', $id)
       
  1042       ->fields(array('blob1' => $data))
       
  1043       ->execute();
       
  1044 
       
  1045     $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
       
  1046     $this->assertTrue($r['blob1'] === $data, format_string('Can update a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
       
  1047   }
       
  1048 
       
  1049   /**
       
  1050    * Confirm that we can update two blob columns in the same table.
       
  1051    */
       
  1052   function testUpdateMultipleBlob() {
       
  1053     $id = db_insert('test_two_blobs')
       
  1054       ->fields(array(
       
  1055         'blob1' => 'This is',
       
  1056         'blob2' => 'a test',
       
  1057       ))
       
  1058       ->execute();
       
  1059 
       
  1060     db_update('test_two_blobs')
       
  1061       ->condition('id', $id)
       
  1062       ->fields(array('blob1' => 'and so', 'blob2' => 'is this'))
       
  1063       ->execute();
       
  1064 
       
  1065     $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
       
  1066     $this->assertTrue($r['blob1'] === 'and so' && $r['blob2'] === 'is this', 'Can update multiple blobs per row.');
       
  1067   }
       
  1068 }
       
  1069 
       
  1070 /**
       
  1071  * Delete/Truncate tests.
       
  1072  *
       
  1073  * The DELETE tests are not as extensive, as all of the interesting code for
       
  1074  * DELETE queries is in the conditional which is identical to the UPDATE and
       
  1075  * SELECT conditional handling.
       
  1076  *
       
  1077  * The TRUNCATE tests are not extensive either, because the behavior of
       
  1078  * TRUNCATE queries is not consistent across database engines. We only test
       
  1079  * that a TRUNCATE query actually deletes all rows from the target table.
       
  1080  */
       
  1081 class DatabaseDeleteTruncateTestCase extends DatabaseTestCase {
       
  1082 
       
  1083   public static function getInfo() {
       
  1084     return array(
       
  1085       'name' => 'Delete/Truncate tests',
       
  1086       'description' => 'Test the Delete and Truncate query builders.',
       
  1087       'group' => 'Database',
       
  1088     );
       
  1089   }
       
  1090 
       
  1091   /**
       
  1092    * Confirm that we can use a subselect in a delete successfully.
       
  1093    */
       
  1094   function testSubselectDelete() {
       
  1095     $num_records_before = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
       
  1096     $pid_to_delete = db_query("SELECT * FROM {test_task} WHERE task = 'sleep'")->fetchField();
       
  1097 
       
  1098     $subquery = db_select('test', 't')
       
  1099       ->fields('t', array('id'))
       
  1100       ->condition('t.id', array($pid_to_delete), 'IN');
       
  1101     $delete = db_delete('test_task')
       
  1102       ->condition('task', 'sleep')
       
  1103       ->condition('pid', $subquery, 'IN');
       
  1104 
       
  1105     $num_deleted = $delete->execute();
       
  1106     $this->assertEqual($num_deleted, 1, "Deleted 1 record.");
       
  1107 
       
  1108     $num_records_after = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
       
  1109     $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.');
       
  1110   }
       
  1111 
       
  1112   /**
       
  1113    * Confirm that we can delete a single record successfully.
       
  1114    */
       
  1115   function testSimpleDelete() {
       
  1116     $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
  1117 
       
  1118     $num_deleted = db_delete('test')
       
  1119       ->condition('id', 1)
       
  1120       ->execute();
       
  1121     $this->assertIdentical($num_deleted, 1, 'Deleted 1 record.');
       
  1122 
       
  1123     $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
  1124     $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.');
       
  1125   }
       
  1126 
       
  1127   /**
       
  1128    * Confirm that we can truncate a whole table successfully.
       
  1129    */
       
  1130   function testTruncate() {
       
  1131     $num_records_before = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
       
  1132 
       
  1133     db_truncate('test')->execute();
       
  1134 
       
  1135     $num_records_after = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
       
  1136     $this->assertEqual(0, $num_records_after, 'Truncate really deletes everything.');
       
  1137   }
       
  1138 }
       
  1139 
       
  1140 /**
       
  1141  * Test the MERGE query builder.
       
  1142  */
       
  1143 class DatabaseMergeTestCase extends DatabaseTestCase {
       
  1144 
       
  1145   public static function getInfo() {
       
  1146     return array(
       
  1147       'name' => 'Merge tests',
       
  1148       'description' => 'Test the Merge query builder.',
       
  1149       'group' => 'Database',
       
  1150     );
       
  1151   }
       
  1152 
       
  1153   /**
       
  1154    * Confirm that we can merge-insert a record successfully.
       
  1155    */
       
  1156   function testMergeInsert() {
       
  1157     $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1158 
       
  1159     $result = db_merge('test_people')
       
  1160       ->key(array('job' => 'Presenter'))
       
  1161       ->fields(array(
       
  1162         'age' => 31,
       
  1163         'name' => 'Tiffany',
       
  1164       ))
       
  1165       ->execute();
       
  1166 
       
  1167     $this->assertEqual($result, MergeQuery::STATUS_INSERT, 'Insert status returned.');
       
  1168 
       
  1169     $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1170     $this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.');
       
  1171 
       
  1172     $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
       
  1173     $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
       
  1174     $this->assertEqual($person->age, 31, 'Age set correctly.');
       
  1175     $this->assertEqual($person->job, 'Presenter', 'Job set correctly.');
       
  1176   }
       
  1177 
       
  1178   /**
       
  1179    * Confirm that we can merge-update a record successfully.
       
  1180    */
       
  1181   function testMergeUpdate() {
       
  1182     $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1183 
       
  1184     $result = db_merge('test_people')
       
  1185       ->key(array('job' => 'Speaker'))
       
  1186       ->fields(array(
       
  1187         'age' => 31,
       
  1188         'name' => 'Tiffany',
       
  1189       ))
       
  1190       ->execute();
       
  1191 
       
  1192     $this->assertEqual($result, MergeQuery::STATUS_UPDATE, 'Update status returned.');
       
  1193 
       
  1194     $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1195     $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
       
  1196 
       
  1197     $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
       
  1198     $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
       
  1199     $this->assertEqual($person->age, 31, 'Age set correctly.');
       
  1200     $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
       
  1201   }
       
  1202 
       
  1203   /**
       
  1204    * Confirm that we can merge-update a record successfully, with different insert and update.
       
  1205    */
       
  1206   function testMergeUpdateExcept() {
       
  1207     $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1208 
       
  1209     db_merge('test_people')
       
  1210       ->key(array('job' => 'Speaker'))
       
  1211       ->insertFields(array('age' => 31))
       
  1212       ->updateFields(array('name' => 'Tiffany'))
       
  1213       ->execute();
       
  1214 
       
  1215     $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1216     $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
       
  1217 
       
  1218     $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
       
  1219     $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
       
  1220     $this->assertEqual($person->age, 30, 'Age skipped correctly.');
       
  1221     $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
       
  1222   }
       
  1223 
       
  1224   /**
       
  1225    * Confirm that we can merge-update a record successfully, with alternate replacement.
       
  1226    */
       
  1227   function testMergeUpdateExplicit() {
       
  1228     $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1229 
       
  1230     db_merge('test_people')
       
  1231       ->key(array('job' => 'Speaker'))
       
  1232       ->insertFields(array(
       
  1233         'age' => 31,
       
  1234         'name' => 'Tiffany',
       
  1235       ))
       
  1236       ->updateFields(array(
       
  1237         'name' => 'Joe',
       
  1238       ))
       
  1239       ->execute();
       
  1240 
       
  1241     $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1242     $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
       
  1243 
       
  1244     $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
       
  1245     $this->assertEqual($person->name, 'Joe', 'Name set correctly.');
       
  1246     $this->assertEqual($person->age, 30, 'Age skipped correctly.');
       
  1247     $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
       
  1248   }
       
  1249 
       
  1250   /**
       
  1251    * Confirm that we can merge-update a record successfully, with expressions.
       
  1252    */
       
  1253   function testMergeUpdateExpression() {
       
  1254     $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1255 
       
  1256     $age_before = db_query('SELECT age FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetchField();
       
  1257 
       
  1258     // This is a very contrived example, as I have no idea why you'd want to
       
  1259     // change age this way, but that's beside the point.
       
  1260     // Note that we are also double-setting age here, once as a literal and
       
  1261     // once as an expression. This test will only pass if the expression wins,
       
  1262     // which is what is supposed to happen.
       
  1263     db_merge('test_people')
       
  1264       ->key(array('job' => 'Speaker'))
       
  1265       ->fields(array('name' => 'Tiffany'))
       
  1266       ->insertFields(array('age' => 31))
       
  1267       ->expression('age', 'age + :age', array(':age' => 4))
       
  1268       ->execute();
       
  1269 
       
  1270     $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1271     $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
       
  1272 
       
  1273     $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
       
  1274     $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
       
  1275     $this->assertEqual($person->age, $age_before + 4, 'Age updated correctly.');
       
  1276     $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
       
  1277   }
       
  1278 
       
  1279   /**
       
  1280    * Test that we can merge-insert without any update fields.
       
  1281    */
       
  1282   function testMergeInsertWithoutUpdate() {
       
  1283     $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1284 
       
  1285     db_merge('test_people')
       
  1286       ->key(array('job' => 'Presenter'))
       
  1287       ->execute();
       
  1288 
       
  1289     $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1290     $this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.');
       
  1291 
       
  1292     $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
       
  1293     $this->assertEqual($person->name, '', 'Name set correctly.');
       
  1294     $this->assertEqual($person->age, 0, 'Age set correctly.');
       
  1295     $this->assertEqual($person->job, 'Presenter', 'Job set correctly.');
       
  1296   }
       
  1297 
       
  1298   /**
       
  1299    * Confirm that we can merge-update without any update fields.
       
  1300    */
       
  1301   function testMergeUpdateWithoutUpdate() {
       
  1302     $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1303 
       
  1304     db_merge('test_people')
       
  1305       ->key(array('job' => 'Speaker'))
       
  1306       ->execute();
       
  1307 
       
  1308     $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1309     $this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.');
       
  1310 
       
  1311     $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
       
  1312     $this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.');
       
  1313     $this->assertEqual($person->age, 30, 'Age skipped correctly.');
       
  1314     $this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.');
       
  1315 
       
  1316     db_merge('test_people')
       
  1317       ->key(array('job' => 'Speaker'))
       
  1318       ->insertFields(array('age' => 31))
       
  1319       ->execute();
       
  1320 
       
  1321     $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
       
  1322     $this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.');
       
  1323 
       
  1324     $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
       
  1325     $this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.');
       
  1326     $this->assertEqual($person->age, 30, 'Age skipped correctly.');
       
  1327     $this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.');
       
  1328   }
       
  1329 
       
  1330   /**
       
  1331    * Test that an invalid merge query throws an exception like it is supposed to.
       
  1332    */
       
  1333   function testInvalidMerge() {
       
  1334     try {
       
  1335       // This query should die because there is no key field specified.
       
  1336       db_merge('test_people')
       
  1337         ->fields(array(
       
  1338           'age' => 31,
       
  1339           'name' => 'Tiffany',
       
  1340         ))
       
  1341         ->execute();
       
  1342     }
       
  1343     catch (InvalidMergeQueryException $e) {
       
  1344       $this->pass('InvalidMergeQueryException thrown for invalid query.');
       
  1345       return;
       
  1346     }
       
  1347     $this->fail('No InvalidMergeQueryException thrown');
       
  1348   }
       
  1349 }
       
  1350 
       
  1351 /**
       
  1352  * Test the SELECT builder.
       
  1353  */
       
  1354 class DatabaseSelectTestCase extends DatabaseTestCase {
       
  1355 
       
  1356   public static function getInfo() {
       
  1357     return array(
       
  1358       'name' => 'Select tests',
       
  1359       'description' => 'Test the Select query builder.',
       
  1360       'group' => 'Database',
       
  1361     );
       
  1362   }
       
  1363 
       
  1364   /**
       
  1365    * Test rudimentary SELECT statements.
       
  1366    */
       
  1367   function testSimpleSelect() {
       
  1368     $query = db_select('test');
       
  1369     $name_field = $query->addField('test', 'name');
       
  1370     $age_field = $query->addField('test', 'age', 'age');
       
  1371     $result = $query->execute();
       
  1372 
       
  1373     $num_records = 0;
       
  1374     foreach ($result as $record) {
       
  1375       $num_records++;
       
  1376     }
       
  1377 
       
  1378     $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
       
  1379   }
       
  1380 
       
  1381   /**
       
  1382    * Test rudimentary SELECT statement with a COMMENT.
       
  1383    */
       
  1384   function testSimpleComment() {
       
  1385     $query = db_select('test')->comment('Testing query comments');
       
  1386     $name_field = $query->addField('test', 'name');
       
  1387     $age_field = $query->addField('test', 'age', 'age');
       
  1388     $result = $query->execute();
       
  1389 
       
  1390     $num_records = 0;
       
  1391     foreach ($result as $record) {
       
  1392       $num_records++;
       
  1393     }
       
  1394 
       
  1395     $query = (string)$query;
       
  1396     $expected = "/* Testing query comments */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
       
  1397 
       
  1398     $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
       
  1399     $this->assertEqual($query, $expected, 'The flattened query contains the comment string.');
       
  1400   }
       
  1401 
       
  1402   /**
       
  1403    * Test query COMMENT system against vulnerabilities.
       
  1404    */
       
  1405   function testVulnerableComment() {
       
  1406     $query = db_select('test')->comment('Testing query comments */ SELECT nid FROM {node}; --');
       
  1407     $name_field = $query->addField('test', 'name');
       
  1408     $age_field = $query->addField('test', 'age', 'age');
       
  1409     $result = $query->execute();
       
  1410 
       
  1411     $num_records = 0;
       
  1412     foreach ($result as $record) {
       
  1413       $num_records++;
       
  1414     }
       
  1415 
       
  1416     $query = (string)$query;
       
  1417     $expected = "/* Testing query comments  * / SELECT nid FROM {node}; -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
       
  1418 
       
  1419     $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
       
  1420     $this->assertEqual($query, $expected, 'The flattened query contains the sanitised comment string.');
       
  1421 
       
  1422     $connection = Database::getConnection();
       
  1423     foreach ($this->makeCommentsProvider() as $test_set) {
       
  1424       list($expected, $comments) = $test_set;
       
  1425       $this->assertEqual($expected, $connection->makeComment($comments));
       
  1426     }
       
  1427   }
       
  1428 
       
  1429   /**
       
  1430    * Provides expected and input values for testVulnerableComment().
       
  1431    */
       
  1432   function makeCommentsProvider() {
       
  1433     return array(
       
  1434       array(
       
  1435         '/*  */ ',
       
  1436         array(''),
       
  1437       ),
       
  1438       // Try and close the comment early.
       
  1439       array(
       
  1440         '/* Exploit  * / DROP TABLE node; -- */ ',
       
  1441         array('Exploit */ DROP TABLE node; --'),
       
  1442       ),
       
  1443       // Variations on comment closing.
       
  1444       array(
       
  1445         '/* Exploit  * / * / DROP TABLE node; -- */ ',
       
  1446         array('Exploit */*/ DROP TABLE node; --'),
       
  1447       ),
       
  1448       array(
       
  1449         '/* Exploit  *  * // DROP TABLE node; -- */ ',
       
  1450         array('Exploit **// DROP TABLE node; --'),
       
  1451       ),
       
  1452       // Try closing the comment in the second string which is appended.
       
  1453       array(
       
  1454         '/* Exploit  * / DROP TABLE node; --; Another try  * / DROP TABLE node; -- */ ',
       
  1455         array('Exploit */ DROP TABLE node; --', 'Another try */ DROP TABLE node; --'),
       
  1456       ),
       
  1457     );
       
  1458   }
       
  1459 
       
  1460   /**
       
  1461    * Test basic conditionals on SELECT statements.
       
  1462    */
       
  1463   function testSimpleSelectConditional() {
       
  1464     $query = db_select('test');
       
  1465     $name_field = $query->addField('test', 'name');
       
  1466     $age_field = $query->addField('test', 'age', 'age');
       
  1467     $query->condition('age', 27);
       
  1468     $result = $query->execute();
       
  1469 
       
  1470     // Check that the aliases are being created the way we want.
       
  1471     $this->assertEqual($name_field, 'name', 'Name field alias is correct.');
       
  1472     $this->assertEqual($age_field, 'age', 'Age field alias is correct.');
       
  1473 
       
  1474     // Ensure that we got the right record.
       
  1475     $record = $result->fetch();
       
  1476     $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
       
  1477     $this->assertEqual($record->$age_field, 27, 'Fetched age is correct.');
       
  1478   }
       
  1479 
       
  1480   /**
       
  1481    * Test SELECT statements with expressions.
       
  1482    */
       
  1483   function testSimpleSelectExpression() {
       
  1484     $query = db_select('test');
       
  1485     $name_field = $query->addField('test', 'name');
       
  1486     $age_field = $query->addExpression("age*2", 'double_age');
       
  1487     $query->condition('age', 27);
       
  1488     $result = $query->execute();
       
  1489 
       
  1490     // Check that the aliases are being created the way we want.
       
  1491     $this->assertEqual($name_field, 'name', 'Name field alias is correct.');
       
  1492     $this->assertEqual($age_field, 'double_age', 'Age field alias is correct.');
       
  1493 
       
  1494     // Ensure that we got the right record.
       
  1495     $record = $result->fetch();
       
  1496     $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
       
  1497     $this->assertEqual($record->$age_field, 27*2, 'Fetched age expression is correct.');
       
  1498   }
       
  1499 
       
  1500   /**
       
  1501    * Test SELECT statements with multiple expressions.
       
  1502    */
       
  1503   function testSimpleSelectExpressionMultiple() {
       
  1504     $query = db_select('test');
       
  1505     $name_field = $query->addField('test', 'name');
       
  1506     $age_double_field = $query->addExpression("age*2");
       
  1507     $age_triple_field = $query->addExpression("age*3");
       
  1508     $query->condition('age', 27);
       
  1509     $result = $query->execute();
       
  1510 
       
  1511     // Check that the aliases are being created the way we want.
       
  1512     $this->assertEqual($age_double_field, 'expression', 'Double age field alias is correct.');
       
  1513     $this->assertEqual($age_triple_field, 'expression_2', 'Triple age field alias is correct.');
       
  1514 
       
  1515     // Ensure that we got the right record.
       
  1516     $record = $result->fetch();
       
  1517     $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
       
  1518     $this->assertEqual($record->$age_double_field, 27*2, 'Fetched double age expression is correct.');
       
  1519     $this->assertEqual($record->$age_triple_field, 27*3, 'Fetched triple age expression is correct.');
       
  1520   }
       
  1521 
       
  1522   /**
       
  1523    * Test adding multiple fields to a select statement at the same time.
       
  1524    */
       
  1525   function testSimpleSelectMultipleFields() {
       
  1526     $record = db_select('test')
       
  1527       ->fields('test', array('id', 'name', 'age', 'job'))
       
  1528       ->condition('age', 27)
       
  1529       ->execute()->fetchObject();
       
  1530 
       
  1531     // Check that all fields we asked for are present.
       
  1532     $this->assertNotNull($record->id, 'ID field is present.');
       
  1533     $this->assertNotNull($record->name, 'Name field is present.');
       
  1534     $this->assertNotNull($record->age, 'Age field is present.');
       
  1535     $this->assertNotNull($record->job, 'Job field is present.');
       
  1536 
       
  1537     // Ensure that we got the right record.
       
  1538     // Check that all fields we asked for are present.
       
  1539     $this->assertEqual($record->id, 2, 'ID field has the correct value.');
       
  1540     $this->assertEqual($record->name, 'George', 'Name field has the correct value.');
       
  1541     $this->assertEqual($record->age, 27, 'Age field has the correct value.');
       
  1542     $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.');
       
  1543   }
       
  1544 
       
  1545   /**
       
  1546    * Test adding all fields from a given table to a select statement.
       
  1547    */
       
  1548   function testSimpleSelectAllFields() {
       
  1549     $record = db_select('test')
       
  1550       ->fields('test')
       
  1551       ->condition('age', 27)
       
  1552       ->execute()->fetchObject();
       
  1553 
       
  1554     // Check that all fields we asked for are present.
       
  1555     $this->assertNotNull($record->id, 'ID field is present.');
       
  1556     $this->assertNotNull($record->name, 'Name field is present.');
       
  1557     $this->assertNotNull($record->age, 'Age field is present.');
       
  1558     $this->assertNotNull($record->job, 'Job field is present.');
       
  1559 
       
  1560     // Ensure that we got the right record.
       
  1561     // Check that all fields we asked for are present.
       
  1562     $this->assertEqual($record->id, 2, 'ID field has the correct value.');
       
  1563     $this->assertEqual($record->name, 'George', 'Name field has the correct value.');
       
  1564     $this->assertEqual($record->age, 27, 'Age field has the correct value.');
       
  1565     $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.');
       
  1566   }
       
  1567 
       
  1568   /**
       
  1569    * Test that we can find a record with a NULL value.
       
  1570    */
       
  1571   function testNullCondition() {
       
  1572     $this->ensureSampleDataNull();
       
  1573 
       
  1574     $names = db_select('test_null', 'tn')
       
  1575       ->fields('tn', array('name'))
       
  1576       ->isNull('age')
       
  1577       ->execute()->fetchCol();
       
  1578 
       
  1579     $this->assertEqual(count($names), 1, 'Correct number of records found with NULL age.');
       
  1580     $this->assertEqual($names[0], 'Fozzie', 'Correct record returned for NULL age.');
       
  1581   }
       
  1582 
       
  1583   /**
       
  1584    * Test that we can find a record without a NULL value.
       
  1585    */
       
  1586   function testNotNullCondition() {
       
  1587     $this->ensureSampleDataNull();
       
  1588 
       
  1589     $names = db_select('test_null', 'tn')
       
  1590       ->fields('tn', array('name'))
       
  1591       ->isNotNull('tn.age')
       
  1592       ->orderBy('name')
       
  1593       ->execute()->fetchCol();
       
  1594 
       
  1595     $this->assertEqual(count($names), 2, 'Correct number of records found withNOT NULL age.');
       
  1596     $this->assertEqual($names[0], 'Gonzo', 'Correct record returned for NOT NULL age.');
       
  1597     $this->assertEqual($names[1], 'Kermit', 'Correct record returned for NOT NULL age.');
       
  1598   }
       
  1599 
       
  1600   /**
       
  1601    * Test that we can UNION multiple Select queries together. This is
       
  1602    * semantically equal to UNION DISTINCT, so we don't explicity test that.
       
  1603    */
       
  1604   function testUnion() {
       
  1605     $query_1 = db_select('test', 't')
       
  1606       ->fields('t', array('name'))
       
  1607       ->condition('age', array(27, 28), 'IN');
       
  1608 
       
  1609     $query_2 = db_select('test', 't')
       
  1610       ->fields('t', array('name'))
       
  1611       ->condition('age', 28);
       
  1612 
       
  1613     $query_1->union($query_2);
       
  1614 
       
  1615     $names = $query_1->execute()->fetchCol();
       
  1616 
       
  1617     // Ensure we only get 2 records.
       
  1618     $this->assertEqual(count($names), 2, 'UNION correctly discarded duplicates.');
       
  1619 
       
  1620     $this->assertEqual($names[0], 'George', 'First query returned correct name.');
       
  1621     $this->assertEqual($names[1], 'Ringo', 'Second query returned correct name.');
       
  1622   }
       
  1623 
       
  1624   /**
       
  1625    * Test that we can UNION ALL multiple Select queries together.
       
  1626    */
       
  1627   function testUnionAll() {
       
  1628     $query_1 = db_select('test', 't')
       
  1629       ->fields('t', array('name'))
       
  1630       ->condition('age', array(27, 28), 'IN');
       
  1631 
       
  1632     $query_2 = db_select('test', 't')
       
  1633       ->fields('t', array('name'))
       
  1634       ->condition('age', 28);
       
  1635 
       
  1636     $query_1->union($query_2, 'ALL');
       
  1637 
       
  1638     $names = $query_1->execute()->fetchCol();
       
  1639 
       
  1640     // Ensure we get all 3 records.
       
  1641     $this->assertEqual(count($names), 3, 'UNION ALL correctly preserved duplicates.');
       
  1642 
       
  1643     $this->assertEqual($names[0], 'George', 'First query returned correct first name.');
       
  1644     $this->assertEqual($names[1], 'Ringo', 'Second query returned correct second name.');
       
  1645     $this->assertEqual($names[2], 'Ringo', 'Third query returned correct name.');
       
  1646   }
       
  1647 
       
  1648   /**
       
  1649    * Test that random ordering of queries works.
       
  1650    *
       
  1651    * We take the approach of testing the Drupal layer only, rather than trying
       
  1652    * to test that the database's random number generator actually produces
       
  1653    * random queries (which is very difficult to do without an unacceptable risk
       
  1654    * of the test failing by accident).
       
  1655    *
       
  1656    * Therefore, in this test we simply run the same query twice and assert that
       
  1657    * the two results are reordered versions of each other (as well as of the
       
  1658    * same query without the random ordering). It is reasonable to assume that
       
  1659    * if we run the same select query twice and the results are in a different
       
  1660    * order each time, the only way this could happen is if we have successfully
       
  1661    * triggered the database's random ordering functionality.
       
  1662    */
       
  1663   function testRandomOrder() {
       
  1664     // Use 52 items, so the chance that this test fails by accident will be the
       
  1665     // same as the chance that a deck of cards will come out in the same order
       
  1666     // after shuffling it (in other words, nearly impossible).
       
  1667     $number_of_items = 52;
       
  1668     while (db_query("SELECT MAX(id) FROM {test}")->fetchField() < $number_of_items) {
       
  1669       db_insert('test')->fields(array('name' => $this->randomName()))->execute();
       
  1670     }
       
  1671 
       
  1672     // First select the items in order and make sure we get an ordered list.
       
  1673     $expected_ids = range(1, $number_of_items);
       
  1674     $ordered_ids = db_select('test', 't')
       
  1675       ->fields('t', array('id'))
       
  1676       ->range(0, $number_of_items)
       
  1677       ->orderBy('id')
       
  1678       ->execute()
       
  1679       ->fetchCol();
       
  1680     $this->assertEqual($ordered_ids, $expected_ids, 'A query without random ordering returns IDs in the correct order.');
       
  1681 
       
  1682     // Now perform the same query, but instead choose a random ordering. We
       
  1683     // expect this to contain a differently ordered version of the original
       
  1684     // result.
       
  1685     $randomized_ids = db_select('test', 't')
       
  1686       ->fields('t', array('id'))
       
  1687       ->range(0, $number_of_items)
       
  1688       ->orderRandom()
       
  1689       ->execute()
       
  1690       ->fetchCol();
       
  1691     $this->assertNotEqual($randomized_ids, $ordered_ids, 'A query with random ordering returns an unordered set of IDs.');
       
  1692     $sorted_ids = $randomized_ids;
       
  1693     sort($sorted_ids);
       
  1694     $this->assertEqual($sorted_ids, $ordered_ids, 'After sorting the random list, the result matches the original query.');
       
  1695 
       
  1696     // Now perform the exact same query again, and make sure the order is
       
  1697     // different.
       
  1698     $randomized_ids_second_set = db_select('test', 't')
       
  1699       ->fields('t', array('id'))
       
  1700       ->range(0, $number_of_items)
       
  1701       ->orderRandom()
       
  1702       ->execute()
       
  1703       ->fetchCol();
       
  1704     $this->assertNotEqual($randomized_ids_second_set, $randomized_ids, 'Performing the query with random ordering a second time returns IDs in a different order.');
       
  1705     $sorted_ids_second_set = $randomized_ids_second_set;
       
  1706     sort($sorted_ids_second_set);
       
  1707     $this->assertEqual($sorted_ids_second_set, $sorted_ids, 'After sorting the second random list, the result matches the sorted version of the first random list.');
       
  1708   }
       
  1709 
       
  1710   /**
       
  1711    * Test that aliases are renamed when duplicates.
       
  1712    */
       
  1713   function testSelectDuplicateAlias() {
       
  1714     $query = db_select('test', 't');
       
  1715     $alias1 = $query->addField('t', 'name', 'the_alias');
       
  1716     $alias2 = $query->addField('t', 'age', 'the_alias');
       
  1717     $this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.');
       
  1718   }
       
  1719 }
       
  1720 
       
  1721 /**
       
  1722  * Test case for subselects in a dynamic SELECT query.
       
  1723  */
       
  1724 class DatabaseSelectSubqueryTestCase extends DatabaseTestCase {
       
  1725 
       
  1726   public static function getInfo() {
       
  1727     return array(
       
  1728       'name' => 'Select tests, subqueries',
       
  1729       'description' => 'Test the Select query builder.',
       
  1730       'group' => 'Database',
       
  1731     );
       
  1732   }
       
  1733 
       
  1734   /**
       
  1735    * Test that we can use a subquery in a FROM clause.
       
  1736    */
       
  1737   function testFromSubquerySelect() {
       
  1738     // Create a subquery, which is just a normal query object.
       
  1739     $subquery = db_select('test_task', 'tt');
       
  1740     $subquery->addField('tt', 'pid', 'pid');
       
  1741     $subquery->addField('tt', 'task', 'task');
       
  1742     $subquery->condition('priority', 1);
       
  1743 
       
  1744     for ($i = 0; $i < 2; $i++) {
       
  1745       // Create another query that joins against the virtual table resulting
       
  1746       // from the subquery.
       
  1747       $select = db_select($subquery, 'tt2');
       
  1748       $select->join('test', 't', 't.id=tt2.pid');
       
  1749       $select->addField('t', 'name');
       
  1750       if ($i) {
       
  1751         // Use a different number of conditions here to confuse the subquery
       
  1752         // placeholder counter, testing http://drupal.org/node/1112854.
       
  1753         $select->condition('name', 'John');
       
  1754       }
       
  1755       $select->condition('task', 'code');
       
  1756 
       
  1757       // The resulting query should be equivalent to:
       
  1758       // SELECT t.name
       
  1759       // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt WHERE priority=1) tt
       
  1760       //   INNER JOIN test t ON t.id=tt.pid
       
  1761       // WHERE tt.task = 'code'
       
  1762       $people = $select->execute()->fetchCol();
       
  1763 
       
  1764       $this->assertEqual(count($people), 1, 'Returned the correct number of rows.');
       
  1765     }
       
  1766   }
       
  1767 
       
  1768   /**
       
  1769    * Test that we can use a subquery in a FROM clause with a limit.
       
  1770    */
       
  1771   function testFromSubquerySelectWithLimit() {
       
  1772     // Create a subquery, which is just a normal query object.
       
  1773     $subquery = db_select('test_task', 'tt');
       
  1774     $subquery->addField('tt', 'pid', 'pid');
       
  1775     $subquery->addField('tt', 'task', 'task');
       
  1776     $subquery->orderBy('priority', 'DESC');
       
  1777     $subquery->range(0, 1);
       
  1778 
       
  1779     // Create another query that joins against the virtual table resulting
       
  1780     // from the subquery.
       
  1781     $select = db_select($subquery, 'tt2');
       
  1782     $select->join('test', 't', 't.id=tt2.pid');
       
  1783     $select->addField('t', 'name');
       
  1784 
       
  1785     // The resulting query should be equivalent to:
       
  1786     // SELECT t.name
       
  1787     // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt ORDER BY priority DESC LIMIT 1 OFFSET 0) tt
       
  1788     //   INNER JOIN test t ON t.id=tt.pid
       
  1789     $people = $select->execute()->fetchCol();
       
  1790 
       
  1791     $this->assertEqual(count($people), 1, 'Returned the correct number of rows.');
       
  1792   }
       
  1793 
       
  1794   /**
       
  1795    * Test that we can use a subquery in a WHERE clause.
       
  1796    */
       
  1797   function testConditionSubquerySelect() {
       
  1798     // Create a subquery, which is just a normal query object.
       
  1799     $subquery = db_select('test_task', 'tt');
       
  1800     $subquery->addField('tt', 'pid', 'pid');
       
  1801     $subquery->condition('tt.priority', 1);
       
  1802 
       
  1803     // Create another query that joins against the virtual table resulting
       
  1804     // from the subquery.
       
  1805     $select = db_select('test_task', 'tt2');
       
  1806     $select->addField('tt2', 'task');
       
  1807     $select->condition('tt2.pid', $subquery, 'IN');
       
  1808 
       
  1809     // The resulting query should be equivalent to:
       
  1810     // SELECT tt2.name
       
  1811     // FROM test tt2
       
  1812     // WHERE tt2.pid IN (SELECT tt.pid AS pid FROM test_task tt WHERE tt.priority=1)
       
  1813     $people = $select->execute()->fetchCol();
       
  1814     $this->assertEqual(count($people), 5, 'Returned the correct number of rows.');
       
  1815   }
       
  1816 
       
  1817   /**
       
  1818    * Test that we can use a subquery in a JOIN clause.
       
  1819    */
       
  1820   function testJoinSubquerySelect() {
       
  1821     // Create a subquery, which is just a normal query object.
       
  1822     $subquery = db_select('test_task', 'tt');
       
  1823     $subquery->addField('tt', 'pid', 'pid');
       
  1824     $subquery->condition('priority', 1);
       
  1825 
       
  1826     // Create another query that joins against the virtual table resulting
       
  1827     // from the subquery.
       
  1828     $select = db_select('test', 't');
       
  1829     $select->join($subquery, 'tt', 't.id=tt.pid');
       
  1830     $select->addField('t', 'name');
       
  1831 
       
  1832     // The resulting query should be equivalent to:
       
  1833     // SELECT t.name
       
  1834     // FROM test t
       
  1835     //   INNER JOIN (SELECT tt.pid AS pid FROM test_task tt WHERE priority=1) tt ON t.id=tt.pid
       
  1836     $people = $select->execute()->fetchCol();
       
  1837 
       
  1838     $this->assertEqual(count($people), 2, 'Returned the correct number of rows.');
       
  1839   }
       
  1840 
       
  1841   /**
       
  1842    * Test EXISTS subquery conditionals on SELECT statements.
       
  1843    *
       
  1844    * We essentially select all rows from the {test} table that have matching
       
  1845    * rows in the {test_people} table based on the shared name column.
       
  1846    */
       
  1847   function testExistsSubquerySelect() {
       
  1848     // Put George into {test_people}.
       
  1849     db_insert('test_people')
       
  1850       ->fields(array(
       
  1851         'name' => 'George',
       
  1852         'age' => 27,
       
  1853         'job' => 'Singer',
       
  1854       ))
       
  1855       ->execute();
       
  1856     // Base query to {test}.
       
  1857     $query = db_select('test', 't')
       
  1858       ->fields('t', array('name'));
       
  1859     // Subquery to {test_people}.
       
  1860     $subquery = db_select('test_people', 'tp')
       
  1861       ->fields('tp', array('name'))
       
  1862       ->where('tp.name = t.name');
       
  1863     $query->exists($subquery);
       
  1864     $result = $query->execute();
       
  1865 
       
  1866     // Ensure that we got the right record.
       
  1867     $record = $result->fetch();
       
  1868     $this->assertEqual($record->name, 'George', 'Fetched name is correct using EXISTS query.');
       
  1869   }
       
  1870 
       
  1871   /**
       
  1872    * Test NOT EXISTS subquery conditionals on SELECT statements.
       
  1873    *
       
  1874    * We essentially select all rows from the {test} table that don't have
       
  1875    * matching rows in the {test_people} table based on the shared name column.
       
  1876    */
       
  1877   function testNotExistsSubquerySelect() {
       
  1878     // Put George into {test_people}.
       
  1879     db_insert('test_people')
       
  1880       ->fields(array(
       
  1881         'name' => 'George',
       
  1882         'age' => 27,
       
  1883         'job' => 'Singer',
       
  1884       ))
       
  1885       ->execute();
       
  1886 
       
  1887     // Base query to {test}.
       
  1888     $query = db_select('test', 't')
       
  1889       ->fields('t', array('name'));
       
  1890     // Subquery to {test_people}.
       
  1891     $subquery = db_select('test_people', 'tp')
       
  1892       ->fields('tp', array('name'))
       
  1893       ->where('tp.name = t.name');
       
  1894     $query->notExists($subquery);
       
  1895 
       
  1896     // Ensure that we got the right number of records.
       
  1897     $people = $query->execute()->fetchCol();
       
  1898     $this->assertEqual(count($people), 3, 'NOT EXISTS query returned the correct results.');
       
  1899   }
       
  1900 }
       
  1901 
       
  1902 /**
       
  1903  * Test select with order by clauses.
       
  1904  */
       
  1905 class DatabaseSelectOrderedTestCase extends DatabaseTestCase {
       
  1906 
       
  1907   public static function getInfo() {
       
  1908     return array(
       
  1909       'name' => 'Select tests, ordered',
       
  1910       'description' => 'Test the Select query builder.',
       
  1911       'group' => 'Database',
       
  1912     );
       
  1913   }
       
  1914 
       
  1915   /**
       
  1916    * Test basic order by.
       
  1917    */
       
  1918   function testSimpleSelectOrdered() {
       
  1919     $query = db_select('test');
       
  1920     $name_field = $query->addField('test', 'name');
       
  1921     $age_field = $query->addField('test', 'age', 'age');
       
  1922     $query->orderBy($age_field);
       
  1923     $result = $query->execute();
       
  1924 
       
  1925     $num_records = 0;
       
  1926     $last_age = 0;
       
  1927     foreach ($result as $record) {
       
  1928       $num_records++;
       
  1929       $this->assertTrue($record->age >= $last_age, 'Results returned in correct order.');
       
  1930       $last_age = $record->age;
       
  1931     }
       
  1932 
       
  1933     $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
       
  1934   }
       
  1935 
       
  1936   /**
       
  1937    * Test multiple order by.
       
  1938    */
       
  1939   function testSimpleSelectMultiOrdered() {
       
  1940     $query = db_select('test');
       
  1941     $name_field = $query->addField('test', 'name');
       
  1942     $age_field = $query->addField('test', 'age', 'age');
       
  1943     $job_field = $query->addField('test', 'job');
       
  1944     $query->orderBy($job_field);
       
  1945     $query->orderBy($age_field);
       
  1946     $result = $query->execute();
       
  1947 
       
  1948     $num_records = 0;
       
  1949     $expected = array(
       
  1950       array('Ringo', 28, 'Drummer'),
       
  1951       array('John', 25, 'Singer'),
       
  1952       array('George', 27, 'Singer'),
       
  1953       array('Paul', 26, 'Songwriter'),
       
  1954     );
       
  1955     $results = $result->fetchAll(PDO::FETCH_NUM);
       
  1956     foreach ($expected as $k => $record) {
       
  1957       $num_records++;
       
  1958       foreach ($record as $kk => $col) {
       
  1959         if ($expected[$k][$kk] != $results[$k][$kk]) {
       
  1960           $this->assertTrue(FALSE, 'Results returned in correct order.');
       
  1961         }
       
  1962       }
       
  1963     }
       
  1964     $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
       
  1965   }
       
  1966 
       
  1967   /**
       
  1968    * Test order by descending.
       
  1969    */
       
  1970   function testSimpleSelectOrderedDesc() {
       
  1971     $query = db_select('test');
       
  1972     $name_field = $query->addField('test', 'name');
       
  1973     $age_field = $query->addField('test', 'age', 'age');
       
  1974     $query->orderBy($age_field, 'DESC');
       
  1975     $result = $query->execute();
       
  1976 
       
  1977     $num_records = 0;
       
  1978     $last_age = 100000000;
       
  1979     foreach ($result as $record) {
       
  1980       $num_records++;
       
  1981       $this->assertTrue($record->age <= $last_age, 'Results returned in correct order.');
       
  1982       $last_age = $record->age;
       
  1983     }
       
  1984 
       
  1985     $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
       
  1986   }
       
  1987 
       
  1988   /**
       
  1989    * Tests that the sort direction is sanitized properly.
       
  1990    */
       
  1991   function testOrderByEscaping() {
       
  1992     $query = db_select('test')->orderBy('name', 'invalid direction');
       
  1993     $order_bys = $query->getOrderBy();
       
  1994     $this->assertEqual($order_bys['name'], 'ASC', 'Invalid order by direction is converted to ASC.');
       
  1995   }
       
  1996 }
       
  1997 
       
  1998 /**
       
  1999  * Test more complex select statements.
       
  2000  */
       
  2001 class DatabaseSelectComplexTestCase extends DatabaseTestCase {
       
  2002 
       
  2003   public static function getInfo() {
       
  2004     return array(
       
  2005       'name' => 'Select tests, complex',
       
  2006       'description' => 'Test the Select query builder with more complex queries.',
       
  2007       'group' => 'Database',
       
  2008     );
       
  2009   }
       
  2010 
       
  2011   /**
       
  2012    * Test simple JOIN statements.
       
  2013    */
       
  2014   function testDefaultJoin() {
       
  2015     $query = db_select('test_task', 't');
       
  2016     $people_alias = $query->join('test', 'p', 't.pid = p.id');
       
  2017     $name_field = $query->addField($people_alias, 'name', 'name');
       
  2018     $task_field = $query->addField('t', 'task', 'task');
       
  2019     $priority_field = $query->addField('t', 'priority', 'priority');
       
  2020 
       
  2021     $query->orderBy($priority_field);
       
  2022     $result = $query->execute();
       
  2023 
       
  2024     $num_records = 0;
       
  2025     $last_priority = 0;
       
  2026     foreach ($result as $record) {
       
  2027       $num_records++;
       
  2028       $this->assertTrue($record->$priority_field >= $last_priority, 'Results returned in correct order.');
       
  2029       $this->assertNotEqual($record->$name_field, 'Ringo', 'Taskless person not selected.');
       
  2030       $last_priority = $record->$priority_field;
       
  2031     }
       
  2032 
       
  2033     $this->assertEqual($num_records, 7, 'Returned the correct number of rows.');
       
  2034   }
       
  2035 
       
  2036   /**
       
  2037    * Test LEFT OUTER joins.
       
  2038    */
       
  2039   function testLeftOuterJoin() {
       
  2040     $query = db_select('test', 'p');
       
  2041     $people_alias = $query->leftJoin('test_task', 't', 't.pid = p.id');
       
  2042     $name_field = $query->addField('p', 'name', 'name');
       
  2043     $task_field = $query->addField($people_alias, 'task', 'task');
       
  2044     $priority_field = $query->addField($people_alias, 'priority', 'priority');
       
  2045 
       
  2046     $query->orderBy($name_field);
       
  2047     $result = $query->execute();
       
  2048 
       
  2049     $num_records = 0;
       
  2050     $last_name = 0;
       
  2051 
       
  2052     foreach ($result as $record) {
       
  2053       $num_records++;
       
  2054       $this->assertTrue(strcmp($record->$name_field, $last_name) >= 0, 'Results returned in correct order.');
       
  2055       $last_priority = $record->$name_field;
       
  2056     }
       
  2057 
       
  2058     $this->assertEqual($num_records, 8, 'Returned the correct number of rows.');
       
  2059   }
       
  2060 
       
  2061   /**
       
  2062    * Test GROUP BY clauses.
       
  2063    */
       
  2064   function testGroupBy() {
       
  2065     $query = db_select('test_task', 't');
       
  2066     $count_field = $query->addExpression('COUNT(task)', 'num');
       
  2067     $task_field = $query->addField('t', 'task');
       
  2068     $query->orderBy($count_field);
       
  2069     $query->groupBy($task_field);
       
  2070     $result = $query->execute();
       
  2071 
       
  2072     $num_records = 0;
       
  2073     $last_count = 0;
       
  2074     $records = array();
       
  2075     foreach ($result as $record) {
       
  2076       $num_records++;
       
  2077       $this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.');
       
  2078       $last_count = $record->$count_field;
       
  2079       $records[$record->$task_field] = $record->$count_field;
       
  2080     }
       
  2081 
       
  2082     $correct_results = array(
       
  2083       'eat' => 1,
       
  2084       'sleep' => 2,
       
  2085       'code' => 1,
       
  2086       'found new band' => 1,
       
  2087       'perform at superbowl' => 1,
       
  2088     );
       
  2089 
       
  2090     foreach ($correct_results as $task => $count) {
       
  2091       $this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task)));
       
  2092     }
       
  2093 
       
  2094     $this->assertEqual($num_records, 6, 'Returned the correct number of total rows.');
       
  2095   }
       
  2096 
       
  2097   /**
       
  2098    * Test GROUP BY and HAVING clauses together.
       
  2099    */
       
  2100   function testGroupByAndHaving() {
       
  2101     $query = db_select('test_task', 't');
       
  2102     $count_field = $query->addExpression('COUNT(task)', 'num');
       
  2103     $task_field = $query->addField('t', 'task');
       
  2104     $query->orderBy($count_field);
       
  2105     $query->groupBy($task_field);
       
  2106     $query->having('COUNT(task) >= 2');
       
  2107     $result = $query->execute();
       
  2108 
       
  2109     $num_records = 0;
       
  2110     $last_count = 0;
       
  2111     $records = array();
       
  2112     foreach ($result as $record) {
       
  2113       $num_records++;
       
  2114       $this->assertTrue($record->$count_field >= 2, 'Record has the minimum count.');
       
  2115       $this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.');
       
  2116       $last_count = $record->$count_field;
       
  2117       $records[$record->$task_field] = $record->$count_field;
       
  2118     }
       
  2119 
       
  2120     $correct_results = array(
       
  2121       'sleep' => 2,
       
  2122     );
       
  2123 
       
  2124     foreach ($correct_results as $task => $count) {
       
  2125       $this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task)));
       
  2126     }
       
  2127 
       
  2128     $this->assertEqual($num_records, 1, 'Returned the correct number of total rows.');
       
  2129   }
       
  2130 
       
  2131   /**
       
  2132    * Test range queries. The SQL clause varies with the database.
       
  2133    */
       
  2134   function testRange() {
       
  2135     $query = db_select('test');
       
  2136     $name_field = $query->addField('test', 'name');
       
  2137     $age_field = $query->addField('test', 'age', 'age');
       
  2138     $query->range(0, 2);
       
  2139     $result = $query->execute();
       
  2140 
       
  2141     $num_records = 0;
       
  2142     foreach ($result as $record) {
       
  2143       $num_records++;
       
  2144     }
       
  2145 
       
  2146     $this->assertEqual($num_records, 2, 'Returned the correct number of rows.');
       
  2147   }
       
  2148 
       
  2149   /**
       
  2150    * Test distinct queries.
       
  2151    */
       
  2152   function testDistinct() {
       
  2153     $query = db_select('test_task');
       
  2154     $task_field = $query->addField('test_task', 'task');
       
  2155     $query->distinct();
       
  2156     $result = $query->execute();
       
  2157 
       
  2158     $num_records = 0;
       
  2159     foreach ($result as $record) {
       
  2160       $num_records++;
       
  2161     }
       
  2162 
       
  2163     $this->assertEqual($num_records, 6, 'Returned the correct number of rows.');
       
  2164   }
       
  2165 
       
  2166   /**
       
  2167    * Test that we can generate a count query from a built query.
       
  2168    */
       
  2169   function testCountQuery() {
       
  2170     $query = db_select('test');
       
  2171     $name_field = $query->addField('test', 'name');
       
  2172     $age_field = $query->addField('test', 'age', 'age');
       
  2173     $query->orderBy('name');
       
  2174 
       
  2175     $count = $query->countQuery()->execute()->fetchField();
       
  2176 
       
  2177     $this->assertEqual($count, 4, 'Counted the correct number of records.');
       
  2178 
       
  2179     // Now make sure we didn't break the original query!  We should still have
       
  2180     // all of the fields we asked for.
       
  2181     $record = $query->execute()->fetch();
       
  2182     $this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.');
       
  2183     $this->assertEqual($record->$age_field, 27, 'Correct data retrieved.');
       
  2184   }
       
  2185 
       
  2186   function testHavingCountQuery() {
       
  2187     $query = db_select('test')
       
  2188       ->extend('PagerDefault')
       
  2189       ->groupBy('age')
       
  2190       ->having('age + 1 > 0');
       
  2191     $query->addField('test', 'age');
       
  2192     $query->addExpression('age + 1');
       
  2193     $count = count($query->execute()->fetchCol());
       
  2194     $this->assertEqual($count, 4, 'Counted the correct number of records.');
       
  2195   }
       
  2196 
       
  2197   /**
       
  2198    * Test that countQuery properly removes 'all_fields' statements and
       
  2199    * ordering clauses.
       
  2200    */
       
  2201   function testCountQueryRemovals() {
       
  2202     $query = db_select('test');
       
  2203     $query->fields('test');
       
  2204     $query->orderBy('name');
       
  2205     $count = $query->countQuery();
       
  2206 
       
  2207     // Check that the 'all_fields' statement is handled properly.
       
  2208     $tables = $query->getTables();
       
  2209     $this->assertEqual($tables['test']['all_fields'], 1, 'Query correctly sets \'all_fields\' statement.');
       
  2210     $tables = $count->getTables();
       
  2211     $this->assertFalse(isset($tables['test']['all_fields']), 'Count query correctly unsets \'all_fields\' statement.');
       
  2212 
       
  2213     // Check that the ordering clause is handled properly.
       
  2214     $orderby = $query->getOrderBy();
       
  2215     $this->assertEqual($orderby['name'], 'ASC', 'Query correctly sets ordering clause.');
       
  2216     $orderby = $count->getOrderBy();
       
  2217     $this->assertFalse(isset($orderby['name']), 'Count query correctly unsets ordering caluse.');
       
  2218 
       
  2219     // Make sure that the count query works.
       
  2220     $count = $count->execute()->fetchField();
       
  2221 
       
  2222     $this->assertEqual($count, 4, 'Counted the correct number of records.');
       
  2223   }
       
  2224 
       
  2225 
       
  2226   /**
       
  2227    * Test that countQuery properly removes fields and expressions.
       
  2228    */
       
  2229   function testCountQueryFieldRemovals() {
       
  2230     // countQuery should remove all fields and expressions, so this can be
       
  2231     // tested by adding a non-existent field and expression: if it ends
       
  2232     // up in the query, an error will be thrown. If not, it will return the
       
  2233     // number of records, which in this case happens to be 4 (there are four
       
  2234     // records in the {test} table).
       
  2235     $query = db_select('test');
       
  2236     $query->fields('test', array('fail'));
       
  2237     $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed fields');
       
  2238 
       
  2239     $query = db_select('test');
       
  2240     $query->addExpression('fail');
       
  2241     $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed expressions');
       
  2242   }
       
  2243 
       
  2244   /**
       
  2245    * Test that we can generate a count query from a query with distinct.
       
  2246    */
       
  2247   function testCountQueryDistinct() {
       
  2248     $query = db_select('test_task');
       
  2249     $task_field = $query->addField('test_task', 'task');
       
  2250     $query->distinct();
       
  2251 
       
  2252     $count = $query->countQuery()->execute()->fetchField();
       
  2253 
       
  2254     $this->assertEqual($count, 6, 'Counted the correct number of records.');
       
  2255   }
       
  2256 
       
  2257   /**
       
  2258    * Test that we can generate a count query from a query with GROUP BY.
       
  2259    */
       
  2260   function testCountQueryGroupBy() {
       
  2261     $query = db_select('test_task');
       
  2262     $pid_field = $query->addField('test_task', 'pid');
       
  2263     $query->groupBy('pid');
       
  2264 
       
  2265     $count = $query->countQuery()->execute()->fetchField();
       
  2266 
       
  2267     $this->assertEqual($count, 3, 'Counted the correct number of records.');
       
  2268 
       
  2269     // Use a column alias as, without one, the query can succeed for the wrong
       
  2270     // reason.
       
  2271     $query = db_select('test_task');
       
  2272     $pid_field = $query->addField('test_task', 'pid', 'pid_alias');
       
  2273     $query->addExpression('COUNT(test_task.task)', 'count');
       
  2274     $query->groupBy('pid_alias');
       
  2275     $query->orderBy('pid_alias', 'asc');
       
  2276 
       
  2277     $count = $query->countQuery()->execute()->fetchField();
       
  2278 
       
  2279     $this->assertEqual($count, 3, 'Counted the correct number of records.');
       
  2280   }
       
  2281 
       
  2282   /**
       
  2283    * Confirm that we can properly nest conditional clauses.
       
  2284    */
       
  2285   function testNestedConditions() {
       
  2286     // This query should translate to:
       
  2287     // "SELECT job FROM {test} WHERE name = 'Paul' AND (age = 26 OR age = 27)"
       
  2288     // That should find only one record. Yes it's a non-optimal way of writing
       
  2289     // that query but that's not the point!
       
  2290     $query = db_select('test');
       
  2291     $query->addField('test', 'job');
       
  2292     $query->condition('name', 'Paul');
       
  2293     $query->condition(db_or()->condition('age', 26)->condition('age', 27));
       
  2294 
       
  2295     $job = $query->execute()->fetchField();
       
  2296     $this->assertEqual($job, 'Songwriter', 'Correct data retrieved.');
       
  2297   }
       
  2298 
       
  2299   /**
       
  2300    * Confirm we can join on a single table twice with a dynamic alias.
       
  2301    */
       
  2302   function testJoinTwice() {
       
  2303     $query = db_select('test')->fields('test');
       
  2304     $alias = $query->join('test', 'test', 'test.job = %alias.job');
       
  2305     $query->addField($alias, 'name', 'othername');
       
  2306     $query->addField($alias, 'job', 'otherjob');
       
  2307     $query->where("$alias.name <> test.name");
       
  2308     $crowded_job = $query->execute()->fetch();
       
  2309     $this->assertEqual($crowded_job->job, $crowded_job->otherjob, 'Correctly joined same table twice.');
       
  2310     $this->assertNotEqual($crowded_job->name, $crowded_job->othername, 'Correctly joined same table twice.');
       
  2311   }
       
  2312 
       
  2313 }
       
  2314 
       
  2315 /**
       
  2316  * Test more complex select statements, part 2.
       
  2317  */
       
  2318 class DatabaseSelectComplexTestCase2 extends DatabaseTestCase {
       
  2319 
       
  2320   public static function getInfo() {
       
  2321     return array(
       
  2322       'name' => 'Select tests, complex 2',
       
  2323       'description' => 'Test the Select query builder with even more complex queries.',
       
  2324       'group' => 'Database',
       
  2325     );
       
  2326   }
       
  2327 
       
  2328   function setUp() {
       
  2329     DrupalWebTestCase::setUp('database_test', 'node_access_test');
       
  2330 
       
  2331     $schema['test'] = drupal_get_schema('test');
       
  2332     $schema['test_people'] = drupal_get_schema('test_people');
       
  2333     $schema['test_one_blob'] = drupal_get_schema('test_one_blob');
       
  2334     $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs');
       
  2335     $schema['test_task'] = drupal_get_schema('test_task');
       
  2336 
       
  2337     $this->installTables($schema);
       
  2338 
       
  2339     $this->addSampleData();
       
  2340   }
       
  2341 
       
  2342   /**
       
  2343    * Test that we can join on a query.
       
  2344    */
       
  2345   function testJoinSubquery() {
       
  2346     $acct = $this->drupalCreateUser(array('access content'));
       
  2347     $this->drupalLogin($acct);
       
  2348 
       
  2349     $query = db_select('test_task', 'tt', array('target' => 'slave'));
       
  2350     $query->addExpression('tt.pid + 1', 'abc');
       
  2351     $query->condition('priority', 1, '>');
       
  2352     $query->condition('priority', 100, '<');
       
  2353 
       
  2354     $subquery = db_select('test', 'tp');
       
  2355     $subquery->join('test_one_blob', 'tpb', 'tp.id = tpb.id');
       
  2356     $subquery->join('node', 'n', 'tp.id = n.nid');
       
  2357     $subquery->addTag('node_access');
       
  2358     $subquery->addMetaData('account', $acct);
       
  2359     $subquery->addField('tp', 'id');
       
  2360     $subquery->condition('age', 5, '>');
       
  2361     $subquery->condition('age', 500, '<');
       
  2362 
       
  2363     $query->leftJoin($subquery, 'sq', 'tt.pid = sq.id');
       
  2364     $query->join('test_one_blob', 'tb3', 'tt.pid = tb3.id');
       
  2365 
       
  2366     // Construct the query string.
       
  2367     // This is the same sequence that SelectQuery::execute() goes through.
       
  2368     $query->preExecute();
       
  2369     $query->getArguments();
       
  2370     $str = (string) $query;
       
  2371 
       
  2372     // Verify that the string only has one copy of condition placeholder 0.
       
  2373     $pos = strpos($str, 'db_condition_placeholder_0', 0);
       
  2374     $pos2 = strpos($str, 'db_condition_placeholder_0', $pos + 1);
       
  2375     $this->assertFalse($pos2, 'Condition placeholder is not repeated.');
       
  2376   }
       
  2377 }
       
  2378 
       
  2379 class DatabaseSelectPagerDefaultTestCase extends DatabaseTestCase {
       
  2380 
       
  2381   public static function getInfo() {
       
  2382     return array(
       
  2383       'name' => 'Pager query tests',
       
  2384       'description' => 'Test the pager query extender.',
       
  2385       'group' => 'Database',
       
  2386     );
       
  2387   }
       
  2388 
       
  2389   /**
       
  2390    * Confirm that a pager query returns the correct results.
       
  2391    *
       
  2392    * Note that we have to make an HTTP request to a test page handler
       
  2393    * because the pager depends on GET parameters.
       
  2394    */
       
  2395   function testEvenPagerQuery() {
       
  2396     // To keep the test from being too brittle, we determine up front
       
  2397     // what the page count should be dynamically, and pass the control
       
  2398     // information forward to the actual query on the other side of the
       
  2399     // HTTP request.
       
  2400     $limit = 2;
       
  2401     $count = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
  2402 
       
  2403     $correct_number = $limit;
       
  2404     $num_pages = floor($count / $limit);
       
  2405 
       
  2406     // If there is no remainder from rounding, subtract 1 since we index from 0.
       
  2407     if (!($num_pages * $limit < $count)) {
       
  2408       $num_pages--;
       
  2409     }
       
  2410 
       
  2411     for ($page = 0; $page <= $num_pages; ++$page) {
       
  2412       $this->drupalGet('database_test/pager_query_even/' . $limit, array('query' => array('page' => $page)));
       
  2413       $data = json_decode($this->drupalGetContent());
       
  2414 
       
  2415       if ($page == $num_pages) {
       
  2416         $correct_number = $count - ($limit * $page);
       
  2417       }
       
  2418 
       
  2419       $this->assertEqual(count($data->names), $correct_number, format_string('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
       
  2420     }
       
  2421   }
       
  2422 
       
  2423   /**
       
  2424    * Confirm that a pager query returns the correct results.
       
  2425    *
       
  2426    * Note that we have to make an HTTP request to a test page handler
       
  2427    * because the pager depends on GET parameters.
       
  2428    */
       
  2429   function testOddPagerQuery() {
       
  2430     // To keep the test from being too brittle, we determine up front
       
  2431     // what the page count should be dynamically, and pass the control
       
  2432     // information forward to the actual query on the other side of the
       
  2433     // HTTP request.
       
  2434     $limit = 2;
       
  2435     $count = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
       
  2436 
       
  2437     $correct_number = $limit;
       
  2438     $num_pages = floor($count / $limit);
       
  2439 
       
  2440     // If there is no remainder from rounding, subtract 1 since we index from 0.
       
  2441     if (!($num_pages * $limit < $count)) {
       
  2442       $num_pages--;
       
  2443     }
       
  2444 
       
  2445     for ($page = 0; $page <= $num_pages; ++$page) {
       
  2446       $this->drupalGet('database_test/pager_query_odd/' . $limit, array('query' => array('page' => $page)));
       
  2447       $data = json_decode($this->drupalGetContent());
       
  2448 
       
  2449       if ($page == $num_pages) {
       
  2450         $correct_number = $count - ($limit * $page);
       
  2451       }
       
  2452 
       
  2453       $this->assertEqual(count($data->names), $correct_number, format_string('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
       
  2454     }
       
  2455   }
       
  2456 
       
  2457   /**
       
  2458    * Confirm that a pager query with inner pager query returns valid results.
       
  2459    *
       
  2460    * This is a regression test for #467984.
       
  2461    */
       
  2462   function testInnerPagerQuery() {
       
  2463     $query = db_select('test', 't')->extend('PagerDefault');
       
  2464     $query
       
  2465       ->fields('t', array('age'))
       
  2466       ->orderBy('age')
       
  2467       ->limit(5);
       
  2468 
       
  2469     $outer_query = db_select($query);
       
  2470     $outer_query->addField('subquery', 'age');
       
  2471 
       
  2472     $ages = $outer_query
       
  2473       ->execute()
       
  2474       ->fetchCol();
       
  2475     $this->assertEqual($ages, array(25, 26, 27, 28), 'Inner pager query returned the correct ages.');
       
  2476   }
       
  2477 
       
  2478   /**
       
  2479    * Confirm that a paging query with a having expression returns valid results.
       
  2480    *
       
  2481    * This is a regression test for #467984.
       
  2482    */
       
  2483   function testHavingPagerQuery() {
       
  2484     $query = db_select('test', 't')->extend('PagerDefault');
       
  2485     $query
       
  2486       ->fields('t', array('name'))
       
  2487       ->orderBy('name')
       
  2488       ->groupBy('name')
       
  2489       ->having('MAX(age) > :count', array(':count' => 26))
       
  2490       ->limit(5);
       
  2491 
       
  2492     $ages = $query
       
  2493       ->execute()
       
  2494       ->fetchCol();
       
  2495     $this->assertEqual($ages, array('George', 'Ringo'), 'Pager query with having expression returned the correct ages.');
       
  2496   }
       
  2497 
       
  2498   /**
       
  2499    * Confirm that every pager gets a valid non-overlaping element ID.
       
  2500    */
       
  2501   function testElementNumbers() {
       
  2502     $_GET['page'] = '3, 2, 1, 0';
       
  2503 
       
  2504     $name = db_select('test', 't')->extend('PagerDefault')
       
  2505       ->element(2)
       
  2506       ->fields('t', array('name'))
       
  2507       ->orderBy('age')
       
  2508       ->limit(1)
       
  2509       ->execute()
       
  2510       ->fetchField();
       
  2511     $this->assertEqual($name, 'Paul', 'Pager query #1 with a specified element ID returned the correct results.');
       
  2512 
       
  2513     // Setting an element smaller than the previous one
       
  2514     // should not overwrite the pager $maxElement with a smaller value.
       
  2515     $name = db_select('test', 't')->extend('PagerDefault')
       
  2516       ->element(1)
       
  2517       ->fields('t', array('name'))
       
  2518       ->orderBy('age')
       
  2519       ->limit(1)
       
  2520       ->execute()
       
  2521       ->fetchField();
       
  2522     $this->assertEqual($name, 'George', 'Pager query #2 with a specified element ID returned the correct results.');
       
  2523 
       
  2524     $name = db_select('test', 't')->extend('PagerDefault')
       
  2525       ->fields('t', array('name'))
       
  2526       ->orderBy('age')
       
  2527       ->limit(1)
       
  2528       ->execute()
       
  2529       ->fetchField();
       
  2530     $this->assertEqual($name, 'John', 'Pager query #3 with a generated element ID returned the correct results.');
       
  2531 
       
  2532     unset($_GET['page']);
       
  2533   }
       
  2534 }
       
  2535 
       
  2536 
       
  2537 class DatabaseSelectTableSortDefaultTestCase extends DatabaseTestCase {
       
  2538 
       
  2539   public static function getInfo() {
       
  2540     return array(
       
  2541       'name' => 'Tablesort query tests',
       
  2542       'description' => 'Test the tablesort query extender.',
       
  2543       'group' => 'Database',
       
  2544     );
       
  2545   }
       
  2546 
       
  2547   /**
       
  2548    * Confirm that a tablesort query returns the correct results.
       
  2549    *
       
  2550    * Note that we have to make an HTTP request to a test page handler
       
  2551    * because the pager depends on GET parameters.
       
  2552    */
       
  2553   function testTableSortQuery() {
       
  2554     $sorts = array(
       
  2555       array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
       
  2556       array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
       
  2557       array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
       
  2558       array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
       
  2559       // more elements here
       
  2560 
       
  2561     );
       
  2562 
       
  2563     foreach ($sorts as $sort) {
       
  2564       $this->drupalGet('database_test/tablesort/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
       
  2565       $data = json_decode($this->drupalGetContent());
       
  2566 
       
  2567       $first = array_shift($data->tasks);
       
  2568       $last = array_pop($data->tasks);
       
  2569 
       
  2570       $this->assertEqual($first->task, $sort['first'], 'Items appear in the correct order.');
       
  2571       $this->assertEqual($last->task, $sort['last'], 'Items appear in the correct order.');
       
  2572     }
       
  2573   }
       
  2574 
       
  2575   /**
       
  2576    * Confirm that if a tablesort's orderByHeader is called before another orderBy, that the header happens first.
       
  2577    *
       
  2578    */
       
  2579   function testTableSortQueryFirst() {
       
  2580     $sorts = array(
       
  2581       array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
       
  2582       array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
       
  2583       array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
       
  2584       array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
       
  2585       // more elements here
       
  2586 
       
  2587     );
       
  2588 
       
  2589     foreach ($sorts as $sort) {
       
  2590       $this->drupalGet('database_test/tablesort_first/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
       
  2591       $data = json_decode($this->drupalGetContent());
       
  2592 
       
  2593       $first = array_shift($data->tasks);
       
  2594       $last = array_pop($data->tasks);
       
  2595 
       
  2596       $this->assertEqual($first->task, $sort['first'], format_string('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
       
  2597       $this->assertEqual($last->task, $sort['last'], format_string('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
       
  2598     }
       
  2599   }
       
  2600 
       
  2601   /**
       
  2602    * Confirm that if a sort is not set in a tableselect form there is no error thrown when using the default.
       
  2603    */
       
  2604   function testTableSortDefaultSort() {
       
  2605     $this->drupalGet('database_test/tablesort_default_sort');
       
  2606     // Any PHP errors or notices thrown would trigger a simpletest exception, so
       
  2607     // no additional assertions are needed.
       
  2608   }
       
  2609 }
       
  2610 
       
  2611 /**
       
  2612  * Select tagging tests.
       
  2613  *
       
  2614  * Tags are a way to flag queries for alter hooks so they know
       
  2615  * what type of query it is, such as "node_access".
       
  2616  */
       
  2617 class DatabaseTaggingTestCase extends DatabaseTestCase {
       
  2618 
       
  2619   public static function getInfo() {
       
  2620     return array(
       
  2621       'name' => 'Query tagging tests',
       
  2622       'description' => 'Test the tagging capabilities of the Select builder.',
       
  2623       'group' => 'Database',
       
  2624     );
       
  2625   }
       
  2626 
       
  2627   /**
       
  2628    * Confirm that a query has a "tag" added to it.
       
  2629    */
       
  2630   function testHasTag() {
       
  2631     $query = db_select('test');
       
  2632     $query->addField('test', 'name');
       
  2633     $query->addField('test', 'age', 'age');
       
  2634 
       
  2635     $query->addTag('test');
       
  2636 
       
  2637     $this->assertTrue($query->hasTag('test'), 'hasTag() returned true.');
       
  2638     $this->assertFalse($query->hasTag('other'), 'hasTag() returned false.');
       
  2639   }
       
  2640 
       
  2641   /**
       
  2642    * Test query tagging "has all of these tags" functionality.
       
  2643    */
       
  2644   function testHasAllTags() {
       
  2645     $query = db_select('test');
       
  2646     $query->addField('test', 'name');
       
  2647     $query->addField('test', 'age', 'age');
       
  2648 
       
  2649     $query->addTag('test');
       
  2650     $query->addTag('other');
       
  2651 
       
  2652     $this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.');
       
  2653     $this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.');
       
  2654   }
       
  2655 
       
  2656   /**
       
  2657    * Test query tagging "has at least one of these tags" functionality.
       
  2658    */
       
  2659   function testHasAnyTag() {
       
  2660     $query = db_select('test');
       
  2661     $query->addField('test', 'name');
       
  2662     $query->addField('test', 'age', 'age');
       
  2663 
       
  2664     $query->addTag('test');
       
  2665 
       
  2666     $this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.');
       
  2667     $this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.');
       
  2668   }
       
  2669 
       
  2670   /**
       
  2671    * Confirm that an extended query has a "tag" added to it.
       
  2672    */
       
  2673   function testExtenderHasTag() {
       
  2674     $query = db_select('test')
       
  2675       ->extend('SelectQueryExtender');
       
  2676     $query->addField('test', 'name');
       
  2677     $query->addField('test', 'age', 'age');
       
  2678 
       
  2679     $query->addTag('test');
       
  2680 
       
  2681     $this->assertTrue($query->hasTag('test'), 'hasTag() returned true.');
       
  2682     $this->assertFalse($query->hasTag('other'), 'hasTag() returned false.');
       
  2683   }
       
  2684 
       
  2685   /**
       
  2686    * Test extended query tagging "has all of these tags" functionality.
       
  2687    */
       
  2688   function testExtenderHasAllTags() {
       
  2689     $query = db_select('test')
       
  2690       ->extend('SelectQueryExtender');
       
  2691     $query->addField('test', 'name');
       
  2692     $query->addField('test', 'age', 'age');
       
  2693 
       
  2694     $query->addTag('test');
       
  2695     $query->addTag('other');
       
  2696 
       
  2697     $this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.');
       
  2698     $this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.');
       
  2699   }
       
  2700 
       
  2701   /**
       
  2702    * Test extended query tagging "has at least one of these tags" functionality.
       
  2703    */
       
  2704   function testExtenderHasAnyTag() {
       
  2705     $query = db_select('test')
       
  2706       ->extend('SelectQueryExtender');
       
  2707     $query->addField('test', 'name');
       
  2708     $query->addField('test', 'age', 'age');
       
  2709 
       
  2710     $query->addTag('test');
       
  2711 
       
  2712     $this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.');
       
  2713     $this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.');
       
  2714   }
       
  2715 
       
  2716   /**
       
  2717    * Test that we can attach meta data to a query object.
       
  2718    *
       
  2719    * This is how we pass additional context to alter hooks.
       
  2720    */
       
  2721   function testMetaData() {
       
  2722     $query = db_select('test');
       
  2723     $query->addField('test', 'name');
       
  2724     $query->addField('test', 'age', 'age');
       
  2725 
       
  2726     $data = array(
       
  2727       'a' => 'A',
       
  2728       'b' => 'B',
       
  2729     );
       
  2730 
       
  2731     $query->addMetaData('test', $data);
       
  2732 
       
  2733     $return = $query->getMetaData('test');
       
  2734     $this->assertEqual($data, $return, 'Corect metadata returned.');
       
  2735 
       
  2736     $return = $query->getMetaData('nothere');
       
  2737     $this->assertNull($return, 'Non-existent key returned NULL.');
       
  2738   }
       
  2739 }
       
  2740 
       
  2741 /**
       
  2742  * Select alter tests.
       
  2743  *
       
  2744  * @see database_test_query_alter()
       
  2745  */
       
  2746 class DatabaseAlterTestCase extends DatabaseTestCase {
       
  2747 
       
  2748   public static function getInfo() {
       
  2749     return array(
       
  2750       'name' => 'Query altering tests',
       
  2751       'description' => 'Test the hook_query_alter capabilities of the Select builder.',
       
  2752       'group' => 'Database',
       
  2753     );
       
  2754   }
       
  2755 
       
  2756   /**
       
  2757    * Test that we can do basic alters.
       
  2758    */
       
  2759   function testSimpleAlter() {
       
  2760     $query = db_select('test');
       
  2761     $query->addField('test', 'name');
       
  2762     $query->addField('test', 'age', 'age');
       
  2763     $query->addTag('database_test_alter_add_range');
       
  2764 
       
  2765     $result = $query->execute();
       
  2766 
       
  2767     $num_records = 0;
       
  2768     foreach ($result as $record) {
       
  2769       $num_records++;
       
  2770     }
       
  2771 
       
  2772     $this->assertEqual($num_records, 2, 'Returned the correct number of rows.');
       
  2773   }
       
  2774 
       
  2775   /**
       
  2776    * Test that we can alter the joins on a query.
       
  2777    */
       
  2778   function testAlterWithJoin() {
       
  2779     $query = db_select('test_task');
       
  2780     $tid_field = $query->addField('test_task', 'tid');
       
  2781     $task_field = $query->addField('test_task', 'task');
       
  2782     $query->orderBy($task_field);
       
  2783     $query->addTag('database_test_alter_add_join');
       
  2784 
       
  2785     $result = $query->execute();
       
  2786 
       
  2787     $records = $result->fetchAll();
       
  2788 
       
  2789     $this->assertEqual(count($records), 2, 'Returned the correct number of rows.');
       
  2790 
       
  2791     $this->assertEqual($records[0]->name, 'George', 'Correct data retrieved.');
       
  2792     $this->assertEqual($records[0]->$tid_field, 4, 'Correct data retrieved.');
       
  2793     $this->assertEqual($records[0]->$task_field, 'sing', 'Correct data retrieved.');
       
  2794     $this->assertEqual($records[1]->name, 'George', 'Correct data retrieved.');
       
  2795     $this->assertEqual($records[1]->$tid_field, 5, 'Correct data retrieved.');
       
  2796     $this->assertEqual($records[1]->$task_field, 'sleep', 'Correct data retrieved.');
       
  2797   }
       
  2798 
       
  2799   /**
       
  2800    * Test that we can alter a query's conditionals.
       
  2801    */
       
  2802   function testAlterChangeConditional() {
       
  2803     $query = db_select('test_task');
       
  2804     $tid_field = $query->addField('test_task', 'tid');
       
  2805     $pid_field = $query->addField('test_task', 'pid');
       
  2806     $task_field = $query->addField('test_task', 'task');
       
  2807     $people_alias = $query->join('test', 'people', "test_task.pid = people.id");
       
  2808     $name_field = $query->addField($people_alias, 'name', 'name');
       
  2809     $query->condition('test_task.tid', '1');
       
  2810     $query->orderBy($tid_field);
       
  2811     $query->addTag('database_test_alter_change_conditional');
       
  2812 
       
  2813     $result = $query->execute();
       
  2814 
       
  2815     $records = $result->fetchAll();
       
  2816 
       
  2817     $this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
       
  2818     $this->assertEqual($records[0]->$name_field, 'John', 'Correct data retrieved.');
       
  2819     $this->assertEqual($records[0]->$tid_field, 2, 'Correct data retrieved.');
       
  2820     $this->assertEqual($records[0]->$pid_field, 1, 'Correct data retrieved.');
       
  2821     $this->assertEqual($records[0]->$task_field, 'sleep', 'Correct data retrieved.');
       
  2822   }
       
  2823 
       
  2824   /**
       
  2825    * Test that we can alter the fields of a query.
       
  2826    */
       
  2827   function testAlterChangeFields() {
       
  2828     $query = db_select('test');
       
  2829     $name_field = $query->addField('test', 'name');
       
  2830     $age_field = $query->addField('test', 'age', 'age');
       
  2831     $query->orderBy('name');
       
  2832     $query->addTag('database_test_alter_change_fields');
       
  2833 
       
  2834     $record = $query->execute()->fetch();
       
  2835     $this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.');
       
  2836     $this->assertFalse(isset($record->$age_field), 'Age field not found, as intended.');
       
  2837   }
       
  2838 
       
  2839   /**
       
  2840    * Test that we can alter expressions in the query.
       
  2841    */
       
  2842   function testAlterExpression() {
       
  2843     $query = db_select('test');
       
  2844     $name_field = $query->addField('test', 'name');
       
  2845     $age_field = $query->addExpression("age*2", 'double_age');
       
  2846     $query->condition('age', 27);
       
  2847     $query->addTag('database_test_alter_change_expressions');
       
  2848     $result = $query->execute();
       
  2849 
       
  2850     // Ensure that we got the right record.
       
  2851     $record = $result->fetch();
       
  2852 
       
  2853     $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
       
  2854     $this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.');
       
  2855   }
       
  2856 
       
  2857   /**
       
  2858    * Test that we can remove a range() value from a query. This also tests hook_query_TAG_alter().
       
  2859    */
       
  2860   function testAlterRemoveRange() {
       
  2861     $query = db_select('test');
       
  2862     $query->addField('test', 'name');
       
  2863     $query->addField('test', 'age', 'age');
       
  2864     $query->range(0, 2);
       
  2865     $query->addTag('database_test_alter_remove_range');
       
  2866 
       
  2867     $num_records = count($query->execute()->fetchAll());
       
  2868 
       
  2869     $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
       
  2870   }
       
  2871 
       
  2872   /**
       
  2873    * Test that we can do basic alters on subqueries.
       
  2874    */
       
  2875   function testSimpleAlterSubquery() {
       
  2876     // Create a sub-query with an alter tag.
       
  2877     $subquery = db_select('test', 'p');
       
  2878     $subquery->addField('p', 'name');
       
  2879     $subquery->addField('p', 'id');
       
  2880     // Pick out George.
       
  2881     $subquery->condition('age', 27);
       
  2882     $subquery->addExpression("age*2", 'double_age');
       
  2883     // This query alter should change it to age * 3.
       
  2884     $subquery->addTag('database_test_alter_change_expressions');
       
  2885 
       
  2886     // Create a main query and join to sub-query.
       
  2887     $query = db_select('test_task', 'tt');
       
  2888     $query->join($subquery, 'pq', 'pq.id = tt.pid');
       
  2889     $age_field = $query->addField('pq', 'double_age');
       
  2890     $name_field = $query->addField('pq', 'name');
       
  2891 
       
  2892     $record = $query->execute()->fetch();
       
  2893     $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
       
  2894     $this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.');
       
  2895   }
       
  2896 }
       
  2897 
       
  2898 /**
       
  2899  * Regression tests.
       
  2900  */
       
  2901 class DatabaseRegressionTestCase extends DatabaseTestCase {
       
  2902 
       
  2903   public static function getInfo() {
       
  2904     return array(
       
  2905       'name' => 'Regression tests',
       
  2906       'description' => 'Regression tests cases for the database layer.',
       
  2907       'group' => 'Database',
       
  2908     );
       
  2909   }
       
  2910 
       
  2911   /**
       
  2912    * Regression test for #310447.
       
  2913    *
       
  2914    * Tries to insert non-ascii UTF-8 data in a database column and checks
       
  2915    * if its stored properly.
       
  2916    */
       
  2917   function testRegression_310447() {
       
  2918     // That's a 255 character UTF-8 string.
       
  2919     $name = str_repeat("é", 255);
       
  2920     db_insert('test')
       
  2921       ->fields(array(
       
  2922         'name' => $name,
       
  2923         'age' => 20,
       
  2924         'job' => 'Dancer',
       
  2925       ))->execute();
       
  2926 
       
  2927     $from_database = db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
       
  2928     $this->assertIdentical($name, $from_database, "The database handles UTF-8 characters cleanly.");
       
  2929   }
       
  2930 
       
  2931   /**
       
  2932    * Test the db_table_exists() function.
       
  2933    */
       
  2934   function testDBTableExists() {
       
  2935     $this->assertIdentical(TRUE, db_table_exists('node'), 'Returns true for existent table.');
       
  2936     $this->assertIdentical(FALSE, db_table_exists('nosuchtable'), 'Returns false for nonexistent table.');
       
  2937   }
       
  2938 
       
  2939   /**
       
  2940    * Test the db_field_exists() function.
       
  2941    */
       
  2942   function testDBFieldExists() {
       
  2943     $this->assertIdentical(TRUE, db_field_exists('node', 'nid'), 'Returns true for existent column.');
       
  2944     $this->assertIdentical(FALSE, db_field_exists('node', 'nosuchcolumn'), 'Returns false for nonexistent column.');
       
  2945   }
       
  2946 
       
  2947   /**
       
  2948    * Test the db_index_exists() function.
       
  2949    */
       
  2950   function testDBIndexExists() {
       
  2951     $this->assertIdentical(TRUE, db_index_exists('node', 'node_created'), 'Returns true for existent index.');
       
  2952     $this->assertIdentical(FALSE, db_index_exists('node', 'nosuchindex'), 'Returns false for nonexistent index.');
       
  2953   }
       
  2954 }
       
  2955 
       
  2956 /**
       
  2957  * Query logging tests.
       
  2958  */
       
  2959 class DatabaseLoggingTestCase extends DatabaseTestCase {
       
  2960 
       
  2961   public static function getInfo() {
       
  2962     return array(
       
  2963       'name' => 'Query logging',
       
  2964       'description' => 'Test the query logging facility.',
       
  2965       'group' => 'Database',
       
  2966     );
       
  2967   }
       
  2968 
       
  2969   /**
       
  2970    * Test that we can log the existence of a query.
       
  2971    */
       
  2972   function testEnableLogging() {
       
  2973     $log = Database::startLog('testing');
       
  2974 
       
  2975     db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
       
  2976     db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
       
  2977 
       
  2978     // Trigger a call that does not have file in the backtrace.
       
  2979     call_user_func_array('db_query', array('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo')))->fetchCol();
       
  2980 
       
  2981     $queries = Database::getLog('testing', 'default');
       
  2982 
       
  2983     $this->assertEqual(count($queries), 3, 'Correct number of queries recorded.');
       
  2984 
       
  2985     foreach ($queries as $query) {
       
  2986       $this->assertEqual($query['caller']['function'], __FUNCTION__, 'Correct function in query log.');
       
  2987     }
       
  2988   }
       
  2989 
       
  2990   /**
       
  2991    * Test that we can run two logs in parallel.
       
  2992    */
       
  2993   function testEnableMultiLogging() {
       
  2994     Database::startLog('testing1');
       
  2995 
       
  2996     db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
       
  2997 
       
  2998     Database::startLog('testing2');
       
  2999 
       
  3000     db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
       
  3001 
       
  3002     $queries1 = Database::getLog('testing1');
       
  3003     $queries2 = Database::getLog('testing2');
       
  3004 
       
  3005     $this->assertEqual(count($queries1), 2, 'Correct number of queries recorded for log 1.');
       
  3006     $this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for log 2.');
       
  3007   }
       
  3008 
       
  3009   /**
       
  3010    * Test that we can log queries against multiple targets on the same connection.
       
  3011    */
       
  3012   function testEnableTargetLogging() {
       
  3013     // Clone the master credentials to a slave connection and to another fake
       
  3014     // connection.
       
  3015     $connection_info = Database::getConnectionInfo('default');
       
  3016     Database::addConnectionInfo('default', 'slave', $connection_info['default']);
       
  3017 
       
  3018     Database::startLog('testing1');
       
  3019 
       
  3020     db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
       
  3021 
       
  3022     db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'));//->fetchCol();
       
  3023 
       
  3024     $queries1 = Database::getLog('testing1');
       
  3025 
       
  3026     $this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.');
       
  3027     $this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.');
       
  3028     $this->assertEqual($queries1[1]['target'], 'slave', 'Second query used slave target.');
       
  3029   }
       
  3030 
       
  3031   /**
       
  3032    * Test that logs to separate targets collapse to the same connection properly.
       
  3033    *
       
  3034    * This test is identical to the one above, except that it doesn't create
       
  3035    * a fake target so the query should fall back to running on the default
       
  3036    * target.
       
  3037    */
       
  3038   function testEnableTargetLoggingNoTarget() {
       
  3039     Database::startLog('testing1');
       
  3040 
       
  3041     db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
       
  3042 
       
  3043     // We use "fake" here as a target because any non-existent target will do.
       
  3044     // However, because all of the tests in this class share a single page
       
  3045     // request there is likely to be a target of "slave" from one of the other
       
  3046     // unit tests, so we use a target here that we know with absolute certainty
       
  3047     // does not exist.
       
  3048     db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'fake'))->fetchCol();
       
  3049 
       
  3050     $queries1 = Database::getLog('testing1');
       
  3051 
       
  3052     $this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.');
       
  3053     $this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.');
       
  3054     $this->assertEqual($queries1[1]['target'], 'default', 'Second query used default target as fallback.');
       
  3055   }
       
  3056 
       
  3057   /**
       
  3058    * Test that we can log queries separately on different connections.
       
  3059    */
       
  3060   function testEnableMultiConnectionLogging() {
       
  3061     // Clone the master credentials to a fake connection.
       
  3062     // That both connections point to the same physical database is irrelevant.
       
  3063     $connection_info = Database::getConnectionInfo('default');
       
  3064     Database::addConnectionInfo('test2', 'default', $connection_info['default']);
       
  3065 
       
  3066     Database::startLog('testing1');
       
  3067     Database::startLog('testing1', 'test2');
       
  3068 
       
  3069     db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
       
  3070 
       
  3071     $old_key = db_set_active('test2');
       
  3072 
       
  3073     db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'))->fetchCol();
       
  3074 
       
  3075     db_set_active($old_key);
       
  3076 
       
  3077     $queries1 = Database::getLog('testing1');
       
  3078     $queries2 = Database::getLog('testing1', 'test2');
       
  3079 
       
  3080     $this->assertEqual(count($queries1), 1, 'Correct number of queries recorded for first connection.');
       
  3081     $this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for second connection.');
       
  3082   }
       
  3083 }
       
  3084 
       
  3085 /**
       
  3086  * Query serialization tests.
       
  3087  */
       
  3088 class DatabaseSerializeQueryTestCase extends DatabaseTestCase {
       
  3089   public static function getInfo() {
       
  3090     return array(
       
  3091       'name' => 'Serialize query',
       
  3092       'description' => 'Test serializing and unserializing a query.',
       
  3093       'group' => 'Database',
       
  3094     );
       
  3095   }
       
  3096 
       
  3097   /**
       
  3098    * Confirm that a query can be serialized and unserialized.
       
  3099    */
       
  3100   function testSerializeQuery() {
       
  3101     $query = db_select('test');
       
  3102     $query->addField('test', 'age');
       
  3103     $query->condition('name', 'Ringo');
       
  3104     // If this doesn't work, it will throw an exception, so no need for an
       
  3105     // assertion.
       
  3106     $query = unserialize(serialize($query));
       
  3107     $results = $query->execute()->fetchCol();
       
  3108     $this->assertEqual($results[0], 28, 'Query properly executed after unserialization.');
       
  3109   }
       
  3110 }
       
  3111 
       
  3112 /**
       
  3113  * Range query tests.
       
  3114  */
       
  3115 class DatabaseRangeQueryTestCase extends DrupalWebTestCase {
       
  3116   public static function getInfo() {
       
  3117     return array(
       
  3118       'name' => 'Range query test',
       
  3119       'description' => 'Test the Range query functionality.',
       
  3120       'group' => 'Database',
       
  3121     );
       
  3122   }
       
  3123 
       
  3124   function setUp() {
       
  3125     parent::setUp('database_test');
       
  3126   }
       
  3127 
       
  3128   /**
       
  3129    * Confirm that range query work and return correct result.
       
  3130    */
       
  3131   function testRangeQuery() {
       
  3132     // Test if return correct number of rows.
       
  3133     $range_rows = db_query_range("SELECT name FROM {system} ORDER BY name", 2, 3)->fetchAll();
       
  3134     $this->assertEqual(count($range_rows), 3, 'Range query work and return correct number of rows.');
       
  3135 
       
  3136     // Test if return target data.
       
  3137     $raw_rows = db_query('SELECT name FROM {system} ORDER BY name')->fetchAll();
       
  3138     $raw_rows = array_slice($raw_rows, 2, 3);
       
  3139     $this->assertEqual($range_rows, $raw_rows, 'Range query work and return target data.');
       
  3140   }
       
  3141 }
       
  3142 
       
  3143 /**
       
  3144  * Temporary query tests.
       
  3145  */
       
  3146 class DatabaseTemporaryQueryTestCase extends DrupalWebTestCase {
       
  3147   public static function getInfo() {
       
  3148     return array(
       
  3149       'name' => 'Temporary query test',
       
  3150       'description' => 'Test the temporary query functionality.',
       
  3151       'group' => 'Database',
       
  3152     );
       
  3153   }
       
  3154 
       
  3155   function setUp() {
       
  3156     parent::setUp('database_test');
       
  3157   }
       
  3158 
       
  3159   /**
       
  3160    * Return the number of rows of a table.
       
  3161    */
       
  3162   function countTableRows($table_name) {
       
  3163     return db_select($table_name)->countQuery()->execute()->fetchField();
       
  3164   }
       
  3165 
       
  3166   /**
       
  3167    * Confirm that temporary tables work and are limited to one request.
       
  3168    */
       
  3169   function testTemporaryQuery() {
       
  3170     $this->drupalGet('database_test/db_query_temporary');
       
  3171     $data = json_decode($this->drupalGetContent());
       
  3172     if ($data) {
       
  3173       $this->assertEqual($this->countTableRows("system"), $data->row_count, 'The temporary table contains the correct amount of rows.');
       
  3174       $this->assertFalse(db_table_exists($data->table_name), 'The temporary table is, indeed, temporary.');
       
  3175     }
       
  3176     else {
       
  3177       $this->fail("The creation of the temporary table failed.");
       
  3178     }
       
  3179 
       
  3180     // Now try to run two db_query_temporary() in the same request.
       
  3181     $table_name_system = db_query_temporary('SELECT status FROM {system}', array());
       
  3182     $table_name_users = db_query_temporary('SELECT uid FROM {users}', array());
       
  3183 
       
  3184     $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("system"), 'A temporary table was created successfully in this request.');
       
  3185     $this->assertEqual($this->countTableRows($table_name_users), $this->countTableRows("users"), 'A second temporary table was created successfully in this request.');
       
  3186 
       
  3187     // Check that leading whitespace and comments do not cause problems
       
  3188     // in the modified query.
       
  3189     $sql = "
       
  3190       -- Let's select some rows into a temporary table
       
  3191       SELECT name FROM {test}
       
  3192     ";
       
  3193     $table_name_test = db_query_temporary($sql, array());
       
  3194     $this->assertEqual($this->countTableRows($table_name_test), $this->countTableRows('test'), 'Leading white space and comments do not interfere with temporary table creation.');
       
  3195   }
       
  3196 }
       
  3197 
       
  3198 /**
       
  3199  * Test how the current database driver interprets the SQL syntax.
       
  3200  *
       
  3201  * In order to ensure consistent SQL handling throughout Drupal
       
  3202  * across multiple kinds of database systems, we test that the
       
  3203  * database system interprets SQL syntax in an expected fashion.
       
  3204  */
       
  3205 class DatabaseBasicSyntaxTestCase extends DatabaseTestCase {
       
  3206   public static function getInfo() {
       
  3207     return array(
       
  3208       'name' => 'Basic SQL syntax tests',
       
  3209       'description' => 'Test SQL syntax interpretation.',
       
  3210       'group' => 'Database',
       
  3211     );
       
  3212   }
       
  3213 
       
  3214   function setUp() {
       
  3215     parent::setUp('database_test');
       
  3216   }
       
  3217 
       
  3218   /**
       
  3219    * Test for string concatenation.
       
  3220    */
       
  3221   function testBasicConcat() {
       
  3222     $result = db_query('SELECT CONCAT(:a1, CONCAT(:a2, CONCAT(:a3, CONCAT(:a4, :a5))))', array(
       
  3223       ':a1' => 'This',
       
  3224       ':a2' => ' ',
       
  3225       ':a3' => 'is',
       
  3226       ':a4' => ' a ',
       
  3227       ':a5' => 'test.',
       
  3228     ));
       
  3229     $this->assertIdentical($result->fetchField(), 'This is a test.', 'Basic CONCAT works.');
       
  3230   }
       
  3231 
       
  3232   /**
       
  3233    * Test for string concatenation with field values.
       
  3234    */
       
  3235   function testFieldConcat() {
       
  3236     $result = db_query('SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', array(
       
  3237       ':a1' => 'The age of ',
       
  3238       ':a2' => ' is ',
       
  3239       ':a3' => '.',
       
  3240       ':age' => 25,
       
  3241     ));
       
  3242     $this->assertIdentical($result->fetchField(), 'The age of John is 25.', 'Field CONCAT works.');
       
  3243   }
       
  3244 
       
  3245   /**
       
  3246    * Test escaping of LIKE wildcards.
       
  3247    */
       
  3248   function testLikeEscape() {
       
  3249     db_insert('test')
       
  3250       ->fields(array(
       
  3251         'name' => 'Ring_',
       
  3252       ))
       
  3253       ->execute();
       
  3254 
       
  3255     // Match both "Ringo" and "Ring_".
       
  3256     $num_matches = db_select('test', 't')
       
  3257       ->condition('name', 'Ring_', 'LIKE')
       
  3258       ->countQuery()
       
  3259       ->execute()
       
  3260       ->fetchField();
       
  3261     $this->assertIdentical($num_matches, '2', 'Found 2 records.');
       
  3262     // Match only "Ring_" using a LIKE expression with no wildcards.
       
  3263     $num_matches = db_select('test', 't')
       
  3264       ->condition('name', db_like('Ring_'), 'LIKE')
       
  3265       ->countQuery()
       
  3266       ->execute()
       
  3267       ->fetchField();
       
  3268     $this->assertIdentical($num_matches, '1', 'Found 1 record.');
       
  3269   }
       
  3270 
       
  3271   /**
       
  3272    * Test LIKE query containing a backslash.
       
  3273    */
       
  3274   function testLikeBackslash() {
       
  3275     db_insert('test')
       
  3276       ->fields(array('name'))
       
  3277       ->values(array(
       
  3278         'name' => 'abcde\f',
       
  3279       ))
       
  3280       ->values(array(
       
  3281         'name' => 'abc%\_',
       
  3282       ))
       
  3283       ->execute();
       
  3284 
       
  3285     // Match both rows using a LIKE expression with two wildcards and a verbatim
       
  3286     // backslash.
       
  3287     $num_matches = db_select('test', 't')
       
  3288       ->condition('name', 'abc%\\\\_', 'LIKE')
       
  3289       ->countQuery()
       
  3290       ->execute()
       
  3291       ->fetchField();
       
  3292     $this->assertIdentical($num_matches, '2', 'Found 2 records.');
       
  3293     // Match only the former using a LIKE expression with no wildcards.
       
  3294     $num_matches = db_select('test', 't')
       
  3295       ->condition('name', db_like('abc%\_'), 'LIKE')
       
  3296       ->countQuery()
       
  3297       ->execute()
       
  3298       ->fetchField();
       
  3299     $this->assertIdentical($num_matches, '1', 'Found 1 record.');
       
  3300   }
       
  3301 }
       
  3302 
       
  3303 /**
       
  3304  * Test case sensitivity handling.
       
  3305  */
       
  3306 class DatabaseCaseSensitivityTestCase extends DatabaseTestCase {
       
  3307   public static function getInfo() {
       
  3308     return array(
       
  3309       'name' => 'Case sensitivity',
       
  3310       'description' => 'Test handling case sensitive collation.',
       
  3311       'group' => 'Database',
       
  3312     );
       
  3313   }
       
  3314 
       
  3315   /**
       
  3316    * Test BINARY collation in MySQL.
       
  3317    */
       
  3318   function testCaseSensitiveInsert() {
       
  3319     $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
  3320 
       
  3321     $john = db_insert('test')
       
  3322       ->fields(array(
       
  3323         'name' => 'john', // <- A record already exists with name 'John'.
       
  3324         'age' => 2,
       
  3325         'job' => 'Baby',
       
  3326       ))
       
  3327       ->execute();
       
  3328 
       
  3329     $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
       
  3330     $this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
       
  3331     $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'john'))->fetchField();
       
  3332     $this->assertIdentical($saved_age, '2', 'Can retrieve after inserting.');
       
  3333   }
       
  3334 }
       
  3335 
       
  3336 /**
       
  3337  * Test invalid data handling.
       
  3338  */
       
  3339 class DatabaseInvalidDataTestCase extends DatabaseTestCase {
       
  3340   public static function getInfo() {
       
  3341     return array(
       
  3342       'name' => 'Invalid data',
       
  3343       'description' => 'Test handling of some invalid data.',
       
  3344       'group' => 'Database',
       
  3345     );
       
  3346   }
       
  3347 
       
  3348   function setUp() {
       
  3349     parent::setUp('database_test');
       
  3350   }
       
  3351 
       
  3352   /**
       
  3353    * Traditional SQL database systems abort inserts when invalid data is encountered.
       
  3354    */
       
  3355   function testInsertDuplicateData() {
       
  3356     // Try to insert multiple records where at least one has bad data.
       
  3357     try {
       
  3358       db_insert('test')
       
  3359         ->fields(array('name', 'age', 'job'))
       
  3360         ->values(array(
       
  3361           'name' => 'Elvis',
       
  3362           'age' => 63,
       
  3363           'job' => 'Singer',
       
  3364         ))->values(array(
       
  3365           'name' => 'John', // <-- Duplicate value on unique field.
       
  3366           'age' => 17,
       
  3367           'job' => 'Consultant',
       
  3368         ))
       
  3369         ->values(array(
       
  3370           'name' => 'Frank',
       
  3371           'age' => 75,
       
  3372           'job' => 'Singer',
       
  3373         ))
       
  3374         ->execute();
       
  3375       $this->fail('Insert succeedded when it should not have.');
       
  3376     }
       
  3377     catch (Exception $e) {
       
  3378       // Check if the first record was inserted.
       
  3379       $name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 63))->fetchField();
       
  3380 
       
  3381       if ($name == 'Elvis') {
       
  3382         if (!Database::getConnection()->supportsTransactions()) {
       
  3383           // This is an expected fail.
       
  3384           // Database engines that don't support transactions can leave partial
       
  3385           // inserts in place when an error occurs. This is the case for MySQL
       
  3386           // when running on a MyISAM table.
       
  3387           $this->pass("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions");
       
  3388         }
       
  3389         else {
       
  3390           $this->fail('The whole transaction is rolled back when a duplicate key insert occurs.');
       
  3391         }
       
  3392       }
       
  3393       else {
       
  3394         $this->pass('The whole transaction is rolled back when a duplicate key insert occurs.');
       
  3395       }
       
  3396 
       
  3397       // Ensure the other values were not inserted.
       
  3398       $record = db_select('test')
       
  3399         ->fields('test', array('name', 'age'))
       
  3400         ->condition('age', array(17, 75), 'IN')
       
  3401         ->execute()->fetchObject();
       
  3402 
       
  3403       $this->assertFalse($record, 'The rest of the insert aborted as expected.');
       
  3404     }
       
  3405   }
       
  3406 
       
  3407 }
       
  3408 
       
  3409 /**
       
  3410  * Drupal-specific SQL syntax tests.
       
  3411  */
       
  3412 class DatabaseQueryTestCase extends DatabaseTestCase {
       
  3413   public static function getInfo() {
       
  3414     return array(
       
  3415       'name' => 'Custom query syntax tests',
       
  3416       'description' => 'Test Drupal\'s extended prepared statement syntax..',
       
  3417       'group' => 'Database',
       
  3418     );
       
  3419   }
       
  3420 
       
  3421   function setUp() {
       
  3422     parent::setUp('database_test');
       
  3423   }
       
  3424 
       
  3425   /**
       
  3426    * Test that we can specify an array of values in the query by simply passing in an array.
       
  3427    */
       
  3428   function testArraySubstitution() {
       
  3429     $names = db_query('SELECT name FROM {test} WHERE age IN (:ages) ORDER BY age', array(':ages' => array(25, 26, 27)))->fetchAll();
       
  3430 
       
  3431     $this->assertEqual(count($names), 3, 'Correct number of names returned');
       
  3432   }
       
  3433 
       
  3434   /**
       
  3435    * Test SQL injection via database query array arguments.
       
  3436    */
       
  3437   public function testArrayArgumentsSQLInjection() {
       
  3438     // Attempt SQL injection and verify that it does not work.
       
  3439     $condition = array(
       
  3440       "1 ;INSERT INTO {test} (name) VALUES ('test12345678'); -- " => '',
       
  3441       '1' => '',
       
  3442     );
       
  3443     try {
       
  3444       db_query("SELECT * FROM {test} WHERE name = :name", array(':name' => $condition))->fetchObject();
       
  3445       $this->fail('SQL injection attempt via array arguments should result in a PDOException.');
       
  3446     }
       
  3447     catch (PDOException $e) {
       
  3448       $this->pass('SQL injection attempt via array arguments should result in a PDOException.');
       
  3449     }
       
  3450 
       
  3451     // Test that the insert query that was used in the SQL injection attempt did
       
  3452     // not result in a row being inserted in the database.
       
  3453     $result = db_select('test')
       
  3454       ->condition('name', 'test12345678')
       
  3455       ->countQuery()
       
  3456       ->execute()
       
  3457       ->fetchField();
       
  3458     $this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.');
       
  3459   }
       
  3460 
       
  3461 }
       
  3462 
       
  3463 /**
       
  3464  * Test transaction support, particularly nesting.
       
  3465  *
       
  3466  * We test nesting by having two transaction layers, an outer and inner. The
       
  3467  * outer layer encapsulates the inner layer. Our transaction nesting abstraction
       
  3468  * should allow the outer layer function to call any function it wants,
       
  3469  * especially the inner layer that starts its own transaction, and be
       
  3470  * confident that, when the function it calls returns, its own transaction
       
  3471  * is still "alive."
       
  3472  *
       
  3473  * Call structure:
       
  3474  *   transactionOuterLayer()
       
  3475  *     Start transaction
       
  3476  *     transactionInnerLayer()
       
  3477  *       Start transaction (does nothing in database)
       
  3478  *       [Maybe decide to roll back]
       
  3479  *     Do more stuff
       
  3480  *     Should still be in transaction A
       
  3481  *
       
  3482  */
       
  3483 class DatabaseTransactionTestCase extends DatabaseTestCase {
       
  3484 
       
  3485   public static function getInfo() {
       
  3486     return array(
       
  3487       'name' => 'Transaction tests',
       
  3488       'description' => 'Test the transaction abstraction system.',
       
  3489       'group' => 'Database',
       
  3490     );
       
  3491   }
       
  3492 
       
  3493   /**
       
  3494    * Helper method for transaction unit test.
       
  3495    *
       
  3496    * This "outer layer" transaction starts and then encapsulates the
       
  3497    * "inner layer" transaction. This nesting is used to evaluate whether the
       
  3498    * database transaction API properly supports nesting. By "properly supports,"
       
  3499    * we mean the outer transaction continues to exist regardless of what
       
  3500    * functions are called and whether those functions start their own
       
  3501    * transactions.
       
  3502    *
       
  3503    * In contrast, a typical database would commit the outer transaction, start
       
  3504    * a new transaction for the inner layer, commit the inner layer transaction,
       
  3505    * and then be confused when the outer layer transaction tries to commit its
       
  3506    * transaction (which was already committed when the inner transaction
       
  3507    * started).
       
  3508    *
       
  3509    * @param $suffix
       
  3510    *   Suffix to add to field values to differentiate tests.
       
  3511    * @param $rollback
       
  3512    *   Whether or not to try rolling back the transaction when we're done.
       
  3513    * @param $ddl_statement
       
  3514    *   Whether to execute a DDL statement during the inner transaction.
       
  3515    */
       
  3516   protected function transactionOuterLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) {
       
  3517     $connection = Database::getConnection();
       
  3518     $depth = $connection->transactionDepth();
       
  3519     $txn = db_transaction();
       
  3520 
       
  3521     // Insert a single row into the testing table.
       
  3522     db_insert('test')
       
  3523       ->fields(array(
       
  3524         'name' => 'David' . $suffix,
       
  3525         'age' => '24',
       
  3526       ))
       
  3527       ->execute();
       
  3528 
       
  3529     $this->assertTrue($connection->inTransaction(), 'In transaction before calling nested transaction.');
       
  3530 
       
  3531     // We're already in a transaction, but we call ->transactionInnerLayer
       
  3532     // to nest another transaction inside the current one.
       
  3533     $this->transactionInnerLayer($suffix, $rollback, $ddl_statement);
       
  3534 
       
  3535     $this->assertTrue($connection->inTransaction(), 'In transaction after calling nested transaction.');
       
  3536 
       
  3537     if ($rollback) {
       
  3538       // Roll back the transaction, if requested.
       
  3539       // This rollback should propagate to the last savepoint.
       
  3540       $txn->rollback();
       
  3541       $this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().');
       
  3542     }
       
  3543   }
       
  3544 
       
  3545   /**
       
  3546    * Helper method for transaction unit tests. This "inner layer" transaction
       
  3547    * is either used alone or nested inside of the "outer layer" transaction.
       
  3548    *
       
  3549    * @param $suffix
       
  3550    *   Suffix to add to field values to differentiate tests.
       
  3551    * @param $rollback
       
  3552    *   Whether or not to try rolling back the transaction when we're done.
       
  3553    * @param $ddl_statement
       
  3554    *   Whether to execute a DDL statement during the transaction.
       
  3555    */
       
  3556   protected function transactionInnerLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) {
       
  3557     $connection = Database::getConnection();
       
  3558 
       
  3559     $depth = $connection->transactionDepth();
       
  3560     // Start a transaction. If we're being called from ->transactionOuterLayer,
       
  3561     // then we're already in a transaction. Normally, that would make starting
       
  3562     // a transaction here dangerous, but the database API handles this problem
       
  3563     // for us by tracking the nesting and avoiding the danger.
       
  3564     $txn = db_transaction();
       
  3565 
       
  3566     $depth2 = $connection->transactionDepth();
       
  3567     $this->assertTrue($depth < $depth2, 'Transaction depth is has increased with new transaction.');
       
  3568 
       
  3569     // Insert a single row into the testing table.
       
  3570     db_insert('test')
       
  3571       ->fields(array(
       
  3572         'name' => 'Daniel' . $suffix,
       
  3573         'age' => '19',
       
  3574       ))
       
  3575       ->execute();
       
  3576 
       
  3577     $this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.');
       
  3578 
       
  3579     if ($ddl_statement) {
       
  3580       $table = array(
       
  3581         'fields' => array(
       
  3582           'id' => array(
       
  3583             'type' => 'serial',
       
  3584             'unsigned' => TRUE,
       
  3585             'not null' => TRUE,
       
  3586           ),
       
  3587         ),
       
  3588         'primary key' => array('id'),
       
  3589       );
       
  3590       db_create_table('database_test_1', $table);
       
  3591 
       
  3592       $this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.');
       
  3593     }
       
  3594 
       
  3595     if ($rollback) {
       
  3596       // Roll back the transaction, if requested.
       
  3597       // This rollback should propagate to the last savepoint.
       
  3598       $txn->rollback();
       
  3599       $this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().');
       
  3600     }
       
  3601   }
       
  3602 
       
  3603   /**
       
  3604    * Test transaction rollback on a database that supports transactions.
       
  3605    *
       
  3606    * If the active connection does not support transactions, this test does nothing.
       
  3607    */
       
  3608   function testTransactionRollBackSupported() {
       
  3609     // This test won't work right if transactions are not supported.
       
  3610     if (!Database::getConnection()->supportsTransactions()) {
       
  3611       return;
       
  3612     }
       
  3613     try {
       
  3614       // Create two nested transactions. Roll back from the inner one.
       
  3615       $this->transactionOuterLayer('B', TRUE);
       
  3616 
       
  3617       // Neither of the rows we inserted in the two transaction layers
       
  3618       // should be present in the tables post-rollback.
       
  3619       $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
       
  3620       $this->assertNotIdentical($saved_age, '24', 'Cannot retrieve DavidB row after commit.');
       
  3621       $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
       
  3622       $this->assertNotIdentical($saved_age, '19', 'Cannot retrieve DanielB row after commit.');
       
  3623     }
       
  3624     catch (Exception $e) {
       
  3625       $this->fail($e->getMessage());
       
  3626     }
       
  3627   }
       
  3628 
       
  3629   /**
       
  3630    * Test transaction rollback on a database that does not support transactions.
       
  3631    *
       
  3632    * If the active driver supports transactions, this test does nothing.
       
  3633    */
       
  3634   function testTransactionRollBackNotSupported() {
       
  3635     // This test won't work right if transactions are supported.
       
  3636     if (Database::getConnection()->supportsTransactions()) {
       
  3637       return;
       
  3638     }
       
  3639     try {
       
  3640       // Create two nested transactions. Attempt to roll back from the inner one.
       
  3641       $this->transactionOuterLayer('B', TRUE);
       
  3642 
       
  3643       // Because our current database claims to not support transactions,
       
  3644       // the inserted rows should be present despite the attempt to roll back.
       
  3645       $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
       
  3646       $this->assertIdentical($saved_age, '24', 'DavidB not rolled back, since transactions are not supported.');
       
  3647       $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
       
  3648       $this->assertIdentical($saved_age, '19', 'DanielB not rolled back, since transactions are not supported.');
       
  3649     }
       
  3650     catch (Exception $e) {
       
  3651       $this->fail($e->getMessage());
       
  3652     }
       
  3653   }
       
  3654 
       
  3655   /**
       
  3656    * Test committed transaction.
       
  3657    *
       
  3658    * The behavior of this test should be identical for connections that support
       
  3659    * transactions and those that do not.
       
  3660    */
       
  3661   function testCommittedTransaction() {
       
  3662     try {
       
  3663       // Create two nested transactions. The changes should be committed.
       
  3664       $this->transactionOuterLayer('A');
       
  3665 
       
  3666       // Because we committed, both of the inserted rows should be present.
       
  3667       $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidA'))->fetchField();
       
  3668       $this->assertIdentical($saved_age, '24', 'Can retrieve DavidA row after commit.');
       
  3669       $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielA'))->fetchField();
       
  3670       $this->assertIdentical($saved_age, '19', 'Can retrieve DanielA row after commit.');
       
  3671     }
       
  3672     catch (Exception $e) {
       
  3673       $this->fail($e->getMessage());
       
  3674     }
       
  3675   }
       
  3676 
       
  3677   /**
       
  3678    * Test the compatibility of transactions with DDL statements.
       
  3679    */
       
  3680   function testTransactionWithDdlStatement() {
       
  3681     // First, test that a commit works normally, even with DDL statements.
       
  3682     $transaction = db_transaction();
       
  3683     $this->insertRow('row');
       
  3684     $this->executeDDLStatement();
       
  3685     unset($transaction);
       
  3686     $this->assertRowPresent('row');
       
  3687 
       
  3688     // Even in different order.
       
  3689     $this->cleanUp();
       
  3690     $transaction = db_transaction();
       
  3691     $this->executeDDLStatement();
       
  3692     $this->insertRow('row');
       
  3693     unset($transaction);
       
  3694     $this->assertRowPresent('row');
       
  3695 
       
  3696     // Even with stacking.
       
  3697     $this->cleanUp();
       
  3698     $transaction = db_transaction();
       
  3699     $transaction2 = db_transaction();
       
  3700     $this->executeDDLStatement();
       
  3701     unset($transaction2);
       
  3702     $transaction3 = db_transaction();
       
  3703     $this->insertRow('row');
       
  3704     unset($transaction3);
       
  3705     unset($transaction);
       
  3706     $this->assertRowPresent('row');
       
  3707 
       
  3708     // A transaction after a DDL statement should still work the same.
       
  3709     $this->cleanUp();
       
  3710     $transaction = db_transaction();
       
  3711     $transaction2 = db_transaction();
       
  3712     $this->executeDDLStatement();
       
  3713     unset($transaction2);
       
  3714     $transaction3 = db_transaction();
       
  3715     $this->insertRow('row');
       
  3716     $transaction3->rollback();
       
  3717     unset($transaction3);
       
  3718     unset($transaction);
       
  3719     $this->assertRowAbsent('row');
       
  3720 
       
  3721     // The behavior of a rollback depends on the type of database server.
       
  3722     if (Database::getConnection()->supportsTransactionalDDL()) {
       
  3723       // For database servers that support transactional DDL, a rollback
       
  3724       // of a transaction including DDL statements should be possible.
       
  3725       $this->cleanUp();
       
  3726       $transaction = db_transaction();
       
  3727       $this->insertRow('row');
       
  3728       $this->executeDDLStatement();
       
  3729       $transaction->rollback();
       
  3730       unset($transaction);
       
  3731       $this->assertRowAbsent('row');
       
  3732 
       
  3733       // Including with stacking.
       
  3734       $this->cleanUp();
       
  3735       $transaction = db_transaction();
       
  3736       $transaction2 = db_transaction();
       
  3737       $this->executeDDLStatement();
       
  3738       unset($transaction2);
       
  3739       $transaction3 = db_transaction();
       
  3740       $this->insertRow('row');
       
  3741       unset($transaction3);
       
  3742       $transaction->rollback();
       
  3743       unset($transaction);
       
  3744       $this->assertRowAbsent('row');
       
  3745     }
       
  3746     else {
       
  3747       // For database servers that do not support transactional DDL,
       
  3748       // the DDL statement should commit the transaction stack.
       
  3749       $this->cleanUp();
       
  3750       $transaction = db_transaction();
       
  3751       $this->insertRow('row');
       
  3752       $this->executeDDLStatement();
       
  3753       // Rollback the outer transaction.
       
  3754       try {
       
  3755         $transaction->rollback();
       
  3756         unset($transaction);
       
  3757         // @TODO: an exception should be triggered here, but is not, because
       
  3758         // "ROLLBACK" fails silently in MySQL if there is no transaction active.
       
  3759         // $this->fail(t('Rolling back a transaction containing DDL should fail.'));
       
  3760       }
       
  3761       catch (DatabaseTransactionNoActiveException $e) {
       
  3762         $this->pass('Rolling back a transaction containing DDL should fail.');
       
  3763       }
       
  3764       $this->assertRowPresent('row');
       
  3765     }
       
  3766   }
       
  3767 
       
  3768   /**
       
  3769    * Insert a single row into the testing table.
       
  3770    */
       
  3771   protected function insertRow($name) {
       
  3772     db_insert('test')
       
  3773       ->fields(array(
       
  3774         'name' => $name,
       
  3775       ))
       
  3776       ->execute();
       
  3777   }
       
  3778 
       
  3779   /**
       
  3780    * Execute a DDL statement.
       
  3781    */
       
  3782   protected function executeDDLStatement() {
       
  3783     static $count = 0;
       
  3784     $table = array(
       
  3785       'fields' => array(
       
  3786         'id' => array(
       
  3787           'type' => 'serial',
       
  3788           'unsigned' => TRUE,
       
  3789           'not null' => TRUE,
       
  3790         ),
       
  3791       ),
       
  3792       'primary key' => array('id'),
       
  3793     );
       
  3794     db_create_table('database_test_' . ++$count, $table);
       
  3795   }
       
  3796 
       
  3797   /**
       
  3798    * Start over for a new test.
       
  3799    */
       
  3800   protected function cleanUp() {
       
  3801     db_truncate('test')
       
  3802       ->execute();
       
  3803   }
       
  3804 
       
  3805   /**
       
  3806    * Assert that a given row is present in the test table.
       
  3807    *
       
  3808    * @param $name
       
  3809    *   The name of the row.
       
  3810    * @param $message
       
  3811    *   The message to log for the assertion.
       
  3812    */
       
  3813   function assertRowPresent($name, $message = NULL) {
       
  3814     if (!isset($message)) {
       
  3815       $message = format_string('Row %name is present.', array('%name' => $name));
       
  3816     }
       
  3817     $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
       
  3818     return $this->assertTrue($present, $message);
       
  3819   }
       
  3820 
       
  3821   /**
       
  3822    * Assert that a given row is absent from the test table.
       
  3823    *
       
  3824    * @param $name
       
  3825    *   The name of the row.
       
  3826    * @param $message
       
  3827    *   The message to log for the assertion.
       
  3828    */
       
  3829   function assertRowAbsent($name, $message = NULL) {
       
  3830     if (!isset($message)) {
       
  3831       $message = format_string('Row %name is absent.', array('%name' => $name));
       
  3832     }
       
  3833     $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
       
  3834     return $this->assertFalse($present, $message);
       
  3835   }
       
  3836 
       
  3837   /**
       
  3838    * Test transaction stacking and commit / rollback.
       
  3839    */
       
  3840   function testTransactionStacking() {
       
  3841     // This test won't work right if transactions are not supported.
       
  3842     if (!Database::getConnection()->supportsTransactions()) {
       
  3843       return;
       
  3844     }
       
  3845 
       
  3846     $database = Database::getConnection();
       
  3847 
       
  3848     // Standard case: pop the inner transaction before the outer transaction.
       
  3849     $transaction = db_transaction();
       
  3850     $this->insertRow('outer');
       
  3851     $transaction2 = db_transaction();
       
  3852     $this->insertRow('inner');
       
  3853     // Pop the inner transaction.
       
  3854     unset($transaction2);
       
  3855     $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the inner transaction');
       
  3856     // Pop the outer transaction.
       
  3857     unset($transaction);
       
  3858     $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the outer transaction');
       
  3859     $this->assertRowPresent('outer');
       
  3860     $this->assertRowPresent('inner');
       
  3861 
       
  3862     // Pop the transaction in a different order they have been pushed.
       
  3863     $this->cleanUp();
       
  3864     $transaction = db_transaction();
       
  3865     $this->insertRow('outer');
       
  3866     $transaction2 = db_transaction();
       
  3867     $this->insertRow('inner');
       
  3868     // Pop the outer transaction, nothing should happen.
       
  3869     unset($transaction);
       
  3870     $this->insertRow('inner-after-outer-commit');
       
  3871     $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
       
  3872     // Pop the inner transaction, the whole transaction should commit.
       
  3873     unset($transaction2);
       
  3874     $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
       
  3875     $this->assertRowPresent('outer');
       
  3876     $this->assertRowPresent('inner');
       
  3877     $this->assertRowPresent('inner-after-outer-commit');
       
  3878 
       
  3879     // Rollback the inner transaction.
       
  3880     $this->cleanUp();
       
  3881     $transaction = db_transaction();
       
  3882     $this->insertRow('outer');
       
  3883     $transaction2 = db_transaction();
       
  3884     $this->insertRow('inner');
       
  3885     // Now rollback the inner transaction.
       
  3886     $transaction2->rollback();
       
  3887     unset($transaction2);
       
  3888     $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
       
  3889     // Pop the outer transaction, it should commit.
       
  3890     $this->insertRow('outer-after-inner-rollback');
       
  3891     unset($transaction);
       
  3892     $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
       
  3893     $this->assertRowPresent('outer');
       
  3894     $this->assertRowAbsent('inner');
       
  3895     $this->assertRowPresent('outer-after-inner-rollback');
       
  3896 
       
  3897     // Rollback the inner transaction after committing the outer one.
       
  3898     $this->cleanUp();
       
  3899     $transaction = db_transaction();
       
  3900     $this->insertRow('outer');
       
  3901     $transaction2 = db_transaction();
       
  3902     $this->insertRow('inner');
       
  3903     // Pop the outer transaction, nothing should happen.
       
  3904     unset($transaction);
       
  3905     $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
       
  3906     // Now rollback the inner transaction, it should rollback.
       
  3907     $transaction2->rollback();
       
  3908     unset($transaction2);
       
  3909     $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
       
  3910     $this->assertRowPresent('outer');
       
  3911     $this->assertRowAbsent('inner');
       
  3912 
       
  3913     // Rollback the outer transaction while the inner transaction is active.
       
  3914     // In that case, an exception will be triggered because we cannot
       
  3915     // ensure that the final result will have any meaning.
       
  3916     $this->cleanUp();
       
  3917     $transaction = db_transaction();
       
  3918     $this->insertRow('outer');
       
  3919     $transaction2 = db_transaction();
       
  3920     $this->insertRow('inner');
       
  3921     $transaction3 = db_transaction();
       
  3922     $this->insertRow('inner2');
       
  3923     // Rollback the outer transaction.
       
  3924     try {
       
  3925       $transaction->rollback();
       
  3926       unset($transaction);
       
  3927       $this->fail('Rolling back the outer transaction while the inner transaction is active resulted in an exception.');
       
  3928     }
       
  3929     catch (DatabaseTransactionOutOfOrderException $e) {
       
  3930       $this->pass('Rolling back the outer transaction while the inner transaction is active resulted in an exception.');
       
  3931     }
       
  3932     $this->assertFalse($database->inTransaction(), 'No more in a transaction after rolling back the outer transaction');
       
  3933     // Try to commit one inner transaction.
       
  3934     unset($transaction3);
       
  3935     $this->pass('Trying to commit an inner transaction resulted in an exception.');
       
  3936     // Try to rollback one inner transaction.
       
  3937     try {
       
  3938       $transaction->rollback();
       
  3939       unset($transaction2);
       
  3940       $this->fail('Trying to commit an inner transaction resulted in an exception.');
       
  3941     }
       
  3942     catch (DatabaseTransactionNoActiveException $e) {
       
  3943       $this->pass('Trying to commit an inner transaction resulted in an exception.');
       
  3944     }
       
  3945     $this->assertRowAbsent('outer');
       
  3946     $this->assertRowAbsent('inner');
       
  3947     $this->assertRowAbsent('inner2');
       
  3948   }
       
  3949 }
       
  3950 
       
  3951 
       
  3952 /**
       
  3953  * Check the sequences API.
       
  3954  */
       
  3955 class DatabaseNextIdCase extends DrupalWebTestCase {
       
  3956   public static function getInfo() {
       
  3957     return array(
       
  3958       'name' => 'Sequences API',
       
  3959       'description' => 'Test the secondary sequences API.',
       
  3960       'group' => 'Database',
       
  3961     );
       
  3962   }
       
  3963 
       
  3964   /**
       
  3965    * Test that the sequences API work.
       
  3966    */
       
  3967   function testDbNextId() {
       
  3968     $first = db_next_id();
       
  3969     $second = db_next_id();
       
  3970     // We can test for exact increase in here because we know there is no
       
  3971     // other process operating on these tables -- normally we could only
       
  3972     // expect $second > $first.
       
  3973     $this->assertEqual($first + 1, $second, 'The second call from a sequence provides a number increased by one.');
       
  3974     $result = db_next_id(1000);
       
  3975     $this->assertEqual($result, 1001, 'Sequence provides a larger number than the existing ID.');
       
  3976   }
       
  3977 }
       
  3978 
       
  3979 /**
       
  3980  * Tests the empty pseudo-statement class.
       
  3981  */
       
  3982 class DatabaseEmptyStatementTestCase extends DrupalWebTestCase {
       
  3983   public static function getInfo() {
       
  3984     return array(
       
  3985       'name' => 'Empty statement',
       
  3986       'description' => 'Test the empty pseudo-statement class.',
       
  3987       'group' => 'Database',
       
  3988     );
       
  3989   }
       
  3990 
       
  3991   /**
       
  3992    * Test that the empty result set behaves as empty.
       
  3993    */
       
  3994   function testEmpty() {
       
  3995     $result = new DatabaseStatementEmpty();
       
  3996 
       
  3997     $this->assertTrue($result instanceof DatabaseStatementInterface, 'Class implements expected interface');
       
  3998     $this->assertNull($result->fetchObject(), 'Null result returned.');
       
  3999   }
       
  4000 
       
  4001   /**
       
  4002    * Test that the empty result set iterates safely.
       
  4003    */
       
  4004   function testEmptyIteration() {
       
  4005     $result = new DatabaseStatementEmpty();
       
  4006 
       
  4007     foreach ($result as $record) {
       
  4008       $this->fail('Iterating empty result set should not iterate.');
       
  4009       return;
       
  4010     }
       
  4011 
       
  4012     $this->pass('Iterating empty result set skipped iteration.');
       
  4013   }
       
  4014 
       
  4015   /**
       
  4016    * Test that the empty result set mass-fetches in an expected way.
       
  4017    */
       
  4018   function testEmptyFetchAll() {
       
  4019     $result = new DatabaseStatementEmpty();
       
  4020 
       
  4021     $this->assertEqual($result->fetchAll(), array(), 'Empty array returned from empty result set.');
       
  4022   }
       
  4023 }
       
  4024 
       
  4025 /**
       
  4026  * Tests management of database connections.
       
  4027  */
       
  4028 class ConnectionUnitTest extends DrupalUnitTestCase {
       
  4029 
       
  4030   protected $key;
       
  4031   protected $target;
       
  4032 
       
  4033   protected $monitor;
       
  4034   protected $originalCount;
       
  4035 
       
  4036   public static function getInfo() {
       
  4037     return array(
       
  4038       'name' => 'Connection unit tests',
       
  4039       'description' => 'Tests management of database connections.',
       
  4040       'group' => 'Database',
       
  4041     );
       
  4042   }
       
  4043 
       
  4044   function setUp() {
       
  4045     parent::setUp();
       
  4046 
       
  4047     $this->key = 'default';
       
  4048     $this->originalTarget = 'default';
       
  4049     $this->target = 'DatabaseConnectionUnitTest';
       
  4050 
       
  4051     // Determine whether the database driver is MySQL. If it is not, the test
       
  4052     // methods will not be executed.
       
  4053     // @todo Make this test driver-agnostic, or find a proper way to skip it.
       
  4054     // @see http://drupal.org/node/1273478
       
  4055     $connection_info = Database::getConnectionInfo('default');
       
  4056     $this->skipTest = (bool) $connection_info['default']['driver'] != 'mysql';
       
  4057     if ($this->skipTest) {
       
  4058       // Insert an assertion to prevent Simpletest from interpreting the test
       
  4059       // as failure.
       
  4060       $this->pass('This test is only compatible with MySQL.');
       
  4061     }
       
  4062 
       
  4063     // Create an additional connection to monitor the connections being opened
       
  4064     // and closed in this test.
       
  4065     // @see TestBase::changeDatabasePrefix()
       
  4066     $connection_info = Database::getConnectionInfo('default');
       
  4067     Database::addConnectionInfo('default', 'monitor', $connection_info['default']);
       
  4068     global $databases;
       
  4069     $databases['default']['monitor'] = $connection_info['default'];
       
  4070     $this->monitor = Database::getConnection('monitor');
       
  4071   }
       
  4072 
       
  4073   /**
       
  4074    * Adds a new database connection info to Database.
       
  4075    */
       
  4076   protected function addConnection() {
       
  4077     // Add a new target to the connection, by cloning the current connection.
       
  4078     $connection_info = Database::getConnectionInfo($this->key);
       
  4079     Database::addConnectionInfo($this->key, $this->target, $connection_info[$this->originalTarget]);
       
  4080 
       
  4081     // Verify that the new target exists.
       
  4082     $info = Database::getConnectionInfo($this->key);
       
  4083     // Note: Custom assertion message to not expose database credentials.
       
  4084     $this->assertIdentical($info[$this->target], $connection_info[$this->key], 'New connection info found.');
       
  4085   }
       
  4086 
       
  4087   /**
       
  4088    * Returns the connection ID of the current test connection.
       
  4089    *
       
  4090    * @return integer
       
  4091    */
       
  4092   protected function getConnectionID() {
       
  4093     return (int) Database::getConnection($this->target, $this->key)->query('SELECT CONNECTION_ID()')->fetchField();
       
  4094   }
       
  4095 
       
  4096   /**
       
  4097    * Asserts that a connection ID exists.
       
  4098    *
       
  4099    * @param integer $id
       
  4100    *   The connection ID to verify.
       
  4101    */
       
  4102   protected function assertConnection($id) {
       
  4103     $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
       
  4104     return $this->assertTrue(isset($list[$id]), format_string('Connection ID @id found.', array('@id' => $id)));
       
  4105   }
       
  4106 
       
  4107   /**
       
  4108    * Asserts that a connection ID does not exist.
       
  4109    *
       
  4110    * @param integer $id
       
  4111    *   The connection ID to verify.
       
  4112    */
       
  4113   protected function assertNoConnection($id) {
       
  4114     $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
       
  4115     return $this->assertFalse(isset($list[$id]), format_string('Connection ID @id not found.', array('@id' => $id)));
       
  4116   }
       
  4117 
       
  4118   /**
       
  4119    * Tests Database::closeConnection() without query.
       
  4120    *
       
  4121    * @todo getConnectionID() executes a query.
       
  4122    */
       
  4123   function testOpenClose() {
       
  4124     if ($this->skipTest) {
       
  4125       return;
       
  4126     }
       
  4127     // Add and open a new connection.
       
  4128     $this->addConnection();
       
  4129     $id = $this->getConnectionID();
       
  4130     Database::getConnection($this->target, $this->key);
       
  4131 
       
  4132     // Verify that there is a new connection.
       
  4133     $this->assertConnection($id);
       
  4134 
       
  4135     // Close the connection.
       
  4136     Database::closeConnection($this->target, $this->key);
       
  4137     // Wait 20ms to give the database engine sufficient time to react.
       
  4138     usleep(20000);
       
  4139 
       
  4140     // Verify that we are back to the original connection count.
       
  4141     $this->assertNoConnection($id);
       
  4142   }
       
  4143 
       
  4144   /**
       
  4145    * Tests Database::closeConnection() with a query.
       
  4146    */
       
  4147   function testOpenQueryClose() {
       
  4148     if ($this->skipTest) {
       
  4149       return;
       
  4150     }
       
  4151     // Add and open a new connection.
       
  4152     $this->addConnection();
       
  4153     $id = $this->getConnectionID();
       
  4154     Database::getConnection($this->target, $this->key);
       
  4155 
       
  4156     // Verify that there is a new connection.
       
  4157     $this->assertConnection($id);
       
  4158 
       
  4159     // Execute a query.
       
  4160     Database::getConnection($this->target, $this->key)->query('SHOW TABLES');
       
  4161 
       
  4162     // Close the connection.
       
  4163     Database::closeConnection($this->target, $this->key);
       
  4164     // Wait 20ms to give the database engine sufficient time to react.
       
  4165     usleep(20000);
       
  4166 
       
  4167     // Verify that we are back to the original connection count.
       
  4168     $this->assertNoConnection($id);
       
  4169   }
       
  4170 
       
  4171   /**
       
  4172    * Tests Database::closeConnection() with a query and custom prefetch method.
       
  4173    */
       
  4174   function testOpenQueryPrefetchClose() {
       
  4175     if ($this->skipTest) {
       
  4176       return;
       
  4177     }
       
  4178     // Add and open a new connection.
       
  4179     $this->addConnection();
       
  4180     $id = $this->getConnectionID();
       
  4181     Database::getConnection($this->target, $this->key);
       
  4182 
       
  4183     // Verify that there is a new connection.
       
  4184     $this->assertConnection($id);
       
  4185 
       
  4186     // Execute a query.
       
  4187     Database::getConnection($this->target, $this->key)->query('SHOW TABLES')->fetchCol();
       
  4188 
       
  4189     // Close the connection.
       
  4190     Database::closeConnection($this->target, $this->key);
       
  4191     // Wait 20ms to give the database engine sufficient time to react.
       
  4192     usleep(20000);
       
  4193 
       
  4194     // Verify that we are back to the original connection count.
       
  4195     $this->assertNoConnection($id);
       
  4196   }
       
  4197 
       
  4198   /**
       
  4199    * Tests Database::closeConnection() with a select query.
       
  4200    */
       
  4201   function testOpenSelectQueryClose() {
       
  4202     if ($this->skipTest) {
       
  4203       return;
       
  4204     }
       
  4205     // Add and open a new connection.
       
  4206     $this->addConnection();
       
  4207     $id = $this->getConnectionID();
       
  4208     Database::getConnection($this->target, $this->key);
       
  4209 
       
  4210     // Verify that there is a new connection.
       
  4211     $this->assertConnection($id);
       
  4212 
       
  4213     // Create a table.
       
  4214     $name = 'foo';
       
  4215     Database::getConnection($this->target, $this->key)->schema()->createTable($name, array(
       
  4216       'fields' => array(
       
  4217         'name' => array(
       
  4218           'type' => 'varchar',
       
  4219           'length' => 255,
       
  4220         ),
       
  4221       ),
       
  4222     ));
       
  4223 
       
  4224     // Execute a query.
       
  4225     Database::getConnection($this->target, $this->key)->select('foo', 'f')
       
  4226       ->fields('f', array('name'))
       
  4227       ->execute()
       
  4228       ->fetchAll();
       
  4229 
       
  4230     // Drop the table.
       
  4231     Database::getConnection($this->target, $this->key)->schema()->dropTable($name);
       
  4232 
       
  4233     // Close the connection.
       
  4234     Database::closeConnection($this->target, $this->key);
       
  4235     // Wait 20ms to give the database engine sufficient time to react.
       
  4236     usleep(20000);
       
  4237 
       
  4238     // Verify that we are back to the original connection count.
       
  4239     $this->assertNoConnection($id);
       
  4240   }
       
  4241 
       
  4242 }