--- dbtng_example/dbtng_example.info
+++ dbtng_example/dbtng_example.info
@@ -0,0 +1,8 @@
+; $Id$
+name = DBTNG example
+description = An example module showing how use the database API: DBTNG.
+package = Example modules
+core = 7.x
+files[] = dbtng_example.module
+files[] = dbtng_example.install
+files[] = dbtng_example.test

--- dbtng_example/dbtng_example.install
+++ dbtng_example/dbtng_example.install
@@ -0,0 +1,125 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Install, update and uninstall functions for the dbtng example module.
+ */
+
+/**
+ * Implementation of hook_install().
+ *
+ * In Drupal 7, there is no need to install schema using this hook, the schema
+ * is already installed before this hook is called.
+ *
+ * We will create a default entry in the database.
+ *
+ * @see hook_install()
+ */
+function dbtng_example_install() {
+  /* This is the preferred Drupal way to do insert and updates in a database
+   * but this piece of code can not be executed during hook_install.
+   *
+   * @code
+   * $fields = array(
+   *   'uid'     => 0,
+   *   'name'    => 'John',
+   *   'surname' => 'Doe',
+   *   'age'     => 0,
+   * );
+   * drupal_write_record('dbtng_example', $fields);
+   * @endcode
+   *
+   * We need to use db_insert() here.
+   */
+
+  // Add a default entry.
+  $fields = array(
+    'name'    => 'John',
+    'surname' => 'Doe',
+    'age'     => 0,
+  );
+  db_insert('dbtng_example')
+    ->fields($fields)
+    ->execute();
+
+  // Add another entry.
+  $fields = array(
+    'name'    => 'John',
+    'surname' => 'Roe',
+    'age'     => 100,
+    'uid'     => 1,
+  );
+  db_insert('dbtng_example')
+    ->fields($fields)
+    ->execute();
+
+}
+
+/**
+ * Implementation of hook_uninstall().
+ *
+ * As in hook_install, there is no need to uninstall schema, Drupal will do it
+ * for us.
+ *
+ * @see hook_uninstall()
+ */
+function dbtng_example_uninstall() {
+}
+
+
+/**
+ * Implements hook_schema().
+ *
+ * Define the database tables used by this module.
+ *
+ * @see hook_schema()
+ */
+function dbtng_example_schema() {
+
+  $schema['dbtng_example'] = array(
+    'description' => 'Stores example person entries for demonstration purposes.',
+    'fields' => array(
+      'pid'  => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique person ID.',
+      ),
+      'uid' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => "Creator user's {users}.uid",
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Name of the person.',
+      ),
+      'surname' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Surname of the person.',
+      ),
+      'age' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'tiny',
+        'description' => 'The age of the person in years.',
+      )
+    ),
+    'primary key' => array('pid'),
+    'indexes' => array(
+      'name'    => array('name'),
+      'surname' => array('surname'),
+      'age'     => array('age'),
+    ),
+  );
+
+  return $schema;
+}

--- dbtng_example/dbtng_example.module
+++ dbtng_example/dbtng_example.module
@@ -0,0 +1,504 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * This is an example outlining how a module can make use of the new database
+ * API in Drupal 7: DBTNG.
+ *
+ */
+
+/**
+ * Writting to the database
+ * 
+ * Since Drupal 6, the recommended method to save or update an entry in the 
+ * database is drupal_write_record(), however, using db_query it is still 
+ * possible to perform INSERT operations.
+ *
+ * In Drupal 6, the db_query sentence may have some undefined arguments that
+ * will be replaced with know values, passed in order to the function:
+ *
+ * @code
+ *   $query = "INSERT INTO {dbtng_example} (name, surname) VALUES('%s','%s')";
+ *   db_query($query, $name, $surname);
+ * @endcode 
+ *  
+ * In Drupal 7, the usage of db_query for INSERT, UPDATE, or DELETE is not 
+ * recommended any more, and specific functions are provided to perform this 
+ * operations: db_insert(), db_update() and db_delete().
+ * 
+ * db_insert() does not require any condition, only a group of fields:
+ * @code
+ *   // INSERT INTO {dbtng_example} (name, surname) VALUES('John, 'Doe')
+ *   db_insert('dbtng_example')
+ *     ->fields(array('name' => 'John', 'surname' => 'Doe'))
+ *     ->execute();
+ * @endcode
+ * 
+ * db_update() requires the fields to be saved, and the condition to select wich
+ * rows should be modified:
+ * @code
+ *   // UPDATE {dbtng_example} SET name = 'Jane' WHERE name = 'John'
+ *   db_update('dbtng_example')
+ *     ->fields(array('name' => 'Jane'))
+ *     ->condition('name', 'John')
+ *     ->execute();
+ * @endcode
+ * 
+ * db_delete() does not require any fields, just the condition statement:
+ * @code
+ *   // DELETE FROM {dbtng_example} WHERE name = 'Jane'
+ *   db_delete('dbtng_example')
+ *     ->condition('name', 'Jane')
+ *     ->execute();
+ * @endcode
+ *
+ * @see db_insert()
+ * @see db_update()
+ * @see db_delete()
+ * @see drupal_write_record()
+ */
+
+
+/**
+ * Save entry in the database.
+ *
+ * @param $entry
+ *   an array containing all the fields of the entry.
+ *
+ * @ingroup writting the database
+ * @see db_insert
+ */
+function dbtng_example_entry_save($entry) {
+  // The usage of drupal_write_record() is preferred when saving entries in the
+  // database:
+  // @code
+  //   drupal_write_record('example', $entry);
+  // @endcode
+  //
+  // To show you how does the new database system look like, we are going to use
+  // an specific db_insert query:
+  //
+  // In Drupal 6, the creating operation we have been using is:
+  // @code
+  //   db_query(
+  //     "INSERT INTO {dbtng_example} (name, surname, age)
+  //       VALUES ('%s', '%s', '%d')",
+  //     $entry['name'],
+  //     $entry['surname'],
+  //     $entry['age']
+  //   );
+  // @endcode
+  //
+  // In Drupal 7, there is an specific database API call to manage updates.
+  db_insert('dbtng_example')
+    ->fields($entry)
+    ->execute();
+
+}
+
+/**
+ * Update an entry in the database.
+ *
+ * @param $entry
+ *   an array containing all the fields of the entry to be updated.
+ *
+ * @ingroup writting the database
+ * @see db_update
+ */
+function dbtng_example_entry_update($entry) {
+  // The usage of drupal_write_record() is preferred when updating entries in
+  // the database:
+  // @code
+  //   drupal_write_record('example', $entry, 'pid');
+  // @endcode
+  //
+  // To show you how does the new database system look like, we are going to use
+  // an specific db_update query:
+  //
+  // In Drupal 6, the update operation we have been using is:
+  // @code
+  //   db_query(
+  //     "UPDATE {dbtng_example}
+  //       SET name = '%s', surname = '%s', age = '%d'
+  //       WHERE pid = %d",
+  //     $entry['pid']
+  //   );
+  // @endcode
+  //
+  // In Drupal 7, there is an specific database API call to manage updates.
+  db_update('dbtng_example')
+    ->fields($entry)
+    ->condition('pid', $entry['pid'])
+    ->execute();
+
+}
+
+/**
+ * Delete an entry in the database.
+ *
+ * @param $entry
+ *   an array containing at least the person identifier 'pid' element.
+ *
+ * @ingroup writting the database
+ * @see db_delete
+ */
+function dbtng_example_entry_delete($entry) {
+  // The usage of db_query now is not recommended any more to make database
+  // deletions. This is the way used in Drupal 6 to remove entries:
+  // @code
+  //   db_query("DELETE FROM {dbtng_example} WHERE pid = %d", $entry['pid]);
+  // @endcode
+  //
+  // In Drupal 6, the deleting operation we have been using is:
+  // @code
+  //   db_query("DELETE FROM {dbtng_example} WHERE pid = %d", $entry['pid]);
+  // @endcode
+  //
+  // In Drupal 7, there is an specific database API call to manage deletions.
+  db_delete('dbtng_example')
+    ->condition('pid', $entry['pid'])
+    ->execute();
+
+}
+
+
+/**
+ * Reading the database
+ *
+ * Reading the database is more complex than just writting, because of the
+ * number of possible combinations.
+ *
+ * In Drupal 6, the standard function to perform read queries is db_query().
+ *
+ * db_query() used a query string as parameter, being this query as more SQL99
+ * complaint as possible. The query string should include all the SQL parts
+ * required for the query: SELECT (what), FROM, JOINS (and from) , WHERE
+ * (conditions), ORDER and LIMIT. Additionally, other db_query helpers are
+ * available to write especific SQL code, db_query_range() is an example.
+ *
+ * This query string used in Drupal 6 supports unamed placeholders, that should
+ * be passed to db_query as a single array or a serie of parameters but they
+ * must be in correct order.
+ * @code
+ *  $query = "SELECT * FROM {dbtng_example} n WHERE n.uid = %d AND name = '%s'";
+ *  $result = db_query($query, $uid, $name);
+ * @endcode
+ * This call returns a database resource that must be pulled using:
+ * db_object(), db_fetch_array() or db_result() depending on the query.
+ *
+ * Drupal 7 DBTNG provides a better and more complex interface, allowing the
+ * query creation process to be easier to write, read and understand for long
+ * and complex queries.
+ *
+ * db_query() can be used for static, and dynamic / conditional queries, and may
+ * return an object, array or result information without additional API calls.
+ *
+ * @code
+ *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
+ *   db_query(
+ *     "SELECT * FROM {dbtng_example} WHERE uid = :uid and name = :name",
+ *     array(':uid' => 0, ':name' => 'John')
+ *   )->execute();
+ * @endcode
+ *
+ * But for select statements, Drupal provides the db_select() API method. This
+ * end up in several ways to perform the same SQL query.
+ *
+ * @code
+ *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
+ *   db_select('dbtng_example')
+ *     ->condition('uid', 0)
+ *     ->condition('name', 'John')
+ *     ->execute();
+ * @endcode
+ *
+ * DBTNG also accepts strings for condition evaluation. Note that in Drupa 7,
+ * the unnamed placeholders are a single '?' character, instead of '%s', %d
+ * or previously used placeholders.
+ * This is the same query but now using a where condition
+ * @code
+ *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
+ *   db_select('dbtng_example')
+ *     ->where('uid = ? AND name = ?', array(0, 'John'))
+ *     ->execute();
+ * @endcode
+ *
+ * Using named placeholders is preferred to avoid errors or security issues in
+ * the database layer. When using named placeholders, there order of the
+ * arguments is not important.
+ * @code
+ *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
+ *   $arguments = array(':name' => 'John', ':uid' => 0);
+ *   db_select('dbtng_example')
+ *     ->where('uid = :uid AND name = :name', $arguments)
+ *     ->execute();
+ * @endcode
+ *
+ * Conditions are stacked and evaluated as AND and OR depending on the type of
+ * query. For more information about this, you can read the conditional queries
+ * handbook page at: http://drupal.org/node/310086
+ *
+ * The condition argument is an 'equal' evaluation by default, but this can be
+ * altered:
+ * @code
+ *   // SELECT * FROM {dbtng_example} WHERE age > 18
+ *   db_select('dbtng_example')
+ *     ->condition('age', 18, '>')
+ *     ->execute();
+ * @endcode
+ *
+ * @see db_query()
+ * @see db_select()
+ */
+
+/**
+ * Read one or more entries from the database, using a filter array.
+ *
+ * Calling dbtng_example_entry_load(array('name' => 'John')); will return only
+ * entries for persons called John.
+ *
+ * @param $entry
+ *   an array containing all the fields used to search the entries in the table.
+ * @return
+ *   an array containing the loaded entries if found.
+ *
+ * @ingroup reading the database
+ * @see db_select
+ */
+function dbtng_example_entry_load($entry = array()) {
+  /**
+   * In Drupal 6, we have used this code to make as an example implementation
+   * of this function:
+   * @code
+   *   $cond = array();
+   *   $arguments = array();
+   *
+   *   // Turn the conditions into a query.
+   *   foreach ($entry as $key => $value) {
+   *     $cond[] = db_escape_table($key) ." = '%s'";
+   *     $arguments[] = $value;
+   *   }
+   *   $cond = implode(' AND ', $cond);
+   *   $query = "SELECT * FROM {dbtng_example} WHERE ";
+   *   $result = db_query($query. $cond, $arguments);
+   *
+   *   $objects = array();
+   *   while ($object = db_fetch_object($result)) {
+   *     $objects[] = $object;
+   *   }
+   *   return $objects;
+   * @endcode
+   *
+   * Using DBTNG things are much easier.
+   */
+
+  // Read from the dbtng_example table, read all fields of this table.
+  $select = db_select('dbtng_example', 'e');
+  $select->fields('e');
+  
+  // Add each field and value as a condition to this query.
+  foreach ($entry as $field => $value) {
+    $select->condition($field, $value);
+  }
+  // Return the result in object format.
+  return $select->execute()->fetchAll();
+}
+
+
+/**
+ * Render a filtered list of entries in the database.
+ *
+ * DBTNG also helps processing queries returning several rows, providing the
+ * found objects in the same query execution call.
+ *
+ * This function queries the database using a JOIN between users table and the
+ * example entries, to provide the username that created the entry, and creates
+ * a table with the results, processing each row.
+ *
+ * @see db_select()
+ */
+function dbtng_example_advanced_list() {
+  $output = '';
+
+  /**
+   * This example will show how to write the following database query using
+   * DBTNG:
+   *
+   * SELECT
+   *  e.pid as pid, e.name as name, e.surname as surname, e.age as age
+   *  u.name as username
+   * FROM
+   *  {dbtng_example} e
+   * JOIN
+   *  users u ON e.uid = u.uid
+   * WHERE
+   *  e.name = 'John' AND e.age > 18
+   *
+   */
+  $select = db_select('dbtng_example', 'e');
+  // Join the users table, so we can get the entry creator's username
+  $select->join('users', 'u', 'e.uid = u.uid');
+  // Select these specific fields for the output
+  $select->addField('e', 'pid');
+  $select->addField('u', 'name', 'username');
+  $select->addField('e', 'name');
+  $select->addField('e', 'surname');
+  $select->addField('e', 'age');
+  // Filter only persons named "John"
+  $select->condition('e.name', 'John');
+  // Filter only persons older than 18 years
+  $select->condition('e.age', 18, '>');
+
+  // Now, loop all these entries an show them in a table. Note that there is no
+  // db_fetch_* object or array function being called here.
+  if ($entries = $select->execute()->fetchAll()) {
+    $rows = array();
+    foreach ($entries as $entry) {
+      // Each entry is a row, forcing to be array.
+      $rows[] = (array) $entry;
+    }
+    // Make a table for them.
+    $header = array(t('Id'), t('Created by'), t('Name'), t('Surname'), t('Age'));
+    $output .= theme('table', array('header' => $header, 'rows' => $rows));
+  }
+  else {
+    drupal_set_message(t('No entries have been added yet.'));
+  }
+  return $output;
+}
+
+//// Helper functions ////
+
+/**
+ * Implements hook_help().
+ *
+ * Show some help on each form provided by this module.
+ */
+function dbtng_example_help($path) {
+  $output = '';
+  switch ($path) {
+    case 'examples/dbtng':
+      $output = t('This is a list of all entries in the database. There is no active filter in the query.');
+      break;
+    case 'examples/dbtng/advanced':
+      $output  = t('This is a more complex list of entries in the database. ');
+      $output .= t('Only the entries with name = "John" and older than 18 years are shown, and the username of the person how created the entry is also shown.');
+      break;
+    case 'examples/dbtng/add':
+      $output = t('Complete the fields Name, Surname and Age. your user UID is automatically associated to this person entry.');
+      break;
+  }
+  return $output;
+}
+
+
+/**
+ * Set up calls to drupal_get_form() for all our example cases.
+ *
+ * Implements hook_menu().
+ */
+function dbtng_example_menu() {
+  $items = array();
+
+  $items['examples/dbtng'] = array(
+    'title' => 'DBTNG Example',
+    'page callback' => 'dbtng_example_list',
+    'access callback' => TRUE,
+  );
+  $items['examples/dbtng/list'] = array(
+    'title' => 'List',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['examples/dbtng/advanced'] = array(
+    'title' => 'Advanced list',
+    'page callback' => 'dbtng_example_advanced_list',
+    'access callback' => TRUE,
+    'type' => MENU_LOCAL_TASK,
+  );
+  $items['examples/dbtng/add'] = array(
+    'title' => 'Add entry',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('dbtng_example_form_add'),
+    'access callback' => TRUE,
+    'type' => MENU_LOCAL_TASK,
+    'weight' => -9,
+  );
+
+  return $items;
+}
+
+/**
+ * Render a list of entries in the database.
+ */
+function dbtng_example_list() {
+  $output = '';
+
+  // Get all entries in the dbtng_example table
+  if ($entries = dbtng_example_entry_load()) {
+    $rows = array();
+    foreach ($entries as $entry) {
+      // Each entry is a row, forcing to be array.
+      $rows[] = (array) $entry;
+    }
+    // Make a table for them.
+    $header = array(t('Id'), t('uid'), t('Name'), t('Surname'), t('Age'));
+    $output .= theme('table', array('header' => $header, 'rows' => $rows));
+  }
+  else {
+    drupal_set_message(t('No entries have been added yet.'));
+  }
+  return $output;
+}
+
+
+
+/**
+ * Prepare a simple form to add an entry, with all the interesting fields.
+ */
+function dbtng_example_form_add(&$form_state) {
+  $form = array();
+
+  $form['add'] = array(
+    '#type'  => 'fieldset',
+    '#title' => t('Add a person entry'),
+  );
+  $form['add']['name'] = array(
+    '#type'  => 'textfield',
+    '#title' => t('Name'),
+    '#size'  => 15
+  );
+  $form['add']['surname'] = array(
+    '#type'  => 'textfield',
+    '#title' => t('Surname'),
+    '#size'  => 15
+  );
+  $form['add']['age'] = array(
+    '#type'  => 'textfield',
+    '#title' => t('Age'),
+    '#size'  => 5
+  );
+  $form['add']['submit'] = array(
+    '#type'  => 'submit',
+    '#value' => t('Add'),
+  );
+
+  return $form;
+}
+
+/**
+ * Submit handler for 'add entry' form.
+ */
+function dbtng_example_form_add_submit($form, $form_state){
+global $user;
+
+  // Save the submitted entry.
+  $entry = array(
+    'name'    => $form_state['values']['name'],
+    'surname' => $form_state['values']['surname'],
+    'age'     => $form_state['values']['age'],
+    'uid'     => $user->uid,
+  );
+  dbtng_example_entry_save($entry);
+}

--- dbtng_example/dbtng_example.test
+++ dbtng_example/dbtng_example.test
@@ -0,0 +1,123 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * test file for trigger_example module.
+ */
+
+/**
+ * Default test case for the trigger_example module.
+ */
+class DBTNGExampleTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'DBTNG example',
+      'description' => 'Perform various tests on the dbtng module.' ,
+      'group' => 'Examples',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('dbtng_example');
+  }
+
+  /**
+   * Test default module installation, two entries in the database table.
+   */
+  function testInstall() {
+    $result = dbtng_example_entry_load();
+    $this->assertEqual(
+      count($result),
+      2,
+      t('Found two entries in the table after installing the module.')
+    );
+  }
+
+  /**
+   * Test several combinations, adding entries, updating and deleting.
+   */
+  function testSomeFunctionCombos() {
+    // Create a new entry.
+    $entry = array(
+      'name' => 'James',
+      'surname' => 'Doe',
+      'age' => 23,
+    );
+    dbtng_example_entry_save($entry);
+
+    // Save another entry
+    $entry = array(
+      'name' => 'Jane',
+      'surname' => 'Doe',
+      'age' => 19,
+    );
+    dbtng_example_entry_save($entry);
+
+    // Verify 4 records are found in the database
+    $result = dbtng_example_entry_load();
+    $this->assertEqual(
+      count($result),
+      4,
+      t('Found a total of four entries in the table after creating two additional entries.')
+    );
+
+    // Verify 3 of these records have 'Doe' as surname
+    $result = dbtng_example_entry_load(array('surname' => 'Doe'));
+    $this->assertEqual(
+      count($result),
+      3,
+      t('Found three entries in the table with surname = "Doe".')
+    );
+
+    // Read only John Doe entry.
+    $result = dbtng_example_entry_load(array('name' => 'John', 'surname' => 'Doe'));
+    $this->assertEqual(
+      count($result),
+      1,
+      t('Found one entry for John Doe.')
+    );
+    // Get the entry
+    $entry = (array) end($result);
+    // Change age to 45
+    $entry['age'] = 45;
+    // Update entry in database
+    dbtng_example_entry_update($entry);
+
+    // Find entries with age = 45
+    // Read only John Doe entry.
+    $result = dbtng_example_entry_load(array('age' => '45'));
+    $this->assertEqual(
+      count($result),
+      1,
+      t('Found one entry with age = 45.')
+    );
+
+    // Verify it is John Doe.
+    $entry = (array) end($result);
+    $this->assertEqual(
+      $entry['name'],
+      'John',
+      t('The name John is found in the entry')
+    );
+    $this->assertEqual(
+      $entry['surname'],
+      'Doe',
+      t('The surname Doe is found in the entry')
+    );
+
+    // Delete the entry.
+    dbtng_example_entry_delete($entry);
+
+    // Verify that now there are only 3 records
+    $result = dbtng_example_entry_load();
+    $this->assertEqual(
+      count($result),
+      3,
+      t('Found only three records, a record was deleted.')
+    );
+  }
+
+}
+


