Drupal PHP Cross Reference Content Management Systems

Source: /modules/simpletest/tests/database_test.test - 3817 lines - 134698 bytes - Summary - Text - Print

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