This document describes the Database Abstraction Layer (DBTNG) introduced in Drupal 7. DBTNG provides a unified API for database queries that works across different database backends like MySQL, PostgreSQL, and SQLite. It supports both static and dynamic queries. Static queries use placeholders and prepared statements for security, while dynamic queries are built programmatically using a query builder object. Methods are provided for SELECT, INSERT, UPDATE, DELETE, and MERGE queries. Transactions and reading from slave databases are also supported.
5. Basic Queries
Most SELECT queries are simple and
don’t change
Drupal calls these: static queries
$result = db_query("SELECT name, filename FROM {system} WHERE
type = :type AND status = :status", array(':type' => 'module', ':status' => 1));
6. Basic Queries
Table names wrapped in curly braces
No database-specific syntax in the query.
There are no literal values in the query.
Instead, literal values are specified by
placeholders.
7. Placeholders
Must be unique within a query, and must
begin with a colon.
Should never have quotation marks
around them, regardless of the data type.
The database server will handle that for
us.
Should be used for all literal data, even if it
will not vary.
8. Result Object
The return value from a db_query() call is a
result object.
$list = array();
foreach ($result as $record) {
$list[] = t('@name: @filename', array(
'@name' => $record->name,
'@filename' => $record->filename,
));
}
9. Result as array
By default, each $record in the result set is
a stdClass object.
This is how you get an array:
$result = db_query("SELECT name, filename FROM {system} WHERE type =
:type AND status = :status", array(':type' => 'module', ':status' => 1), array('fetch'
=> PDO::FETCH_ASSOC));
10. Fetch option
We can also fetch a single record, or even
just a single field:
// Fetch a single record as an object.
$record = $result->fetchObject();
// Fetch a single record as an array.
$record = $result->fetchAssoc();
// Fetch just the first field of the next record.
$field = $result->fetchField();
// Fetch the entire result set at once into an array.
$records = $result->fetchAll()
12. Dynamic Queries
To start, we create a new query object with
db_select():
$query = db_select('node', 'n');
The first parameter is the name of the
base table of the query and the second is
the alias.
13. Dynamic Queries
We then call additional methods on the
$query object in order to build up the
query logic we want to create dynamically
$query = db_select('node', 'n');
$query->fields('n', array('nid, title'));
$u_alias = $query->innerJoin('users' ,'u', '%alias.uid = n.uid');
$query->addField($u_alias, 'name', 'username');
$query->condition("{$u_alias}.name", 'Bob');
$query->condition('n.created', REQUEST_TIME - 604800, '>=');
$query->orderBy('n.created', 'DESC');
$query->range(0, 5);
$query->addTag('node_access');
$result = $query->execute();
14. Dynamic Queries
The resulting query
SELECT n.nid AS nid, n.title AS title, u.name AS username
FROM {node} n
INNER JOIN {users} u ON u.nid = n.nid
WHERE (n.created >= 1286213869)
AND (u.name = 'Bob')
ORDER BY n.created DESC
LIMIT 5 OFFSET 0
15. Dynamic Queries
There's one more important method to call
—addTag().
It does mark the type of query it is
If a query has been tagged then before it
is turned into an SQL string it will be
passed through hook_query_alter() and
hook_query_TAG_alter().
16. Dynamic Queries
The node_access tag, is most important
as it allows the node access system to
alter the query, to filter out nodes that the
current user should not have access to.
17. Dynamic Queries
Most methods of the select builder return the
select object itself and thus are chainable.
The exceptions are the addField() and join()
methods, as those need to return a generated
alias instead.'n');
$query = db_select('node',
$u_alias = $query->innerJoin('users' ,'u', '%alias.uid = n.uid'); $query-
>addField($u_alias, 'name', 'username');
$result = $query
->fields('n', array('nid, title'));
->condition("{$u_alias}.name", 'Bob');
->condition('n.created', REQUEST_TIME - 604800, '>=');
->orderBy('n.created', 'DESC');
->range(0, 5);
->addTag('node_access')
->execute();
18. Dynamic Queries
SELECT queries have both static and
dynamic versions
INSERT, UPDATE, DELETE, and MERGE
queries only support a dynamic version
23. Insert
On databases that support multi-insert
statements, the preceding code will be run
as a single query. For those that don't,
they will run as separate queries within a
single transaction.
Note that in a multi-insert query the return
value from execute() is undefined and
should be ignored.
25. Update
Update queries look like a hybrid of Insert
and Select statements.
consist of both fields to set on a table and
conditions to restrict the query.
db_update('imports')
->condition('name', 'Chico')
->fields(array('address' => 'Go West St.'))
->execute();
Resulting query:
UPDATE {imports} SET address = 'Go West St.' WHERE name = 'Chico';
26. Update
Return value from execute() for Update
queries = number of records that were
changed.
'changed' does not mean 'matched'.
If the WHERE portion of the query
matches a record but if that record already
has the values that it would be set to, it will
not be changed and would not count
towards the return value from execute().
28. Delete
Delete queries should come as no
surprise, as they consist of essentially just
a WHERE clause:
db_delete('imports')
->condition('name' => 'Zeppo')
->execute();
Return value from execute() = number of
records that were deleted by the query.
30. Merge
one of the oft-forgotten parts of SQL
the most popular open source databases
do not support them directly
"If this record exists, update it with this
query otherwise create it with this other
query"
It is most useful for setting records that
may or may not exist yet, that is, merging
data into the table.
31. Merge
A true merge query is atomic = we're
guaranteed that it will run as a single
uninterrupted operation or fail completely.
Since most of the databases Drupal works
with do not directly support Merge queries,
Drupal emulates them with multiple
queries and a transaction, which in most
cases is close enough.
32. Merge
db_merge('variable')
->key(array('name' => $name))
->fields(array('value' => serialize($value)))
->execute();
The key() method takes an associative
array of field/value pairs that are the pivot
of the query.
The fields() method is about the fields to
set
33. Merge
The query can be read as:
"If there is a record where the field 'name'
has the value $name, set the 'value' field.
If not, insert a new record with name equal
to $name and value equal to the given
string.”
34. Merge
We can also define more complex logic
using the insertFields() and updateFields()
methods.
db_merge('people')
->key(array('job' => 'Speaker'))
->insertFields(array(
'age' => 31,
'name' => 'Meredith',
))
->updateFields(array( 'name' => 'Tiffany',
))
->execute();
36. Transactions
A transaction in a database is a way to wrap
two or more queries together and declare that
they should be atomic.
That is, either all succeed or none succeed.
We start a transaction by creating a
transaction object.
Everything we do to the database is then part
of the transaction until that object is
destroyed, at which point the entire query is
committed at once.
In most cases, we let PHP destroy the
transaction object for us when a function
ends.
38. Slave servers
Drupal also supports Master/slave
database replication
select queries can be run against a slave
server to offload the work to separate
servers
The third parameter to db_query() or
db_select() is an array of options that
tweak the behavior of the query.
39. Slave servers
The key of interest = target
It specifies which database variant the
system should try.
Legal values: default (which is the default)
and slave.
If "slave" is specified, Drupal will try to run
the query against a slave server. If one is
not available, it will silently fall back to the
default server.
40. Slave servers
$result = db_query("SELECT name, filename FROM {system} WHERE type =
:type AND status = :status", array(':type' => 'module', ':status' => 1), array('fetch'
=> PDO::FETCH_ASSOC, 'target' => 'slave'));
41. Slave servers
Data on a slave server is always a little
behind the master server
Not all Select queries can handle their
data being slightly stale
After writing data, we can call
db_ignore_slave().
It will make a note in the active session to
disable the slave server for the current
user only for a configurable period of time.
(The default is five minutes.)
42. Simplify code
Especially for modules creating their own
API
They need their own query builder
Example: Voting API