From ccd20b6f17bf2914478138033ca9d918825e3523 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 17 Dec 2009 23:54:41 +0100 Subject: [PATCH] added SQLite3 driver --- dibi/drivers/sqlite.php | 3 - dibi/drivers/sqlite3.php | 460 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 460 insertions(+), 3 deletions(-) create mode 100644 dibi/drivers/sqlite3.php diff --git a/dibi/drivers/sqlite.php b/dibi/drivers/sqlite.php index 0431e47e..c1065e36 100644 --- a/dibi/drivers/sqlite.php +++ b/dibi/drivers/sqlite.php @@ -230,9 +230,6 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver case dibi::BINARY: return "'" . sqlite_escape_string($value) . "'"; - /*case dibi::BINARY: // SQLite 3 - return "X'" . bin2hex((string) $value) . "'";*/ - case dibi::IDENTIFIER: return '[' . str_replace('.', '].[', strtr($value, '[]', ' ')) . ']'; diff --git a/dibi/drivers/sqlite3.php b/dibi/drivers/sqlite3.php new file mode 100644 index 00000000..ba4475c5 --- /dev/null +++ b/dibi/drivers/sqlite3.php @@ -0,0 +1,460 @@ +fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U'; + $this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U'; + + if (isset($config['resource']) && $config['resource'] instanceof SQLite3) { + $this->connection = $config['resource']; + } else try { + $this->connection = new SQLite3($config['database']); + + } catch (Exception $e) { + throw new DibiDriverException($e->getMessage(), $e->getCode()); + } + + $this->dbcharset = empty($config['dbcharset']) ? 'UTF-8' : $config['dbcharset']; + $this->charset = empty($config['charset']) ? 'UTF-8' : $config['charset']; + if (strcasecmp($this->dbcharset, $this->charset) === 0) { + $this->dbcharset = $this->charset = NULL; + } + } + + + + /** + * Disconnects from a database. + * @return void + */ + public function disconnect() + { + $this->connection->close(); + } + + + + /** + * Executes the SQL query. + * @param string SQL statement. + * @return IDibiDriver|NULL + * @throws DibiDriverException + */ + public function query($sql) + { + if ($this->dbcharset !== NULL) { + $sql = iconv($this->charset, $this->dbcharset . '//IGNORE', $sql); + } + + $this->resultSet = @$this->connection->query($sql); // intentionally @ + if ($this->connection->lastErrorCode()) { + throw new DibiDriverException($this->connection->lastErrorMsg(), $this->connection->lastErrorCode(), $sql); + } + + return $this->resultSet instanceof SQLite3Result ? clone $this : NULL; + } + + + + /** + * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. + * @return int|FALSE number of rows or FALSE on error + */ + public function getAffectedRows() + { + return $this->connection->changes(); + } + + + + /** + * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. + * @return int|FALSE int on success or FALSE on failure + */ + public function getInsertId($sequence) + { + return $this->connection->lastInsertRowID(); + } + + + + /** + * Begins a transaction (if supported). + * @param string optional savepoint name + * @return void + * @throws DibiDriverException + */ + public function begin($savepoint = NULL) + { + $this->query('BEGIN'); + } + + + + /** + * Commits statements in a transaction. + * @param string optional savepoint name + * @return void + * @throws DibiDriverException + */ + public function commit($savepoint = NULL) + { + $this->query('COMMIT'); + } + + + + /** + * Rollback changes in a transaction. + * @param string optional savepoint name + * @return void + * @throws DibiDriverException + */ + public function rollback($savepoint = NULL) + { + $this->query('ROLLBACK'); + } + + + + /** + * Returns the connection resource. + * @return mixed + */ + public function getResource() + { + return $this->connection; + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Encodes data for use in a SQL statement. + * @param mixed value + * @param string type (dibi::TEXT, dibi::BOOL, ...) + * @return string encoded value + * @throws InvalidArgumentException + */ + public function escape($value, $type) + { + switch ($type) { + case dibi::TEXT: + return "'" . $this->connection->escapeString($value) . "'"; + + case dibi::BINARY: + return "X'" . bin2hex((string) $value) . "'"; + + case dibi::IDENTIFIER: + return '[' . str_replace('.', '].[', strtr($value, '[]', ' ')) . ']'; + + case dibi::BOOL: + return $value ? 1 : 0; + + case dibi::DATE: + return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value); + + case dibi::DATETIME: + return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value); + + default: + throw new InvalidArgumentException('Unsupported type.'); + } + } + + + + /** + * Decodes data from result set. + * @param string value + * @param string type (dibi::BINARY) + * @return string decoded value + * @throws InvalidArgumentException + */ + public function unescape($value, $type) + { + if ($type === dibi::BINARY) { + return $value; + } + throw new InvalidArgumentException('Unsupported type.'); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + * @param string &$sql The SQL query that will be modified. + * @param int $limit + * @param int $offset + * @return void + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit < 0 && $offset < 1) return; + $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); + } + + + + /********************* result set ****************d*g**/ + + + + /** + * Returns the number of rows in a result set. + * @return int + */ + public function getRowCount() + { + throw new NotSupportedException('Row count is not available for unbuffered queries.'); + } + + + + /** + * Fetches the row at current position and moves the internal cursor to the next position. + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record + * @internal + */ + public function fetch($assoc) + { + $row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM); + $charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT'; + if ($row && ($assoc || $charset)) { + $tmp = array(); + foreach ($row as $k => $v) { + if ($charset !== NULL && is_string($v)) { + $v = iconv($this->dbcharset, $charset, $v); + } + $tmp[str_replace(array('[', ']'), '', $k)] = $v; + } + return $tmp; + } + return $row; + } + + + + /** + * Moves cursor position without fetching row. + * @param int the 0-based cursor pos to seek to + * @return boolean TRUE on success, FALSE if unable to seek to specified record + * @throws DibiException + */ + public function seek($row) + { + throw new DibiDriverException('Cannot seek an unbuffered result set.'); + } + + + + /** + * Frees the resources allocated for this result set. + * @return void + */ + public function free() + { + $this->resultSet = NULL; + } + + + + /** + * Returns metadata for all columns in a result set. + * @return array + */ + public function getColumnsMeta() + { + $count = $this->resultSet->numColumns(); + $res = array(); + static $types = array(SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', or SQLITE3_NULL => 'null'); + for ($i = 0; $i < $count; $i++) { + $res[] = array( + 'name' => $this->resultSet->columnName($i), + 'table' => NULL, + 'fullname' => $this->resultSet->columnName($i), + 'nativetype' => $types[$this->resultSet->columnType($i)], + ); + } + return $res; + } + + + + /** + * Returns the result set resource. + * @return mixed + */ + public function getResultResource() + { + return $this->resultSet; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + * @return array + */ + public function getTables() + { + $this->query(" + SELECT name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view') + UNION ALL + SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view') + ORDER BY name + "); + $res = array(); + while ($row = $this->fetch(TRUE)) { + $res[] = $row; + } + $this->free(); + return $res; + } + + + + /** + * Returns metadata for all columns in a table. + * @param string + * @return array + */ + public function getColumns($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all indexes in a table. + * @param string + * @return array + */ + public function getIndexes($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all foreign keys in a table. + * @param string + * @return array + */ + public function getForeignKeys($table) + { + throw new NotImplementedException; + } + + + + /********************* user defined functions ****************d*g**/ + + + + /** + * Registers an user defined function for use in SQL statements. + * @param string function name + * @param mixed callback + * @param int num of arguments + * @return void + */ + public function registerFunction($name, $callback, $numArgs = -1) + { + $this->connection->createFunction($name, $callback, $numArgs); + } + + + + /** + * Registers an aggregating user defined function for use in SQL statements. + * @param string function name + * @param mixed callback called for each row of the result set + * @param mixed callback called to aggregate the "stepped" data from each row + * @param int num of arguments + * @return void + */ + public function registerAggregateFunction($name, $rowCallback, $agrCallback, $numArgs = -1) + { + $this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs); + } + +}