From cbd37021f247641b45a7850a2211fb1584375aef Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 30 Nov 2007 10:12:45 +0000 Subject: [PATCH] * new: qualifiy each column name with the table name using DibiResult::setWithTables * removed DibiResult::setType(TRUE) with autodetection * removed DibiResult::getFields() & getMetaData() in favour of new method getColumnsMeta() * MySQLi and MySQL transaction implementation are the same * better escaping in DibiPostgreDriver (new pg_escape_string and addslashes) --- TODO.txt | 2 +- dibi/drivers/mssql.php | 56 +++------ dibi/drivers/mysql.php | 74 +++--------- dibi/drivers/mysqli.php | 74 +++--------- dibi/drivers/odbc.php | 68 +++++------ dibi/drivers/oracle.php | 28 +++-- dibi/drivers/pdo.php | 29 +++-- dibi/drivers/postgre.php | 73 ++++++------ dibi/drivers/sqlite.php | 24 ++-- dibi/libs/DibiConnection.php | 1 + dibi/libs/DibiDriverInterface.php | 15 ++- dibi/libs/DibiResult.php | 186 ++++++++++++++++++------------ examples/fetch.php | 15 +-- examples/metatypes.php | 9 +- examples/sql-builder.php | 12 +- 15 files changed, 313 insertions(+), 353 deletions(-) diff --git a/TODO.txt b/TODO.txt index e55397e5..c116f5dd 100644 --- a/TODO.txt +++ b/TODO.txt @@ -2,6 +2,6 @@ - sjednotit DibiVariable, modifier a type u DibiDriverInterface::format() - odstranit podporu pro modifikátory v kličích pole? - odstranit podporu pro conditional query? -- odstranit podporu pro meta types? +- odstranit podporu pro meta types - odstraneno - event, log, profiler diff --git a/dibi/drivers/mssql.php b/dibi/drivers/mssql.php index 32913e88..c08b2ba1 100644 --- a/dibi/drivers/mssql.php +++ b/dibi/drivers/mssql.php @@ -244,11 +244,12 @@ class DibiMsSqlDriver extends NObject implements DibiDriverInterface * Fetches the row at current position and moves the internal cursor to the next position * internal usage only * - * @return array|FALSE array on success, FALSE if no next record + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record */ - public function fetch() + public function fetch($type) { - return mssql_fetch_assoc($this->resultset); + return mssql_fetch_array($this->resultset, $type ? MSSQL_ASSOC : MSSQL_NUM); } @@ -280,47 +281,20 @@ class DibiMsSqlDriver extends NObject implements DibiDriverInterface - /** this is experimental */ - public function buildMeta() + /** + * Returns metadata for all columns in a result set + * + * @return array + */ + public function getColumnsMeta() { - static $types = array( - 'CHAR' => dibi::FIELD_TEXT, - 'COUNTER' => dibi::FIELD_COUNTER, - 'VARCHAR' => dibi::FIELD_TEXT, - 'LONGCHAR' => dibi::FIELD_TEXT, - 'INTEGER' => dibi::FIELD_INTEGER, - 'DATETIME' => dibi::FIELD_DATETIME, - 'CURRENCY' => dibi::FIELD_FLOAT, - 'BIT' => dibi::FIELD_BOOL, - 'LONGBINARY'=> dibi::FIELD_BINARY, - 'SMALLINT' => dibi::FIELD_INTEGER, - 'BYTE' => dibi::FIELD_INTEGER, - 'BIGINT' => dibi::FIELD_INTEGER, - 'INT' => dibi::FIELD_INTEGER, - 'TINYINT' => dibi::FIELD_INTEGER, - 'REAL' => dibi::FIELD_FLOAT, - 'DOUBLE' => dibi::FIELD_FLOAT, - 'DECIMAL' => dibi::FIELD_FLOAT, - 'NUMERIC' => dibi::FIELD_FLOAT, - 'MONEY' => dibi::FIELD_FLOAT, - 'SMALLMONEY'=> dibi::FIELD_FLOAT, - 'FLOAT' => dibi::FIELD_FLOAT, - 'YESNO' => dibi::FIELD_BOOL, - // and many others? - ); - $count = mssql_num_fields($this->resultset); $meta = array(); - for ($index = 0; $index < $count; $index++) { - - $tmp = mssql_fetch_field($this->resultset, $index); - $type = strtoupper($tmp->type); - $info['native'] = $tmp->type; - $info['type'] = isset($types[$type]) ? $types[$type] : dibi::FIELD_UNKNOWN; - $info['length'] = $tmp->max_length; - $info['table'] = $tmp->column_source; - - $meta[$tmp->name] = $info; + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $info = (array) mssql_fetch_field($this->resultset, $i); + $info['table'] = $info['column_source']; + $meta[] = $info; } return $meta; } diff --git a/dibi/drivers/mysql.php b/dibi/drivers/mysql.php index 7aec643c..eec6db83 100644 --- a/dibi/drivers/mysql.php +++ b/dibi/drivers/mysql.php @@ -204,7 +204,7 @@ class DibiMySqlDriver extends NObject implements DibiDriverInterface */ public function begin() { - $this->query('BEGIN'); + $this->query('START TRANSACTION'); } @@ -292,11 +292,12 @@ class DibiMySqlDriver extends NObject implements DibiDriverInterface * Fetches the row at current position and moves the internal cursor to the next position * internal usage only * - * @return array|FALSE array on success, FALSE if no next record + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record */ - public function fetch() + public function fetch($type) { - return mysql_fetch_assoc($this->resultset); + return mysql_fetch_array($this->resultset, $type ? MYSQL_ASSOC : MYSQL_NUM); } @@ -332,64 +333,19 @@ class DibiMySqlDriver extends NObject implements DibiDriverInterface - /** this is experimental */ - public function buildMeta() + /** + * Returns metadata for all columns in a result set + * + * @return array + */ + public function getColumnsMeta() { - static $types = array( - 'ENUM' => dibi::FIELD_TEXT, // eventually dibi::FIELD_INTEGER - 'SET' => dibi::FIELD_TEXT, // eventually dibi::FIELD_INTEGER - 'CHAR' => dibi::FIELD_TEXT, - 'VARCHAR' => dibi::FIELD_TEXT, - 'STRING' => dibi::FIELD_TEXT, - 'TINYTEXT' => dibi::FIELD_TEXT, - 'TEXT' => dibi::FIELD_TEXT, - 'MEDIUMTEXT'=> dibi::FIELD_TEXT, - 'LONGTEXT' => dibi::FIELD_TEXT, - 'BINARY' => dibi::FIELD_BINARY, - 'VARBINARY' => dibi::FIELD_BINARY, - 'TINYBLOB' => dibi::FIELD_BINARY, - 'BLOB' => dibi::FIELD_BINARY, - 'MEDIUMBLOB'=> dibi::FIELD_BINARY, - 'LONGBLOB' => dibi::FIELD_BINARY, - 'DATE' => dibi::FIELD_DATE, - 'DATETIME' => dibi::FIELD_DATETIME, - 'TIMESTAMP' => dibi::FIELD_DATETIME, - 'TIME' => dibi::FIELD_DATETIME, - 'BIT' => dibi::FIELD_BOOL, - 'YEAR' => dibi::FIELD_INTEGER, - 'TINYINT' => dibi::FIELD_INTEGER, - 'SMALLINT' => dibi::FIELD_INTEGER, - 'MEDIUMINT' => dibi::FIELD_INTEGER, - 'INT' => dibi::FIELD_INTEGER, - 'INTEGER' => dibi::FIELD_INTEGER, - 'BIGINT' => dibi::FIELD_INTEGER, - 'FLOAT' => dibi::FIELD_FLOAT, - 'DOUBLE' => dibi::FIELD_FLOAT, - 'REAL' => dibi::FIELD_FLOAT, - 'DECIMAL' => dibi::FIELD_FLOAT, - 'NUMERIC' => dibi::FIELD_FLOAT, - ); - $count = mysql_num_fields($this->resultset); $meta = array(); - for ($index = 0; $index < $count; $index++) { - - $info['native'] = $native = strtoupper(mysql_field_type($this->resultset, $index)); - $info['flags'] = explode(' ', mysql_field_flags($this->resultset, $index)); - $info['length'] = mysql_field_len($this->resultset, $index); - $info['table'] = mysql_field_table($this->resultset, $index); - - if (in_array('auto_increment', $info['flags'])) { // or 'primary_key' ? - $info['type'] = dibi::FIELD_COUNTER; - } else { - $info['type'] = isset($types[$native]) ? $types[$native] : dibi::FIELD_UNKNOWN; - -// if ($info['type'] === dibi::FIELD_TEXT && $info['length'] > 255) -// $info['type'] = dibi::FIELD_LONG_TEXT; - } - - $name = mysql_field_name($this->resultset, $index); - $meta[$name] = $info; + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $info = (array) mysql_fetch_field($this->resultset, $i); + $meta[] = $info; } return $meta; } diff --git a/dibi/drivers/mysqli.php b/dibi/drivers/mysqli.php index c98b4e31..9fb260bc 100644 --- a/dibi/drivers/mysqli.php +++ b/dibi/drivers/mysqli.php @@ -180,9 +180,7 @@ class DibiMySqliDriver extends NObject implements DibiDriverInterface */ public function begin() { - if (!mysqli_autocommit($this->connection, FALSE)) { - $this->throwException(); - } + $this->query('START TRANSACTION'); } @@ -194,10 +192,7 @@ class DibiMySqliDriver extends NObject implements DibiDriverInterface */ public function commit() { - if (!mysqli_commit($this->connection)) { - $this->throwException(); - } - mysqli_autocommit($this->connection, TRUE); + $this->query('COMMIT'); } @@ -209,10 +204,7 @@ class DibiMySqliDriver extends NObject implements DibiDriverInterface */ public function rollback() { - if (!mysqli_rollback($this->connection)) { - $this->throwException(); - } - mysqli_autocommit($this->connection, TRUE); + $this->query('ROLLBACK'); } @@ -277,11 +269,12 @@ class DibiMySqliDriver extends NObject implements DibiDriverInterface * Fetches the row at current position and moves the internal cursor to the next position * internal usage only * - * @return array|FALSE array on success, FALSE if no next record + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record */ - public function fetch() + public function fetch($type) { - return mysqli_fetch_assoc($this->resultset); + return mysqli_fetch_array($this->resultset, $type ? MYSQLI_ASSOC : MYSQLI_NUM); } @@ -316,52 +309,19 @@ class DibiMySqliDriver extends NObject implements DibiDriverInterface - /** this is experimental */ - public function buildMeta() + /** + * Returns metadata for all columns in a result set + * + * @return array + */ + public function getColumnsMeta() { - static $types = array( - MYSQLI_TYPE_FLOAT => dibi::FIELD_FLOAT, - MYSQLI_TYPE_DOUBLE => dibi::FIELD_FLOAT, - MYSQLI_TYPE_DECIMAL => dibi::FIELD_FLOAT, - // MYSQLI_TYPE_NEWDECIMAL=> dibi::FIELD_FLOAT, - // MYSQLI_TYPE_BIT => dibi::FIELD_INTEGER, - MYSQLI_TYPE_TINY => dibi::FIELD_INTEGER, - MYSQLI_TYPE_SHORT => dibi::FIELD_INTEGER, - MYSQLI_TYPE_LONG => dibi::FIELD_INTEGER, - MYSQLI_TYPE_LONGLONG => dibi::FIELD_INTEGER, - MYSQLI_TYPE_INT24 => dibi::FIELD_INTEGER, - MYSQLI_TYPE_YEAR => dibi::FIELD_INTEGER, - MYSQLI_TYPE_GEOMETRY => dibi::FIELD_INTEGER, - MYSQLI_TYPE_DATE => dibi::FIELD_DATE, - MYSQLI_TYPE_NEWDATE => dibi::FIELD_DATE, - MYSQLI_TYPE_TIMESTAMP => dibi::FIELD_DATETIME, - MYSQLI_TYPE_TIME => dibi::FIELD_DATETIME, - MYSQLI_TYPE_DATETIME => dibi::FIELD_DATETIME, - MYSQLI_TYPE_ENUM => dibi::FIELD_TEXT, // eventually dibi::FIELD_INTEGER - MYSQLI_TYPE_SET => dibi::FIELD_TEXT, // eventually dibi::FIELD_INTEGER - MYSQLI_TYPE_STRING => dibi::FIELD_TEXT, - MYSQLI_TYPE_VAR_STRING=> dibi::FIELD_TEXT, - MYSQLI_TYPE_TINY_BLOB => dibi::FIELD_BINARY, - MYSQLI_TYPE_MEDIUM_BLOB=> dibi::FIELD_BINARY, - MYSQLI_TYPE_LONG_BLOB => dibi::FIELD_BINARY, - MYSQLI_TYPE_BLOB => dibi::FIELD_BINARY, - ); - $count = mysqli_num_fields($this->resultset); $meta = array(); - for ($index = 0; $index < $count; $index++) { - $info = (array) mysqli_fetch_field_direct($this->resultset, $index); - $native = $info['native'] = $info['type']; - - if ($info['flags'] & MYSQLI_AUTO_INCREMENT_FLAG) { // or 'primary_key' ? - $info['type'] = dibi::FIELD_COUNTER; - } else { - $info['type'] = isset($types[$native]) ? $types[$native] : dibi::FIELD_UNKNOWN; -// if ($info['type'] === dibi::FIELD_TEXT && $info['length'] > 255) -// $info['type'] = dibi::FIELD_LONG_TEXT; - } - - $meta[$info['name']] = $info; + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $info = (array) mysqli_fetch_field_direct($this->resultset, $i); + $meta[] = $info; } return $meta; } diff --git a/dibi/drivers/odbc.php b/dibi/drivers/odbc.php index 9ef40ac3..6666f16a 100644 --- a/dibi/drivers/odbc.php +++ b/dibi/drivers/odbc.php @@ -257,11 +257,21 @@ class DibiOdbcDriver extends NObject implements DibiDriverInterface * Fetches the row at current position and moves the internal cursor to the next position * internal usage only * - * @return array|FALSE array on success, FALSE if no next record + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record */ - public function fetch() + public function fetch($type) { - return odbc_fetch_array($this->resultset, ++$this->row); + if ($type) { + return odbc_fetch_array($this->resultset, ++$this->row); + } else { + $set = $this->resultset; + if (!odbc_fetch_row($set, ++$this->row)) return FALSE; + $count = odbc_num_fields($set); + $cols = array(); + for ($i = 1; $i <= $count; $i++) $cols[] = odbc_result($set, $i); + return $cols; + } } @@ -294,46 +304,24 @@ class DibiOdbcDriver extends NObject implements DibiDriverInterface - /** this is experimental */ - public function buildMeta() + /** + * Returns metadata for all columns in a result set + * + * @return array + */ + public function getColumnsMeta() { - static $types = array( - 'CHAR' => dibi::FIELD_TEXT, - 'COUNTER' => dibi::FIELD_COUNTER, - 'VARCHAR' => dibi::FIELD_TEXT, - 'LONGCHAR' => dibi::FIELD_TEXT, - 'INTEGER' => dibi::FIELD_INTEGER, - 'DATETIME' => dibi::FIELD_DATETIME, - 'CURRENCY' => dibi::FIELD_FLOAT, - 'BIT' => dibi::FIELD_BOOL, - 'LONGBINARY'=> dibi::FIELD_BINARY, - 'SMALLINT' => dibi::FIELD_INTEGER, - 'BYTE' => dibi::FIELD_INTEGER, - 'BIGINT' => dibi::FIELD_INTEGER, - 'INT' => dibi::FIELD_INTEGER, - 'TINYINT' => dibi::FIELD_INTEGER, - 'REAL' => dibi::FIELD_FLOAT, - 'DOUBLE' => dibi::FIELD_FLOAT, - 'DECIMAL' => dibi::FIELD_FLOAT, - 'NUMERIC' => dibi::FIELD_FLOAT, - 'MONEY' => dibi::FIELD_FLOAT, - 'SMALLMONEY'=> dibi::FIELD_FLOAT, - 'FLOAT' => dibi::FIELD_FLOAT, - 'YESNO' => dibi::FIELD_BOOL, - // and many others? - ); - $count = odbc_num_fields($this->resultset); $meta = array(); - for ($index = 1; $index <= $count; $index++) { - $native = strtoupper(odbc_field_type($this->resultset, $index)); - $name = odbc_field_name($this->resultset, $index); - $meta[$name] = array( - 'type' => isset($types[$native]) ? $types[$native] : dibi::FIELD_UNKNOWN, - 'native' => $native, - 'length' => odbc_field_len($this->resultset, $index), - 'scale' => odbc_field_scale($this->resultset, $index), - 'precision' => odbc_field_precision($this->resultset, $index), + for ($i = 1; $i <= $count; $i++) { + // items 'name' and 'table' are required + $meta[] = array( + 'name' => odbc_field_name($this->resultset, $i), + 'table' => NULL, + 'type' => odbc_field_type($this->resultset, $i), + 'length' => odbc_field_len($this->resultset, $i), + 'scale' => odbc_field_scale($this->resultset, $i), + 'precision' => odbc_field_precision($this->resultset, $i), ); } return $meta; diff --git a/dibi/drivers/oracle.php b/dibi/drivers/oracle.php index c86b945b..cc9a0d14 100644 --- a/dibi/drivers/oracle.php +++ b/dibi/drivers/oracle.php @@ -249,11 +249,12 @@ class DibiOracleDriver extends NObject implements DibiDriverInterface * Fetches the row at current position and moves the internal cursor to the next position * internal usage only * - * @return array|FALSE array on success, FALSE if no next record + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record */ - public function fetch() + public function fetch($type) { - return oci_fetch_assoc($this->resultset); + return oci_fetch_array($this->resultset, ($type ? OCI_ASSOC : OCI_NUM) | OCI_RETURN_NULLS); } @@ -285,14 +286,25 @@ class DibiOracleDriver extends NObject implements DibiDriverInterface - /** this is experimental */ - public function buildMeta() + /** + * Returns metadata for all columns in a result set + * + * @return array + */ + public function getColumnsMeta() { $count = oci_num_fields($this->resultset); $meta = array(); - for ($index = 0; $index < $count; $index++) { - $name = oci_field_name($this->resultset, $index + 1); - $meta[$name] = array('type' => dibi::FIELD_UNKNOWN); + for ($i = 1; $i <= $count; $i++) { + // items 'name' and 'table' are required + $meta[] = array( + 'name' => oci_field_name($this->resultset, $i), + 'table' => NULL, + 'type' => oci_field_type($this->resultset, $i), + 'size' => oci_field_size($this->resultset, $i), + 'scale' => oci_field_scale($this->resultset, $i), + 'precision' => oci_field_precision($this->resultset, $i), + ); } return $meta; } diff --git a/dibi/drivers/pdo.php b/dibi/drivers/pdo.php index bd3555fb..29047913 100644 --- a/dibi/drivers/pdo.php +++ b/dibi/drivers/pdo.php @@ -261,11 +261,12 @@ class DibiPdoDriver extends NObject implements DibiDriverInterface * Fetches the row at current position and moves the internal cursor to the next position * internal usage only * - * @return array|FALSE array on success, FALSE if no next record + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record */ - public function fetch() + public function fetch($type) { - return $this->resultset->fetch(PDO::FETCH_ASSOC); + return $this->resultset->fetch($type ? PDO::FETCH_ASSOC : PDO::FETCH_NUM); } @@ -296,17 +297,23 @@ class DibiPdoDriver extends NObject implements DibiDriverInterface - /** this is experimental */ - public function buildMeta() + /** + * Returns metadata for all columns in a result set + * + * @return array + * @throws DibiException + */ + public function getColumnsMeta() { $count = $this->resultset->columnCount(); $meta = array(); - for ($index = 0; $index < $count; $index++) { - $meta = $this->resultset->getColumnMeta($index); - // TODO: - $meta['type'] = dibi::FIELD_UNKNOWN; - $name = $meta['name']; - $meta[$name] = $meta; + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $info = @$this->resultset->getColumnsMeta($i); + if ($info === FALSE) { + throw new DibiDriverException('Driver does not support meta data'); + } + $meta[] = $info; } return $meta; } diff --git a/dibi/drivers/postgre.php b/dibi/drivers/postgre.php index 4b3d1c8c..8b596e0b 100644 --- a/dibi/drivers/postgre.php +++ b/dibi/drivers/postgre.php @@ -50,6 +50,13 @@ class DibiPostgreDriver extends NObject implements DibiDriverInterface private $resultset; + /** + * Escape method + * @var int + */ + private $escMethod; + + /** * @throws DibiException @@ -59,6 +66,8 @@ class DibiPostgreDriver extends NObject implements DibiDriverInterface if (!extension_loaded('pgsql')) { throw new DibiDriverException("PHP extension 'pgsql' is not loaded"); } + + $this->escMethod = version_compare(PHP_VERSION , '5.2.0', '>=') ? 1 : 0; } @@ -92,9 +101,12 @@ class DibiPostgreDriver extends NObject implements DibiDriverInterface } if (isset($config['charset'])) { + DibiDriverException::catchError(); @pg_set_client_encoding($this->connection, $config['charset']); - // don't handle this error... + DibiDriverException::restore(); } + + if (!empty($config['escapefix'])) $this->escMethod = -1; } @@ -174,7 +186,7 @@ class DibiPostgreDriver extends NObject implements DibiDriverInterface */ public function begin() { - $this->query('BEGIN'); + $this->query('START TRANSACTION'); } @@ -213,7 +225,11 @@ class DibiPostgreDriver extends NObject implements DibiDriverInterface */ public function format($value, $type) { - if ($type === dibi::FIELD_TEXT) return "'" . pg_escape_string($value) . "'"; + if ($type === dibi::FIELD_TEXT) { + if ($this->escMethod === -1) return "'" . addSlashes($value) . "'"; + if ($this->escMethod === 1 && $this->connection) return "'" . pg_escape_string($this->connection, $value) . "'"; + return "'" . pg_escape_string($value) . "'"; + } if ($type === dibi::IDENTIFIER) return '"' . str_replace('.', '"."', str_replace('"', '""', $value)) . '"'; if ($type === dibi::FIELD_BOOL) return $value ? 'TRUE' : 'FALSE'; if ($type === dibi::FIELD_DATE) return date("'Y-m-d'", $value); @@ -260,11 +276,12 @@ class DibiPostgreDriver extends NObject implements DibiDriverInterface * Fetches the row at current position and moves the internal cursor to the next position * internal usage only * - * @return array|FALSE array on success, FALSE if no next record + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record */ - public function fetch() + public function fetch($type) { - return pg_fetch_array($this->resultset, NULL, PGSQL_ASSOC); + return pg_fetch_array($this->resultset, NULL, $type ? PGSQL_ASSOC : PGSQL_NUM); } @@ -296,37 +313,25 @@ class DibiPostgreDriver extends NObject implements DibiDriverInterface - /** this is experimental */ - public function buildMeta() + /** + * Returns metadata for all columns in a result set + * + * @return array + */ + public function getColumnsMeta() { - static $types = array( - 'bool' => dibi::FIELD_BOOL, - 'int2' => dibi::FIELD_INTEGER, - 'int4' => dibi::FIELD_INTEGER, - 'int8' => dibi::FIELD_INTEGER, - 'numeric' => dibi::FIELD_FLOAT, - 'float4' => dibi::FIELD_FLOAT, - 'float8' => dibi::FIELD_FLOAT, - 'timestamp' => dibi::FIELD_DATETIME, - 'date' => dibi::FIELD_DATE, - 'time' => dibi::FIELD_DATETIME, - 'varchar' => dibi::FIELD_TEXT, - 'bpchar' => dibi::FIELD_TEXT, - 'inet' => dibi::FIELD_TEXT, - 'money' => dibi::FIELD_FLOAT, - ); - + $hasTable = version_compare(PHP_VERSION , '5.2.0', '>='); $count = pg_num_fields($this->resultset); $meta = array(); - for ($index = 0; $index < $count; $index++) { - - $info['native'] = $native = pg_field_type($this->resultset, $index); - $info['length'] = pg_field_size($this->resultset, $index); - $info['table'] = pg_field_table($this->resultset, $index); - $info['type'] = isset($types[$native]) ? $types[$native] : dibi::FIELD_UNKNOWN; - - $name = pg_field_name($this->resultset, $index); - $meta[$name] = $info; + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $meta[] = array( + 'name' => pg_field_name($this->resultset, $i), + 'table' => $hasTable ? pg_field_table($this->resultset, $i) : NULL, + 'type' => pg_field_type($this->resultset, $i), + 'size' => pg_field_size($this->resultset, $i), + 'prtlen' => pg_field_prtlen($this->resultset, $i), + ); } return $meta; } diff --git a/dibi/drivers/sqlite.php b/dibi/drivers/sqlite.php index 4f723dcb..9c51659f 100644 --- a/dibi/drivers/sqlite.php +++ b/dibi/drivers/sqlite.php @@ -255,11 +255,12 @@ class DibiSqliteDriver extends NObject implements DibiDriverInterface * Fetches the row at current position and moves the internal cursor to the next position * internal usage only * - * @return array|FALSE array on success, FALSE if no next record + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record */ - public function fetch() + public function fetch($type) { - return sqlite_fetch_array($this->resultset, SQLITE_ASSOC); + return sqlite_fetch_array($this->resultset, $type ? SQLITE_ASSOC : SQLITE_NUM); } @@ -293,14 +294,21 @@ class DibiSqliteDriver extends NObject implements DibiDriverInterface - /** this is experimental */ - public function buildMeta() + /** + * Returns metadata for all columns in a result set + * + * @return array + */ + public function getColumnsMeta() { $count = sqlite_num_fields($this->resultset); $meta = array(); - for ($index = 0; $index < $count; $index++) { - $name = sqlite_field_name($this->resultset, $index); - $meta[$name] = array('type' => dibi::FIELD_UNKNOWN); + for ($i = 0; $i < $count; $i++) { + // items 'name' and 'table' are required + $meta[] = array( + 'name' => sqlite_field_name($this->resultset, $i), + 'table' => NULL, + ); } return $meta; } diff --git a/dibi/libs/DibiConnection.php b/dibi/libs/DibiConnection.php index 2859b512..3aca8c9a 100644 --- a/dibi/libs/DibiConnection.php +++ b/dibi/libs/DibiConnection.php @@ -99,6 +99,7 @@ class DibiConnection extends NObject */ public function __destruct() { + // disconnects and rolls back transaction - do not rely on auto-disconnect and rollback! $this->disconnect(); } diff --git a/dibi/libs/DibiDriverInterface.php b/dibi/libs/DibiDriverInterface.php index 67e28239..8cc620e5 100644 --- a/dibi/libs/DibiDriverInterface.php +++ b/dibi/libs/DibiDriverInterface.php @@ -153,9 +153,10 @@ interface DibiDriverInterface * Fetches the row at current position and moves the internal cursor to the next position * internal usage only * - * @return array|FALSE array on success, FALSE if no next record + * @param bool TRUE for associative array, FALSE for numeric + * @return array array on success, nonarray if no next record */ - function fetch(); + function fetch($type); @@ -169,6 +170,16 @@ interface DibiDriverInterface + /** + * Returns metadata for all columns in a result set + * + * @return array + * @throws DibiException + */ + function getColumnsMeta(); + + + /** * Returns the connection resource * diff --git a/dibi/libs/DibiResult.php b/dibi/libs/DibiResult.php index 66c1bad9..f3ed25db 100644 --- a/dibi/libs/DibiResult.php +++ b/dibi/libs/DibiResult.php @@ -55,10 +55,11 @@ class DibiResult extends NObject implements IteratorAggregate, Countable private $xlat; /** - * Describes columns types + * Cache for $driver->getColumnsMeta() * @var array */ - private $meta; + private $metaCache; + /** * Already fetched? Used for allowance for first seek(0) @@ -66,6 +67,12 @@ class DibiResult extends NObject implements IteratorAggregate, Countable */ private $fetched = FALSE; + /** + * Qualifiy each column name with the table name? + * @var array|FALSE + */ + private $withTables = FALSE; + private static $types = array( @@ -151,6 +158,52 @@ class DibiResult extends NObject implements IteratorAggregate, Countable + /** + * Qualifiy each column name with the table name? + * + * @param bool + * @return void + * @throws DibiException + */ + final public function setWithTables($val) + { + if ($val) { + if ($this->metaCache === NULL) { + $this->metaCache = $this->getDriver()->getColumnsMeta(); + } + + $cols = array(); + foreach ($this->metaCache as $col) { + // intentional == + $name = $col['table'] == '' ? $col['name'] : ($col['table'] . '.' . $col['name']); + if (isset($cols[$name])) { + $fix = 1; + while (isset($cols[$name . '#' . $fix])) $fix++; + $name .= '#' . $fix; + } + $cols[$name] = TRUE; + } + $this->withTables = array_keys($cols); + + } else { + $this->withTables = FALSE; + } + } + + + + /** + * Qualifiy each key with the table name? + * + * @return bool + */ + final public function getWithTables() + { + return (bool) $this->withTables; + } + + + /** * Fetches the row at current position, process optional type conversion * and moves the internal cursor to the next position @@ -159,16 +212,23 @@ class DibiResult extends NObject implements IteratorAggregate, Countable */ final public function fetch() { - $row = $this->getDriver()->fetch(); - if (!is_array($row)) return FALSE; + if ($this->withTables === FALSE) { + $row = $this->getDriver()->fetch(TRUE); + if (!is_array($row)) return FALSE; + + } else { + $row = $this->getDriver()->fetch(FALSE); + if (!is_array($row)) return FALSE; + $row = array_combine($this->withTables, $row); + } + $this->fetched = TRUE; // types-converting? - if ($this->xlat) { - $xlat = $this->xlat; // little speed-up - foreach ($row as $key => $value) { - if (isset($xlat[$key])) { - $row[$key] = $this->convert($value, $xlat[$key]); + if ($this->xlat !== NULL) { + foreach ($this->xlat as $col => $type) { + if (isset($row[$col])) { + $row[$col] = $this->convert($row[$col], $type); } } } @@ -185,21 +245,18 @@ class DibiResult extends NObject implements IteratorAggregate, Countable */ final function fetchSingle() { - $row = $this->getDriver()->fetch(); + $row = $this->getDriver()->fetch(TRUE); if (!is_array($row)) return FALSE; $this->fetched = TRUE; + $value = reset($row); // types-converting? - if ($this->xlat) { - $xlat = $this->xlat; // little speed-up - $value = reset($row); - $key = key($row); - return isset($xlat[$key]) - ? $this->convert($value, $xlat[$key]) - : $value; + $key = key($row); + if (isset($this->xlat[$key])) { + return $this->convert($value, $this->xlat[$key]); } - return reset($row); + return $value; } @@ -252,7 +309,7 @@ class DibiResult extends NObject implements IteratorAggregate, Countable $data = NULL; $assoc = explode(',', $assoc); - // check fields + // check columns foreach ($assoc as $as) { if ($as !== '#' && $as !== '=' && !array_key_exists($as, $row)) { throw new InvalidArgumentException("Unknown column '$as' in associative descriptor"); @@ -321,7 +378,7 @@ class DibiResult extends NObject implements IteratorAggregate, Countable if ($value === NULL) { if ($key !== NULL) { - throw new InvalidArgumentException("Either none or both fields must be specified"); + throw new InvalidArgumentException("Either none or both columns must be specified"); } if (count($row) < 2) { @@ -359,25 +416,21 @@ class DibiResult extends NObject implements IteratorAggregate, Countable - final public function setType($field, $type = NULL) + final public function setType($col, $type = NULL) { - if ($field === TRUE) { - $this->buildMeta(); - - } elseif (is_array($field)) { - $this->xlat = $field; + if (is_array($col)) { + $this->xlat = $col; } else { - $this->xlat[$field] = $type; + $this->xlat[$col] = $type; } } - /** is this needed? */ - final public function getType($field) + final public function getType($col) { - return isset($this->xlat[$field]) ? $this->xlat[$field] : NULL; + return isset($this->xlat[$col]) ? $this->xlat[$col] : NULL; } @@ -402,46 +455,23 @@ class DibiResult extends NObject implements IteratorAggregate, Countable - /** - * Gets an array of field names - * - * @return array - */ - final public function getFields() - { - $this->buildMeta(); - return array_keys($this->meta); - } - - - /** * Gets an array of meta informations about column * - * @param string column name * @return array */ - final public function getMetaData($field) + final public function getColumnsMeta() { - $this->buildMeta(); - return isset($this->meta[$field]) ? $this->meta[$field] : FALSE; - } - - - - /** - * Acquires .... - * - * @return void - */ - final protected function buildMeta() - { - if ($this->meta === NULL) { - $this->meta = $this->getDriver()->buildMeta(); - foreach ($this->meta as $name => $info) { - $this->xlat[$name] = $info['type']; - } + if ($this->metaCache === NULL) { + $this->metaCache = $this->getDriver()->getColumnsMeta(); } + + $cols = array(); + foreach ($this->metaCache as $col) { + $name = (!$this->withTables || $col['table'] === NULL) ? $col['name'] : ($col['table'] . '.' . $col['name']); + $cols[$name] = $col; + } + return $cols; } @@ -453,24 +483,32 @@ class DibiResult extends NObject implements IteratorAggregate, Countable */ final public function dump() { - echo "\n\n\n\t\n\t\t\n"; + $none = TRUE; + foreach ($this as $i => $row) { + if ($none) { + echo "\n
#row
\n\n\t\n\t\t\n"; - foreach ($this->getFields() as $field) { - echo "\t\t\n"; - } + foreach ($row as $col => $foo) { + echo "\t\t\n"; + } - echo "\t\n\n\n"; + echo "\t\n\n\n"; + $none = FALSE; + } - foreach ($this as $row => $fields) { - echo "\t\n\t\t\n"; - foreach ($fields as $field) { - //if (is_object($field)) $field = $field->__toString(); - echo "\t\t\n"; + echo "\t\n\t\t\n"; + foreach ($row as $col) { + //if (is_object($col)) $col = $col->__toString(); + echo "\t\t\n"; } echo "\t\n"; } - echo "\n
#row" . htmlSpecialChars($field) . "" . htmlSpecialChars($col) . "
", $row, "", htmlSpecialChars($field), "
", $i, "", htmlSpecialChars($col), "
\n"; + if ($none) { + echo '

empty resultset

'; + } else { + echo "\n\n"; + } } diff --git a/examples/fetch.php b/examples/fetch.php index a1c19363..ea7bfc79 100644 --- a/examples/fetch.php +++ b/examples/fetch.php @@ -55,26 +55,27 @@ echo '
'; // fetch row by row -foreach ($res as $row => $fields) { - print_r($fields); +foreach ($res as $n => $row) { + print_r($row); } echo '
'; // fetch row by row with defined offset -foreach ($res->getIterator(2) as $row => $fields) { - print_r($fields); +foreach ($res->getIterator(2) as $n => $row) { + print_r($row); } // fetch row by row with defined offset and limit -foreach ($res->getIterator(2, 1) as $row => $fields) { - print_r($fields); +foreach ($res->getIterator(2, 1) as $n => $row) { + print_r($row); } // more complex association array $res = dibi::query(' -SELECT * FROM [products] +SELECT * +FROM [products] INNER JOIN [orders] USING ([product_id]) INNER JOIN [customers] USING ([customer_id]) '); diff --git a/examples/metatypes.php b/examples/metatypes.php index 453ae903..8f802d21 100644 --- a/examples/metatypes.php +++ b/examples/metatypes.php @@ -13,14 +13,7 @@ dibi::connect(array( $res = dibi::query('SELECT * FROM [customers]'); -// auto-convert this field to integer +// auto-convert this column to integer $res->setType('customer_id', Dibi::FIELD_INTEGER); $row = $res->fetch(); var_dump($row); - - -// auto-detect all types -// WARNING: THIS WILL NOT WORK WITH SQLITE -$res->setType(TRUE); -$row = $res->fetch(); -var_dump($row); diff --git a/examples/sql-builder.php b/examples/sql-builder.php index bc4d9649..d28dd4a8 100644 --- a/examples/sql-builder.php +++ b/examples/sql-builder.php @@ -41,7 +41,8 @@ dibi::test(" UPDATE [colors] SET", array( 'color' => 'blue', 'order' => 12, -), "WHERE [id]=%i", 123); +), " +WHERE [id]=%i", 123); // SELECT @@ -51,10 +52,15 @@ $timestamp = mktime(0, 0, 0, 10, 13, 1997); dibi::test(' SELECT COUNT(*) as [count] FROM [comments] -WHERE [ip] LIKE %s', $ipMask, 'AND [date] > ', dibi::date($timestamp) +WHERE [ip] LIKE %s', $ipMask, ' +AND [date] > ', dibi::date($timestamp) ); // IN array $array = array(1, 2, 3); -dibi::test("SELECT * FROM [people] WHERE [id] IN (", $array, ")"); +dibi::test(" +SELECT * +FROM [people] +WHERE [id] IN (", $array, ") +");