statementClass = 'PDOStatement'; $this->transactionSupport = isset($connection_options['transactions']) ? $connection_options['transactions'] : TRUE; $dns = 'sqlite:'. $connection_options['database']; parent::__construct($dns, '', '', array( // Force column names to lower case. PDO::ATTR_CASE => PDO::CASE_LOWER, )); $this->exec('PRAGMA encoding="UTF-8"'); // Create functions needed by SQLite. $this->sqliteCreateFunction('if', array($this, 'sqlFunctionIf')); $this->sqliteCreateFunction('greatest', array($this, 'sqlFunctionGreatest')); $this->sqliteCreateFunction('pow', 'pow', 2); $this->sqliteCreateFunction('length', 'strlen', 1); $this->sqliteCreateFunction('concat', array($this, 'sqlFunctionConcat')); $this->sqliteCreateFunction('substring', array($this, 'sqlFunctionSubstring'), 3); $this->sqliteCreateFunction('rand', array($this, 'sqlFunctionRand')); } /** * SQLite compatibility implementation for the IF() SQL function. */ public function sqlFunctionIf($condition, $expr1, $expr2 = NULL) { return $condition ? $expr1 : $expr2; } /** * SQLite compatibility implementation for the GREATEST() SQL function. */ public function sqlFunctionGreatest() { $args = func_get_args(); foreach ($args as $k => $v) { if (is_null($v)) { unset($args); } } if (count($args)) { return max($args); } else { return NULL; } } /** * SQLite compatibility implementation for the CONCAT() SQL function. */ public function sqlFunctionConcat() { $args = func_get_args(); return implode('', $args); } /** * SQLite compatibility implementation for the SUBSTRING() SQL function. */ public function sqlFunctionSubstring($string, $from, $length) { return substr($string, $from - 1, $length); } /** * SQLite compatibility implementation for the RAND() SQL function. */ public function sqlFunctionRand($seed = NULL) { if (isset($seed)) { mt_srand($seed); } return mt_rand() / mt_getrandmax(); } /** * SQLite-specific implementation of DatabaseConnection::prepare(). * * We don't use prepared statements at all at this stage. We just create * a DatabaseStatement_sqlite object, that will create a PDOStatement * using the semi-private PDOPrepare() method below. */ public function prepare($query, Array $options = array()) { return new DatabaseStatement_sqlite($this, $query, $options); } /** * NEVER CALL THIS FUNCTION: YOU MIGHT DEADLOCK YOUR PHP PROCESS. * * This is a wrapper around the parent PDO::prepare method. However, as * the PDO SQLite driver only closes SELECT statements when the PDOStatement * destructor is called and SQLite does not allow data change (INSERT, * UPDATE etc) on a table which has open SELECT statements, you should never * call this function and keep a PDOStatement object alive as that can lead * to a deadlock. This really, really should be private, but as * DatabaseStatement_sqlite needs to call it, we have no other choice but to * expose this function to the world. */ public function PDOPrepare($query, Array $options = array()) { return parent::prepare($query, $options); } public function queryRange($query, Array $args, $from, $count, Array $options = array()) { return $this->query($query . ' LIMIT ' . $from . ', ' . $count, $args, $options); } public function queryTemporary($query, Array $args, $tablename, Array $options = array()) { return $this->query(preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE ' . $tablename . ' AS SELECT', $query), $args, $options); } public function driver() { return 'sqlite'; } public function databaseType() { return 'sqlite'; } public function supportsTransactions() { return $this->transactionSupport; } public function mapConditionOperator($operator) { // We don't want to override any of the defaults. return NULL; } protected function prepareQuery($query) { // It makes no sense to use the static prepared statement cache here, // because all the work in our implementation is done in // DatabaseStatement_sqlite::execute() and cannot be cached. return $this->prepare($this->prefixTables($query)); } /** * @todo Remove this as soon as db_rewrite_sql() has been exterminated. */ public function distinctField($table, $field, $query) { $field_to_select = 'DISTINCT(' . $table . '.' . $field . ')'; // (?= :count) * fail. We replace numeric placeholders in the query ourselves to work * around this bug. * * See http://bugs.php.net/bug.php?id=45259 for more details. */ protected function getStatement($query, &$args = array()) { if (count($args)) { // Check if $args is a simple numeric array. if (range(0, count($args) - 1) === array_keys($args)) { // In that case, we have unnamed placeholders. $count = 0; $new_args = array(); foreach ($args as $value) { if (is_numeric($value)) { $query = substr_replace($query, $value, strpos($query, '?'), 1); } else { $placeholder = ':db_statement_placeholder_' . $count++; $query = substr_replace($query, $placeholder, strpos($query, '?'), 1); $new_args[$placeholder] = $value; } } $args = $new_args; } else { // Else, this is using named placeholders. foreach ($args as $placeholder => $value) { if (is_numeric($value)) { $query = str_replace($placeholder, $value, $query); unset($args[$placeholder]); } } } } return $this->dbh->PDOPrepare($query); } public function execute($args, $options) { try { $return = parent::execute($args, $options); } catch (PDOException $e) { if (!empty($e->errorInfo[1]) && $e->errorInfo[1] === 17) { // The schema has changed. SQLite specifies that we must resend the query. $return = parent::execute($args, $options); } else { // Rethrow the exception. throw $e; } } // In some weird cases, SQLite will prefix some column names by the name // of the table. We post-process the data, by renaming the column names // using the same convention as MySQL and PostgreSQL. $rename_columns = array(); foreach ($this->columnNames as $k => $column) { if (preg_match("/^.*\.(.*)$/", $column, $matches)) { $rename_columns[$column] = $matches[1]; $this->columnNames[$k] = $matches[1]; } } if ($rename_columns) { foreach ($this->data as $k => $row) { foreach ($rename_columns as $old_column => $new_column) { $this->data[$k][$new_column] = $this->data[$k][$old_column]; unset($this->data[$k][$old_column]); } } } // We will iterate this array so we need to make sure the array pointer is // at the beginning. reset($this->data); return $return; } } /** * @} End of "ingroup database". */