$e->query_string = $query; } $e->args = $args; throw $e; } return NULL; } } /** * Expands out shorthand placeholders. * * Drupal supports an alternate syntax for doing arrays of values. We * therefore need to expand them out into a full, executable query string. * * @param $query * The query string to modify. * @param $args * The arguments for the query. * * @return * TRUE if the query was modified, FALSE otherwise. */ protected function expandArguments(&$query, &$args) { $modified = FALSE; // If the placeholder value to insert is an array, assume that we need // to expand it out into a comma-delimited set of placeholders. foreach (array_filter($args, 'is_array') as $key => $data) { $new_keys = array(); foreach (array_values($data) as $i => $value) { // This assumes that there are no other placeholders that use the same // name. For example, if the array placeholder is defined as :example // and there is already an :example_2 placeholder, this will generate // a duplicate key. We do not account for that as the calling code // is already broken if that happens. $new_keys[$key . '_' . $i] = $value; } // Update the query with the new placeholders. // preg_replace is necessary to ensure the replacement does not affect // placeholders that start with the same exact text. For example, if the // query contains the placeholders :foo and :foobar, and :foo has an // array of values, using str_replace would affect both placeholders, // but using the following preg_replace would only affect :foo because // it is followed by a non-word character. $query = preg_replace('#' . $key . '\b#', implode(', ', array_keys($new_keys)), $query); // Update the args array with the new placeholders. unset($args[$key]); $args += $new_keys; $modified = TRUE; } return $modified; } /** * Gets the driver-specific override class if any for the specified class. * * @param string $class * The class for which we want the potentially driver-specific class. * @param array $files * The name of the files in which the driver-specific class can be. * @param $use_autoload * If TRUE, attempt to load classes using PHP's autoload capability * as well as the manual approach here. * @return string * The name of the class that should be used for this driver. */ public function getDriverClass($class, array $files = array(), $use_autoload = FALSE) { if (empty($this->driverClasses[$class])) { $driver = $this->driver(); $this->driverClasses[$class] = $class . '_' . $driver; Database::loadDriverFile($driver, $files); if (!class_exists($this->driverClasses[$class], $use_autoload)) { $this->driverClasses[$class] = $class; } } return $this->driverClasses[$class]; } /** * Prepares and returns a SELECT query object. * * @param $table * The base table for this query, that is, the first table in the FROM * clause. This table will also be used as the "base" table for query_alter * hook implementations. * @param $alias * The alias of the base table of this query. * @param $options * An array of options on the query. * * @return SelectQueryInterface * An appropriate SelectQuery object for this database connection. Note that * it may be a driver-specific subclass of SelectQuery, depending on the * driver. * * @see SelectQuery */ public function select($table, $alias = NULL, array $options = array()) { $class = $this->getDriverClass('SelectQuery', array('query.inc', 'select.inc')); return new $class($table, $alias, $this, $options); } /** * Prepares and returns an INSERT query object. * * @param $options * An array of options on the query. * * @return InsertQuery * A new InsertQuery object. * * @see InsertQuery */ public function insert($table, array $options = array()) { $class = $this->getDriverClass('InsertQuery', array('query.inc')); return new $class($this, $table, $options); } /** * Prepares and returns a MERGE query object. * * @param $options * An array of options on the query. * * @return MergeQuery * A new MergeQuery object. * * @see MergeQuery */ public function merge($table, array $options = array()) { $class = $this->getDriverClass('MergeQuery', array('query.inc')); return new $class($this, $table, $options); } /** * Prepares and returns an UPDATE query object. * * @param $options * An array of options on the query. * * @return UpdateQuery * A new UpdateQuery object. * * @see UpdateQuery */ public function update($table, array $options = array()) { $class = $this->getDriverClass('UpdateQuery', array('query.inc')); return new $class($this, $table, $options); } /** * Prepares and returns a DELETE query object. * * @param $options * An array of options on the query. * * @return DeleteQuery * A new DeleteQuery object. * * @see DeleteQuery */ public function delete($table, array $options = array()) { $class = $this->getDriverClass('DeleteQuery', array('query.inc')); return new $class($this, $table, $options); } /** * Prepares and returns a TRUNCATE query object. * * @param $options * An array of options on the query. * * @return TruncateQuery * A new TruncateQuery object. * * @see TruncateQuery */ public function truncate($table, array $options = array()) { $class = $this->getDriverClass('TruncateQuery', array('query.inc')); return new $class($this, $table, $options); } /** * Returns a DatabaseSchema object for manipulating the schema. * * This method will lazy-load the appropriate schema library file. * * @return DatabaseSchema * The DatabaseSchema object for this connection. */ public function schema() { if (empty($this->schema)) { $class = $this->getDriverClass('DatabaseSchema', array('schema.inc')); if (class_exists($class)) { $this->schema = new $class($this); } } return $this->schema; } /** * Escapes a table name string. * * Force all table names to be strictly alphanumeric-plus-underscore. * For some database drivers, it may also wrap the table name in * database-specific escape characters. * * @return * The sanitized table name string. */ public function escapeTable($table) { return preg_replace('/[^A-Za-z0-9_.]+/', '', $table); } /** * Escapes a field name string. * * Force all field names to be strictly alphanumeric-plus-underscore. * For some database drivers, it may also wrap the field name in * database-specific escape characters. * * @return * The sanitized field name string. */ public function escapeField($field) { return preg_replace('/[^A-Za-z0-9_.]+/', '', $field); } /** * Escapes an alias name string. * * Force all alias names to be strictly alphanumeric-plus-underscore. In * contrast to DatabaseConnection::escapeField() / * DatabaseConnection::escapeTable(), this doesn't allow the period (".") * because that is not allowed in aliases. * * @return * The sanitized field name string. */ public function escapeAlias($field) { return preg_replace('/[^A-Za-z0-9_]+/', '', $field); } /** * Escapes characters that work as wildcard characters in a LIKE pattern. * * The wildcard characters "%" and "_" as well as backslash are prefixed with * a backslash. Use this to do a search for a verbatim string without any * wildcard behavior. * * For example, the following does a case-insensitive query for all rows whose * name starts with $prefix: * @code * $result = db_query( * 'SELECT * FROM person WHERE name LIKE :pattern', * array(':pattern' => db_like($prefix) . '%') * ); * @endcode * * Backslash is defined as escape character for LIKE patterns in * DatabaseCondition::mapConditionOperator(). * * @param $string * The string to escape. * * @return * The escaped string. */ public function escapeLike($string) { return addcslashes($string, '\%_'); } /** * Determines if there is an active transaction open. * * @return * TRUE if we're currently in a transaction, FALSE otherwise. */ public function inTransaction() { return ($this->transactionDepth() > 0); } /** * Determines current transaction depth. */ public function transactionDepth() { return count($this->transactionLayers); } /** * Returns a new DatabaseTransaction object on this connection. * * @param $name * Optional name of the savepoint. * * @return DatabaseTransaction * A DatabaseTransaction object. * * @see DatabaseTransaction */ public function startTransaction($name = '') { $class = $this->getDriverClass('DatabaseTransaction'); return new $class($this, $name); } /** * Rolls back the transaction entirely or to a named savepoint. * * This method throws an exception if no transaction is active. * * @param $savepoint_name * The name of the savepoint. The default, 'drupal_transaction', will roll * the entire transaction back. * * @throws DatabaseTransactionNoActiveException * * @see DatabaseTransaction::rollback() */ public function rollback($savepoint_name = 'drupal_transaction') { if (!$this->supportsTransactions()) { return; } if (!$this->inTransaction()) { throw new DatabaseTransactionNoActiveException(); } // A previous rollback to an earlier savepoint may mean that the savepoint // in question has already been accidentally committed. if (!isset($this->transactionLayers[$savepoint_name])) { throw new DatabaseTransactionNoActiveException(); } // We need to find the point we're rolling back to, all other savepoints // before are no longer needed. If we rolled back other active savepoints, // we need to throw an exception. $rolled_back_other_active_savepoints = FALSE; while ($savepoint = array_pop($this->transactionLayers)) { if ($savepoint == $savepoint_name) { // If it is the last the transaction in the stack, then it is not a // savepoint, it is the transaction itself so we will need to roll back // the transaction rather than a savepoint. if (empty($this->transactionLayers)) { break; } $this->query('ROLLBACK TO SAVEPOINT ' . $savepoint); $this->popCommittableTransactions(); if ($rolled_back_other_active_savepoints) { throw new DatabaseTransactionOutOfOrderException(); } return; } else { $rolled_back_other_active_savepoints = TRUE; } } parent::rollBack(); if ($rolled_back_other_active_savepoints) { throw new DatabaseTransactionOutOfOrderException(); } } /** * Increases the depth of transaction nesting. * * If no transaction is already active, we begin a new transaction. * * @throws DatabaseTransactionNameNonUniqueException * * @see DatabaseTransaction */ public function pushTransaction($name) { if (!$this->supportsTransactions()) { return; } if (isset($this->transactionLayers[$name])) { throw new DatabaseTransactionNameNonUniqueException($name . " is already in use."); } // If we're already in a transaction then we want to create a savepoint // rather than try to create another transaction. if ($this->inTransaction()) { $this->query('SAVEPOINT ' . $name); } else { parent::beginTransaction(); } $this->transactionLayers[$name] = $name; } /** * Decreases the depth of transaction nesting. * * If we pop off the last transaction layer, then we either commit or roll * back the transaction as necessary. If no transaction is active, we return * because the transaction may have manually been rolled back. * * @param $name * The name of the savepoint * * @throws DatabaseTransactionNoActiveException * @throws DatabaseTransactionCommitFailedException * * @see DatabaseTransaction */ public function popTransaction($name) { if (!$this->supportsTransactions()) { return; } // The transaction has already been committed earlier. There is nothing we // need to do. If this transaction was part of an earlier out-of-order // rollback, an exception would already have been thrown by // Database::rollback(). if (!isset($this->transactionLayers[$name])) { return; } // Mark this layer as committable. $this->transactionLayers[$name] = FALSE; $this->popCommittableTransactions(); } /** * Internal function: commit all the transaction layers that can commit. */ protected function popCommittableTransactions() { // Commit all the committable layers. foreach (array_reverse($this->transactionLayers) as $name => $active) { // Stop once we found an active transaction. if ($active) { break; } // If there are no more layers left then we should commit. unset($this->transactionLayers[$name]); if (empty($this->transactionLayers)) { if (!parent::commit()) { throw new DatabaseTransactionCommitFailedException(); } } else { $this->query('RELEASE SAVEPOINT ' . $name); } } } /** * Runs a limited-range query on this database object. * * Use this as a substitute for ->query() when a subset of the query is to be * returned. User-supplied arguments to the query should be passed in as * separate parameters so that they can be properly escaped to avoid SQL * injection attacks. * * @param $query * A string containing an SQL query. * @param $args * An array of values to substitute into the query at placeholder markers. * @param $from * The first result row to return. * @param $count * The maximum number of result rows to return. * @param $options * An array of options on the query. * * @return DatabaseStatementInterface * A database query result resource, or NULL if the query was not executed * correctly. */ abstract public function queryRange($query, $from, $count, array $args = array(), array $options = array()); /** * Generates a temporary table name. * * @return * A table name. */ protected function generateTemporaryTableName() { return "db_temporary_" . $this->temporaryNameIndex++; } /** * Runs a SELECT query and stores its results in a temporary table. * * Use this as a substitute for ->query() when the results need to stored * in a temporary table. Temporary tables exist for the duration of the page * request. User-supplied arguments to the query should be passed in as * separate parameters so that they can be properly escaped to avoid SQL * injection attacks. * * Note that if you need to know how many results were returned, you should do * a SELECT COUNT(*) on the temporary table afterwards. * * @param $query * A string containing a normal SELECT SQL query. * @param $args * An array of values to substitute into the query at placeholder markers. * @param $options * An associative array of options to control how the query is run. See * the documentation for DatabaseConnection::defaultOptions() for details. * * @return * The name of the temporary table. */ abstract function queryTemporary($query, array $args = array(), array $options = array()); /** * Returns the type of database driver. * * This is not necessarily the same as the type of the database itself. For * instance, there could be two MySQL drivers, mysql and mysql_mock. This * function would return different values for each, but both would return * "mysql" for databaseType(). */ abstract public function driver(); /** * Returns the version of the database server. */ public function version() { return $this->getAttribute(PDO::ATTR_SERVER_VERSION); } /** * Determines if this driver supports transactions. * * @return * TRUE if this connection supports transactions, FALSE otherwise. */ public function supportsTransactions() { return $this->transactionSupport; } /** * Determines if this driver supports transactional DDL. * * DDL queries are those that change the schema, such as ALTER queries. * * @return * TRUE if this connection supports transactions for DDL queries, FALSE * otherwise. */ public function supportsTransactionalDDL() { return $this->transactionalDDLSupport; } /** * Returns the name of the PDO driver for this connection. */ abstract public function databaseType(); /** * Gets any special processing requirements for the condition operator. * * Some condition types require special processing, such as IN, because * the value data they pass in is not a simple value. This is a simple * overridable lookup function. Database connections should define only * those operators they wish to be handled differently than the default. * * @param $operator * The condition operator, such as "IN", "BETWEEN", etc. Case-sensitive. * * @return * The extra handling directives for the specified operator, or NULL. * * @see DatabaseCondition::compile() */ abstract public function mapConditionOperator($operator); /** * Throws an exception to deny direct access to transaction commits. * * We do not want to allow users to commit transactions at any time, only * by destroying the transaction object or allowing it to go out of scope. * A direct commit bypasses all of the safety checks we've built on top of * PDO's transaction routines. * * @throws DatabaseTransactionExplicitCommitNotAllowedException * * @see DatabaseTransaction */ public function commit() { throw new DatabaseTransactionExplicitCommitNotAllowedException(); } /** * Retrieves an unique id from a given sequence. * * Use this function if for some reason you can't use a serial field. For * example, MySQL has no ways of reading of the current value of a sequence * and PostgreSQL can not advance the sequence to be larger than a given * value. Or sometimes you just need a unique integer. * * @param $existing_id * After a database import, it might be that the sequences table is behind, * so by passing in the maximum existing id, it can be assured that we * never issue the same id. * * @return * An integer number larger than any number returned by earlier calls and * also larger than the $existing_id if one was passed in. */ abstract public function nextId($existing_id = 0); } /** * Primary front-controller for the database system. * * This class is uninstantiatable and un-extendable. It acts to encapsulate * all control and shepherding of database connections into a single location * without the use of globals. */ abstract class Database { /** * Flag to indicate a query call should simply return NULL. * * This is used for queries that have no reasonable return value anyway, such * as INSERT statements to a table without a serial primary key. */ const RETURN_NULL = 0; /** * Flag to indicate a query call should return the prepared statement. */ const RETURN_STATEMENT = 1; /** * Flag to indicate a query call should return the number of affected rows. */ const RETURN_AFFECTED = 2; /** * Flag to indicate a query call should return the "last insert id". */ const RETURN_INSERT_ID = 3; /** * An nested array of all active connections. It is keyed by database name * and target. * * @var array */ static protected $connections = array(); /** * A processed copy of the database connection information from settings.php. * * @var array */ static protected $databaseInfo = NULL; /** * A list of key/target credentials to simply ignore. * * @var array */ static protected $ignoreTargets = array(); /** * The key of the currently active database connection. * * @var string */ static protected $activeKey = 'default'; /** * An array of active query log objects. * * Every connection has one and only one logger object for all targets and * logging keys. * * array( * '$db_key' => DatabaseLog object. * ); * * @var array */ static protected $logs = array(); /** * Starts logging a given logging key on the specified connection. * * @param $logging_key * The logging key to log. * @param $key * The database connection key for which we want to log. * * @return DatabaseLog * The query log object. Note that the log object does support richer * methods than the few exposed through the Database class, so in some * cases it may be desirable to access it directly. * * @see DatabaseLog */ final public static function startLog($logging_key, $key = 'default') { if (empty(self::$logs[$key])) { self::$logs[$key] = new DatabaseLog($key); // Every target already active for this connection key needs to have the // logging object associated with it. if (!empty(self::$connections[$key])) { foreach (self::$connections[$key] as $connection) { $connection->setLogger(self::$logs[$key]); } } } self::$logs[$key]->start($logging_key); return self::$logs[$key]; } /** * Retrieves the queries logged on for given logging key. * * This method also ends logging for the specified key. To get the query log * to date without ending the logger request the logging object by starting * it again (which does nothing to an open log key) and call methods on it as * desired. * * @param $logging_key * The logging key to log. * @param $key * The database connection key for which we want to log. * * @return array * The query log for the specified logging key and connection. * * @see DatabaseLog */ final public static function getLog($logging_key, $key = 'default') { if (empty(self::$logs[$key])) { return NULL; } $queries = self::$logs[$key]->get($logging_key); self::$logs[$key]->end($logging_key); return $queries; } /** * Gets the connection object for the specified database key and target. * * @param $target * The database target name. * @param $key * The database connection key. Defaults to NULL which means the active key. * * @return DatabaseConnection * The corresponding connection object. */ final public static function getConnection($target = 'default', $key = NULL) { if (!isset($key)) { // By default, we want the active connection, set in setActiveConnection. $key = self::$activeKey; } // If the requested target does not exist, or if it is ignored, we fall back // to the default target. The target is typically either "default" or // "slave", indicating to use a slave SQL server if one is available. If // it's not available, then the default/master server is the correct server // to use. if (!empty(self::$ignoreTargets[$key][$target]) || !isset(self::$databaseInfo[$key][$target])) { $target = 'default'; } if (!isset(self::$connections[$key][$target])) { // If necessary, a new connection is opened. self::$connections[$key][$target] = self::openConnection($key, $target); } return self::$connections[$key][$target]; } /** * Determines if there is an active connection. * * Note that this method will return FALSE if no connection has been * established yet, even if one could be. * * @return * TRUE if there is at least one database connection established, FALSE * otherwise. */ final public static function isActiveConnection() { return !empty(self::$activeKey) && !empty(self::$connections) && !empty(self::$connections[self::$activeKey]); } /** * Sets the active connection to the specified key. * * @return * The previous database connection key. */ final public static function setActiveConnection($key = 'default') { if (empty(self::$databaseInfo)) { self::parseConnectionInfo(); } if (!empty(self::$databaseInfo[$key])) { $old_key = self::$activeKey; self::$activeKey = $key; return $old_key; } } /** * Process the configuration file for database information. */ final public static function parseConnectionInfo() { global $databases; $database_info = is_array($databases) ? $databases : array(); foreach ($database_info as $index => $info) { foreach ($database_info[$index] as $target => $value) { // If there is no "driver" property, then we assume it's an array of // possible connections for this target. Pick one at random. That allows // us to have, for example, multiple slave servers. if (empty($value['driver'])) { $database_info[$index][$target] = $database_info[$index][$target][mt_rand(0, count($database_info[$index][$target]) - 1)]; } // Parse the prefix information. if (!isset($database_info[$index][$target]['prefix'])) { // Default to an empty prefix. $database_info[$index][$target]['prefix'] = array( 'default' => '', ); } elseif (!is_array($database_info[$index][$target]['prefix'])) { // Transform the flat form into an array form. $database_info[$index][$target]['prefix'] = array( 'default' => $database_info[$index][$target]['prefix'], ); } } } if (!is_array(self::$databaseInfo)) { self::$databaseInfo = $database_info; } // Merge the new $database_info into the existing. // array_merge_recursive() cannot be used, as it would make multiple // database, user, and password keys in the same database array. else { foreach ($database_info as $database_key => $database_values) { foreach ($database_values as $target => $target_values) { self::$databaseInfo[$database_key][$target] = $target_values; } } } } /** * Adds database connection information for a given key/target. * * This method allows the addition of new connection credentials at runtime. * Under normal circumstances the preferred way to specify database * credentials is via settings.php. However, this method allows them to be * added at arbitrary times, such as during unit tests, when connecting to * admin-defined third party databases, etc. * * If the given key/target pair already exists, this method will be ignored. * * @param $key * The database key. * @param $target * The database target name. * @param $info * The database connection information, as it would be defined in * settings.php. Note that the structure of this array will depend on the * database driver it is connecting to. */ public static function addConnectionInfo($key, $target, $info) { if (empty(self::$databaseInfo[$key][$target])) { self::$databaseInfo[$key][$target] = $info; } } /** * Gets information on the specified database connection. * * @param $connection * The connection key for which we want information. */ final public static function getConnectionInfo($key = 'default') { if (empty(self::$databaseInfo)) { self::parseConnectionInfo(); } if (!empty(self::$databaseInfo[$key])) { return self::$databaseInfo[$key]; } } /** * Rename a connection and its corresponding connection information. * * @param $old_key * The old connection key. * @param $new_key * The new connection key. * @return * TRUE in case of success, FALSE otherwise. */ final public static function renameConnection($old_key, $new_key) { if (empty(self::$databaseInfo)) { self::parseConnectionInfo(); } if (!empty(self::$databaseInfo[$old_key]) && empty(self::$databaseInfo[$new_key])) { // Migrate the database connection information. self::$databaseInfo[$new_key] = self::$databaseInfo[$old_key]; unset(self::$databaseInfo[$old_key]); // Migrate over the DatabaseConnection object if it exists. if (isset(self::$connections[$old_key])) { self::$connections[$new_key] = self::$connections[$old_key]; unset(self::$connections[$old_key]); } return TRUE; } else { return FALSE; } } /** * Remove a connection and its corresponding connection information. * * @param $key * The connection key. * @return * TRUE in case of success, FALSE otherwise. */ final public static function removeConnection($key) { if (isset(self::$databaseInfo[$key])) { self::closeConnection(NULL, $key); unset(self::$databaseInfo[$key]); return TRUE; } else { return FALSE; } } /** * Opens a connection to the server specified by the given key and target. * * @param $key * The database connection key, as specified in settings.php. The default is * "default". * @param $target * The database target to open. * * @throws DatabaseConnectionNotDefinedException * @throws DatabaseDriverNotSpecifiedException */ final protected static function openConnection($key, $target) { if (empty(self::$databaseInfo)) { self::parseConnectionInfo(); } // If the requested database does not exist then it is an unrecoverable // error. if (!isset(self::$databaseInfo[$key])) { throw new DatabaseConnectionNotDefinedException('The specified database connection is not defined: ' . $key); } if (!$driver = self::$databaseInfo[$key][$target]['driver']) { throw new DatabaseDriverNotSpecifiedException('Driver not specified for this database connection: ' . $key); } // We cannot rely on the registry yet, because the registry requires an // open database connection. $driver_class = 'DatabaseConnection_' . $driver; require_once DRUPAL_ROOT . '/includes/database/' . $driver . '/database.inc'; $new_connection = new $driver_class(self::$databaseInfo[$key][$target]); $new_connection->setTarget($target); $new_connection->setKey($key); // If we have any active logging objects for this connection key, we need // to associate them with the connection we just opened. if (!empty(self::$logs[$key])) { $new_connection->setLogger(self::$logs[$key]); } return $new_connection; } /** * Closes a connection to the server specified by the given key and target. * * @param $target * The database target name. Defaults to NULL meaning that all target * connections will be closed. * @param $key * The database connection key. Defaults to NULL which means the active key. */ public static function closeConnection($target = NULL, $key = NULL) { // Gets the active connection by default. if (!isset($key)) { $key = self::$activeKey; } // To close a connection, it needs to be set to NULL and removed from the // static variable. In all cases, closeConnection() might be called for a // connection that was not opened yet, in which case the key is not defined // yet and we just ensure that the connection key is undefined. if (isset($target)) { if (isset(self::$connections[$key][$target])) { self::$connections[$key][$target]->destroy(); self::$connections[$key][$target] = NULL; } unset(self::$connections[$key][$target]); } else { if (isset(self::$connections[$key])) { foreach (self::$connections[$key] as $target => $connection) { self::$connections[$key][$target]->destroy(); self::$connections[$key][$target] = NULL; } } unset(self::$connections[$key]); } } /** * Instructs the system to temporarily ignore a given key/target. * * At times we need to temporarily disable slave queries. To do so, call this * method with the database key and the target to disable. That database key * will then always fall back to 'default' for that key, even if it's defined. * * @param $key * The database connection key. * @param $target * The target of the specified key to ignore. */ public static function ignoreTarget($key, $target) { self::$ignoreTargets[$key][$target] = TRUE; } /** * Load a file for the database that might hold a class. * * @param $driver * The name of the driver. * @param array $files * The name of the files the driver specific class can be. */ public static function loadDriverFile($driver, array $files = array()) { static $base_path; if (empty($base_path)) { $base_path = dirname(realpath(__FILE__)); } $driver_base_path = "$base_path/$driver"; foreach ($files as $file) { // Load the base file first so that classes extending base classes will // have the base class loaded. foreach (array("$base_path/$file", "$driver_base_path/$file") as $filename) { // The OS caches file_exists() and PHP caches require_once(), so // we'll let both of those take care of performance here. if (file_exists($filename)) { require_once $filename; } } } } } /** * Exception for when popTransaction() is called with no active transaction. */ class DatabaseTransactionNoActiveException extends Exception { } /** * Exception thrown when a savepoint or transaction name occurs twice. */ class DatabaseTransactionNameNonUniqueException extends Exception { } /** * Exception thrown when a commit() function fails. */ class DatabaseTransactionCommitFailedException extends Exception { } /** * Exception to deny attempts to explicitly manage transactions. * * This exception will be thrown when the PDO connection commit() is called. * Code should never call this method directly. */ class DatabaseTransactionExplicitCommitNotAllowedException extends Exception { } /** * Exception thrown when a rollback() resulted in other active transactions being rolled-back. */ class DatabaseTransactionOutOfOrderException extends Exception { } /** * Exception thrown for merge queries that do not make semantic sense. * * There are many ways that a merge query could be malformed. They should all * throw this exception and set an appropriately descriptive message. */ class InvalidMergeQueryException extends Exception {} /** * Exception thrown if an insert query specifies a field twice. * * It is not allowed to specify a field as default and insert field, this * exception is thrown if that is the case. */ class FieldsOverlapException extends Exception {} /** * Exception thrown if an insert query doesn't specify insert or default fields. */ class NoFieldsException extends Exception {} /** * Exception thrown if an undefined database connection is requested. */ class DatabaseConnectionNotDefinedException extends Exception {} /** * Exception thrown if no driver is specified for a database connection. */ class DatabaseDriverNotSpecifiedException extends Exception {} /** * A wrapper class for creating and managing database transactions. * * Not all databases or database configurations support transactions. For * example, MySQL MyISAM tables do not. It is also easy to begin a transaction * and then forget to commit it, which can lead to connection errors when * another transaction is started. * * This class acts as a wrapper for transactions. To begin a transaction, * simply instantiate it. When the object goes out of scope and is destroyed * it will automatically commit. It also will check to see if the specified * connection supports transactions. If not, it will simply skip any transaction * commands, allowing user-space code to proceed normally. The only difference * is that rollbacks won't actually do anything. * * In the vast majority of cases, you should not instantiate this class * directly. Instead, call ->startTransaction(), from the appropriate connection * object. */ class DatabaseTransaction { /** * The connection object for this transaction. * * @var DatabaseConnection */ protected $connection; /** * A boolean value to indicate whether this transaction has been rolled back. * * @var Boolean */ protected $rolledBack = FALSE; /** * The name of the transaction. * * This is used to label the transaction savepoint. It will be overridden to * 'drupal_transaction' if there is no transaction depth. */ protected $name; public function __construct(DatabaseConnection $connection, $name = NULL) { $this->connection = $connection; // If there is no transaction depth, then no transaction has started. Name // the transaction 'drupal_transaction'. if (!$depth = $connection->transactionDepth()) { $this->name = 'drupal_transaction'; } // Within transactions, savepoints are used. Each savepoint requires a // name. So if no name is present we need to create one. elseif (!$name) { $this->name = 'savepoint_' . $depth; } else { $this->name = $name; } $this->connection->pushTransaction($this->name); } public function __destruct() { // If we rolled back then the transaction would have already been popped. if (!$this->rolledBack) { $this->connection->popTransaction($this->name); } } /** * Retrieves the name of the transaction or savepoint. */ public function name() { return $this->name; } /** * Rolls back the current transaction. * * This is just a wrapper method to rollback whatever transaction stack we are * currently in, which is managed by the connection object itself. Note that * logging (preferable with watchdog_exception()) needs to happen after a * transaction has been rolled back or the log messages will be rolled back * too. * * @see DatabaseConnection::rollback() * @see watchdog_exception() */ public function rollback() { $this->rolledBack = TRUE; $this->connection->rollback($this->name); } } /** * Represents a prepared statement. * * Some methods in that class are purposefully commented out. Due to a change in * how PHP defines PDOStatement, we can't define a signature for those methods * that will work the same way between versions older than 5.2.6 and later * versions. See http://bugs.php.net/bug.php?id=42452 for more details. * * Child implementations should either extend PDOStatement: * @code * class DatabaseStatement_oracle extends PDOStatement implements DatabaseStatementInterface {} * @endcode * or define their own class. If defining their own class, they will also have * to implement either the Iterator or IteratorAggregate interface before * DatabaseStatementInterface: * @code * class DatabaseStatement_oracle implements Iterator, DatabaseStatementInterface {} * @endcode */ interface DatabaseStatementInterface extends Traversable { /** * Executes a prepared statement * * @param $args * An array of values with as many elements as there are bound parameters in * the SQL statement being executed. * @param $options * An array of options for this query. * * @return * TRUE on success, or FALSE on failure. */ public function execute($args = array(), $options = array()); /** * Gets the query string of this statement. * * @return * The query string, in its form with placeholders. */ public function getQueryString(); /** * Returns the number of rows affected by the last SQL statement. * * @return * The number of rows affected by the last DELETE, INSERT, or UPDATE * statement executed. */ public function rowCount(); /** * Sets the default fetch mode for this statement. * * See http://php.net/manual/en/pdo.constants.php for the definition of the * constants used. * * @param $mode * One of the PDO::FETCH_* constants. * @param $a1 * An option depending of the fetch mode specified by $mode: * - for PDO::FETCH_COLUMN, the index of the column to fetch * - for PDO::FETCH_CLASS, the name of the class to create * - for PDO::FETCH_INTO, the object to add the data to * @param $a2 * If $mode is PDO::FETCH_CLASS, the optional arguments to pass to the * constructor. */ // public function setFetchMode($mode, $a1 = NULL, $a2 = array()); /** * Fetches the next row from a result set. * * See http://php.net/manual/en/pdo.constants.php for the definition of the * constants used. * * @param $mode * One of the PDO::FETCH_* constants. * Default to what was specified by setFetchMode(). * @param $cursor_orientation * Not implemented in all database drivers, don't use. * @param $cursor_offset * Not implemented in all database drivers, don't use. * * @return * A result, formatted according to $mode. */ // public function fetch($mode = NULL, $cursor_orientation = NULL, $cursor_offset = NULL); /** * Returns a single field from the next record of a result set. * * @param $index * The numeric index of the field to return. Defaults to the first field. * * @return * A single field from the next record, or FALSE if there is no next record. */ public function fetchField($index = 0); /** * Fetches the next row and returns it as an object. * * The object will be of the class specified by DatabaseStatementInterface::setFetchMode() * or stdClass if not specified. */ // public function fetchObject(); /** * Fetches the next row and returns it as an associative array. * * This method corresponds to PDOStatement::fetchObject(), but for associative * arrays. For some reason PDOStatement does not have a corresponding array * helper method, so one is added. * * @return * An associative array, or FALSE if there is no next row. */ public function fetchAssoc(); /** * Returns an array containing all of the result set rows. * * @param $mode * One of the PDO::FETCH_* constants. * @param $column_index * If $mode is PDO::FETCH_COLUMN, the index of the column to fetch. * @param $constructor_arguments * If $mode is PDO::FETCH_CLASS, the arguments to pass to the constructor. * * @return * An array of results. */ // function fetchAll($mode = NULL, $column_index = NULL, array $constructor_arguments); /** * Returns an entire single column of a result set as an indexed array. * * Note that this method will run the result set to the end. * * @param $index * The index of the column number to fetch. * * @return * An indexed array, or an empty array if there is no result set. */ public function fetchCol($index = 0); /** * Returns the entire result set as a single associative array. * * This method is only useful for two-column result sets. It will return an * associative array where the key is one column from the result set and the * value is another field. In most cases, the default of the first two columns * is appropriate. * * Note that this method will run the result set to the end. * * @param $key_index * The numeric index of the field to use as the array key. * @param $value_index * The numeric index of the field to use as the array value. * * @return * An associative array, or an empty array if there is no result set. */ public function fetchAllKeyed($key_index = 0, $value_index = 1); /** * Returns the result set as an associative array keyed by the given field. * * If the given key appears multiple times, later records will overwrite * earlier ones. * * @param $key * The name of the field on which to index the array. * @param $fetch * The fetchmode to use. If set to PDO::FETCH_ASSOC, PDO::FETCH_NUM, or * PDO::FETCH_BOTH the returned value with be an array of arrays. For any * other value it will be an array of objects. By default, the fetch mode * set for the query will be used. * * @return * An associative array, or an empty array if there is no result set. */ public function fetchAllAssoc($key, $fetch = NULL); } /** * Default implementation of DatabaseStatementInterface. * * PDO allows us to extend the PDOStatement class to provide additional * functionality beyond that offered by default. We do need extra * functionality. By default, this class is not driver-specific. If a given * driver needs to set a custom statement class, it may do so in its * constructor. * * @see http://us.php.net/pdostatement */ class DatabaseStatementBase extends PDOStatement implements DatabaseStatementInterface { /** * Reference to the database connection object for this statement. * * The name $dbh is inherited from PDOStatement. * * @var DatabaseConnection */ public $dbh; protected function __construct($dbh) { $this->dbh = $dbh; $this->setFetchMode(PDO::FETCH_OBJ); } public function execute($args = array(), $options = array()) { if (isset($options['fetch'])) { if (is_string($options['fetch'])) { // Default to an object. Note: db fields will be added to the object // before the constructor is run. If you need to assign fields after // the constructor is run, see http://drupal.org/node/315092. $this->setFetchMode(PDO::FETCH_CLASS, $options['fetch']); } else { $this->setFetchMode($options['fetch']); } } $logger = $this->dbh->getLogger(); if (!empty($logger)) { $query_start = microtime(TRUE); } $return = parent::execute($args); if (!empty($logger)) { $query_end = microtime(TRUE); $logger->log($this, $args, $query_end - $query_start); } return $return; } public function getQueryString() { return $this->queryString; } public function fetchCol($index = 0) { return $this->fetchAll(PDO::FETCH_COLUMN, $index); } public function fetchAllAssoc($key, $fetch = NULL) { $return = array(); if (isset($fetch)) { if (is_string($fetch)) { $this->setFetchMode(PDO::FETCH_CLASS, $fetch); } else { $this->setFetchMode($fetch); } } foreach ($this as $record) { $record_key = is_object($record) ? $record->$key : $record[$key]; $return[$record_key] = $record; } return $return; } public function fetchAllKeyed($key_index = 0, $value_index = 1) { $return = array(); $this->setFetchMode(PDO::FETCH_NUM); foreach ($this as $record) { $return[$record[$key_index]] = $record[$value_index]; } return $return; } public function fetchField($index = 0) { // Call PDOStatement::fetchColumn to fetch the field. return $this->fetchColumn($index); } public function fetchAssoc() { // Call PDOStatement::fetch to fetch the row. return $this->fetch(PDO::FETCH_ASSOC); } } /** * Empty implementation of a database statement. * * This class satisfies the requirements of being a database statement/result * object, but does not actually contain data. It is useful when developers * need to safely return an "empty" result set without connecting to an actual * database. Calling code can then treat it the same as if it were an actual * result set that happens to contain no records. * * @see SearchQuery */ class DatabaseStatementEmpty implements Iterator, DatabaseStatementInterface { public function execute($args = array(), $options = array()) { return FALSE; } public function getQueryString() { return ''; } public function rowCount() { return 0; } public function setFetchMode($mode, $a1 = NULL, $a2 = array()) { return; } public function fetch($mode = NULL, $cursor_orientation = NULL, $cursor_offset = NULL) { return NULL; } public function fetchField($index = 0) { return NULL; } public function fetchObject() { return NULL; } public function fetchAssoc() { return NULL; } function fetchAll($mode = NULL, $column_index = NULL, array $constructor_arguments = array()) { return array(); } public function fetchCol($index = 0) { return array(); } public function fetchAllKeyed($key_index = 0, $value_index = 1) { return array(); } public function fetchAllAssoc($key, $fetch = NULL) { return array(); } /* Implementations of Iterator. */ public function current() { return NULL; } public function key() { return NULL; } public function rewind() { // Nothing to do: our DatabaseStatement can't be rewound. } public function next() { // Do nothing, since this is an always-empty implementation. } public function valid() { return FALSE; } } /** * The following utility functions are simply convenience wrappers. * * They should never, ever have any database-specific code in them. */ /** * Executes an arbitrary query string against the active database. * * Use this function for SELECT queries if it is just a simple query string. * If the caller or other modules need to change the query, use db_select() * instead. * * Do not use this function for INSERT, UPDATE, or DELETE queries. Those should * be handled via db_insert(), db_update() and db_delete() respectively. * * @param $query * The prepared statement query to run. Although it will accept both named and * unnamed placeholders, named placeholders are strongly preferred as they are * more self-documenting. * @param $args * An array of values to substitute into the query. If the query uses named * placeholders, this is an associative array in any order. If the query uses * unnamed placeholders (?), this is an indexed array and the order must match * the order of placeholders in the query string. * @param $options * An array of options to control how the query operates. * * @return DatabaseStatementInterface * A prepared statement object, already executed. * * @see DatabaseConnection::defaultOptions() */ function db_query($query, array $args = array(), array $options = array()) { if (empty($options['target'])) { $options['target'] = 'default'; } return Database::getConnection($options['target'])->query($query, $args, $options); } /** * Executes a query against the active database, restricted to a range. * * @param $query * The prepared statement query to run. Although it will accept both named and * unnamed placeholders, named placeholders are strongly preferred as they are * more self-documenting. * @param $from * The first record from the result set to return. * @param $count * The number of records to return from the result set. * @param $args * An array of values to substitute into the query. If the query uses named * placeholders, this is an associative array in any order. If the query uses * unnamed placeholders (?), this is an indexed array and the order must match * the order of placeholders in the query string. * @param $options * An array of options to control how the query operates. * * @return DatabaseStatementInterface * A prepared statement object, already executed. * * @see DatabaseConnection::defaultOptions() */ function db_query_range($query, $from, $count, array $args = array(), array $options = array()) { if (empty($options['target'])) { $options['target'] = 'default'; } return Database::getConnection($options['target'])->queryRange($query, $from, $count, $args, $options); } /** * Executes a query string and saves the result set to a temporary table. * * The execution of the query string happens against the active database. * * @param $query * The prepared statement query to run. Although it will accept both named and * unnamed placeholders, named placeholders are strongly preferred as they are * more self-documenting. * @param $args * An array of values to substitute into the query. If the query uses named * placeholders, this is an associative array in any order. If the query uses * unnamed placeholders (?), this is an indexed array and the order must match * the order of placeholders in the query string. * @param $options * An array of options to control how the query operates. * * @return * The name of the temporary table. * * @see DatabaseConnection::defaultOptions() */ function db_query_temporary($query, array $args = array(), array $options = array()) { if (empty($options['target'])) { $options['target'] = 'default'; } return Database::getConnection($options['target'])->queryTemporary($query, $args, $options); } /** * Returns a new InsertQuery object for the active database. * * @param $table * The table into which to insert. * @param $options * An array of options to control how the query operates. * * @return InsertQuery * A new InsertQuery object for this connection. */ function db_insert($table, array $options = array()) { if (empty($options['target']) || $options['target'] == 'slave') { $options['target'] = 'default'; } return Database::getConnection($options['target'])->insert($table, $options); } /** * Returns a new MergeQuery object for the active database. * * @param $table * The table into which to merge. * @param $options * An array of options to control how the query operates. * * @return MergeQuery * A new MergeQuery object for this connection. */ function db_merge($table, array $options = array()) { if (empty($options['target']) || $options['target'] == 'slave') { $options['target'] = 'default'; } return Database::getConnection($options['target'])->merge($table, $options); } /** * Returns a new UpdateQuery object for the active database. * * @param $table * The table to update. * @param $options * An array of options to control how the query operates. * * @return UpdateQuery * A new UpdateQuery object for this connection. */ function db_update($table, array $options = array()) { if (empty($options['target']) || $options['target'] == 'slave') { $options['target'] = 'default'; } return Database::getConnection($options['target'])->update($table, $options); } /** * Returns a new DeleteQuery object for the active database. * * @param $table * The table from which to delete. * @param $options * An array of options to control how the query operates. * * @return DeleteQuery * A new DeleteQuery object for this connection. */ function db_delete($table, array $options = array()) { if (empty($options['target']) || $options['target'] == 'slave') { $options['target'] = 'default'; } return Database::getConnection($options['target'])->delete($table, $options); } /** * Returns a new TruncateQuery object for the active database. * * @param $table * The table from which to delete. * @param $options * An array of options to control how the query operates. * * @return TruncateQuery * A new TruncateQuery object for this connection. */ function db_truncate($table, array $options = array()) { if (empty($options['target']) || $options['target'] == 'slave') { $options['target'] = 'default'; } return Database::getConnection($options['target'])->truncate($table, $options); } /** * Returns a new SelectQuery object for the active database. * * @param $table * The base table for this query. May be a string or another SelectQuery * object. If a query object is passed, it will be used as a subselect. * @param $alias * The alias for the base table of this query. * @param $options * An array of options to control how the query operates. * * @return SelectQuery * A new SelectQuery object for this connection. */ function db_select($table, $alias = NULL, array $options = array()) { if (empty($options['target'])) { $options['target'] = 'default'; } return Database::getConnection($options['target'])->select($table, $alias, $options); } /** * Returns a new transaction object for the active database. * * @param string $name * Optional name of the transaction. * @param array $options * An array of options to control how the transaction operates: * - target: The database target name. * * @return DatabaseTransaction * A new DatabaseTransaction object for this connection. */ function db_transaction($name = NULL, array $options = array()) { if (empty($options['target'])) { $options['target'] = 'default'; } return Database::getConnection($options['target'])->startTransaction($name); } /** * Sets a new active database. * * @param $key * The key in the $databases array to set as the default database. * * @return * The key of the formerly active database. */ function db_set_active($key = 'default') { return Database::setActiveConnection($key); } /** * Restricts a dynamic table name to safe characters. * * Only keeps alphanumeric and underscores. * * @param $table * The table name to escape. * * @return * The escaped table name as a string. */ function db_escape_table($table) { return Database::getConnection()->escapeTable($table); } /** * Restricts a dynamic column or constraint name to safe characters. * * Only keeps alphanumeric and underscores. * * @param $field * The field name to escape. * * @return * The escaped field name as a string. */ function db_escape_field($field) { return Database::getConnection()->escapeField($field); } /** * Escapes characters that work as wildcard characters in a LIKE pattern. * * The wildcard characters "%" and "_" as well as backslash are prefixed with * a backslash. Use this to do a search for a verbatim string without any * wildcard behavior. * * For example, the following does a case-insensitive query for all rows whose * name starts with $prefix: * @code * $result = db_query( * 'SELECT * FROM person WHERE name LIKE :pattern', * array(':pattern' => db_like($prefix) . '%') * ); * @endcode * * Backslash is defined as escape character for LIKE patterns in * DatabaseCondition::mapConditionOperator(). * * @param $string * The string to escape. * * @return * The escaped string. */ function db_like($string) { return Database::getConnection()->escapeLike($string); } /** * Retrieves the name of the currently active database driver. * * @return * The name of the currently active database driver. */ function db_driver() { return Database::getConnection()->driver(); } /** * Closes the active database connection. * * @param $options * An array of options to control which connection is closed. Only the target * key has any meaning in this case. */ function db_close(array $options = array()) { if (empty($options['target'])) { $options['target'] = NULL; } Database::closeConnection($options['target']); } /** * Retrieves a unique id. * * Use this function if for some reason you can't use a serial field. Using a * serial field is preferred, and InsertQuery::execute() returns the value of * the last ID inserted. * * @param $existing_id * After a database import, it might be that the sequences table is behind, so * by passing in a minimum ID, it can be assured that we never issue the same * ID. * * @return * An integer number larger than any number returned before for this sequence. */ function db_next_id($existing_id = 0) { return Database::getConnection()->nextId($existing_id); } /** * Returns a new DatabaseCondition, set to "OR" all conditions together. * * @return DatabaseCondition */ function db_or() { return new DatabaseCondition('OR'); } /** * Returns a new DatabaseCondition, set to "AND" all conditions together. * * @return DatabaseCondition */ function db_and() { return new DatabaseCondition('AND'); } /** * Returns a new DatabaseCondition, set to "XOR" all conditions together. * * @return DatabaseCondition */ function db_xor() { return new DatabaseCondition('XOR'); } /** * Returns a new DatabaseCondition, set to the specified conjunction. * * Internal API function call. The db_and(), db_or(), and db_xor() * functions are preferred. * * @param $conjunction * The conjunction to use for query conditions (AND, OR or XOR). * @return DatabaseCondition */ function db_condition($conjunction) { return new DatabaseCondition($conjunction); } /** * @} End of "defgroup database". */ /** * @addtogroup schemaapi * @{ */ /** * Creates a new table from a Drupal table definition. * * @param $name * The name of the table to create. * @param $table * A Schema API table definition array. */ function db_create_table($name, $table) { return Database::getConnection()->schema()->createTable($name, $table); } /** * Returns an array of field names from an array of key/index column specifiers. * * This is usually an identity function but if a key/index uses a column prefix * specification, this function extracts just the name. * * @param $fields * An array of key/index column specifiers. * * @return * An array of field names. */ function db_field_names($fields) { return Database::getConnection()->schema()->fieldNames($fields); } /** * Checks if an index exists in the given table. * * @param $table * The name of the table in drupal (no prefixing). * @param $name * The name of the index in drupal (no prefixing). * * @return * TRUE if the given index exists, otherwise FALSE. */ function db_index_exists($table, $name) { return Database::getConnection()->schema()->indexExists($table, $name); } /** * Checks if a table exists. * * @param $table * The name of the table in drupal (no prefixing). * * @return * TRUE if the given table exists, otherwise FALSE. */ function db_table_exists($table) { return Database::getConnection()->schema()->tableExists($table); } /** * Checks if a column exists in the given table. * * @param $table * The name of the table in drupal (no prefixing). * @param $field * The name of the field. * * @return * TRUE if the given column exists, otherwise FALSE. */ function db_field_exists($table, $field) { return Database::getConnection()->schema()->fieldExists($table, $field); } /** * Finds all tables that are like the specified base table name. * * @param $table_expression * An SQL expression, for example "simpletest%" (without the quotes). * BEWARE: this is not prefixed, the caller should take care of that. * * @return * Array, both the keys and the values are the matching tables. */ function db_find_tables($table_expression) { return Database::getConnection()->schema()->findTables($table_expression); } function _db_create_keys_sql($spec) { return Database::getConnection()->schema()->createKeysSql($spec); } /** * Renames a table. * * @param $table * The current name of the table to be renamed. * @param $new_name * The new name for the table. */ function db_rename_table($table, $new_name) { return Database::getConnection()->schema()->renameTable($table, $new_name); } /** * Drops a table. * * @param $table * The table to be dropped. */ function db_drop_table($table) { return Database::getConnection()->schema()->dropTable($table); } /** * Adds a new field to a table. * * @param $table * Name of the table to be altered. * @param $field * Name of the field to be added. * @param $spec * The field specification array, as taken from a schema definition. The * specification may also contain the key 'initial'; the newly-created field * will be set to the value of the key in all rows. This is most useful for * creating NOT NULL columns with no default value in existing tables. * @param $keys_new * Optional keys and indexes specification to be created on the table along * with adding the field. The format is the same as a table specification, but * without the 'fields' element. If you are adding a type 'serial' field, you * MUST specify at least one key or index including it in this array. See * db_change_field() for more explanation why. * * @see db_change_field() */ function db_add_field($table, $field, $spec, $keys_new = array()) { return Database::getConnection()->schema()->addField($table, $field, $spec, $keys_new); } /** * Drops a field. * * @param $table * The table to be altered. * @param $field * The field to be dropped. */ function db_drop_field($table, $field) { return Database::getConnection()->schema()->dropField($table, $field); } /** * Sets the default value for a field. * * @param $table * The table to be altered. * @param $field * The field to be altered. * @param $default * Default value to be set. NULL for 'default NULL'. */ function db_field_set_default($table, $field, $default) { return Database::getConnection()->schema()->fieldSetDefault($table, $field, $default); } /** * Sets a field to have no default value. * * @param $table * The table to be altered. * @param $field * The field to be altered. */ function db_field_set_no_default($table, $field) { return Database::getConnection()->schema()->fieldSetNoDefault($table, $field); } /** * Adds a primary key to a database table. * * @param $table * Name of the table to be altered. * @param $fields * Array of fields for the primary key. */ function db_add_primary_key($table, $fields) { return Database::getConnection()->schema()->addPrimaryKey($table, $fields); } /** * Drops the primary key of a database table. * * @param $table * Name of the table to be altered. */ function db_drop_primary_key($table) { return Database::getConnection()->schema()->dropPrimaryKey($table); } /** * Adds a unique key. * * @param $table * The table to be altered. * @param $name * The name of the key. * @param $fields * An array of field names. */ function db_add_unique_key($table, $name, $fields) { return Database::getConnection()->schema()->addUniqueKey($table, $name, $fields); } /** * Drops a unique key. * * @param $table * The table to be altered. * @param $name * The name of the key. */ function db_drop_unique_key($table, $name) { return Database::getConnection()->schema()->dropUniqueKey($table, $name); } /** * Adds an index. * * @param $table * The table to be altered. * @param $name * The name of the index. * @param $fields * An array of field names. */ function db_add_index($table, $name, $fields) { return Database::getConnection()->schema()->addIndex($table, $name, $fields); } /** * Drops an index. * * @param $table * The table to be altered. * @param $name * The name of the index. */ function db_drop_index($table, $name) { return Database::getConnection()->schema()->dropIndex($table, $name); } /** * Changes a field definition. * * IMPORTANT NOTE: To maintain database portability, you have to explicitly * recreate all indices and primary keys that are using the changed field. * * That means that you have to drop all affected keys and indexes with * db_drop_{primary_key,unique_key,index}() before calling db_change_field(). * To recreate the keys and indices, pass the key definitions as the optional * $keys_new argument directly to db_change_field(). * * For example, suppose you have: * @code * $schema['foo'] = array( * 'fields' => array( * 'bar' => array('type' => 'int', 'not null' => TRUE) * ), * 'primary key' => array('bar') * ); * @endcode * and you want to change foo.bar to be type serial, leaving it as the primary * key. The correct sequence is: * @code * db_drop_primary_key('foo'); * db_change_field('foo', 'bar', 'bar', * array('type' => 'serial', 'not null' => TRUE), * array('primary key' => array('bar'))); * @endcode * * The reasons for this are due to the different database engines: * * On PostgreSQL, changing a field definition involves adding a new field and * dropping an old one which causes any indices, primary keys and sequences * (from serial-type fields) that use the changed field to be dropped. * * On MySQL, all type 'serial' fields must be part of at least one key or index * as soon as they are created. You cannot use * db_add_{primary_key,unique_key,index}() for this purpose because the ALTER * TABLE command will fail to add the column without a key or index * specification. The solution is to use the optional $keys_new argument to * create the key or index at the same time as field. * * You could use db_add_{primary_key,unique_key,index}() in all cases unless you * are converting a field to be type serial. You can use the $keys_new argument * in all cases. * * @param $table * Name of the table. * @param $field * Name of the field to change. * @param $field_new * New name for the field (set to the same as $field if you don't want to * change the name). * @param $spec * The field specification for the new field. * @param $keys_new * Optional keys and indexes specification to be created on the table along * with changing the field. The format is the same as a table specification * but without the 'fields' element. */ function db_change_field($table, $field, $field_new, $spec, $keys_new = array()) { return Database::getConnection()->schema()->changeField($table, $field, $field_new, $spec, $keys_new); } /** * @} End of "addtogroup schemaapi". */ /** * Sets a session variable specifying the lag time for ignoring a slave server. */ function db_ignore_slave() { $connection_info = Database::getConnectionInfo(); // Only set ignore_slave_server if there are slave servers being used, which // is assumed if there are more than one. if (count($connection_info) > 1) { // Five minutes is long enough to allow the slave to break and resume // interrupted replication without causing problems on the Drupal site from // the old data. $duration = variable_get('maximum_replication_lag', 300); // Set session variable with amount of time to delay before using slave. $_SESSION['ignore_slave_server'] = REQUEST_TIME + $duration; } }
Fatal error: Call to undefined function db_query() in /home/matt7t/conflictmineralsolutions.com/includes/cache.inc on line 344