formatPlain($sql); $msg = "Couldn't run query:\n\n{$formattedSQL}\n\n{$msg}"; } if($errorLevel === E_USER_ERROR) { // Treating errors as exceptions better allows for responding to errors // in code, such as credential checking during installation throw new SS_DatabaseException($msg, 0, null, $sql, $parameters); } else { user_error($msg, $errorLevel); } } /** * Determine if this SQL statement is a destructive operation (write or ddl) * * @param string $sql * @return bool */ public function isQueryMutable($sql) { $operations = array_merge( Config::inst()->get(get_class($this), 'write_operations'), Config::inst()->get(get_class($this), 'ddl_operations') ); return $this->isQueryType($sql, $operations); } /** * Determine if this SQL statement is a DDL operation * * @param string $sql * @return bool */ public function isQueryDDL($sql) { $operations = Config::inst()->get(get_class($this), 'ddl_operations'); return $this->isQueryType($sql, $operations); } /** * Determine if this SQL statement is a write operation * (alters content but not structure) * * @param string $sql * @return bool */ public function isQueryWrite($sql) { $operations = Config::inst()->get(get_class($this), 'write_operations'); return $this->isQueryType($sql, $operations); } /** * Determine if a query is of the given type * * @param string $sql Raw SQL * @param string|array $type Type or list of types (first word in the query). Must be lowercase */ protected function isQueryType($sql, $type) { if(!preg_match('/^(?\w+)\b/', $sql, $matches)) { return false; } $operation = $matches['operation']; if(is_array($type)) { return in_array(strtolower($operation), $type); } else { return strcasecmp($sql, $type) === 0; } } /** * Extracts only the parameter values for error reporting * * @param array $parameters * @return array List of parameter values */ protected function parameterValues($parameters) { $values = array(); foreach($parameters as $value) { $values[] = is_array($value) ? $value['value'] : $value; } return $values; } /** * Link this connector to the database given the specified parameters * Will throw an exception rather than return a success state. * The connector should not select the database once connected until * explicitly called by selectDatabase() * * @param array $parameters List of parameters such as * * @param boolean $selectDB By default database selection should be * handled by the database controller (to enable database creation on the * fly if necessary), but some interfaces require that the database is * specified during connection (SQLite, Azure, etc). */ abstract public function connect($parameters, $selectDB = false); /** * Query for the version of the currently connected database * * @return string Version of this database */ abstract public function getVersion(); /** * Given a value escape this for use in a query for the current database * connector. Note that this does not quote the value. * * @param string $value The value to be escaped * @return string The appropritaely escaped string for value */ abstract public function escapeString($value); /** * Given a value escape and quote this appropriately for the current * database connector. * * @param string $value The value to be injected into a query * @return string The appropriately escaped and quoted string for $value */ abstract public function quoteString($value); /** * Escapes an identifier (table / database name). Typically the value * is simply double quoted. Don't pass in already escaped identifiers in, * as this will double escape the value! * * @param string $value The identifier to escape * @param string $separator optional identifier splitter */ public function escapeIdentifier($value, $separator = '.') { // ANSI standard id escape is to surround with double quotes if(empty($separator)) return '"'.trim($value).'"'; // Split, escape, and glue back multiple identifiers $segments = array(); foreach(explode($separator, $value) as $item) { $segments[] = $this->escapeIdentifier($item, null); } return implode($separator, $segments); } /** * Executes the following query with the specified error level. * Implementations of this function should respect previewWrite and benchmarkQuery * * @see http://php.net/manual/en/errorfunc.constants.php * @param string $sql The SQL query to execute * @param integer $errorLevel For errors to this query, raise PHP errors * using this error level. */ abstract public function query($sql, $errorLevel = E_USER_ERROR); /** * Execute the given SQL parameterised query with the specified arguments * * @param string $sql The SQL query to execute. The ? character will denote parameters. * @param array $parameters An ordered list of arguments. * @param int $errorLevel The level of error reporting to enable for the query * @return SS_Query */ abstract public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR); /** * Select a database by name * * @param string $name Name of database * @return boolean Flag indicating success */ abstract public function selectDatabase($name); /** * Retrieves the name of the currently selected database * * @return string Name of the database, or null if none selected */ abstract public function getSelectedDatabase(); /** * De-selects the currently selected database */ abstract public function unloadDatabase(); /** * Retrieves the last error generated from the database connection * * @return string The error message */ abstract public function getLastError(); /** * Determines the last ID generated from the specified table. * Note that some connectors may not be able to return $table specific responses, * and this parameter may be ignored. * * @param string $table The target table to return the last generated ID for * @return integer ID value */ abstract public function getGeneratedID($table); /** * Determines the number of affected rows from the last SQL query * * @return integer Number of affected rows */ abstract public function affectedRows(); /** * Determines if we are connected to a server AND have a valid database * selected. * * @return boolean Flag indicating that a valid database is connected */ abstract public function isActive(); }