= :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 = []) { 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 = []; foreach ($args as $value) { if (is_float($value) || is_int($value)) { if (is_float($value)) { // Force the conversion to float so as not to loose precision // in the automatic cast. $value = sprintf('%F', $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_float($value) || is_int($value)) { if (is_float($value)) { // Force the conversion to float so as not to loose precision // in the automatic cast. $value = sprintf('%F', $value); } // We will remove this placeholder from the query as PDO throws an // exception if the number of placeholders in the query and the // arguments does not match. unset($args[$placeholder]); // PDO allows placeholders to not be prefixed by a colon. See // http://marc.info/?l=php-internals&m=111234321827149&w=2 for // more. if ($placeholder[0] != ':') { $placeholder = ":$placeholder"; } // When replacing the placeholders, make sure we search for the // exact placeholder. For example, if searching for // ':db_placeholder_1', do not replace ':db_placeholder_11'. $query = preg_replace('/' . preg_quote($placeholder) . '\b/', $value, $query); } } } } return $this->pdoConnection->prepare($query); } /** * {@inheritdoc} */ 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 = []; foreach ($this->columnNames as $k => $column) { // In some SQLite versions, SELECT DISTINCT(field) will return "(field)" // instead of "field". if (preg_match("/^\((.*)\)$/", $column, $matches)) { $rename_columns[$column] = $matches[1]; $this->columnNames[$k] = $matches[1]; $column = $matches[1]; } // Remove "table." prefixes. if (preg_match("/^.*\.(.*)$/", $column, $matches)) { $rename_columns[$column] = $matches[1]; $this->columnNames[$k] = $matches[1]; } } if ($rename_columns) { // DatabaseStatementPrefetch already extracted the first row, // put it back into the result set. if (isset($this->currentRow)) { $this->data[0] = &$this->currentRow; } // Then rename all the columns across the result set. 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]); } } // Finally, extract the first row again. $this->currentRow = $this->data[0]; unset($this->data[0]); } return $return; } }