home | career | drupal | java | mac | mysql | perl | php | scala | uml | unix

Drupal example source code file (database_test.test)

This example Drupal source code file (database_test.test) is included in the DevDaily.com "Drupal Source Code Warehouse" project. The intent of this project is to help you "Learn Drupal by Example".

PHP - Drupal tags/keywords

age, array, correct, count, from, function, name, php, query, record, result, test, the, where

The database_test.test Drupal example source code

<?php
// $Id: database_test.test,v 1.110 2010/12/31 20:43:43 webchick Exp $

/**
 * Dummy class for fetching into a class.
 *
 * PDO supports using a new instance of an arbitrary class for records
 * rather than just a stdClass or array. This class is for testing that
 * functionality. (See testQueryFetchClass() below)
 */
class FakeRecord { }

/**
 * Base test class for databases.
 *
 * Because all database tests share the same test data, we can centralize that
 * here.
 */
class DatabaseTestCase extends DrupalWebTestCase {
  protected $profile = 'testing';

  function setUp() {
    parent::setUp('database_test');

    $schema['test'] = drupal_get_schema('test');
    $schema['test_people'] = drupal_get_schema('test_people');
    $schema['test_one_blob'] = drupal_get_schema('test_one_blob');
    $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs');
    $schema['test_task'] = drupal_get_schema('test_task');

    $this->installTables($schema);

    $this->addSampleData();
  }

  /**
   * Set up several tables needed by a certain test.
   *
   * @param $schema
   *   An array of table definitions to install.
   */
  function installTables($schema) {
    // This ends up being a test for table drop and create, too, which is nice.
    foreach ($schema as $name => $data) {
      if (db_table_exists($name)) {
        db_drop_table($name);
      }
      db_create_table($name, $data);
    }

    foreach ($schema as $name => $data) {
      $this->assertTrue(db_table_exists($name), t('Table @name created successfully.', array('@name' => $name)));
    }
  }

  /**
   * Set up tables for NULL handling.
   */
  function ensureSampleDataNull() {
    $schema['test_null'] = drupal_get_schema('test_null');
    $this->installTables($schema);

    db_insert('test_null')
    ->fields(array('name', 'age'))
    ->values(array(
      'name' => 'Kermit',
      'age' => 25,
    ))
    ->values(array(
      'name' => 'Fozzie',
      'age' => NULL,
    ))
    ->values(array(
      'name' => 'Gonzo',
      'age' => 27,
    ))
    ->execute();
  }

  /**
   * Setup our sample data.
   *
   * These are added using db_query(), since we're not trying to test the
   * INSERT operations here, just populate.
   */
  function addSampleData() {
    // We need the IDs, so we can't use a multi-insert here.
    $john = db_insert('test')
      ->fields(array(
        'name' => 'John',
        'age' => 25,
        'job' => 'Singer',
      ))
      ->execute();

    $george = db_insert('test')
      ->fields(array(
        'name' => 'George',
        'age' => 27,
        'job' => 'Singer',
      ))
      ->execute();

    $ringo = db_insert('test')
      ->fields(array(
        'name' => 'Ringo',
        'age' => 28,
        'job' => 'Drummer',
      ))
      ->execute();

    $paul = db_insert('test')
      ->fields(array(
        'name' => 'Paul',
        'age' => 26,
        'job' => 'Songwriter',
      ))
      ->execute();

    db_insert('test_people')
      ->fields(array(
        'name' => 'Meredith',
        'age' => 30,
        'job' => 'Speaker',
      ))
      ->execute();

    db_insert('test_task')
      ->fields(array('pid', 'task', 'priority'))
      ->values(array(
        'pid' => $john,
        'task' => 'eat',
        'priority' => 3,
      ))
      ->values(array(
        'pid' => $john,
        'task' => 'sleep',
        'priority' => 4,
      ))
      ->values(array(
        'pid' => $john,
        'task' => 'code',
        'priority' => 1,
      ))
      ->values(array(
        'pid' => $george,
        'task' => 'sing',
        'priority' => 2,
      ))
      ->values(array(
        'pid' => $george,
        'task' => 'sleep',
        'priority' => 2,
      ))
      ->values(array(
        'pid' => $paul,
        'task' => 'found new band',
        'priority' => 1,
      ))
      ->values(array(
        'pid' => $paul,
        'task' => 'perform at superbowl',
        'priority' => 3,
      ))
      ->execute();
  }
}

/**
 * Test connection management.
 */
class DatabaseConnectionTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Connection tests',
      'description' => 'Tests of the core database system.',
      'group' => 'Database',
    );
  }

  /**
   * Test that connections return appropriate connection objects.
   */
  function testConnectionRouting() {
    // Clone the master credentials to a slave connection.
    // Note this will result in two independent connection objects that happen
    // to point to the same place.
    $connection_info = Database::getConnectionInfo('default');
    Database::addConnectionInfo('default', 'slave', $connection_info['default']);

    $db1 = Database::getConnection('default', 'default');
    $db2 = Database::getConnection('slave', 'default');

    $this->assertNotNull($db1, t('default connection is a real connection object.'));
    $this->assertNotNull($db2, t('slave connection is a real connection object.'));
    $this->assertNotIdentical($db1, $db2, t('Each target refers to a different connection.'));

    // Try to open those targets another time, that should return the same objects.
    $db1b = Database::getConnection('default', 'default');
    $db2b = Database::getConnection('slave', 'default');
    $this->assertIdentical($db1, $db1b, t('A second call to getConnection() returns the same object.'));
    $this->assertIdentical($db2, $db2b, t('A second call to getConnection() returns the same object.'));

    // Try to open an unknown target.
    $unknown_target = $this->randomName();
    $db3 = Database::getConnection($unknown_target, 'default');
    $this->assertNotNull($db3, t('Opening an unknown target returns a real connection object.'));
    $this->assertIdentical($db1, $db3, t('An unknown target opens the default connection.'));

    // Try to open that unknown target another time, that should return the same object.
    $db3b = Database::getConnection($unknown_target, 'default');
    $this->assertIdentical($db3, $db3b, t('A second call to getConnection() returns the same object.'));
  }

  /**
   * Test that connections return appropriate connection objects.
   */
  function testConnectionRoutingOverride() {
    // Clone the master credentials to a slave connection.
    // Note this will result in two independent connection objects that happen
    // to point to the same place.
    $connection_info = Database::getConnectionInfo('default');
    Database::addConnectionInfo('default', 'slave', $connection_info['default']);

    Database::ignoreTarget('default', 'slave');

    $db1 = Database::getConnection('default', 'default');
    $db2 = Database::getConnection('slave', 'default');

    $this->assertIdentical($db1, $db2, t('Both targets refer to the same connection.'));
  }

  /**
   * Tests the closing of a database connection.
   */
  function testConnectionClosing() {
    // Open the default target so we have an object to compare.
    $db1 = Database::getConnection('default', 'default');

    // Try to close the the default connection, then open a new one.
    Database::closeConnection('default', 'default');
    $db2 = Database::getConnection('default', 'default');

    // Opening a connection after closing it should yield an object different than the original.
    $this->assertNotIdentical($db1, $db2, t('Opening the default connection after it is closed returns a new object.'));
  }

  /**
   * Tests the connection options of the active database.
   */
  function testConnectionOptions() {
    $connection_info = Database::getConnectionInfo('default');

    // Be sure we're connected to the default database.
    $db = Database::getConnection('default', 'default');
    $connectionOptions = $db->getConnectionOptions();

    // In the MySQL driver, the port can be different, so check individual
    // options.
    $this->assertEqual($connection_info['default']['driver'], $connectionOptions['driver'], t('The default connection info driver matches the current connection options driver.'));
    $this->assertEqual($connection_info['default']['database'], $connectionOptions['database'], t('The default connection info database matches the current connection options database.'));

    // Set up identical slave and confirm connection options are identical.
    Database::addConnectionInfo('default', 'slave', $connection_info['default']);
    $db2 = Database::getConnection('slave', 'default');
    $connectionOptions2 = $db2->getConnectionOptions();

    // Get a fresh copy of the default connection options.
    $connectionOptions = $db->getConnectionOptions();
    $this->assertIdentical($connectionOptions, $connectionOptions2, t('The default and slave connection options are identical.'));

    // Set up a new connection with different connection info.
    $test = $connection_info['default'];
    $test['database'] .= 'test';
    Database::addConnectionInfo('test', 'default', $test);
    $connection_info = Database::getConnectionInfo('test');

    // Get a fresh copy of the default connection options.
    $connectionOptions = $db->getConnectionOptions();
    $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], t('The test connection info database does not match the current connection options database.'));
  }
}

/**
 * Test fetch actions, part 1.
 *
 * We get timeout errors if we try to run too many tests at once.
 */
class DatabaseFetchTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Fetch tests',
      'description' => 'Test the Database system\'s various fetch capabilities.',
      'group' => 'Database',
    );
  }

  /**
   * Confirm that we can fetch a record properly in default object mode.
   */
  function testQueryFetchDefault() {
    $records = array();
    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25));
    $this->assertTrue($result instanceof DatabaseStatementInterface, t('Result set is a Drupal statement object.'));
    foreach ($result as $record) {
      $records[] = $record;
      $this->assertTrue(is_object($record), t('Record is an object.'));
      $this->assertIdentical($record->name, 'John', t('25 year old is John.'));
    }

    $this->assertIdentical(count($records), 1, t('There is only one record.'));
  }

  /**
   * Confirm that we can fetch a record to an object explicitly.
   */
  function testQueryFetchObject() {
    $records = array();
    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_OBJ));
    foreach ($result as $record) {
      $records[] = $record;
      $this->assertTrue(is_object($record), t('Record is an object.'));
      $this->assertIdentical($record->name, 'John', t('25 year old is John.'));
    }

    $this->assertIdentical(count($records), 1, t('There is only one record.'));
  }

  /**
   * Confirm that we can fetch a record to an array associative explicitly.
   */
  function testQueryFetchArray() {
    $records = array();
    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_ASSOC));
    foreach ($result as $record) {
      $records[] = $record;
      if ($this->assertTrue(is_array($record), t('Record is an array.'))) {
        $this->assertIdentical($record['name'], 'John', t('Record can be accessed associatively.'));
      }
    }

    $this->assertIdentical(count($records), 1, t('There is only one record.'));
  }

  /**
   * Confirm that we can fetch a record into a new instance of a custom class.
   *
   * @see FakeRecord
   */
  function testQueryFetchClass() {
    $records = array();
    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => 'FakeRecord'));
    foreach ($result as $record) {
      $records[] = $record;
      if ($this->assertTrue($record instanceof FakeRecord, t('Record is an object of class FakeRecord.'))) {
        $this->assertIdentical($record->name, 'John', t('25 year old is John.'));
      }
    }

    $this->assertIdentical(count($records), 1, t('There is only one record.'));
  }
}

/**
 * Test fetch actions, part 2.
 *
 * We get timeout errors if we try to run too many tests at once.
 */
class DatabaseFetch2TestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Fetch tests, part 2',
      'description' => 'Test the Database system\'s various fetch capabilities.',
      'group' => 'Database',
    );
  }

  function setUp() {
    parent::setUp();
  }

  // Confirm that we can fetch a record into an indexed array explicitly.
  function testQueryFetchNum() {
    $records = array();
    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_NUM));
    foreach ($result as $record) {
      $records[] = $record;
      if ($this->assertTrue(is_array($record), t('Record is an array.'))) {
        $this->assertIdentical($record[0], 'John', t('Record can be accessed numerically.'));
      }
    }

    $this->assertIdentical(count($records), 1, 'There is only one record');
  }

  /**
   * Confirm that we can fetch a record into a doubly-keyed array explicitly.
   */
  function testQueryFetchBoth() {
    $records = array();
    $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_BOTH));
    foreach ($result as $record) {
      $records[] = $record;
      if ($this->assertTrue(is_array($record), t('Record is an array.'))) {
        $this->assertIdentical($record[0], 'John', t('Record can be accessed numerically.'));
        $this->assertIdentical($record['name'], 'John', t('Record can be accessed associatively.'));
      }
    }

    $this->assertIdentical(count($records), 1, t('There is only one record.'));
  }

  /**
   * Confirm that we can fetch an entire column of a result set at once.
   */
  function testQueryFetchCol() {
    $records = array();
    $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
    $column = $result->fetchCol();
    $this->assertIdentical(count($column), 3, t('fetchCol() returns the right number of records.'));

    $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
    $i = 0;
    foreach ($result as $record) {
      $this->assertIdentical($record->name, $column[$i++], t('Column matches direct accesss.'));
    }
  }
}

/**
 * Test the insert builder.
 */
class DatabaseInsertTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Insert tests',
      'description' => 'Test the Insert query builder.',
      'group' => 'Database',
    );
  }

  /**
   * Test the very basic insert functionality.
   */
  function testSimpleInsert() {
    $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();

    $query = db_insert('test');
    $query->fields(array(
      'name' => 'Yoko',
      'age' => '29',
    ));
    $query->execute();

    $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
    $this->assertIdentical($num_records_before + 1, (int) $num_records_after, t('Record inserts correctly.'));
    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Yoko'))->fetchField();
    $this->assertIdentical($saved_age, '29', t('Can retrieve after inserting.'));
  }

  /**
   * Test that we can insert multiple records in one query object.
   */
  function testMultiInsert() {
    $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();

    $query = db_insert('test');
    $query->fields(array(
      'name' => 'Larry',
      'age' => '30',
    ));

    // We should be able to specify values in any order if named.
    $query->values(array(
      'age' => '31',
      'name' => 'Curly',
    ));

    // We should be able to say "use the field order".
    // This is not the recommended mechanism for most cases, but it should work.
    $query->values(array('Moe', '32'));
    $query->execute();

    $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
    $this->assertIdentical($num_records_before + 3, $num_records_after, t('Record inserts correctly.'));
    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
    $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.'));
    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
    $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.'));
  }

  /**
   * Test that an insert object can be reused with new data after it executes.
   */
  function testRepeatedInsert() {
    $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();

    $query = db_insert('test');

    $query->fields(array(
      'name' => 'Larry',
      'age' => '30',
    ));
    $query->execute();  // This should run the insert, but leave the fields intact.

    // We should be able to specify values in any order if named.
    $query->values(array(
      'age' => '31',
      'name' => 'Curly',
    ));
    $query->execute();

    // We should be able to say "use the field order".
    $query->values(array('Moe', '32'));
    $query->execute();

    $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
    $this->assertIdentical((int) $num_records_before + 3, (int) $num_records_after, t('Record inserts correctly.'));
    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
    $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.'));
    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
    $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.'));
  }

  /**
   * Test that we can specify fields without values and specify values later.
   */
  function testInsertFieldOnlyDefinintion() {
    // This is useful for importers, when we want to create a query and define
    // its fields once, then loop over a multi-insert execution.
    db_insert('test')
      ->fields(array('name', 'age'))
      ->values(array('Larry', '30'))
      ->values(array('Curly', '31'))
      ->values(array('Moe', '32'))
      ->execute();
    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
    $this->assertIdentical($saved_age, '31', t('Can retrieve after inserting.'));
    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
    $this->assertIdentical($saved_age, '32', t('Can retrieve after inserting.'));
  }

  /**
   * Test that inserts return the proper auto-increment ID.
   */
  function testInsertLastInsertID() {
    $id = db_insert('test')
      ->fields(array(
        'name' => 'Larry',
        'age' => '30',
      ))
      ->execute();

    $this->assertIdentical($id, '5', t('Auto-increment ID returned successfully.'));
  }

  /**
   * Test that the INSERT INTO ... SELECT ... syntax works.
   */
  function testInsertSelect() {
    $query = db_select('test_people', 'tp');
    // The query builder will always append expressions after fields.
    // Add the expression first to test that the insert fields are correctly
    // re-ordered.
    $query->addExpression('tp.age', 'age');
    $query
      ->fields('tp', array('name','job'))
      ->condition('tp.name', 'Meredith');

    // The resulting query should be equivalent to:
    // INSERT INTO test (age, name, job)
    // SELECT tp.age AS age, tp.name AS name, tp.job AS job
    // FROM test_people tp
    // WHERE tp.name = 'Meredith'
    db_insert('test')
      ->from($query)
      ->execute();

    $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();
    $this->assertIdentical($saved_age, '30', t('Can retrieve after inserting.'));
  }
}

/**
 * Insert tests using LOB fields, which are weird on some databases.
 */
class DatabaseInsertLOBTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Insert tests, LOB fields',
      'description' => 'Test the Insert query builder with LOB fields.',
      'group' => 'Database',
    );
  }

  /**
   * Test that we can insert a single blob field successfully.
   */
  function testInsertOneBlob() {
    $data = "This is\000a test.";
    $this->assertTrue(strlen($data) === 15, t('Test data contains a NULL.'));
    $id = db_insert('test_one_blob')
      ->fields(array('blob1' => $data))
      ->execute();
    $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
    $this->assertTrue($r['blob1'] === $data, t('Can insert a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
  }

  /**
   * Test that we can insert multiple blob fields in the same query.
   */
  function testInsertMultipleBlob() {
    $id = db_insert('test_two_blobs')
      ->fields(array(
        'blob1' => 'This is',
        'blob2' => 'a test',
      ))
      ->execute();
    $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
    $this->assertTrue($r['blob1'] === 'This is' && $r['blob2'] === 'a test', t('Can insert multiple blobs per row.'));
  }
}

/**
 * Insert tests for "database default" values.
 */
class DatabaseInsertDefaultsTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Insert tests, default fields',
      'description' => 'Test the Insert query builder with default values.',
      'group' => 'Database',
    );
  }

  /**
   * Test that we can run a query that is "default values for everything".
   */
  function testDefaultInsert() {
    $query = db_insert('test')->useDefaults(array('job'));
    $id = $query->execute();

    $schema = drupal_get_schema('test');

    $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
    $this->assertEqual($job, $schema['fields']['job']['default'], t('Default field value is set.'));
  }

  /**
   * Test that no action will be preformed if no fields are specified.
   */
  function testDefaultEmptyInsert() {
    $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();

    try {
      $result = db_insert('test')->execute();
      // This is only executed if no exception has been thrown.
      $this->fail(t('Expected exception NoFieldsException has not been thrown.'));
    } catch (NoFieldsException $e) {
      $this->pass(t('Expected exception NoFieldsException has been thrown.'));
    }

    $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
    $this->assertIdentical($num_records_before, $num_records_after, t('Do nothing as no fields are specified.'));
  }

  /**
   * Test that we can insert fields with values and defaults in the same query.
   */
  function testDefaultInsertWithFields() {
    $query = db_insert('test')
      ->fields(array('name' => 'Bob'))
      ->useDefaults(array('job'));
    $id = $query->execute();

    $schema = drupal_get_schema('test');

    $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
    $this->assertEqual($job, $schema['fields']['job']['default'], t('Default field value is set.'));
  }
}

/**
 * Update builder tests.
 */
class DatabaseUpdateTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Update tests',
      'description' => 'Test the Update query builder.',
      'group' => 'Database',
    );
  }

  /**
   * Confirm that we can update a single record successfully.
   */
  function testSimpleUpdate() {
    $num_updated = db_update('test')
      ->fields(array('name' => 'Tiffany'))
      ->condition('id', 1)
      ->execute();
    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));

    $saved_name = db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 1))->fetchField();
    $this->assertIdentical($saved_name, 'Tiffany', t('Updated name successfully.'));
  }

  /**
   * Confirm that we can update a multiple records successfully.
   */
  function testMultiUpdate() {
    $num_updated = db_update('test')
      ->fields(array('job' => 'Musician'))
      ->condition('job', 'Singer')
      ->execute();
    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));

    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
  }

  /**
   * Confirm that we can update a multiple records with a non-equality condition.
   */
  function testMultiGTUpdate() {
    $num_updated = db_update('test')
      ->fields(array('job' => 'Musician'))
      ->condition('age', 26, '>')
      ->execute();
    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));

    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
  }

  /**
   * Confirm that we can update a multiple records with a where call.
   */
  function testWhereUpdate() {
    $num_updated = db_update('test')
      ->fields(array('job' => 'Musician'))
      ->where('age > :age', array(':age' => 26))
      ->execute();
    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));

    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
  }

  /**
   * Confirm that we can stack condition and where calls.
   */
  function testWhereAndConditionUpdate() {
    $update = db_update('test')
      ->fields(array('job' => 'Musician'))
      ->where('age > :age', array(':age' => 26))
      ->condition('name', 'Ringo');
    $num_updated = $update->execute();
    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));

    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));
  }

  /**
   * Test updating with expressions.
   */
  function testExpressionUpdate() {
    // Set age = 1 for a single row for this test to work.
    db_update('test')
      ->condition('id', 1)
      ->fields(array('age' => 1))
      ->execute();

    // Ensure that expressions are handled properly.  This should set every
    // record's age to a square of itself, which will change only three of the
    // four records in the table since 1*1 = 1. That means only three records
    // are modified, so we should get back 3, not 4, from execute().
    $num_rows = db_update('test')
      ->expression('age', 'age * age')
      ->execute();
    $this->assertIdentical($num_rows, 3, t('Number of affected rows are returned.'));
  }
}

/**
 * Tests for more complex update statements.
 */
class DatabaseUpdateComplexTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Update tests, Complex',
      'description' => 'Test the Update query builder, complex queries.',
      'group' => 'Database',
    );
  }

  /**
   * Test updates with OR conditionals.
   */
  function testOrConditionUpdate() {
    $update = db_update('test')
      ->fields(array('job' => 'Musician'))
      ->condition(db_or()
        ->condition('name', 'John')
        ->condition('name', 'Paul')
      );
    $num_updated = $update->execute();
    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));

    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
  }

  /**
   * Test WHERE IN clauses.
   */
  function testInConditionUpdate() {
    $num_updated = db_update('test')
      ->fields(array('job' => 'Musician'))
      ->condition('name', array('John', 'Paul'), 'IN')
      ->execute();
    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));

    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
  }

  /**
   * Test WHERE NOT IN clauses.
   */
  function testNotInConditionUpdate() {
    // The o is lowercase in the 'NoT IN' operator, to make sure the operators
    // work in mixed case.
    $num_updated = db_update('test')
      ->fields(array('job' => 'Musician'))
      ->condition('name', array('John', 'Paul', 'George'), 'NoT IN')
      ->execute();
    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));

    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));
  }

  /**
   * Test BETWEEN conditional clauses.
   */
  function testBetweenConditionUpdate() {
    $num_updated = db_update('test')
      ->fields(array('job' => 'Musician'))
      ->condition('age', array(25, 26), 'BETWEEN')
      ->execute();
    $this->assertIdentical($num_updated, 2, t('Updated 2 records.'));

    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
    $this->assertIdentical($num_matches, '2', t('Updated fields successfully.'));
  }

  /**
   * Test LIKE conditionals.
   */
  function testLikeConditionUpdate() {
    $num_updated = db_update('test')
      ->fields(array('job' => 'Musician'))
      ->condition('name', '%ge%', 'LIKE')
      ->execute();
    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));

    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));
  }

  /**
   * Test update with expression values.
   */
  function testUpdateExpression() {
    $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
    $GLOBALS['larry_test'] = 1;
    $num_updated = db_update('test')
      ->condition('name', 'Ringo')
      ->fields(array('job' => 'Musician'))
      ->expression('age', 'age + :age', array(':age' => 4))
      ->execute();
    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));

    $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
    $this->assertIdentical($num_matches, '1', t('Updated fields successfully.'));

    $person = db_query('SELECT * FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetch();
    $this->assertEqual($person->name, 'Ringo', t('Name set correctly.'));
    $this->assertEqual($person->age, $before_age + 4, t('Age set correctly.'));
    $this->assertEqual($person->job, 'Musician', t('Job set correctly.'));
    $GLOBALS['larry_test'] = 0;
  }

  /**
   * Test update with only expression values.
   */
  function testUpdateOnlyExpression() {
    $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
    $num_updated = db_update('test')
      ->condition('name', 'Ringo')
      ->expression('age', 'age + :age', array(':age' => 4))
      ->execute();
    $this->assertIdentical($num_updated, 1, t('Updated 1 record.'));

    $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
    $this->assertEqual($before_age + 4, $after_age, t('Age updated correctly'));
  }
}

/**
 * Test update queries involving LOB values.
 */
class DatabaseUpdateLOBTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Update tests, LOB',
      'description' => 'Test the Update query builder with LOB fields.',
      'group' => 'Database',
    );
  }

  /**
   * Confirm that we can update a blob column.
   */
  function testUpdateOneBlob() {
    $data = "This is\000a test.";
    $this->assertTrue(strlen($data) === 15, t('Test data contains a NULL.'));
    $id = db_insert('test_one_blob')
      ->fields(array('blob1' => $data))
      ->execute();

    $data .= $data;
    db_update('test_one_blob')
      ->condition('id', $id)
      ->fields(array('blob1' => $data))
      ->execute();

    $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
    $this->assertTrue($r['blob1'] === $data, t('Can update a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
  }

  /**
   * Confirm that we can update two blob columns in the same table.
   */
  function testUpdateMultipleBlob() {
    $id = db_insert('test_two_blobs')
      ->fields(array(
        'blob1' => 'This is',
        'blob2' => 'a test',
      ))
      ->execute();

    db_update('test_two_blobs')
      ->condition('id', $id)
      ->fields(array('blob1' => 'and so', 'blob2' => 'is this'))
      ->execute();

    $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
    $this->assertTrue($r['blob1'] === 'and so' && $r['blob2'] === 'is this', t('Can update multiple blobs per row.'));
  }
}

/**
 * Delete/Truncate tests.
 *
 * The DELETE tests are not as extensive, as all of the interesting code for
 * DELETE queries is in the conditional which is identical to the UPDATE and
 * SELECT conditional handling.
 *
 * The TRUNCATE tests are not extensive either, because the behavior of
 * TRUNCATE queries is not consistent across database engines. We only test
 * that a TRUNCATE query actually deletes all rows from the target table.
 */
class DatabaseDeleteTruncateTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Delete/Truncate tests',
      'description' => 'Test the Delete and Truncate query builders.',
      'group' => 'Database',
    );
  }

  /**
   * Confirm that we can use a subselect in a delete successfully.
   */
  function testSubselectDelete() {
    $num_records_before = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
    $pid_to_delete = db_query("SELECT * FROM {test_task} WHERE task = 'sleep'")->fetchField();

    $subquery = db_select('test', 't')
      ->fields('t', array('id'))
      ->condition('t.id', array($pid_to_delete), 'IN');
    $delete = db_delete('test_task')
      ->condition('task', 'sleep')
      ->condition('pid', $subquery, 'IN');

    $num_deleted = $delete->execute();
    $this->assertEqual($num_deleted, 1, t("Deleted 1 record."));

    $num_records_after = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
    $this->assertEqual($num_records_before, $num_records_after + $num_deleted, t('Deletion adds up.'));
  }

  /**
   * Confirm that we can delete a single record successfully.
   */
  function testSimpleDelete() {
    $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();

    $num_deleted = db_delete('test')
      ->condition('id', 1)
      ->execute();
    $this->assertIdentical($num_deleted, 1, t('Deleted 1 record.'));

    $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
    $this->assertEqual($num_records_before, $num_records_after + $num_deleted, t('Deletion adds up.'));
  }

  /**
   * Confirm that we can truncate a whole table successfully.
   */
  function testTruncate() {
    $num_records_before = db_query("SELECT COUNT(*) FROM {test}")->fetchField();

    db_truncate('test')->execute();

    $num_records_after = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
    $this->assertEqual(0, $num_records_after, t('Truncate really deletes everything.'));
  }
}

/**
 * Test the MERGE query builder.
 */
class DatabaseMergeTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Merge tests',
      'description' => 'Test the Merge query builder.',
      'group' => 'Database',
    );
  }

  /**
   * Confirm that we can merge-insert a record successfully.
   */
  function testMergeInsert() {
    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();

    $result = db_merge('test_people')
      ->key(array('job' => 'Presenter'))
      ->fields(array(
        'age' => 31,
        'name' => 'Tiffany',
      ))
      ->execute();

    $this->assertEqual($result, MergeQuery::STATUS_INSERT, t('Insert status returned.'));

    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
    $this->assertEqual($num_records_before + 1, $num_records_after, t('Merge inserted properly.'));

    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
    $this->assertEqual($person->age, 31, t('Age set correctly.'));
    $this->assertEqual($person->job, 'Presenter', t('Job set correctly.'));
  }

  /**
   * Confirm that we can merge-update a record successfully.
   */
  function testMergeUpdate() {
    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();

    $result = db_merge('test_people')
      ->key(array('job' => 'Speaker'))
      ->fields(array(
        'age' => 31,
        'name' => 'Tiffany',
      ))
      ->execute();

    $this->assertEqual($result, MergeQuery::STATUS_UPDATE, t('Update status returned.'));

    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));

    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
    $this->assertEqual($person->age, 31, t('Age set correctly.'));
    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
  }

  /**
   * Confirm that we can merge-update a record successfully, with different insert and update.
   */
  function testMergeUpdateExcept() {
    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();

    db_merge('test_people')
      ->key(array('job' => 'Speaker'))
      ->insertFields(array('age' => 31))
      ->updateFields(array('name' => 'Tiffany'))
      ->execute();

    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));

    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
  }

  /**
   * Confirm that we can merge-update a record successfully, with alternate replacement.
   */
  function testMergeUpdateExplicit() {
    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();

    db_merge('test_people')
      ->key(array('job' => 'Speaker'))
      ->insertFields(array(
        'age' => 31,
        'name' => 'Tiffany',
      ))
      ->updateFields(array(
        'name' => 'Joe',
      ))
      ->execute();

    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));

    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
    $this->assertEqual($person->name, 'Joe', t('Name set correctly.'));
    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
  }

  /**
   * Confirm that we can merge-update a record successfully, with expressions.
   */
  function testMergeUpdateExpression() {
    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();

    $age_before = db_query('SELECT age FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetchField();

    // This is a very contrived example, as I have no idea why you'd want to
    // change age this way, but that's beside the point.
    // Note that we are also double-setting age here, once as a literal and
    // once as an expression. This test will only pass if the expression wins,
    // which is what is supposed to happen.
    db_merge('test_people')
      ->key(array('job' => 'Speaker'))
      ->fields(array('name' => 'Tiffany'))
      ->insertFields(array('age' => 31))
      ->expression('age', 'age + :age', array(':age' => 4))
      ->execute();

    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
    $this->assertEqual($num_records_before, $num_records_after, t('Merge updated properly.'));

    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
    $this->assertEqual($person->name, 'Tiffany', t('Name set correctly.'));
    $this->assertEqual($person->age, $age_before + 4, t('Age updated correctly.'));
    $this->assertEqual($person->job, 'Speaker', t('Job set correctly.'));
  }

  /**
   * Test that we can merge-insert without any update fields.
   */
  function testMergeInsertWithoutUpdate() {
    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();

    db_merge('test_people')
      ->key(array('job' => 'Presenter'))
      ->execute();

    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
    $this->assertEqual($num_records_before + 1, $num_records_after, t('Merge inserted properly.'));

    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
    $this->assertEqual($person->name, '', t('Name set correctly.'));
    $this->assertEqual($person->age, 0, t('Age set correctly.'));
    $this->assertEqual($person->job, 'Presenter', t('Job set correctly.'));
  }

  /**
   * Confirm that we can merge-update without any update fields.
   */
  function testMergeUpdateWithoutUpdate() {
    $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();

    db_merge('test_people')
      ->key(array('job' => 'Speaker'))
      ->execute();

    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
    $this->assertEqual($num_records_before, $num_records_after, t('Merge skipped properly.'));

    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
    $this->assertEqual($person->name, 'Meredith', t('Name skipped correctly.'));
    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
    $this->assertEqual($person->job, 'Speaker', t('Job skipped correctly.'));

    db_merge('test_people')
      ->key(array('job' => 'Speaker'))
      ->insertFields(array('age' => 31))
      ->execute();

    $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
    $this->assertEqual($num_records_before, $num_records_after, t('Merge skipped properly.'));

    $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
    $this->assertEqual($person->name, 'Meredith', t('Name skipped correctly.'));
    $this->assertEqual($person->age, 30, t('Age skipped correctly.'));
    $this->assertEqual($person->job, 'Speaker', t('Job skipped correctly.'));
  }

  /**
   * Test that an invalid merge query throws an exception like it is supposed to.
   */
  function testInvalidMerge() {
    try {
      // This query should die because there is no key field specified.
      db_merge('test_people')
        ->fields(array(
          'age' => 31,
          'name' => 'Tiffany',
        ))
        ->execute();
    }
    catch (InvalidMergeQueryException $e) {
      $this->pass(t('InvalidMergeQueryException thrown for invalid query.'));
      return;
    }
    $this->fail(t('No InvalidMergeQueryException thrown'));
  }
}

/**
 * Test the SELECT builder.
 */
class DatabaseSelectTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Select tests',
      'description' => 'Test the Select query builder.',
      'group' => 'Database',
    );
  }

  /**
   * Test rudimentary SELECT statements.
   */
  function testSimpleSelect() {
    $query = db_select('test');
    $name_field = $query->addField('test', 'name');
    $age_field = $query->addField('test', 'age', 'age');
    $result = $query->execute();

    $num_records = 0;
    foreach ($result as $record) {
      $num_records++;
    }

    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
  }

  /**
   * Test rudimentary SELECT statement with a COMMENT.
   */
  function testSimpleComment() {
    $query = db_select('test')->comment('Testing query comments');
    $name_field = $query->addField('test', 'name');
    $age_field = $query->addField('test', 'age', 'age');
    $result = $query->execute();

    $num_records = 0;
    foreach ($result as $record) {
      $num_records++;
    }

    $query = (string)$query;
    $expected = "/* Testing query comments */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";

    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
    $this->assertEqual($query, $expected, t('The flattened query contains the comment string.'));
  }

  /**
   * Test basic conditionals on SELECT statements.
   */
  function testSimpleSelectConditional() {
    $query = db_select('test');
    $name_field = $query->addField('test', 'name');
    $age_field = $query->addField('test', 'age', 'age');
    $query->condition('age', 27);
    $result = $query->execute();

    // Check that the aliases are being created the way we want.
    $this->assertEqual($name_field, 'name', t('Name field alias is correct.'));
    $this->assertEqual($age_field, 'age', t('Age field alias is correct.'));

    // Ensure that we got the right record.
    $record = $result->fetch();
    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
    $this->assertEqual($record->$age_field, 27, t('Fetched age is correct.'));
  }

  /**
   * Test SELECT statements with expressions.
   */
  function testSimpleSelectExpression() {
    $query = db_select('test');
    $name_field = $query->addField('test', 'name');
    $age_field = $query->addExpression("age*2", 'double_age');
    $query->condition('age', 27);
    $result = $query->execute();

    // Check that the aliases are being created the way we want.
    $this->assertEqual($name_field, 'name', t('Name field alias is correct.'));
    $this->assertEqual($age_field, 'double_age', t('Age field alias is correct.'));

    // Ensure that we got the right record.
    $record = $result->fetch();
    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
    $this->assertEqual($record->$age_field, 27*2, t('Fetched age expression is correct.'));
  }

  /**
   * Test SELECT statements with multiple expressions.
   */
  function testSimpleSelectExpressionMultiple() {
    $query = db_select('test');
    $name_field = $query->addField('test', 'name');
    $age_double_field = $query->addExpression("age*2");
    $age_triple_field = $query->addExpression("age*3");
    $query->condition('age', 27);
    $result = $query->execute();

    // Check that the aliases are being created the way we want.
    $this->assertEqual($age_double_field, 'expression', t('Double age field alias is correct.'));
    $this->assertEqual($age_triple_field, 'expression_2', t('Triple age field alias is correct.'));

    // Ensure that we got the right record.
    $record = $result->fetch();
    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
    $this->assertEqual($record->$age_double_field, 27*2, t('Fetched double age expression is correct.'));
    $this->assertEqual($record->$age_triple_field, 27*3, t('Fetched triple age expression is correct.'));
  }

  /**
   * Test adding multiple fields to a select statement at the same time.
   */
  function testSimpleSelectMultipleFields() {
    $record = db_select('test')
      ->fields('test', array('id', 'name', 'age', 'job'))
      ->condition('age', 27)
      ->execute()->fetchObject();

    // Check that all fields we asked for are present.
    $this->assertNotNull($record->id, t('ID field is present.'));
    $this->assertNotNull($record->name, t('Name field is present.'));
    $this->assertNotNull($record->age, t('Age field is present.'));
    $this->assertNotNull($record->job, t('Job field is present.'));

    // Ensure that we got the right record.
    // Check that all fields we asked for are present.
    $this->assertEqual($record->id, 2, t('ID field has the correct value.'));
    $this->assertEqual($record->name, 'George', t('Name field has the correct value.'));
    $this->assertEqual($record->age, 27, t('Age field has the correct value.'));
    $this->assertEqual($record->job, 'Singer', t('Job field has the correct value.'));
  }

  /**
   * Test adding all fields from a given table to a select statement.
   */
  function testSimpleSelectAllFields() {
    $record = db_select('test')
      ->fields('test')
      ->condition('age', 27)
      ->execute()->fetchObject();

    // Check that all fields we asked for are present.
    $this->assertNotNull($record->id, t('ID field is present.'));
    $this->assertNotNull($record->name, t('Name field is present.'));
    $this->assertNotNull($record->age, t('Age field is present.'));
    $this->assertNotNull($record->job, t('Job field is present.'));

    // Ensure that we got the right record.
    // Check that all fields we asked for are present.
    $this->assertEqual($record->id, 2, t('ID field has the correct value.'));
    $this->assertEqual($record->name, 'George', t('Name field has the correct value.'));
    $this->assertEqual($record->age, 27, t('Age field has the correct value.'));
    $this->assertEqual($record->job, 'Singer', t('Job field has the correct value.'));
  }

  /**
   * Test that we can find a record with a NULL value.
   */
  function testNullCondition() {
    $this->ensureSampleDataNull();

    $names = db_select('test_null', 'tn')
      ->fields('tn', array('name'))
      ->isNull('age')
      ->execute()->fetchCol();

    $this->assertEqual(count($names), 1, t('Correct number of records found with NULL age.'));
    $this->assertEqual($names[0], 'Fozzie', t('Correct record returned for NULL age.'));
  }

  /**
   * Test that we can find a record without a NULL value.
   */
  function testNotNullCondition() {
    $this->ensureSampleDataNull();

    $names = db_select('test_null', 'tn')
      ->fields('tn', array('name'))
      ->isNotNull('tn.age')
      ->orderBy('name')
      ->execute()->fetchCol();

    $this->assertEqual(count($names), 2, t('Correct number of records found withNOT NULL age.'));
    $this->assertEqual($names[0], 'Gonzo', t('Correct record returned for NOT NULL age.'));
    $this->assertEqual($names[1], 'Kermit', t('Correct record returned for NOT NULL age.'));
  }

  /**
   * Test that we can UNION multiple Select queries together. This is
   * semantically equal to UNION DISTINCT, so we don't explicity test that.
   */
  function testUnion() {
    $query_1 = db_select('test', 't')
      ->fields('t', array('name'))
      ->condition('age', array(27, 28), 'IN');

    $query_2 = db_select('test', 't')
      ->fields('t', array('name'))
      ->condition('age', 28);

    $query_1->union($query_2);

    $names = $query_1->execute()->fetchCol();

    // Ensure we only get 2 records.
    $this->assertEqual(count($names), 2, t('UNION correctly discarded duplicates.'));

    $this->assertEqual($names[0], 'George', t('First query returned correct name.'));
    $this->assertEqual($names[1], 'Ringo', t('Second query returned correct name.'));
  }

  /**
   * Test that we can UNION ALL multiple Select queries together.
   */
  function testUnionAll() {
    $query_1 = db_select('test', 't')
      ->fields('t', array('name'))
      ->condition('age', array(27, 28), 'IN');

    $query_2 = db_select('test', 't')
      ->fields('t', array('name'))
      ->condition('age', 28);

    $query_1->union($query_2, 'ALL');

    $names = $query_1->execute()->fetchCol();

    // Ensure we get all 3 records.
    $this->assertEqual(count($names), 3, t('UNION ALL correctly preserved duplicates.'));

    $this->assertEqual($names[0], 'George', t('First query returned correct first name.'));
    $this->assertEqual($names[1], 'Ringo', t('Second query returned correct second name.'));
    $this->assertEqual($names[2], 'Ringo', t('Third query returned correct name.'));
  }

  /**
   * Test that random ordering of queries works.
   *
   * We take the approach of testing the Drupal layer only, rather than trying
   * to test that the database's random number generator actually produces
   * random queries (which is very difficult to do without an unacceptable risk
   * of the test failing by accident).
   *
   * Therefore, in this test we simply run the same query twice and assert that
   * the two results are reordered versions of each other (as well as of the
   * same query without the random ordering). It is reasonable to assume that
   * if we run the same select query twice and the results are in a different
   * order each time, the only way this could happen is if we have successfully
   * triggered the database's random ordering functionality.
   */
  function testRandomOrder() {
    // Use 52 items, so the chance that this test fails by accident will be the
    // same as the chance that a deck of cards will come out in the same order
    // after shuffling it (in other words, nearly impossible).
    $number_of_items = 52;
    while (db_query("SELECT MAX(id) FROM {test}")->fetchField() < $number_of_items) {
      db_insert('test')->fields(array('name' => $this->randomName()))->execute();
    }

    // First select the items in order and make sure we get an ordered list.
    $expected_ids = range(1, $number_of_items);
    $ordered_ids = db_select('test', 't')
      ->fields('t', array('id'))
      ->range(0, $number_of_items)
      ->orderBy('id')
      ->execute()
      ->fetchCol();
    $this->assertEqual($ordered_ids, $expected_ids, t('A query without random ordering returns IDs in the correct order.'));

    // Now perform the same query, but instead choose a random ordering. We
    // expect this to contain a differently ordered version of the original
    // result.
    $randomized_ids = db_select('test', 't')
      ->fields('t', array('id'))
      ->range(0, $number_of_items)
      ->orderRandom()
      ->execute()
      ->fetchCol();
    $this->assertNotEqual($randomized_ids, $ordered_ids, t('A query with random ordering returns an unordered set of IDs.'));
    $sorted_ids = $randomized_ids;
    sort($sorted_ids);
    $this->assertEqual($sorted_ids, $ordered_ids, t('After sorting the random list, the result matches the original query.'));

    // Now perform the exact same query again, and make sure the order is
    // different.
    $randomized_ids_second_set = db_select('test', 't')
      ->fields('t', array('id'))
      ->range(0, $number_of_items)
      ->orderRandom()
      ->execute()
      ->fetchCol();
    $this->assertNotEqual($randomized_ids_second_set, $randomized_ids, t('Performing the query with random ordering a second time returns IDs in a different order.'));
    $sorted_ids_second_set = $randomized_ids_second_set;
    sort($sorted_ids_second_set);
    $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.'));
  }

  /**
   * Test that aliases are renamed when duplicates.
   */
  function testSelectDuplicateAlias() {
    $query = db_select('test', 't');
    $alias1 = $query->addField('t', 'name', 'the_alias');
    $alias2 = $query->addField('t', 'age', 'the_alias');
    $this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.');
  }
}

/**
 * Test case for subselects in a dynamic SELECT query.
 */
class DatabaseSelectSubqueryTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Select tests, subqueries',
      'description' => 'Test the Select query builder.',
      'group' => 'Database',
    );
  }

  /**
   * Test that we can use a subquery in a FROM clause.
   */
  function testFromSubquerySelect() {
    // Create a subquery, which is just a normal query object.
    $subquery = db_select('test_task', 'tt');
    $subquery->addField('tt', 'pid', 'pid');
    $subquery->addField('tt', 'task', 'task');
    $subquery->condition('priority', 1);

    // Create another query that joins against the virtual table resulting
    // from the subquery.
    $select = db_select($subquery, 'tt2');
    $select->join('test', 't', 't.id=tt2.pid');
    $select->addField('t', 'name');

    $select->condition('task', 'code');

    // The resulting query should be equivalent to:
    // SELECT t.name
    // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt WHERE priority=1) tt
    //   INNER JOIN test t ON t.id=tt.pid
    // WHERE tt.task = 'code'
    $people = $select->execute()->fetchCol();

    $this->assertEqual(count($people), 1, t('Returned the correct number of rows.'));
  }

  /**
   * Test that we can use a subquery in a FROM clause with a limit.
   */
  function testFromSubquerySelectWithLimit() {
    // Create a subquery, which is just a normal query object.
    $subquery = db_select('test_task', 'tt');
    $subquery->addField('tt', 'pid', 'pid');
    $subquery->addField('tt', 'task', 'task');
    $subquery->orderBy('priority', 'DESC');
    $subquery->range(0, 1);

    // Create another query that joins against the virtual table resulting
    // from the subquery.
    $select = db_select($subquery, 'tt2');
    $select->join('test', 't', 't.id=tt2.pid');
    $select->addField('t', 'name');

    // The resulting query should be equivalent to:
    // SELECT t.name
    // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt ORDER BY priority DESC LIMIT 1 OFFSET 0) tt
    //   INNER JOIN test t ON t.id=tt.pid
    $people = $select->execute()->fetchCol();

    $this->assertEqual(count($people), 1, t('Returned the correct number of rows.'));
  }

  /**
   * Test that we can use a subquery in a WHERE clause.
   */
  function testConditionSubquerySelect() {
    // Create a subquery, which is just a normal query object.
    $subquery = db_select('test_task', 'tt');
    $subquery->addField('tt', 'pid', 'pid');
    $subquery->condition('tt.priority', 1);

    // Create another query that joins against the virtual table resulting
    // from the subquery.
    $select = db_select('test_task', 'tt2');
    $select->addField('tt2', 'task');
    $select->condition('tt2.pid', $subquery, 'IN');

    // The resulting query should be equivalent to:
    // SELECT tt2.name
    // FROM test tt2
    // WHERE tt2.pid IN (SELECT tt.pid AS pid FROM test_task tt WHERE tt.priority=1)
    $people = $select->execute()->fetchCol();
    $this->assertEqual(count($people), 5, t('Returned the correct number of rows.'));
  }

  /**
   * Test that we can use a subquery in a JOIN clause.
   */
  function testJoinSubquerySelect() {
    // Create a subquery, which is just a normal query object.
    $subquery = db_select('test_task', 'tt');
    $subquery->addField('tt', 'pid', 'pid');
    $subquery->condition('priority', 1);

    // Create another query that joins against the virtual table resulting
    // from the subquery.
    $select = db_select('test', 't');
    $select->join($subquery, 'tt', 't.id=tt.pid');
    $select->addField('t', 'name');

    // The resulting query should be equivalent to:
    // SELECT t.name
    // FROM test t
    //   INNER JOIN (SELECT tt.pid AS pid FROM test_task tt WHERE priority=1) tt ON t.id=tt.pid
    $people = $select->execute()->fetchCol();

    $this->assertEqual(count($people), 2, t('Returned the correct number of rows.'));
  }

  /**
   * Test EXISTS subquery conditionals on SELECT statements.
   */
  function testExistsSubquerySelect() {
    // Put George into {test_people}.
    db_insert('test_people')
      ->fields(array(
        'name' => 'George',
        'age' => 27,
        'job' => 'Singer',
      ))
      ->execute();
    // Base query to {test}.
    $query = db_select('test', 't')
      ->fields('t', array('name'));
    // Subquery to {test_people}.
    $subquery = db_select('test_people', 'tp')
      ->fields('tp', array('name'))
      ->condition('name', 'George');
    $query->exists($subquery);
    $result = $query->execute();

    // Ensure that we got the right record.
    $record = $result->fetch();
    $this->assertEqual($record->name, 'George', t('Fetched name is correct using EXISTS query.'));
  }

  /**
   * Test NOT EXISTS subquery conditionals on SELECT statements.
   */
  function testNotExistsSubquerySelect() {
    // Put George into {test_people}.
    db_insert('test_people')
      ->fields(array(
        'name' => 'George',
        'age' => 27,
        'job' => 'Singer',
      ))
      ->execute();

    // Base query to {test}.
    $query = db_select('test', 't')
      ->fields('t', array('name'));
    // Subquery to {test_people}.
    $subquery = db_select('test_people', 'tp')
      ->fields('tp', array('name'))
      ->condition('name', 'George');
    $query->notExists($subquery);
    $result = $query->execute();

    // Ensure that we got the right record.
    $record = $result->fetch();
    $this->assertFalse($record, t('NOT EXISTS query returned no results.'));
  }
}

/**
 * Test select with order by clauses.
 */
class DatabaseSelectOrderedTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Select tests, ordered',
      'description' => 'Test the Select query builder.',
      'group' => 'Database',
    );
  }

  /**
   * Test basic order by.
   */
  function testSimpleSelectOrdered() {
    $query = db_select('test');
    $name_field = $query->addField('test', 'name');
    $age_field = $query->addField('test', 'age', 'age');
    $query->orderBy($age_field);
    $result = $query->execute();

    $num_records = 0;
    $last_age = 0;
    foreach ($result as $record) {
      $num_records++;
      $this->assertTrue($record->age >= $last_age, t('Results returned in correct order.'));
      $last_age = $record->age;
    }

    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
  }

  /**
   * Test multiple order by.
   */
  function testSimpleSelectMultiOrdered() {
    $query = db_select('test');
    $name_field = $query->addField('test', 'name');
    $age_field = $query->addField('test', 'age', 'age');
    $job_field = $query->addField('test', 'job');
    $query->orderBy($job_field);
    $query->orderBy($age_field);
    $result = $query->execute();

    $num_records = 0;
    $expected = array(
      array('Ringo', 28, 'Drummer'),
      array('John', 25, 'Singer'),
      array('George', 27, 'Singer'),
      array('Paul', 26, 'Songwriter'),
    );
    $results = $result->fetchAll(PDO::FETCH_NUM);
    foreach ($expected as $k => $record) {
      $num_records++;
      foreach ($record as $kk => $col) {
        if ($expected[$k][$kk] != $results[$k][$kk]) {
          $this->assertTrue(FALSE, t('Results returned in correct order.'));
        }
      }
    }
    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
  }

  /**
   * Test order by descending.
   */
  function testSimpleSelectOrderedDesc() {
    $query = db_select('test');
    $name_field = $query->addField('test', 'name');
    $age_field = $query->addField('test', 'age', 'age');
    $query->orderBy($age_field, 'DESC');
    $result = $query->execute();

    $num_records = 0;
    $last_age = 100000000;
    foreach ($result as $record) {
      $num_records++;
      $this->assertTrue($record->age <= $last_age, t('Results returned in correct order.'));
      $last_age = $record->age;
    }

    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
  }
}

/**
 * Test more complex select statements.
 */
class DatabaseSelectComplexTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Select tests, complex',
      'description' => 'Test the Select query builder with more complex queries.',
      'group' => 'Database',
    );
  }

  /**
   * Test simple JOIN statements.
   */
  function testDefaultJoin() {
    $query = db_select('test_task', 't');
    $people_alias = $query->join('test', 'p', 't.pid = p.id');
    $name_field = $query->addField($people_alias, 'name', 'name');
    $task_field = $query->addField('t', 'task', 'task');
    $priority_field = $query->addField('t', 'priority', 'priority');

    $query->orderBy($priority_field);
    $result = $query->execute();

    $num_records = 0;
    $last_priority = 0;
    foreach ($result as $record) {
      $num_records++;
      $this->assertTrue($record->$priority_field >= $last_priority, t('Results returned in correct order.'));
      $this->assertNotEqual($record->$name_field, 'Ringo', t('Taskless person not selected.'));
      $last_priority = $record->$priority_field;
    }

    $this->assertEqual($num_records, 7, t('Returned the correct number of rows.'));
  }

  /**
   * Test LEFT OUTER joins.
   */
  function testLeftOuterJoin() {
    $query = db_select('test', 'p');
    $people_alias = $query->leftJoin('test_task', 't', 't.pid = p.id');
    $name_field = $query->addField('p', 'name', 'name');
    $task_field = $query->addField($people_alias, 'task', 'task');
    $priority_field = $query->addField($people_alias, 'priority', 'priority');

    $query->orderBy($name_field);
    $result = $query->execute();

    $num_records = 0;
    $last_name = 0;

    foreach ($result as $record) {
      $num_records++;
      $this->assertTrue(strcmp($record->$name_field, $last_name) >= 0, t('Results returned in correct order.'));
      $last_priority = $record->$name_field;
    }

    $this->assertEqual($num_records, 8, t('Returned the correct number of rows.'));
  }

  /**
   * Test GROUP BY clauses.
   */
  function testGroupBy() {
    $query = db_select('test_task', 't');
    $count_field = $query->addExpression('COUNT(task)', 'num');
    $task_field = $query->addField('t', 'task');
    $query->orderBy($count_field);
    $query->groupBy($task_field);
    $result = $query->execute();

    $num_records = 0;
    $last_count = 0;
    $records = array();
    foreach ($result as $record) {
      $num_records++;
      $this->assertTrue($record->$count_field >= $last_count, t('Results returned in correct order.'));
      $last_count = $record->$count_field;
      $records[$record->$task_field] = $record->$count_field;
    }

    $correct_results = array(
      'eat' => 1,
      'sleep' => 2,
      'code' => 1,
      'found new band' => 1,
      'perform at superbowl' => 1,
    );

    foreach ($correct_results as $task => $count) {
      $this->assertEqual($records[$task], $count, t("Correct number of '@task' records found.", array('@task' => $task)));
    }

    $this->assertEqual($num_records, 6, t('Returned the correct number of total rows.'));
  }

  /**
   * Test GROUP BY and HAVING clauses together.
   */
  function testGroupByAndHaving() {
    $query = db_select('test_task', 't');
    $count_field = $query->addExpression('COUNT(task)', 'num');
    $task_field = $query->addField('t', 'task');
    $query->orderBy($count_field);
    $query->groupBy($task_field);
    $query->having('COUNT(task) >= 2');
    $result = $query->execute();

    $num_records = 0;
    $last_count = 0;
    $records = array();
    foreach ($result as $record) {
      $num_records++;
      $this->assertTrue($record->$count_field >= 2, t('Record has the minimum count.'));
      $this->assertTrue($record->$count_field >= $last_count, t('Results returned in correct order.'));
      $last_count = $record->$count_field;
      $records[$record->$task_field] = $record->$count_field;
    }

    $correct_results = array(
      'sleep' => 2,
    );

    foreach ($correct_results as $task => $count) {
      $this->assertEqual($records[$task], $count, t("Correct number of '@task' records found.", array('@task' => $task)));
    }

    $this->assertEqual($num_records, 1, t('Returned the correct number of total rows.'));
  }

  /**
   * Test range queries. The SQL clause varies with the database.
   */
  function testRange() {
    $query = db_select('test');
    $name_field = $query->addField('test', 'name');
    $age_field = $query->addField('test', 'age', 'age');
    $query->range(0, 2);
    $result = $query->execute();

    $num_records = 0;
    foreach ($result as $record) {
      $num_records++;
    }

    $this->assertEqual($num_records, 2, t('Returned the correct number of rows.'));
  }

  /**
   * Test distinct queries.
   */
  function testDistinct() {
    $query = db_select('test_task');
    $task_field = $query->addField('test_task', 'task');
    $query->distinct();
    $result = $query->execute();

    $num_records = 0;
    foreach ($result as $record) {
      $num_records++;
    }

    $this->assertEqual($num_records, 6, t('Returned the correct number of rows.'));
  }

  /**
   * Test that we can generate a count query from a built query.
   */
  function testCountQuery() {
    $query = db_select('test');
    $name_field = $query->addField('test', 'name');
    $age_field = $query->addField('test', 'age', 'age');
    $query->orderBy('name');

    $count = $query->countQuery()->execute()->fetchField();

    $this->assertEqual($count, 4, t('Counted the correct number of records.'));

    // Now make sure we didn't break the original query!  We should still have
    // all of the fields we asked for.
    $record = $query->execute()->fetch();
    $this->assertEqual($record->$name_field, 'George', t('Correct data retrieved.'));
    $this->assertEqual($record->$age_field, 27, t('Correct data retrieved.'));
  }

  /**
   * Test that countQuery properly removes 'all_fields' statements and
   * ordering clauses.
   */
  function testCountQueryRemovals() {
    $query = db_select('test');
    $query->fields('test');
    $query->orderBy('name');
    $count = $query->countQuery();

    // Check that the 'all_fields' statement is handled properly.
    $tables = $query->getTables();
    $this->assertEqual($tables['test']['all_fields'], 1, t('Query correctly sets \'all_fields\' statement.'));
    $tables = $count->getTables();
    $this->assertFalse(isset($tables['test']['all_fields']), t('Count query correctly unsets \'all_fields\' statement.'));

    // Check that the ordering clause is handled properly.
    $orderby = $query->getOrderBy();
    $this->assertEqual($orderby['name'], 'ASC', t('Query correctly sets ordering clause.'));
    $orderby = $count->getOrderBy();
    $this->assertFalse(isset($orderby['name']), t('Count query correctly unsets ordering caluse.'));

    // Make sure that the count query works.
    $count = $count->execute()->fetchField();

    $this->assertEqual($count, 4, t('Counted the correct number of records.'));
  }


  /**
   * Test that countQuery properly removes fields and expressions.
   */
  function testCountQueryFieldRemovals() {
    // countQuery should remove all fields and expressions, so this can be
    // tested by adding a non-existant field and expression: if it ends
    // up in the query, an error will be thrown. If not, it will return the
    // number of records, which in this case happens to be 4 (there are four
    // records in the {test} table).
    $query = db_select('test');
    $query->fields('test', array('fail'));
    $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), t('Count Query removed fields'));

    $query = db_select('test');
    $query->addExpression('fail');
    $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), t('Count Query removed expressions'));
  }

  /**
   * Test that we can generate a count query from a query with distinct.
   */
  function testCountQueryDistinct() {
    $query = db_select('test_task');
    $task_field = $query->addField('test_task', 'task');
    $query->distinct();

    $count = $query->countQuery()->execute()->fetchField();

    $this->assertEqual($count, 6, t('Counted the correct number of records.'));
  }

  /**
   * Test that we can generate a count query from a query with GROUP BY.
   */
  function testCountQueryGroupBy() {
    $query = db_select('test_task');
    $pid_field = $query->addField('test_task', 'pid');
    $query->groupBy('pid');

    $count = $query->countQuery()->execute()->fetchField();

    $this->assertEqual($count, 3, t('Counted the correct number of records.'));

    // Use a column alias as, without one, the query can succeed for the wrong
    // reason.
    $query = db_select('test_task');
    $pid_field = $query->addField('test_task', 'pid', 'pid_alias');
    $query->addExpression('COUNT(test_task.task)', 'count');
    $query->groupBy('pid_alias');
    $query->orderBy('pid_alias', 'asc');

    $count = $query->countQuery()->execute()->fetchField();

    $this->assertEqual($count, 3, t('Counted the correct number of records.'));
  }

  /**
   * Confirm that we can properly nest conditional clauses.
   */
  function testNestedConditions() {
    // This query should translate to:
    // "SELECT job FROM {test} WHERE name = 'Paul' AND (age = 26 OR age = 27)"
    // That should find only one record. Yes it's a non-optimal way of writing
    // that query but that's not the point!
    $query = db_select('test');
    $query->addField('test', 'job');
    $query->condition('name', 'Paul');
    $query->condition(db_or()->condition('age', 26)->condition('age', 27));

    $job = $query->execute()->fetchField();
    $this->assertEqual($job, 'Songwriter', t('Correct data retrieved.'));
  }

  /**
   * Confirm we can join on a single table twice with a dynamic alias.
   */
  function testJoinTwice() {
    $query = db_select('test')->fields('test');
    $alias = $query->join('test', 'test', 'test.job = %alias.job');
    $query->addField($alias, 'name', 'othername');
    $query->addField($alias, 'job', 'otherjob');
    $query->where("$alias.name <> test.name");
    $crowded_job = $query->execute()->fetch();
    $this->assertEqual($crowded_job->job, $crowded_job->otherjob, t('Correctly joined same table twice.'));
    $this->assertNotEqual($crowded_job->name, $crowded_job->othername, t('Correctly joined same table twice.'));
  }

}

/**
 * Test more complex select statements, part 2.
 */
class DatabaseSelectComplexTestCase2 extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Select tests, complex 2',
      'description' => 'Test the Select query builder with even more complex queries.',
      'group' => 'Database',
    );
  }

  function setUp() {
    DrupalWebTestCase::setUp('database_test', 'node_access_test');

    $schema['test'] = drupal_get_schema('test');
    $schema['test_people'] = drupal_get_schema('test_people');
    $schema['test_one_blob'] = drupal_get_schema('test_one_blob');
    $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs');
    $schema['test_task'] = drupal_get_schema('test_task');

    $this->installTables($schema);

    $this->addSampleData();
  }

  /**
   * Test that we can join on a query.
   */
  function testJoinSubquery() {
    $acct = $this->drupalCreateUser(array('access content'));
    $this->drupalLogin($acct);

    $query = db_select('test_task', 'tt', array('target' => 'slave'));
    $query->addExpression('tt.pid + 1', 'abc');
    $query->condition('priority', 1, '>');
    $query->condition('priority', 100, '<');

    $subquery = db_select('test', 'tp');
    $subquery->join('test_one_blob', 'tpb', 'tp.id = tpb.id');
    $subquery->join('node', 'n', 'tp.id = n.nid');
    $subquery->addTag('node_access');
    $subquery->addMetaData('account', $acct);
    $subquery->addField('tp', 'id');
    $subquery->condition('age', 5, '>');
    $subquery->condition('age', 500, '<');

    $query->leftJoin($subquery, 'sq', 'tt.pid = sq.id');
    $query->join('test_one_blob', 'tb3', 'tt.pid = tb3.id');

    // Construct the query string.
    // This is the same sequence that SelectQuery::execute() goes through.
    $query->preExecute();
    $query->getArguments();
    $str = (string) $query;

    // Verify that the string only has one copy of condition placeholder 0.
    $pos = strpos($str, 'db_condition_placeholder_0', 0);
    $pos2 = strpos($str, 'db_condition_placeholder_0', $pos + 1);
    $this->assertFalse($pos2, "Condition placeholder is not repeated");
  }
}

class DatabaseSelectPagerDefaultTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Pager query tests',
      'description' => 'Test the pager query extender.',
      'group' => 'Database',
    );
  }

  /**
   * Confirm that a pager query returns the correct results.
   *
   * Note that we have to make an HTTP request to a test page handler
   * because the pager depends on GET parameters.
   */
  function testEvenPagerQuery() {
    // To keep the test from being too brittle, we determine up front
    // what the page count should be dynamically, and pass the control
    // information forward to the actual query on the other side of the
    // HTTP request.
    $limit = 2;
    $count = db_query('SELECT COUNT(*) FROM {test}')->fetchField();

    $correct_number = $limit;
    $num_pages = floor($count / $limit);

    // If there is no remainder from rounding, subtract 1 since we index from 0.
    if (!($num_pages * $limit < $count)) {
      $num_pages--;
    }

    for ($page = 0; $page <= $num_pages; ++$page) {
      $this->drupalGet('database_test/pager_query_even/' . $limit, array('query' => array('page' => $page)));
      $data = json_decode($this->drupalGetContent());

      if ($page == $num_pages) {
        $correct_number = $count - ($limit * $page);
      }

      $this->assertEqual(count($data->names), $correct_number, t('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
    }
  }

  /**
   * Confirm that a pager query returns the correct results.
   *
   * Note that we have to make an HTTP request to a test page handler
   * because the pager depends on GET parameters.
   */
  function testOddPagerQuery() {
    // To keep the test from being too brittle, we determine up front
    // what the page count should be dynamically, and pass the control
    // information forward to the actual query on the other side of the
    // HTTP request.
    $limit = 2;
    $count = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();

    $correct_number = $limit;
    $num_pages = floor($count / $limit);

    // If there is no remainder from rounding, subtract 1 since we index from 0.
    if (!($num_pages * $limit < $count)) {
      $num_pages--;
    }

    for ($page = 0; $page <= $num_pages; ++$page) {
      $this->drupalGet('database_test/pager_query_odd/' . $limit, array('query' => array('page' => $page)));
      $data = json_decode($this->drupalGetContent());

      if ($page == $num_pages) {
        $correct_number = $count - ($limit * $page);
      }

      $this->assertEqual(count($data->names), $correct_number, t('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
    }
  }

  /**
   * Confirm that a pager query with inner pager query returns valid results.
   *
   * This is a regression test for #467984.
   */
  function testInnerPagerQuery() {
    $query = db_select('test', 't')->extend('PagerDefault');
    $query
      ->fields('t', array('age'))
      ->orderBy('age')
      ->limit(5);

    $outer_query = db_select($query);
    $outer_query->addField('subquery', 'age');

    $ages = $outer_query
      ->execute()
      ->fetchCol();
    $this->assertEqual($ages, array(25, 26, 27, 28), t('Inner pager query returned the correct ages.'));
  }

  /**
   * Confirm that a paging query with a having expression returns valid results.
   *
   * This is a regression test for #467984.
   */
  function testHavingPagerQuery() {
    $query = db_select('test', 't')->extend('PagerDefault');
    $query
      ->fields('t', array('name'))
      ->orderBy('name')
      ->groupBy('name')
      ->having('MAX(age) > :count', array(':count' => 26))
      ->limit(5);

    $ages = $query
      ->execute()
      ->fetchCol();
    $this->assertEqual($ages, array('George', 'Ringo'), t('Pager query with having expression returned the correct ages.'));
  }

  /**
  * Confirm that every pager gets a valid non-overlaping element ID.
  */
  function testElementNumbers() {
    $_GET['page'] = '3, 2, 1, 0';

    $name = db_select('test', 't')->extend('PagerDefault')
      ->element(2)
      ->fields('t', array('name'))
      ->orderBy('age')
      ->limit(1)
      ->execute()
      ->fetchField();
    $this->assertEqual($name, 'Paul', t('Pager query #1 with a specified element ID returned the correct results.'));

    // Setting an element smaller than the previous one
    // should not overwrite the pager $maxElement with a smaller value.
    $name = db_select('test', 't')->extend('PagerDefault')
      ->element(1)
      ->fields('t', array('name'))
      ->orderBy('age')
      ->limit(1)
      ->execute()
      ->fetchField();
    $this->assertEqual($name, 'George', t('Pager query #2 with a specified element ID returned the correct results.'));

    $name = db_select('test', 't')->extend('PagerDefault')
      ->fields('t', array('name'))
      ->orderBy('age')
      ->limit(1)
      ->execute()
      ->fetchField();
    $this->assertEqual($name, 'John', t('Pager query #3 with a generated element ID returned the correct results.'));

    unset($_GET['page']);
  }
}


class DatabaseSelectTableSortDefaultTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Tablesort query tests',
      'description' => 'Test the tablesort query extender.',
      'group' => 'Database',
    );
  }

  /**
   * Confirm that a tablesort query returns the correct results.
   *
   * Note that we have to make an HTTP request to a test page handler
   * because the pager depends on GET parameters.
   */
  function testTableSortQuery() {
    $sorts = array(
      array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
      array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
      array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
      array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
      // more elements here

    );

    foreach ($sorts as $sort) {
      $this->drupalGet('database_test/tablesort/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
      $data = json_decode($this->drupalGetContent());

      $first = array_shift($data->tasks);
      $last = array_pop($data->tasks);

      $this->assertEqual($first->task, $sort['first'], t('Items appear in the correct order.'));
      $this->assertEqual($last->task, $sort['last'], t('Items appear in the correct order.'));
    }
  }

  /**
   * Confirm that if a tablesort's orderByHeader is called before another orderBy, that the header happens first.
   *
   */
  function testTableSortQueryFirst() {
    $sorts = array(
      array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
      array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
      array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
      array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
      // more elements here

    );

    foreach ($sorts as $sort) {
      $this->drupalGet('database_test/tablesort_first/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
      $data = json_decode($this->drupalGetContent());

      $first = array_shift($data->tasks);
      $last = array_pop($data->tasks);

      $this->assertEqual($first->task, $sort['first'], t('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
      $this->assertEqual($last->task, $sort['last'], t('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
    }
  }

  /**
   * Confirm that if a sort is not set in a tableselect form there is no error thrown when using the default.
   */
  function testTableSortDefaultSort() {
    $this->drupalGet('database_test/tablesort_default_sort');
    // Any PHP errors or notices thrown would trigger a simpletest exception, so
    // no additional assertions are needed.
  }
}

/**
 * Select tagging tests.
 *
 * Tags are a way to flag queries for alter hooks so they know
 * what type of query it is, such as "node_access".
 */
class DatabaseTaggingTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Query tagging tests',
      'description' => 'Test the tagging capabilities of the Select builder.',
      'group' => 'Database',
    );
  }

  /**
   * Confirm that a query has a "tag" added to it.
   */
  function testHasTag() {
    $query = db_select('test');
    $query->addField('test', 'name');
    $query->addField('test', 'age', 'age');

    $query->addTag('test');

    $this->assertTrue($query->hasTag('test'), t('hasTag() returned true.'));
    $this->assertFalse($query->hasTag('other'), t('hasTag() returned false.'));
  }

  /**
   * Test query tagging "has all of these tags" functionality.
   */
  function testHasAllTags() {
    $query = db_select('test');
    $query->addField('test', 'name');
    $query->addField('test', 'age', 'age');

    $query->addTag('test');
    $query->addTag('other');

    $this->assertTrue($query->hasAllTags('test', 'other'), t('hasAllTags() returned true.'));
    $this->assertFalse($query->hasAllTags('test', 'stuff'), t('hasAllTags() returned false.'));
  }

  /**
   * Test query tagging "has at least one of these tags" functionality.
   */
  function testHasAnyTag() {
    $query = db_select('test');
    $query->addField('test', 'name');
    $query->addField('test', 'age', 'age');

    $query->addTag('test');

    $this->assertTrue($query->hasAnyTag('test', 'other'), t('hasAnyTag() returned true.'));
    $this->assertFalse($query->hasAnyTag('other', 'stuff'), t('hasAnyTag() returned false.'));
  }

  /**
   * Test that we can attach meta data to a query object.
   *
   * This is how we pass additional context to alter hooks.
   */
  function testMetaData() {
    $query = db_select('test');
    $query->addField('test', 'name');
    $query->addField('test', 'age', 'age');

    $data = array(
      'a' => 'A',
      'b' => 'B',
    );

    $query->addMetaData('test', $data);

    $return = $query->getMetaData('test');
    $this->assertEqual($data, $return, t('Corect metadata returned.'));

    $return = $query->getMetaData('nothere');
    $this->assertNull($return, t('Non-existent key returned NULL.'));
  }
}

/**
 * Select alter tests.
 *
 * @see database_test_query_alter()
 */
class DatabaseAlterTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Query altering tests',
      'description' => 'Test the hook_query_alter capabilities of the Select builder.',
      'group' => 'Database',
    );
  }

  /**
   * Test that we can do basic alters.
   */
  function testSimpleAlter() {
    $query = db_select('test');
    $query->addField('test', 'name');
    $query->addField('test', 'age', 'age');
    $query->addTag('database_test_alter_add_range');

    $result = $query->execute();

    $num_records = 0;
    foreach ($result as $record) {
      $num_records++;
    }

    $this->assertEqual($num_records, 2, t('Returned the correct number of rows.'));
  }

  /**
   * Test that we can alter the joins on a query.
   */
  function testAlterWithJoin() {
    $query = db_select('test_task');
    $tid_field = $query->addField('test_task', 'tid');
    $task_field = $query->addField('test_task', 'task');
    $query->orderBy($task_field);
    $query->addTag('database_test_alter_add_join');

    $result = $query->execute();

    $records = $result->fetchAll();

    $this->assertEqual(count($records), 2, t('Returned the correct number of rows.'));

    $this->assertEqual($records[0]->name, 'George', t('Correct data retrieved.'));
    $this->assertEqual($records[0]->$tid_field, 4, t('Correct data retrieved.'));
    $this->assertEqual($records[0]->$task_field, 'sing', t('Correct data retrieved.'));
    $this->assertEqual($records[1]->name, 'George', t('Correct data retrieved.'));
    $this->assertEqual($records[1]->$tid_field, 5, t('Correct data retrieved.'));
    $this->assertEqual($records[1]->$task_field, 'sleep', t('Correct data retrieved.'));
  }

  /**
   * Test that we can alter a query's conditionals.
   */
  function testAlterChangeConditional() {
    $query = db_select('test_task');
    $tid_field = $query->addField('test_task', 'tid');
    $pid_field = $query->addField('test_task', 'pid');
    $task_field = $query->addField('test_task', 'task');
    $people_alias = $query->join('test', 'people', "test_task.pid = people.id");
    $name_field = $query->addField($people_alias, 'name', 'name');
    $query->condition('test_task.tid', '1');
    $query->orderBy($tid_field);
    $query->addTag('database_test_alter_change_conditional');

    $result = $query->execute();

    $records = $result->fetchAll();

    $this->assertEqual(count($records), 1, t('Returned the correct number of rows.'));
    $this->assertEqual($records[0]->$name_field, 'John', t('Correct data retrieved.'));
    $this->assertEqual($records[0]->$tid_field, 2, t('Correct data retrieved.'));
    $this->assertEqual($records[0]->$pid_field, 1, t('Correct data retrieved.'));
    $this->assertEqual($records[0]->$task_field, 'sleep', t('Correct data retrieved.'));
  }

  /**
   * Test that we can alter the fields of a query.
   */
  function testAlterChangeFields() {
    $query = db_select('test');
    $name_field = $query->addField('test', 'name');
    $age_field = $query->addField('test', 'age', 'age');
    $query->orderBy('name');
    $query->addTag('database_test_alter_change_fields');

    $record = $query->execute()->fetch();
    $this->assertEqual($record->$name_field, 'George', t('Correct data retrieved.'));
    $this->assertFalse(isset($record->$age_field), t('Age field not found, as intended.'));
  }

  /**
   * Test that we can alter expressions in the query.
   */
  function testAlterExpression() {
    $query = db_select('test');
    $name_field = $query->addField('test', 'name');
    $age_field = $query->addExpression("age*2", 'double_age');
    $query->condition('age', 27);
    $query->addTag('database_test_alter_change_expressions');
    $result = $query->execute();

    // Ensure that we got the right record.
    $record = $result->fetch();

    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
    $this->assertEqual($record->$age_field, 27*3, t('Fetched age expression is correct.'));
  }

  /**
   * Test that we can remove a range() value from a query. This also tests hook_query_TAG_alter().
   */
  function testAlterRemoveRange() {
    $query = db_select('test');
    $query->addField('test', 'name');
    $query->addField('test', 'age', 'age');
    $query->range(0, 2);
    $query->addTag('database_test_alter_remove_range');

    $num_records = count($query->execute()->fetchAll());

    $this->assertEqual($num_records, 4, t('Returned the correct number of rows.'));
  }

  /**
   * Test that we can do basic alters on subqueries.
   */
  function testSimpleAlterSubquery() {
    // Create a sub-query with an alter tag.
    $subquery = db_select('test', 'p');
    $subquery->addField('p', 'name');
    $subquery->addField('p', 'id');
    // Pick out George.
    $subquery->condition('age', 27);
    $subquery->addExpression("age*2", 'double_age');
    // This query alter should change it to age * 3.
    $subquery->addTag('database_test_alter_change_expressions');

    // Create a main query and join to sub-query.
    $query = db_select('test_task', 'tt');
    $query->join($subquery, 'pq', 'pq.id = tt.pid');
    $age_field = $query->addField('pq', 'double_age');
    $name_field = $query->addField('pq', 'name');

    $record = $query->execute()->fetch();
    $this->assertEqual($record->$name_field, 'George', t('Fetched name is correct.'));
    $this->assertEqual($record->$age_field, 27*3, t('Fetched age expression is correct.'));
  }
}

/**
 * Regression tests.
 */
class DatabaseRegressionTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Regression tests',
      'description' => 'Regression tests cases for the database layer.',
      'group' => 'Database',
    );
  }

  /**
   * Regression test for #310447.
   *
   * Tries to insert non-ascii UTF-8 data in a database column and checks
   * if its stored properly.
   */
  function testRegression_310447() {
    // That's a 255 character UTF-8 string.
    $name = str_repeat("é", 255);
    db_insert('test')
      ->fields(array(
        'name' => $name,
        'age' => 20,
        'job' => 'Dancer',
      ))->execute();

    $from_database = db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
    $this->assertIdentical($name, $from_database, t("The database handles UTF-8 characters cleanly."));
  }

  /**
   * Test the db_table_exists() function.
   */
  function testDBTableExists() {
    $this->assertIdentical(TRUE, db_table_exists('node'), t('Returns true for existent table.'));
    $this->assertIdentical(FALSE, db_table_exists('nosuchtable'), t('Returns false for nonexistent table.'));
  }

  /**
   * Test the db_field_exists() function.
   */
  function testDBFieldExists() {
    $this->assertIdentical(TRUE, db_field_exists('node', 'nid'), t('Returns true for existent column.'));
    $this->assertIdentical(FALSE, db_field_exists('node', 'nosuchcolumn'), t('Returns false for nonexistent column.'));
  }

  /**
   * Test the db_index_exists() function.
   */
  function testDBIndexExists() {
    $this->assertIdentical(TRUE, db_index_exists('node', 'node_created'), t('Returns true for existent index.'));
    $this->assertIdentical(FALSE, db_index_exists('node', 'nosuchindex'), t('Returns false for nonexistent index.'));
  }
}

/**
 * Query logging tests.
 */
class DatabaseLoggingTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Query logging',
      'description' => 'Test the query logging facility.',
      'group' => 'Database',
    );
  }

  /**
   * Test that we can log the existence of a query.
   */
  function testEnableLogging() {
    Database::startLog('testing');

    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();

    $queries = Database::getLog('testing', 'default');

    $this->assertEqual(count($queries), 2, t('Correct number of queries recorded.'));

    foreach ($queries as $query) {
      $this->assertEqual($query['caller']['function'], __FUNCTION__, t('Correct function in query log.'));
    }
  }

  /**
   * Test that we can run two logs in parallel.
   */
  function testEnableMultiLogging() {
    Database::startLog('testing1');

    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();

    Database::startLog('testing2');

    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();

    $queries1 = Database::getLog('testing1');
    $queries2 = Database::getLog('testing2');

    $this->assertEqual(count($queries1), 2, t('Correct number of queries recorded for log 1.'));
    $this->assertEqual(count($queries2), 1, t('Correct number of queries recorded for log 2.'));
  }

  /**
   * Test that we can log queries against multiple targets on the same connection.
   */
  function testEnableTargetLogging() {
    // Clone the master credentials to a slave connection and to another fake
    // connection.
    $connection_info = Database::getConnectionInfo('default');
    Database::addConnectionInfo('default', 'slave', $connection_info['default']);

    Database::startLog('testing1');

    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();

    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'));//->fetchCol();

    $queries1 = Database::getLog('testing1');

    $this->assertEqual(count($queries1), 2, t('Recorded queries from all targets.'));
    $this->assertEqual($queries1[0]['target'], 'default', t('First query used default target.'));
    $this->assertEqual($queries1[1]['target'], 'slave', t('Second query used slave target.'));
  }

  /**
   * Test that logs to separate targets collapse to the same connection properly.
   *
   * This test is identical to the one above, except that it doesn't create
   * a fake target so the query should fall back to running on the default
   * target.
   */
  function testEnableTargetLoggingNoTarget() {
    Database::startLog('testing1');

    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();

    // We use "fake" here as a target because any non-existent target will do.
    // However, because all of the tests in this class share a single page
    // request there is likely to be a target of "slave" from one of the other
    // unit tests, so we use a target here that we know with absolute certainty
    // does not exist.
    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'fake'))->fetchCol();

    $queries1 = Database::getLog('testing1');

    $this->assertEqual(count($queries1), 2, t('Recorded queries from all targets.'));
    $this->assertEqual($queries1[0]['target'], 'default', t('First query used default target.'));
    $this->assertEqual($queries1[1]['target'], 'default', t('Second query used default target as fallback.'));
  }

  /**
   * Test that we can log queries separately on different connections.
   */
  function testEnableMultiConnectionLogging() {
    // Clone the master credentials to a fake connection.
    // That both connections point to the same physical database is irrelevant.
    $connection_info = Database::getConnectionInfo('default');
    Database::addConnectionInfo('test2', 'default', $connection_info['default']);

    Database::startLog('testing1');
    Database::startLog('testing1', 'test2');

    db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();

    $old_key = db_set_active('test2');

    db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'))->fetchCol();

    db_set_active($old_key);

    $queries1 = Database::getLog('testing1');
    $queries2 = Database::getLog('testing1', 'test2');

    $this->assertEqual(count($queries1), 1, t('Correct number of queries recorded for first connection.'));
    $this->assertEqual(count($queries2), 1, t('Correct number of queries recorded for second connection.'));
  }
}

/**
 * Query serialization tests.
 */
class DatabaseSerializeQueryTestCase extends DatabaseTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Serialize query',
      'description' => 'Test serializing and unserializing a query.',
      'group' => 'Database',
    );
  }

  /**
   * Confirm that a query can be serialized and unserialized.
   */
  function testSerializeQuery() {
    $query = db_select('test');
    $query->addField('test', 'age');
    $query->condition('name', 'Ringo');
    // If this doesn't work, it will throw an exception, so no need for an
    // assertion.
    $query = unserialize(serialize($query));
    $results = $query->execute()->fetchCol();
    $this->assertEqual($results[0], 28, t('Query properly executed after unserialization.'));
  }
}

/**
 * Range query tests.
 */
class DatabaseRangeQueryTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Range query test',
      'description' => 'Test the Range query functionality.',
      'group' => 'Database',
    );
  }

  function setUp() {
    parent::setUp('database_test');
  }

  /**
   * Confirm that range query work and return correct result.
   */
  function testRangeQuery() {
    // Test if return correct number of rows.
    $range_rows = db_query_range("SELECT name FROM {system} ORDER BY name", 2, 3)->fetchAll();
    $this->assertEqual(count($range_rows), 3, t('Range query work and return correct number of rows.'));

    // Test if return target data.
    $raw_rows = db_query('SELECT name FROM {system} ORDER BY name')->fetchAll();
    $raw_rows = array_slice($raw_rows, 2, 3);
    $this->assertEqual($range_rows, $raw_rows, t('Range query work and return target data.'));
  }
}

/**
 * Temporary query tests.
 */
class DatabaseTemporaryQueryTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Temporary query test',
      'description' => 'Test the temporary query functionality.',
      'group' => 'Database',
    );
  }

  function setUp() {
    parent::setUp('database_test');
  }

  /**
   * Return the number of rows of a table.
   */
  function countTableRows($table_name) {
    return db_select($table_name)->countQuery()->execute()->fetchField();
  }

  /**
   * Confirm that temporary tables work and are limited to one request.
   */
  function testTemporaryQuery() {
    $this->drupalGet('database_test/db_query_temporary');
    $data = json_decode($this->drupalGetContent());
    if ($data) {
      $this->assertEqual($this->countTableRows("system"), $data->row_count, t('The temporary table contains the correct amount of rows.'));
      $this->assertFalse(db_table_exists($data->table_name), t('The temporary table is, indeed, temporary.'));
    }
    else {
      $this->fail(t("The creation of the temporary table failed."));
    }

    // Now try to run two db_query_temporary() in the same request.
    $table_name_system = db_query_temporary('SELECT status FROM {system}', array());
    $table_name_users = db_query_temporary('SELECT uid FROM {users}', array());

    $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("system"), t('A temporary table was created successfully in this request.'));
    $this->assertEqual($this->countTableRows($table_name_users), $this->countTableRows("users"), t('A second temporary table was created successfully in this request.'));
  }
}

/**
 * Test how the current database driver interprets the SQL syntax.
 *
 * In order to ensure consistent SQL handling throughout Drupal
 * across multiple kinds of database systems, we test that the
 * database system interprets SQL syntax in an expected fashion.
 */
class DatabaseBasicSyntaxTestCase extends DatabaseTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Basic SQL syntax tests',
      'description' => 'Test SQL syntax interpretation.',
      'group' => 'Database',
    );
  }

  function setUp() {
    parent::setUp('database_test');
  }

  /**
   * Test for string concatenation.
   */
  function testBasicConcat() {
    $result = db_query('SELECT CONCAT(:a1, CONCAT(:a2, CONCAT(:a3, CONCAT(:a4, :a5))))', array(
      ':a1' => 'This',
      ':a2' => ' ',
      ':a3' => 'is',
      ':a4' => ' a ',
      ':a5' => 'test.',
    ));
    $this->assertIdentical($result->fetchField(), 'This is a test.', t('Basic CONCAT works.'));
  }

  /**
   * Test for string concatenation with field values.
   */
  function testFieldConcat() {
    $result = db_query('SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', array(
      ':a1' => 'The age of ',
      ':a2' => ' is ',
      ':a3' => '.',
      ':age' => 25,
    ));
    $this->assertIdentical($result->fetchField(), 'The age of John is 25.', t('Field CONCAT works.'));
  }

  /**
   * Test escaping of LIKE wildcards.
   */
  function testLikeEscape() {
    db_insert('test')
      ->fields(array(
        'name' => 'Ring_',
      ))
      ->execute();

    // Match both "Ringo" and "Ring_".
    $num_matches = db_select('test', 't')
      ->condition('name', 'Ring_', 'LIKE')
      ->countQuery()
      ->execute()
      ->fetchField();
    $this->assertIdentical($num_matches, '2', t('Found 2 records.'));
    // Match only "Ring_" using a LIKE expression with no wildcards.
    $num_matches = db_select('test', 't')
      ->condition('name', db_like('Ring_'), 'LIKE')
      ->countQuery()
      ->execute()
      ->fetchField();
    $this->assertIdentical($num_matches, '1', t('Found 1 record.'));
  }

  /**
   * Test LIKE query containing a backslash.
   */
  function testLikeBackslash() {
    db_insert('test')
      ->fields(array('name'))
      ->values(array(
        'name' => 'abcde\f',
      ))
      ->values(array(
        'name' => 'abc%\_',
      ))
      ->execute();

    // Match both rows using a LIKE expression with two wildcards and a verbatim
    // backslash.
    $num_matches = db_select('test', 't')
      ->condition('name', 'abc%\\\\_', 'LIKE')
      ->countQuery()
      ->execute()
      ->fetchField();
    $this->assertIdentical($num_matches, '2', t('Found 2 records.'));
    // Match only the former using a LIKE expression with no wildcards.
    $num_matches = db_select('test', 't')
      ->condition('name', db_like('abc%\_'), 'LIKE')
      ->countQuery()
      ->execute()
      ->fetchField();
    $this->assertIdentical($num_matches, '1', t('Found 1 record.'));
  }
}

/**
 * Test invalid data handling.
 */
class DatabaseInvalidDataTestCase extends DatabaseTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Invalid data',
      'description' => 'Test handling of some invalid data.',
      'group' => 'Database',
    );
  }

  function setUp() {
    parent::setUp('database_test');
  }

  /**
   * Traditional SQL database systems abort inserts when invalid data is encountered.
   */
  function testInsertDuplicateData() {
    // Try to insert multiple records where at least one has bad data.
    try {
      db_insert('test')
        ->fields(array('name', 'age', 'job'))
        ->values(array(
          'name' => 'Elvis',
          'age' => 63,
          'job' => 'Singer',
        ))->values(array(
          'name' => 'John', // <-- Duplicate value on unique field.
          'age' => 17,
          'job' => 'Consultant',
        ))
        ->values(array(
          'name' => 'Frank',
          'age' => 75,
          'job' => 'Singer',
        ))
        ->execute();
      $this->fail(t('Insert succeedded when it should not have.'));
    }
    catch (Exception $e) {
      // Check if the first record was inserted.
      $name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 63))->fetchField();

      if ($name == 'Elvis') {
        if (!Database::getConnection()->supportsTransactions()) {
          // This is an expected fail.
          // Database engines that don't support transactions can leave partial
          // inserts in place when an error occurs. This is the case for MySQL
          // when running on a MyISAM table.
          $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"));
        }
        else {
          $this->fail(t('The whole transaction is rolled back when a duplicate key insert occurs.'));
        }
      }
      else {
        $this->pass(t('The whole transaction is rolled back when a duplicate key insert occurs.'));
      }

      // Ensure the other values were not inserted.
      $record = db_select('test')
        ->fields('test', array('name', 'age'))
        ->condition('age', array(17, 75), 'IN')
        ->execute()->fetchObject();

      $this->assertFalse($record, t('The rest of the insert aborted as expected.'));
    }
  }

}

/**
 * Drupal-specific SQL syntax tests.
 */
class DatabaseQueryTestCase extends DatabaseTestCase {
  public static function getInfo() {
    return array(
      'name' => 'Custom query syntax tests',
      'description' => 'Test Drupal\'s extended prepared statement syntax..',
      'group' => 'Database',
    );
  }

  function setUp() {
    parent::setUp('database_test');
  }

  /**
   * Test that we can specify an array of values in the query by simply passing in an array.
   */
  function testArraySubstitution() {
    $names = db_query('SELECT name FROM {test} WHERE age IN (:ages) ORDER BY age', array(':ages' => array(25, 26, 27)))->fetchAll();

    $this->assertEqual(count($names), 3, t('Correct number of names returned'));
  }
}

/**
 * Test transaction support, particularly nesting.
 *
 * We test nesting by having two transaction layers, an outer and inner. The
 * outer layer encapsulates the inner layer. Our transaction nesting abstraction
 * should allow the outer layer function to call any function it wants,
 * especially the inner layer that starts its own transaction, and be
 * confident that, when the function it calls returns, its own transaction
 * is still "alive."
 *
 * Call structure:
 *   transactionOuterLayer()
 *     Start transaction
 *     transactionInnerLayer()
 *       Start transaction (does nothing in database)
 *       [Maybe decide to roll back]
 *     Do more stuff
 *     Should still be in transaction A
 *
 */
class DatabaseTransactionTestCase extends DatabaseTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Transaction tests',
      'description' => 'Test the transaction abstraction system.',
      'group' => 'Database',
    );
  }

  /**
   * Helper method for transaction unit test. This "outer layer" transaction
   * starts and then encapsulates the "inner layer" transaction. This nesting
   * is used to evaluate whether the the database transaction API properly
   * supports nesting. By "properly supports," we mean the outer transaction
   * continues to exist regardless of what functions are called and whether
   * those functions start their own transactions.
   *
   * In contrast, a typical database would commit the outer transaction, start
   * a new transaction for the inner layer, commit the inner layer transaction,
   * and then be confused when the outer layer transaction tries to commit its
   * transaction (which was already committed when the inner transaction
   * started).
   *
   * @param $suffix
   *   Suffix to add to field values to differentiate tests.
   * @param $rollback
   *   Whether or not to try rolling back the transaction when we're done.
   */
  protected function transactionOuterLayer($suffix, $rollback = FALSE) {
    $connection = Database::getConnection();
    $depth = $connection->transactionDepth();
    $txn = db_transaction();

    // Insert a single row into the testing table.
    db_insert('test')
      ->fields(array(
        'name' => 'David' . $suffix,
        'age' => '24',
      ))
      ->execute();

    $this->assertTrue($connection->inTransaction(), t('In transaction before calling nested transaction.'));

    // We're already in a transaction, but we call ->transactionInnerLayer
    // to nest another transaction inside the current one.
    $this->transactionInnerLayer($suffix, $rollback);

    $this->assertTrue($connection->inTransaction(), t('In transaction after calling nested transaction.'));

    if ($rollback) {
      // Roll back the transaction, if requested.
      // This rollback should propagate to the last savepoint.
      $txn->rollback();
      $this->assertTrue(($connection->transactionDepth() == $depth), t('Transaction has rolled back to the last savepoint after calling rollback().'));
    }
  }

  /**
   * Helper method for transaction unit tests. This "inner layer" transaction
   * is either used alone or nested inside of the "outer layer" transaction.
   *
   * @param $suffix
   *   Suffix to add to field values to differentiate tests.
   * @param $rollback
   *   Whether or not to try rolling back the transaction when we're done.
   */
  protected function transactionInnerLayer($suffix, $rollback = FALSE) {
    $connection = Database::getConnection();

    $this->assertTrue($connection->inTransaction(), t('In transaction in nested transaction.'));

    $depth = $connection->transactionDepth();
    // Start a transaction. If we're being called from ->transactionOuterLayer,
    // then we're already in a transaction. Normally, that would make starting
    // a transaction here dangerous, but the database API handles this problem
    // for us by tracking the nesting and avoiding the danger.
    $txn = db_transaction();

    $depth2 = $connection->transactionDepth();
    $this->assertTrue($depth < $depth2, t('Transaction depth is has increased with new transaction.'));

    // Insert a single row into the testing table.
    db_insert('test')
      ->fields(array(
        'name' => 'Daniel' . $suffix,
        'age' => '19',
      ))
      ->execute();

    $this->assertTrue($connection->inTransaction(), t('In transaction inside nested transaction.'));

    if ($rollback) {
      // Roll back the transaction, if requested.
      // This rollback should propagate to the last savepoint.
      $txn->rollback();
      $this->assertTrue(($connection->transactionDepth() == $depth), t('Transaction has rolled back to the last savepoint after calling rollback().'));
    }
  }

  /**
   * Test transaction rollback on a database that supports transactions.
   *
   * If the active connection does not support transactions, this test does nothing.
   */
  function testTransactionRollBackSupported() {
    // This test won't work right if transactions are not supported.
    if (!Database::getConnection()->supportsTransactions()) {
      return;
    }
    try {
      // Create two nested transactions. Roll back from the inner one.
      $this->transactionOuterLayer('B', TRUE);

      // Neither of the rows we inserted in the two transaction layers
      // should be present in the tables post-rollback.
      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
      $this->assertNotIdentical($saved_age, '24', t('Cannot retrieve DavidB row after commit.'));
      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
      $this->assertNotIdentical($saved_age, '19', t('Cannot retrieve DanielB row after commit.'));
    }
    catch (Exception $e) {
      $this->fail($e->getMessage());
    }
  }

  /**
   * Test transaction rollback on a database that does not support transactions.
   *
   * If the active driver supports transactions, this test does nothing.
   */
  function testTransactionRollBackNotSupported() {
    // This test won't work right if transactions are supported.
    if (Database::getConnection()->supportsTransactions()) {
      return;
    }
    try {
      // Create two nested transactions. Attempt to roll back from the inner one.
      $this->transactionOuterLayer('B', TRUE);

      // Because our current database claims to not support transactions,
      // the inserted rows should be present despite the attempt to roll back.
      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
      $this->assertIdentical($saved_age, '24', t('DavidB not rolled back, since transactions are not supported.'));
      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
      $this->assertIdentical($saved_age, '19', t('DanielB not rolled back, since transactions are not supported.'));
    }
    catch (Exception $e) {
      $this->fail($e->getMessage());
    }
  }

  /**
   * Test committed transaction.
   *
   * The behavior of this test should be identical for connections that support
   * transactions and those that do not.
   */
  function testCommittedTransaction() {
    try {
      // Create two nested transactions. The changes should be committed.
      $this->transactionOuterLayer('A');

      // Because we committed, both of the inserted rows should be present.
      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidA'))->fetchField();
      $this->assertIdentical($saved_age, '24', t('Can retrieve DavidA row after commit.'));
      $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielA'))->fetchField();
      $this->assertIdentical($saved_age, '19', t('Can retrieve DanielA row after commit.'));
    }
    catch (Exception $e) {
      $this->fail($e->getMessage());
    }
  }
}


/**
 * Check the sequences API.
 */
class DatabaseNextIdCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => t('Sequences API'),
      'description' => t('Test the secondary sequences API.'),
      'group' => t('Database'),
    );
  }

  /**
   * Test that the sequences API work.
   */
  function testDbNextId() {
    $first = db_next_id();
    $second = db_next_id();
    // We can test for exact increase in here because we know there is no
    // other process operating on these tables -- normally we could only
    // expect $second > $first.
    $this->assertEqual($first + 1, $second, t('The second call from a sequence provides a number increased by one.'));
    $result = db_next_id(1000);
    $this->assertEqual($result, 1001, t('Sequence provides a larger number than the existing ID.'));
  }
}

/**
 * Tests the empty pseudo-statement class.
 */
class DatabaseEmptyStatementTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => t('Empty statement'),
      'description' => t('Test the empty pseudo-statement class.'),
      'group' => t('Database'),
    );
  }

  /**
   * Test that the empty result set behaves as empty.
   */
  function testEmpty() {
    $result = new DatabaseStatementEmpty();

    $this->assertTrue($result instanceof DatabaseStatementInterface, t('Class implements expected interface'));
    $this->assertNull($result->fetchObject(), t('Null result returned.'));
  }

  /**
   * Test that the empty result set iterates safely.
   */
  function testEmptyIteration() {
    $result = new DatabaseStatementEmpty();

    foreach ($result as $record) {
      $this->fail(t('Iterating empty result set should not iterate.'));
      return;
    }

    $this->pass(t('Iterating empty result set skipped iteration.'));
  }

  /**
   * Test that the empty result set mass-fetches in an expected way.
   */
  function testEmptyFetchAll() {
    $result = new DatabaseStatementEmpty();

    $this->assertEqual($result->fetchAll(), array(), t('Empty array returned from empty result set.'));
  }
}

Other Drupal examples (source code examples)

Here is a short list of links related to this Drupal database_test.test source code file:

new blog posts

"Drupal" is a registered trademark of Dries Buytaert.

my drupal tutorials and examples  

Copyright 1998-2016 Alvin Alexander, alvinalexander.com
All Rights Reserved.

Beginning in 2016, a portion of the proceeds from pages under the '/drupal-code-examples/' URI will be donated to charity.