pax_global_header00006660000000000000000000000064121361404650014514gustar00rootroot0000000000000052 comment=c63912ec3b80fdd7d59baa3ac7fb1c132a29338a php-mdb2-driver-pgsql-1.5.0b4/000077500000000000000000000000001213614046500157735ustar00rootroot00000000000000php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/000077500000000000000000000000001213614046500220655ustar00rootroot00000000000000php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/000077500000000000000000000000001213614046500225515ustar00rootroot00000000000000php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/000077500000000000000000000000001213614046500240045ustar00rootroot00000000000000php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/Datatype/000077500000000000000000000000001213614046500255575ustar00rootroot00000000000000php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/Datatype/pgsql.php000066400000000000000000000467341213614046500274340ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: pgsql.php 327310 2012-08-27 15:16:18Z danielc $ require_once 'MDB2/Driver/Datatype/Common.php'; /** * MDB2 PostGreSQL driver * * @package MDB2 * @category Database * @author Paul Cooper */ class MDB2_Driver_Datatype_pgsql extends MDB2_Driver_Datatype_Common { // {{{ _baseConvertResult() /** * General type conversion method * * @param mixed $value refernce to a value to be converted * @param string $type specifies which type to convert to * @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text * @return object a MDB2 error on failure * @access protected */ function _baseConvertResult($value, $type, $rtrim = true) { if (null === $value) { return null; } switch ($type) { case 'boolean': return ($value == 'f')? false : !empty($value); case 'float': return doubleval($value); case 'date': return $value; case 'time': return substr($value, 0, strlen('HH:MM:SS')); case 'timestamp': return substr($value, 0, strlen('YYYY-MM-DD HH:MM:SS')); case 'blob': $value = pg_unescape_bytea($value); return parent::_baseConvertResult($value, $type, $rtrim); } return parent::_baseConvertResult($value, $type, $rtrim); } // }}} // {{{ getTypeDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare an text type * field to be used in statements like CREATE TABLE. * * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * length * Integer value that determines the maximum length of the text * field. If this argument is missing the field should be * declared to have the longest length allowed by the DBMS. * * default * Text value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access public */ function getTypeDeclaration($field) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } switch ($field['type']) { case 'text': $length = !empty($field['length']) ? $field['length'] : false; $fixed = !empty($field['fixed']) ? $field['fixed'] : false; return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')') : ($length ? 'VARCHAR('.$length.')' : 'TEXT'); case 'clob': return 'TEXT'; case 'blob': return 'BYTEA'; case 'integer': if (!empty($field['autoincrement'])) { if (!empty($field['length'])) { $length = $field['length']; if ($length > 4) { return 'BIGSERIAL PRIMARY KEY'; } } return 'SERIAL PRIMARY KEY'; } if (!empty($field['length'])) { $length = $field['length']; if ($length <= 2) { return 'SMALLINT'; } elseif ($length == 3 || $length == 4) { return 'INT'; } elseif ($length > 4) { return 'BIGINT'; } } return 'INT'; case 'boolean': return 'BOOLEAN'; case 'date': return 'DATE'; case 'time': return 'TIME without time zone'; case 'timestamp': return 'TIMESTAMP without time zone'; case 'float': return 'FLOAT8'; case 'decimal': $length = !empty($field['length']) ? $field['length'] : 18; $scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places']; return 'NUMERIC('.$length.','.$scale.')'; } } // }}} // {{{ _getIntegerDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare an integer type * field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * unsigned * Boolean flag that indicates whether the field should be * declared as unsigned integer if possible. * * default * Integer value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access protected */ function _getIntegerDeclaration($name, $field) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (!empty($field['unsigned'])) { $db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer"; } if (!empty($field['autoincrement'])) { $name = $db->quoteIdentifier($name, true); return $name.' '.$this->getTypeDeclaration($field); } $default = ''; if (array_key_exists('default', $field)) { if ($field['default'] === '') { $field['default'] = empty($field['notnull']) ? null : 0; } $default = ' DEFAULT '.$this->quote($field['default'], 'integer'); } $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; if (empty($default) && empty($notnull)) { $default = ' DEFAULT NULL'; } $name = $db->quoteIdentifier($name, true); return $name.' '.$this->getTypeDeclaration($field).$default.$notnull; } // }}} // {{{ _quoteCLOB() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteCLOB($value, $quote, $escape_wildcards) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if ($db->options['lob_allow_url_include']) { $value = $this->_readFile($value); if (MDB2::isError($value)) { return $value; } } return $this->_quoteText($value, $quote, $escape_wildcards); } // }}} // {{{ _quoteBLOB() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteBLOB($value, $quote, $escape_wildcards) { if (!$quote) { return $value; } $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if ($db->options['lob_allow_url_include']) { $value = $this->_readFile($value); if (MDB2::isError($value)) { return $value; } } if (version_compare(PHP_VERSION, '5.2.0RC6', '>=')) { $connection = $db->getConnection(); if (MDB2::isError($connection)) { return $connection; } $value = @pg_escape_bytea($connection, $value); } else { $value = @pg_escape_bytea($value); } return "'".$value."'"; } // }}} // {{{ _quoteBoolean() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteBoolean($value, $quote, $escape_wildcards) { $value = $value ? 't' : 'f'; if (!$quote) { return $value; } return "'".$value."'"; } // }}} // {{{ matchPattern() /** * build a pattern matching string * * @access public * * @param array $pattern even keys are strings, odd are patterns (% and _) * @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future) * @param string $field optional field name that is being matched against * (might be required when emulating ILIKE) * * @return string SQL pattern */ function matchPattern($pattern, $operator = null, $field = null) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $match = ''; if (null !== $operator) { $field = (null === $field) ? '' : $field.' '; $operator = strtoupper($operator); switch ($operator) { // case insensitive case 'ILIKE': $match = $field.'ILIKE '; break; case 'NOT ILIKE': $match = $field.'NOT ILIKE '; break; // case sensitive case 'LIKE': $match = $field.'LIKE '; break; case 'NOT LIKE': $match = $field.'NOT LIKE '; break; default: return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'not a supported operator type:'. $operator, __FUNCTION__); } } $match.= "'"; foreach ($pattern as $key => $value) { if ($key % 2) { $match.= $value; } else { $match.= $db->escapePattern($db->escape($value)); } } $match.= "'"; $match.= $this->patternEscapeString(); return $match; } // }}} // {{{ patternEscapeString() /** * build string to define escape pattern string * * @access public * * * @return string define escape pattern */ function patternEscapeString() { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return ' ESCAPE '.$this->quote($db->string_quoting['escape_pattern']); } // }}} // {{{ _mapNativeDatatype() /** * Maps a native array description of a field to a MDB2 datatype and length * * @param array $field native field description * @return array containing the various possible types, length, sign, fixed * @access public */ function _mapNativeDatatype($field) { $db_type = strtolower($field['type']); $length = $field['length']; $type = array(); $unsigned = $fixed = null; switch ($db_type) { case 'smallint': case 'int2': $type[] = 'integer'; $unsigned = false; $length = 2; if ($length == '2') { $type[] = 'boolean'; if (preg_match('/^(is|has)/', $field['name'])) { $type = array_reverse($type); } } break; case 'int': case 'int4': case 'integer': case 'serial': case 'serial4': $type[] = 'integer'; $unsigned = false; $length = 4; break; case 'bigint': case 'int8': case 'bigserial': case 'serial8': $type[] = 'integer'; $unsigned = false; $length = 8; break; case 'bool': case 'boolean': $type[] = 'boolean'; $length = null; break; case 'text': case 'varchar': $fixed = false; case 'unknown': case 'char': case 'bpchar': $type[] = 'text'; if ($length == '1') { $type[] = 'boolean'; if (preg_match('/^(is|has)/', $field['name'])) { $type = array_reverse($type); } } elseif (strstr($db_type, 'text')) { $type[] = 'clob'; $type = array_reverse($type); } if ($fixed !== false) { $fixed = true; } break; case 'date': $type[] = 'date'; $length = null; break; case 'datetime': case 'timestamp': case 'timestamptz': $type[] = 'timestamp'; $length = null; break; case 'time': $type[] = 'time'; $length = null; break; case 'float': case 'float4': case 'float8': case 'double': case 'real': $type[] = 'float'; break; case 'decimal': case 'money': case 'numeric': $type[] = 'decimal'; if (isset($field['scale'])) { $length = $length.','.$field['scale']; } break; case 'tinyblob': case 'mediumblob': case 'longblob': case 'blob': case 'bytea': $type[] = 'blob'; $length = null; break; case 'oid': $type[] = 'blob'; $type[] = 'clob'; $length = null; break; case 'year': $type[] = 'integer'; $type[] = 'date'; $length = null; break; default: $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'unknown database attribute type: '.$db_type, __FUNCTION__); } if ((int)$length <= 0) { $length = null; } return array($type, $length, $unsigned, $fixed); } // }}} // {{{ mapPrepareDatatype() /** * Maps an mdb2 datatype to native prepare type * * @param string $type * @return string * @access public */ function mapPrepareDatatype($type) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (!empty($db->options['datatype_map'][$type])) { $type = $db->options['datatype_map'][$type]; if (!empty($db->options['datatype_map_callback'][$type])) { $parameter = array('type' => $type); return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter)); } } switch ($type) { case 'integer': return 'int'; case 'boolean': return 'bool'; case 'decimal': case 'float': return 'numeric'; case 'clob': return 'text'; case 'blob': return 'bytea'; default: break; } return $type; } // }}} } ?> php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/Function/000077500000000000000000000000001213614046500255715ustar00rootroot00000000000000php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/Function/pgsql.php000066400000000000000000000133271213614046500274360ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: pgsql.php 327310 2012-08-27 15:16:18Z danielc $ require_once 'MDB2/Driver/Function/Common.php'; /** * MDB2 MySQL driver for the function modules * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Driver_Function_pgsql extends MDB2_Driver_Function_Common { // {{{ executeStoredProc() /** * Execute a stored procedure and return any results * * @param string $name string that identifies the function to execute * @param mixed $params array that contains the paramaters to pass the stored proc * @param mixed $types array that contains the types of the columns in * the result set * @param mixed $result_class string which specifies which result class to use * @param mixed $result_wrap_class string which specifies which class to wrap results in * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure * @access public */ function executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $query = 'SELECT * FROM '.$name; $query .= $params ? '('.implode(', ', $params).')' : '()'; return $db->query($query, $types, $result_class, $result_wrap_class); } // }}} // {{{ unixtimestamp() /** * return string to call a function to get the unix timestamp from a iso timestamp * * @param string $expression * * @return string to call a variable with the timestamp * @access public */ function unixtimestamp($expression) { return 'EXTRACT(EPOCH FROM DATE_TRUNC(\'seconds\', CAST ((' . $expression . ') AS TIMESTAMP)))'; } // }}} // {{{ substring() /** * return string to call a function to get a substring inside an SQL statement * * @return string to call a function to get a substring * @access public */ function substring($value, $position = 1, $length = null) { if (null !== $length) { return "SUBSTRING(CAST($value AS VARCHAR) FROM $position FOR $length)"; } return "SUBSTRING(CAST($value AS VARCHAR) FROM $position)"; } // }}} // {{{ random() /** * return string to call a function to get random value inside an SQL statement * * @return return string to generate float between 0 and 1 * @access public */ function random() { return 'RANDOM()'; } // }}} } ?> php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/Manager/000077500000000000000000000000001213614046500253565ustar00rootroot00000000000000php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/Manager/pgsql.php000066400000000000000000001054321213614046500272220ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: pgsql.php 327310 2012-08-27 15:16:18Z danielc $ require_once 'MDB2/Driver/Manager/Common.php'; /** * MDB2 MySQL driver for the management modules * * @package MDB2 * @category Database * @author Paul Cooper */ class MDB2_Driver_Manager_pgsql extends MDB2_Driver_Manager_Common { // {{{ createDatabase() /** * create a new database * * @param string $name name of the database that should be created * @param array $options array with charset info * * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function createDatabase($name, $options = array()) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($name, true); $query = 'CREATE DATABASE ' . $name; if (!empty($options['charset'])) { $query .= ' WITH ENCODING ' . $db->quote($options['charset'], 'text'); } return $db->standaloneQuery($query, null, true); } // }}} // {{{ alterDatabase() /** * alter an existing database * * @param string $name name of the database that is intended to be changed * @param array $options array with name, owner info * * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function alterDatabase($name, $options = array()) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $query = ''; if (!empty($options['name'])) { $query .= ' RENAME TO ' . $options['name']; } if (!empty($options['owner'])) { $query .= ' OWNER TO ' . $options['owner']; } if (empty($query)) { return MDB2_OK; } $query = 'ALTER DATABASE '. $db->quoteIdentifier($name, true) . $query; return $db->standaloneQuery($query, null, true); } // }}} // {{{ dropDatabase() /** * drop an existing database * * @param string $name name of the database that should be dropped * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function dropDatabase($name) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($name, true); $query = "DROP DATABASE $name"; return $db->standaloneQuery($query, null, true); } // }}} // {{{ _getAdvancedFKOptions() /** * Return the FOREIGN KEY query section dealing with non-standard options * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... * * @param array $definition * @return string * @access protected */ function _getAdvancedFKOptions($definition) { $query = ''; if (!empty($definition['match'])) { $query .= ' MATCH '.$definition['match']; } if (!empty($definition['onupdate'])) { $query .= ' ON UPDATE '.$definition['onupdate']; } if (!empty($definition['ondelete'])) { $query .= ' ON DELETE '.$definition['ondelete']; } if (!empty($definition['deferrable'])) { $query .= ' DEFERRABLE'; } else { $query .= ' NOT DEFERRABLE'; } if (!empty($definition['initiallydeferred'])) { $query .= ' INITIALLY DEFERRED'; } else { $query .= ' INITIALLY IMMEDIATE'; } return $query; } // }}} // {{{ truncateTable() /** * Truncate an existing table (if the TRUNCATE TABLE syntax is not supported, * it falls back to a DELETE FROM TABLE query) * * @param string $name name of the table that should be truncated * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function truncateTable($name) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($name, true); $result = $db->exec("TRUNCATE TABLE $name"); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } // }}} // {{{ vacuum() /** * Optimize (vacuum) all the tables in the db (or only the specified table) * and optionally run ANALYZE. * * @param string $table table name (all the tables if empty) * @param array $options an array with driver-specific options: * - timeout [int] (in seconds) [mssql-only] * - analyze [boolean] [pgsql and mysql] * - full [boolean] [pgsql-only] * - freeze [boolean] [pgsql-only] * * @return mixed MDB2_OK success, a MDB2 error on failure * @access public */ function vacuum($table = null, $options = array()) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $query = 'VACUUM'; if (!empty($options['full'])) { $query .= ' FULL'; } if (!empty($options['freeze'])) { $query .= ' FREEZE'; } if (!empty($options['analyze'])) { $query .= ' ANALYZE'; } if (!empty($table)) { $query .= ' '.$db->quoteIdentifier($table, true); } $result = $db->exec($query); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } // }}} // {{{ alterTable() /** * alter an existing table * * @param string $name name of the table that is intended to be changed. * @param array $changes associative array that contains the details of each type * of change that is intended to be performed. The types of * changes that are currently supported are defined as follows: * * name * * New name for the table. * * add * * Associative array with the names of fields to be added as * indexes of the array. The value of each entry of the array * should be set to another associative array with the properties * of the fields to be added. The properties of the fields should * be the same as defined by the MDB2 parser. * * * remove * * Associative array with the names of fields to be removed as indexes * of the array. Currently the values assigned to each entry are ignored. * An empty array should be used for future compatibility. * * rename * * Associative array with the names of fields to be renamed as indexes * of the array. The value of each entry of the array should be set to * another associative array with the entry named name with the new * field name and the entry named Declaration that is expected to contain * the portion of the field declaration already in DBMS specific SQL code * as it is used in the CREATE TABLE statement. * * change * * Associative array with the names of the fields to be changed as indexes * of the array. Keep in mind that if it is intended to change either the * name of a field and any other properties, the change array entries * should have the new names of the fields as array indexes. * * The value of each entry of the array should be set to another associative * array with the properties of the fields to that are meant to be changed as * array entries. These entries should be assigned to the new values of the * respective properties. The properties of the fields should be the same * as defined by the MDB2 parser. * * Example * array( * 'name' => 'userlist', * 'add' => array( * 'quota' => array( * 'type' => 'integer', * 'unsigned' => 1 * ) * ), * 'remove' => array( * 'file_limit' => array(), * 'time_limit' => array() * ), * 'change' => array( * 'name' => array( * 'length' => '20', * 'definition' => array( * 'type' => 'text', * 'length' => 20, * ), * ) * ), * 'rename' => array( * 'sex' => array( * 'name' => 'gender', * 'definition' => array( * 'type' => 'text', * 'length' => 1, * 'default' => 'M', * ), * ) * ) * ) * * @param boolean $check indicates whether the function should just check if the DBMS driver * can perform the requested table alterations if the value is true or * actually perform them otherwise. * @access public * * @return mixed MDB2_OK on success, a MDB2 error on failure */ function alterTable($name, $changes, $check) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } foreach ($changes as $change_name => $change) { switch ($change_name) { case 'add': case 'remove': case 'change': case 'name': case 'rename': break; default: return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null, 'change type "'.$change_name.'\" not yet supported', __FUNCTION__); } } if ($check) { return MDB2_OK; } $name = $db->quoteIdentifier($name, true); if (!empty($changes['remove']) && is_array($changes['remove'])) { foreach ($changes['remove'] as $field_name => $field) { $field_name = $db->quoteIdentifier($field_name, true); $query = 'DROP ' . $field_name; $result = $db->exec("ALTER TABLE $name $query"); if (MDB2::isError($result)) { return $result; } } } if (!empty($changes['rename']) && is_array($changes['rename'])) { foreach ($changes['rename'] as $field_name => $field) { $field_name = $db->quoteIdentifier($field_name, true); $result = $db->exec("ALTER TABLE $name RENAME COLUMN $field_name TO ".$db->quoteIdentifier($field['name'], true)); if (MDB2::isError($result)) { return $result; } } } if (!empty($changes['add']) && is_array($changes['add'])) { foreach ($changes['add'] as $field_name => $field) { $query = 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field); $result = $db->exec("ALTER TABLE $name $query"); if (MDB2::isError($result)) { return $result; } } } if (!empty($changes['change']) && is_array($changes['change'])) { foreach ($changes['change'] as $field_name => $field) { $field_name = $db->quoteIdentifier($field_name, true); if (!empty($field['definition']['type'])) { $server_info = $db->getServerVersion(); if (MDB2::isError($server_info)) { return $server_info; } if (is_array($server_info) && $server_info['major'] < 8) { return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null, 'changing column type for "'.$change_name.'\" requires PostgreSQL 8.0 or above', __FUNCTION__); } $db->loadModule('Datatype', null, true); $type = $db->datatype->getTypeDeclaration($field['definition']); $query = "ALTER $field_name TYPE $type USING CAST($field_name AS $type)"; $result = $db->exec("ALTER TABLE $name $query"); if (MDB2::isError($result)) { return $result; } } if (array_key_exists('default', $field['definition'])) { $query = "ALTER $field_name SET DEFAULT ".$db->quote($field['definition']['default'], $field['definition']['type']); $result = $db->exec("ALTER TABLE $name $query"); if (MDB2::isError($result)) { return $result; } } if (array_key_exists('notnull', $field['definition'])) { $query = "ALTER $field_name ".($field['definition']['notnull'] ? 'SET' : 'DROP').' NOT NULL'; $result = $db->exec("ALTER TABLE $name $query"); if (MDB2::isError($result)) { return $result; } } } } if (!empty($changes['name'])) { $change_name = $db->quoteIdentifier($changes['name'], true); $result = $db->exec("ALTER TABLE $name RENAME TO ".$change_name); if (MDB2::isError($result)) { return $result; } } return MDB2_OK; } // }}} // {{{ listDatabases() /** * list all databases * * @return mixed array of database names on success, a MDB2 error on failure * @access public */ function listDatabases() { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $query = 'SELECT datname FROM pg_database'; $result2 = $db->standaloneQuery($query, array('text'), false); if (!MDB2::isResultCommon($result2)) { return $result2; } $result = $result2->fetchCol(); $result2->free(); if (MDB2::isError($result)) { return $result; } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); } return $result; } // }}} // {{{ listUsers() /** * list all users * * @return mixed array of user names on success, a MDB2 error on failure * @access public */ function listUsers() { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $query = 'SELECT usename FROM pg_user'; $result2 = $db->standaloneQuery($query, array('text'), false); if (!MDB2::isResultCommon($result2)) { return $result2; } $result = $result2->fetchCol(); $result2->free(); return $result; } // }}} // {{{ listViews() /** * list all views in the current database * * @return mixed array of view names on success, a MDB2 error on failure * @access public */ function listViews() { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $query = "SELECT viewname FROM pg_views WHERE schemaname NOT IN ('pg_catalog', 'information_schema') AND viewname !~ '^pg_'"; $result = $db->queryCol($query); if (MDB2::isError($result)) { return $result; } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); } return $result; } // }}} // {{{ listTableViews() /** * list the views in the database that reference a given table * * @param string table for which all referenced views should be found * @return mixed array of view names on success, a MDB2 error on failure * @access public */ function listTableViews($table) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $query = 'SELECT viewname FROM pg_views NATURAL JOIN pg_tables'; $query.= ' WHERE tablename ='.$db->quote($table, 'text'); $result = $db->queryCol($query); if (MDB2::isError($result)) { return $result; } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); } return $result; } // }}} // {{{ listFunctions() /** * list all functions in the current database * * @return mixed array of function names on success, a MDB2 error on failure * @access public */ function listFunctions() { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $query = " SELECT proname FROM pg_proc pr, pg_type tp WHERE tp.oid = pr.prorettype AND pr.proisagg = FALSE AND tp.typname <> 'trigger' AND pr.pronamespace IN (SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')"; $result = $db->queryCol($query); if (MDB2::isError($result)) { return $result; } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); } return $result; } // }}} // {{{ listTableTriggers() /** * list all triggers in the database that reference a given table * * @param string table for which all referenced triggers should be found * @return mixed array of trigger names on success, a MDB2 error on failure * @access public */ function listTableTriggers($table = null) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $query = 'SELECT trg.tgname AS trigger_name FROM pg_trigger trg, pg_class tbl WHERE trg.tgrelid = tbl.oid'; if (null !== $table) { $table = $db->quote(strtoupper($table), 'text'); $query .= " AND UPPER(tbl.relname) = $table"; } $result = $db->queryCol($query); if (MDB2::isError($result)) { return $result; } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); } return $result; } // }}} // {{{ listTables() /** * list all tables in the current database * * @return mixed array of table names on success, a MDB2 error on failure * @access public */ function listTables() { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } // gratuitously stolen from PEAR DB _getSpecialQuery in pgsql.php $query = 'SELECT c.relname AS "Name"' . ' FROM pg_class c, pg_user u' . ' WHERE c.relowner = u.usesysid' . " AND c.relkind = 'r'" . ' AND NOT EXISTS' . ' (SELECT 1 FROM pg_views' . ' WHERE viewname = c.relname)' . " AND c.relname !~ '^(pg_|sql_)'" . ' UNION' . ' SELECT c.relname AS "Name"' . ' FROM pg_class c' . " WHERE c.relkind = 'r'" . ' AND NOT EXISTS' . ' (SELECT 1 FROM pg_views' . ' WHERE viewname = c.relname)' . ' AND NOT EXISTS' . ' (SELECT 1 FROM pg_user' . ' WHERE usesysid = c.relowner)' . " AND c.relname !~ '^pg_'"; $result = $db->queryCol($query); if (MDB2::isError($result)) { return $result; } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); } return $result; } // }}} // {{{ listTableFields() /** * list all fields in a table in the current database * * @param string $table name of table that should be used in method * @return mixed array of field names on success, a MDB2 error on failure * @access public */ function listTableFields($table) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } list($schema, $table) = $this->splitTableSchema($table); $table = $db->quoteIdentifier($table, true); if (!empty($schema)) { $table = $db->quoteIdentifier($schema, true) . '.' .$table; } $db->setLimit(1); $result2 = $db->query("SELECT * FROM $table"); if (MDB2::isError($result2)) { return $result2; } $result = $result2->getColumnNames(); $result2->free(); if (MDB2::isError($result)) { return $result; } return array_flip($result); } // }}} // {{{ listTableIndexes() /** * list all indexes in a table * * @param string $table name of table that should be used in method * @return mixed array of index names on success, a MDB2 error on failure * @access public */ function listTableIndexes($table) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } list($schema, $table) = $this->splitTableSchema($table); $table = $db->quote($table, 'text'); $subquery = "SELECT indexrelid FROM pg_index LEFT JOIN pg_class ON pg_class.oid = pg_index.indrelid LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid WHERE pg_class.relname = $table AND indisunique != 't' AND indisprimary != 't'"; if (!empty($schema)) { $subquery .= ' AND pg_namespace.nspname = '.$db->quote($schema, 'text'); } $query = "SELECT relname FROM pg_class WHERE oid IN ($subquery)"; $indexes = $db->queryCol($query, 'text'); if (MDB2::isError($indexes)) { return $indexes; } $result = array(); foreach ($indexes as $index) { $index = $this->_fixIndexName($index); if (!empty($index)) { $result[$index] = true; } } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { $result = array_change_key_case($result, $db->options['field_case']); } return array_keys($result); } // }}} // {{{ dropConstraint() /** * drop existing constraint * * @param string $table name of table that should be used in method * @param string $name name of the constraint to be dropped * @param string $primary hint if the constraint is primary * * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function dropConstraint($table, $name, $primary = false) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } // is it an UNIQUE index? $query = 'SELECT relname FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index, pg_class WHERE pg_class.relname = '.$db->quote($table, 'text').' AND pg_class.oid = pg_index.indrelid AND indisunique = \'t\') EXCEPT SELECT conname FROM pg_constraint, pg_class WHERE pg_constraint.conrelid = pg_class.oid AND relname = '. $db->quote($table, 'text'); $unique = $db->queryCol($query, 'text'); if (MDB2::isError($unique) || empty($unique)) { // not an UNIQUE index, maybe a CONSTRAINT return parent::dropConstraint($table, $name, $primary); } if (in_array($name, $unique)) { $result = $db->exec('DROP INDEX '.$db->quoteIdentifier($name, true)); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } $idxname = $db->getIndexName($name); if (in_array($idxname, $unique)) { $result = $db->exec('DROP INDEX '.$db->quoteIdentifier($idxname, true)); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, $name . ' is not an existing constraint for table ' . $table, __FUNCTION__); } // }}} // {{{ listTableConstraints() /** * list all constraints in a table * * @param string $table name of table that should be used in method * @return mixed array of constraint names on success, a MDB2 error on failure * @access public */ function listTableConstraints($table) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } list($schema, $table) = $this->splitTableSchema($table); $table = $db->quote($table, 'text'); $query = 'SELECT conname FROM pg_constraint LEFT JOIN pg_class ON pg_constraint.conrelid = pg_class.oid LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid WHERE relname = ' .$table; if (!empty($schema)) { $query .= ' AND pg_namespace.nspname = ' . $db->quote($schema, 'text'); } $query .= ' UNION DISTINCT SELECT relname FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index LEFT JOIN pg_class ON pg_class.oid = pg_index.indrelid LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid WHERE pg_class.relname = '.$table.' AND indisunique = \'t\''; if (!empty($schema)) { $query .= ' AND pg_namespace.nspname = ' . $db->quote($schema, 'text'); } $query .= ')'; $constraints = $db->queryCol($query); if (MDB2::isError($constraints)) { return $constraints; } $result = array(); foreach ($constraints as $constraint) { $constraint = $this->_fixIndexName($constraint); if (!empty($constraint)) { $result[$constraint] = true; } } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE && $db->options['field_case'] == CASE_LOWER ) { $result = array_change_key_case($result, $db->options['field_case']); } return array_keys($result); } // }}} // {{{ createSequence() /** * create sequence * * @param string $seq_name name of the sequence to be created * @param string $start start value of the sequence; default is 1 * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function createSequence($seq_name, $start = 1) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true); $result = $db->exec("CREATE SEQUENCE $sequence_name INCREMENT 1". ($start < 1 ? " MINVALUE $start" : '')." START $start"); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } // }}} // {{{ dropSequence() /** * drop existing sequence * * @param string $seq_name name of the sequence to be dropped * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function dropSequence($seq_name) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true); $result = $db->exec("DROP SEQUENCE $sequence_name"); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } // }}} // {{{ listSequences() /** * list all sequences in the current database * * @return mixed array of sequence names on success, a MDB2 error on failure * @access public */ function listSequences() { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $query = "SELECT relname FROM pg_class WHERE relkind = 'S' AND relnamespace IN"; $query.= "(SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')"; $table_names = $db->queryCol($query); if (MDB2::isError($table_names)) { return $table_names; } $result = array(); foreach ($table_names as $table_name) { $result[] = $this->_fixSequenceName($table_name); } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result); } return $result; } } ?> php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/Native/000077500000000000000000000000001213614046500252325ustar00rootroot00000000000000php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/Native/pgsql.php000066400000000000000000000101431213614046500270700ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: pgsql.php 327310 2012-08-27 15:16:18Z danielc $ require_once 'MDB2/Driver/Native/Common.php'; /** * MDB2 PostGreSQL driver for the native module * * @package MDB2 * @category Database * @author Paul Cooper */ class MDB2_Driver_Native_pgsql extends MDB2_Driver_Native_Common { // }}} // {{{ deleteOID() /** * delete an OID * * @param integer $OID * @return mixed MDB2_OK on success or MDB2 Error Object on failure * @access public */ function deleteOID($OID) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $connection = $db->getConnection(); if (MDB2::isError($connection)) { return $connection; } if (!@pg_lo_unlink($connection, $OID)) { return $db->raiseError(null, null, null, 'Unable to unlink OID: '.$OID, __FUNCTION__); } return MDB2_OK; } } ?> php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/Reverse/000077500000000000000000000000001213614046500254175ustar00rootroot00000000000000php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/Reverse/pgsql.php000066400000000000000000000600421213614046500272600ustar00rootroot00000000000000 | // | Lorenzo Alberton | // +----------------------------------------------------------------------+ // // $Id: pgsql.php 327310 2012-08-27 15:16:18Z danielc $ require_once 'MDB2/Driver/Reverse/Common.php'; /** * MDB2 PostGreSQL driver for the schema reverse engineering module * * @package MDB2 * @category Database * @author Paul Cooper * @author Lorenzo Alberton */ class MDB2_Driver_Reverse_pgsql extends MDB2_Driver_Reverse_Common { // {{{ getTableFieldDefinition() /** * Get the structure of a field into an array * * @param string $table_name name of table that should be used in method * @param string $field_name name of field that should be used in method * @return mixed data array on success, a MDB2 error on failure * @access public */ function getTableFieldDefinition($table_name, $field_name) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $result = $db->loadModule('Datatype', null, true); if (MDB2::isError($result)) { return $result; } list($schema, $table) = $this->splitTableSchema($table_name); $query = "SELECT a.attname AS name, t.typname AS type, CASE a.attlen WHEN -1 THEN CASE t.typname WHEN 'numeric' THEN (a.atttypmod / 65536) WHEN 'decimal' THEN (a.atttypmod / 65536) WHEN 'money' THEN (a.atttypmod / 65536) ELSE CASE a.atttypmod WHEN -1 THEN NULL ELSE a.atttypmod - 4 END END ELSE a.attlen END AS length, CASE t.typname WHEN 'numeric' THEN (a.atttypmod % 65536) - 4 WHEN 'decimal' THEN (a.atttypmod % 65536) - 4 WHEN 'money' THEN (a.atttypmod % 65536) - 4 ELSE 0 END AS scale, a.attnotnull, a.atttypmod, a.atthasdef, (SELECT substring(pg_get_expr(d.adbin, d.adrelid) for 128) FROM pg_attrdef d WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef ) as default FROM pg_attribute a, pg_class c, pg_type t WHERE c.relname = ".$db->quote($table, 'text')." AND a.atttypid = t.oid AND c.oid = a.attrelid AND NOT a.attisdropped AND a.attnum > 0 AND a.attname = ".$db->quote($field_name, 'text')." ORDER BY a.attnum"; $column = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC); if (MDB2::isError($column)) { return $column; } if (empty($column)) { return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'it was not specified an existing table column', __FUNCTION__); } $column = array_change_key_case($column, CASE_LOWER); $mapped_datatype = $db->datatype->mapNativeDatatype($column); if (MDB2::isError($mapped_datatype)) { return $mapped_datatype; } list($types, $length, $unsigned, $fixed) = $mapped_datatype; $notnull = false; if (!empty($column['attnotnull']) && $column['attnotnull'] == 't') { $notnull = true; } $default = null; if ($column['atthasdef'] === 't' && strpos($column['default'], 'NULL') !== 0 && !preg_match("/nextval\('([^']+)'/", $column['default']) ) { $pattern = '/^\'(.*)\'::[\w ]+$/i'; $default = $column['default'];#substr($column['adsrc'], 1, -1); if ((null === $default) && $notnull) { $default = ''; } elseif (!empty($default) && preg_match($pattern, $default)) { //remove data type cast $default = preg_replace ($pattern, '\\1', $default); } } $autoincrement = false; if (preg_match("/nextval\('([^']+)'/", $column['default'], $nextvals)) { $autoincrement = true; } $definition[0] = array('notnull' => $notnull, 'nativetype' => $column['type']); if (null !== $length) { $definition[0]['length'] = $length; } if (null !== $unsigned) { $definition[0]['unsigned'] = $unsigned; } if (null !== $fixed) { $definition[0]['fixed'] = $fixed; } if ($default !== false) { $definition[0]['default'] = $default; } if ($autoincrement !== false) { $definition[0]['autoincrement'] = $autoincrement; } foreach ($types as $key => $type) { $definition[$key] = $definition[0]; if ($type == 'clob' || $type == 'blob') { unset($definition[$key]['default']); } $definition[$key]['type'] = $type; $definition[$key]['mdb2type'] = $type; } return $definition; } // }}} // {{{ getTableIndexDefinition() /** * Get the structure of an index into an array * * @param string $table_name name of table that should be used in method * @param string $index_name name of index that should be used in method * @return mixed data array on success, a MDB2 error on failure * @access public */ function getTableIndexDefinition($table_name, $index_name) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } list($schema, $table) = $this->splitTableSchema($table_name); $query = 'SELECT relname, indkey FROM pg_index, pg_class'; $query.= ' WHERE pg_class.oid = pg_index.indexrelid'; $query.= " AND indisunique != 't' AND indisprimary != 't'"; $query.= ' AND pg_class.relname = %s'; $index_name_mdb2 = $db->getIndexName($index_name); $row = $db->queryRow(sprintf($query, $db->quote($index_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC); if (MDB2::isError($row) || empty($row)) { // fallback to the given $index_name, without transformation $row = $db->queryRow(sprintf($query, $db->quote($index_name, 'text')), null, MDB2_FETCHMODE_ASSOC); } if (MDB2::isError($row)) { return $row; } if (empty($row)) { return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'it was not specified an existing table index', __FUNCTION__); } $row = array_change_key_case($row, CASE_LOWER); $db->loadModule('Manager', null, true); $columns = $db->manager->listTableFields($table_name); $definition = array(); $index_column_numbers = explode(' ', $row['indkey']); $colpos = 1; foreach ($index_column_numbers as $number) { $definition['fields'][$columns[($number - 1)]] = array( 'position' => $colpos++, 'sorting' => 'ascending', ); } return $definition; } // }}} // {{{ getTableConstraintDefinition() /** * Get the structure of a constraint into an array * * @param string $table_name name of table that should be used in method * @param string $constraint_name name of constraint that should be used in method * @return mixed data array on success, a MDB2 error on failure * @access public */ function getTableConstraintDefinition($table_name, $constraint_name) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } list($schema, $table) = $this->splitTableSchema($table_name); $query = "SELECT c.oid, c.conname AS constraint_name, CASE WHEN c.contype = 'c' THEN 1 ELSE 0 END AS \"check\", CASE WHEN c.contype = 'f' THEN 1 ELSE 0 END AS \"foreign\", CASE WHEN c.contype = 'p' THEN 1 ELSE 0 END AS \"primary\", CASE WHEN c.contype = 'u' THEN 1 ELSE 0 END AS \"unique\", CASE WHEN c.condeferrable = 'f' THEN 0 ELSE 1 END AS deferrable, CASE WHEN c.condeferred = 'f' THEN 0 ELSE 1 END AS initiallydeferred, --array_to_string(c.conkey, ' ') AS constraint_key, t.relname AS table_name, t2.relname AS references_table, CASE confupdtype WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END AS onupdate, CASE confdeltype WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END AS ondelete, CASE confmatchtype WHEN 'u' THEN 'UNSPECIFIED' WHEN 'f' THEN 'FULL' WHEN 'p' THEN 'PARTIAL' END AS match, --array_to_string(c.confkey, ' ') AS fk_constraint_key, consrc FROM pg_constraint c LEFT JOIN pg_class t ON c.conrelid = t.oid LEFT JOIN pg_class t2 ON c.confrelid = t2.oid WHERE c.conname = %s AND t.relname = " . $db->quote($table, 'text'); $constraint_name_mdb2 = $db->getIndexName($constraint_name); $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC); if (MDB2::isError($row) || empty($row)) { // fallback to the given $index_name, without transformation $constraint_name_mdb2 = $constraint_name; $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC); } if (MDB2::isError($row)) { return $row; } $uniqueIndex = false; if (empty($row)) { // We might be looking for a UNIQUE index that was not created // as a constraint but should be treated as such. $query = 'SELECT relname AS constraint_name, indkey, 0 AS "check", 0 AS "foreign", 0 AS "primary", 1 AS "unique", 0 AS deferrable, 0 AS initiallydeferred, NULL AS references_table, NULL AS onupdate, NULL AS ondelete, NULL AS match FROM pg_index, pg_class WHERE pg_class.oid = pg_index.indexrelid AND indisunique = \'t\' AND pg_class.relname = %s'; $constraint_name_mdb2 = $db->getIndexName($constraint_name); $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC); if (MDB2::isError($row) || empty($row)) { // fallback to the given $index_name, without transformation $constraint_name_mdb2 = $constraint_name; $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC); } if (MDB2::isError($row)) { return $row; } if (empty($row)) { return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, $constraint_name . ' is not an existing table constraint', __FUNCTION__); } $uniqueIndex = true; } $row = array_change_key_case($row, CASE_LOWER); $definition = array( 'primary' => (boolean)$row['primary'], 'unique' => (boolean)$row['unique'], 'foreign' => (boolean)$row['foreign'], 'check' => (boolean)$row['check'], 'fields' => array(), 'references' => array( 'table' => $row['references_table'], 'fields' => array(), ), 'deferrable' => (boolean)$row['deferrable'], 'initiallydeferred' => (boolean)$row['initiallydeferred'], 'onupdate' => $row['onupdate'], 'ondelete' => $row['ondelete'], 'match' => $row['match'], ); if ($uniqueIndex) { $db->loadModule('Manager', null, true); $columns = $db->manager->listTableFields($table_name); $index_column_numbers = explode(' ', $row['indkey']); $colpos = 1; foreach ($index_column_numbers as $number) { $definition['fields'][$columns[($number - 1)]] = array( 'position' => $colpos++, 'sorting' => 'ascending', ); } return $definition; } $query = 'SELECT a.attname FROM pg_constraint c LEFT JOIN pg_class t ON c.conrelid = t.oid LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(c.conkey) WHERE c.conname = %s AND t.relname = ' . $db->quote($table, 'text'); $fields = $db->queryCol(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null); if (MDB2::isError($fields)) { return $fields; } $colpos = 1; foreach ($fields as $field) { $definition['fields'][$field] = array( 'position' => $colpos++, 'sorting' => 'ascending', ); } if ($definition['foreign']) { $query = 'SELECT a.attname FROM pg_constraint c LEFT JOIN pg_class t ON c.confrelid = t.oid LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(c.confkey) WHERE c.conname = %s AND t.relname = ' . $db->quote($definition['references']['table'], 'text'); $foreign_fields = $db->queryCol(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null); if (MDB2::isError($foreign_fields)) { return $foreign_fields; } $colpos = 1; foreach ($foreign_fields as $foreign_field) { $definition['references']['fields'][$foreign_field] = array( 'position' => $colpos++, ); } } if ($definition['check']) { $check_def = $db->queryOne("SELECT pg_get_constraintdef(" . $row['oid'] . ", 't')"); // ... } return $definition; } // }}} // {{{ getTriggerDefinition() /** * Get the structure of a trigger into an array * * EXPERIMENTAL * * WARNING: this function is experimental and may change the returned value * at any time until labelled as non-experimental * * @param string $trigger name of trigger that should be used in method * @return mixed data array on success, a MDB2 error on failure * @access public * * @TODO: add support for plsql functions and functions with args */ function getTriggerDefinition($trigger) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $query = "SELECT trg.tgname AS trigger_name, tbl.relname AS table_name, CASE WHEN p.proname IS NOT NULL THEN 'EXECUTE PROCEDURE ' || p.proname || '();' ELSE '' END AS trigger_body, CASE trg.tgtype & cast(2 as int2) WHEN 0 THEN 'AFTER' ELSE 'BEFORE' END AS trigger_type, CASE trg.tgtype & cast(28 as int2) WHEN 16 THEN 'UPDATE' WHEN 8 THEN 'DELETE' WHEN 4 THEN 'INSERT' WHEN 20 THEN 'INSERT, UPDATE' WHEN 28 THEN 'INSERT, UPDATE, DELETE' WHEN 24 THEN 'UPDATE, DELETE' WHEN 12 THEN 'INSERT, DELETE' END AS trigger_event, CASE trg.tgenabled WHEN 'O' THEN 't' ELSE trg.tgenabled END AS trigger_enabled, obj_description(trg.oid, 'pg_trigger') AS trigger_comment FROM pg_trigger trg, pg_class tbl, pg_proc p WHERE trg.tgrelid = tbl.oid AND trg.tgfoid = p.oid AND trg.tgname = ". $db->quote($trigger, 'text'); $types = array( 'trigger_name' => 'text', 'table_name' => 'text', 'trigger_body' => 'text', 'trigger_type' => 'text', 'trigger_event' => 'text', 'trigger_comment' => 'text', 'trigger_enabled' => 'boolean', ); return $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC); } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * NOTE: only supports 'table' and 'flags' if $result * is a table name. * * @param object|string $result MDB2_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A MDB2_Error object on failure. * * @see MDB2_Driver_Common::tableInfo() */ function tableInfo($result, $mode = null) { if (is_string($result)) { return parent::tableInfo($result, $mode); } $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $resource = MDB2::isResultCommon($result) ? $result->getResource() : $result; if (!is_resource($resource)) { return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'Could not generate result resource', __FUNCTION__); } if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { if ($db->options['field_case'] == CASE_LOWER) { $case_func = 'strtolower'; } else { $case_func = 'strtoupper'; } } else { $case_func = 'strval'; } $count = @pg_num_fields($resource); $res = array(); if ($mode) { $res['num_fields'] = $count; } $db->loadModule('Datatype', null, true); for ($i = 0; $i < $count; $i++) { $res[$i] = array( 'table' => function_exists('pg_field_table') ? @pg_field_table($resource, $i) : '', 'name' => $case_func(@pg_field_name($resource, $i)), 'type' => @pg_field_type($resource, $i), 'length' => @pg_field_size($resource, $i), 'flags' => '', ); $mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]); if (MDB2::isError($mdb2type_info)) { return $mdb2type_info; } $res[$i]['mdb2type'] = $mdb2type_info[0][0]; if ($mode & MDB2_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & MDB2_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } return $res; } } ?> php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/MDB2/Driver/pgsql.php000066400000000000000000001620121213614046500256450ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: pgsql.php 327317 2012-08-27 15:17:08Z danielc $ /** * MDB2 PostGreSQL driver * * @package MDB2 * @category Database * @author Paul Cooper */ class MDB2_Driver_pgsql extends MDB2_Driver_Common { // {{{ properties var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => '\\'); var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"'); // }}} // {{{ constructor /** * Constructor */ function __construct() { parent::__construct(); $this->phptype = 'pgsql'; $this->dbsyntax = 'pgsql'; $this->supported['sequences'] = true; $this->supported['indexes'] = true; $this->supported['affected_rows'] = true; $this->supported['summary_functions'] = true; $this->supported['order_by_text'] = true; $this->supported['transactions'] = true; $this->supported['savepoints'] = true; $this->supported['current_id'] = true; $this->supported['limit_queries'] = true; $this->supported['LOBs'] = true; $this->supported['replace'] = 'emulated'; $this->supported['sub_selects'] = true; $this->supported['triggers'] = true; $this->supported['auto_increment'] = 'emulated'; $this->supported['primary_key'] = true; $this->supported['result_introspection'] = true; $this->supported['prepared_statements'] = true; $this->supported['identifier_quoting'] = true; $this->supported['pattern_escaping'] = true; $this->supported['new_link'] = true; $this->options['DBA_username'] = false; $this->options['DBA_password'] = false; $this->options['multi_query'] = false; $this->options['disable_smart_seqname'] = true; $this->options['max_identifiers_length'] = 63; } // }}} // {{{ errorInfo() /** * This method is used to collect information about an error * * @param integer $error * @return array * @access public */ function errorInfo($error = null) { // Fall back to MDB2_ERROR if there was no mapping. $error_code = MDB2_ERROR; $native_msg = ''; if (is_resource($error)) { $native_msg = @pg_result_error($error); } elseif ($this->connection) { $native_msg = @pg_last_error($this->connection); if (!$native_msg && @pg_connection_status($this->connection) === PGSQL_CONNECTION_BAD) { $native_msg = 'Database connection has been lost.'; $error_code = MDB2_ERROR_CONNECT_FAILED; } } else { $native_msg = @pg_last_error(); } static $error_regexps; if (empty($error_regexps)) { $error_regexps = array( '/column .* (of relation .*)?does not exist/i' => MDB2_ERROR_NOSUCHFIELD, '/(relation|sequence|table).*does not exist|class .* not found/i' => MDB2_ERROR_NOSUCHTABLE, '/database .* does not exist/' => MDB2_ERROR_NOT_FOUND, '/constraint .* does not exist/' => MDB2_ERROR_NOT_FOUND, '/index .* does not exist/' => MDB2_ERROR_NOT_FOUND, '/database .* already exists/i' => MDB2_ERROR_ALREADY_EXISTS, '/relation .* already exists/i' => MDB2_ERROR_ALREADY_EXISTS, '/(divide|division) by zero$/i' => MDB2_ERROR_DIVZERO, '/pg_atoi: error in .*: can\'t parse /i' => MDB2_ERROR_INVALID_NUMBER, '/invalid input syntax for( type)? (integer|numeric)/i' => MDB2_ERROR_INVALID_NUMBER, '/value .* is out of range for type \w*int/i' => MDB2_ERROR_INVALID_NUMBER, '/integer out of range/i' => MDB2_ERROR_INVALID_NUMBER, '/value too long for type character/i' => MDB2_ERROR_INVALID, '/attribute .* not found|relation .* does not have attribute/i' => MDB2_ERROR_NOSUCHFIELD, '/column .* specified in USING clause does not exist in (left|right) table/i' => MDB2_ERROR_NOSUCHFIELD, '/parser: parse error at or near/i' => MDB2_ERROR_SYNTAX, '/syntax error at/' => MDB2_ERROR_SYNTAX, '/column reference .* is ambiguous/i' => MDB2_ERROR_SYNTAX, '/permission denied/' => MDB2_ERROR_ACCESS_VIOLATION, '/violates not-null constraint/' => MDB2_ERROR_CONSTRAINT_NOT_NULL, '/violates [\w ]+ constraint/' => MDB2_ERROR_CONSTRAINT, '/referential integrity violation/' => MDB2_ERROR_CONSTRAINT, '/more expressions than target columns/i' => MDB2_ERROR_VALUE_COUNT_ON_ROW, ); } if (is_numeric($error) && $error < 0) { $error_code = $error; } else { foreach ($error_regexps as $regexp => $code) { if (preg_match($regexp, $native_msg)) { $error_code = $code; break; } } } return array($error_code, null, $native_msg); } // }}} // {{{ escape() /** * Quotes a string so it can be safely used in a query. It will quote * the text so it can safely be used within a query. * * @param string the input string to quote * @param bool escape wildcards * * @return string quoted string * * @access public */ function escape($text, $escape_wildcards = false) { if ($escape_wildcards) { $text = $this->escapePattern($text); } $connection = $this->getConnection(); if (MDB2::isError($connection)) { return $connection; } if (is_resource($connection) && version_compare(PHP_VERSION, '5.2.0RC5', '>=')) { $text = @pg_escape_string($connection, $text); } else { $text = @pg_escape_string($text); } return $text; } // }}} // {{{ beginTransaction() /** * Start a transaction or set a savepoint. * * @param string name of a savepoint to set * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function beginTransaction($savepoint = null) { $this->debug('Starting transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); if (null !== $savepoint) { if (!$this->in_transaction) { return $this->raiseError(MDB2_ERROR_INVALID, null, null, 'savepoint cannot be released when changes are auto committed', __FUNCTION__); } $query = 'SAVEPOINT '.$savepoint; return $this->_doQuery($query, true); } if ($this->in_transaction) { return MDB2_OK; //nothing to do } if (!$this->destructor_registered && $this->opened_persistent) { $this->destructor_registered = true; register_shutdown_function('MDB2_closeOpenTransactions'); } $result = $this->_doQuery('BEGIN', true); if (MDB2::isError($result)) { return $result; } $this->in_transaction = true; return MDB2_OK; } // }}} // {{{ commit() /** * Commit the database changes done during a transaction that is in * progress or release a savepoint. This function may only be called when * auto-committing is disabled, otherwise it will fail. Therefore, a new * transaction is implicitly started after committing the pending changes. * * @param string name of a savepoint to release * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function commit($savepoint = null) { $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); if (!$this->in_transaction) { return $this->raiseError(MDB2_ERROR_INVALID, null, null, 'commit/release savepoint cannot be done changes are auto committed', __FUNCTION__); } if (null !== $savepoint) { $query = 'RELEASE SAVEPOINT '.$savepoint; return $this->_doQuery($query, true); } $result = $this->_doQuery('COMMIT', true); if (MDB2::isError($result)) { return $result; } $this->in_transaction = false; return MDB2_OK; } // }}} // {{{ rollback() /** * Cancel any database changes done during a transaction or since a specific * savepoint that is in progress. This function may only be called when * auto-committing is disabled, otherwise it will fail. Therefore, a new * transaction is implicitly started after canceling the pending changes. * * @param string name of a savepoint to rollback to * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function rollback($savepoint = null) { $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); if (!$this->in_transaction) { return $this->raiseError(MDB2_ERROR_INVALID, null, null, 'rollback cannot be done changes are auto committed', __FUNCTION__); } if (null !== $savepoint) { $query = 'ROLLBACK TO SAVEPOINT '.$savepoint; return $this->_doQuery($query, true); } $query = 'ROLLBACK'; $result = $this->_doQuery($query, true); if (MDB2::isError($result)) { return $result; } $this->in_transaction = false; return MDB2_OK; } // }}} // {{{ function setTransactionIsolation() /** * Set the transacton isolation level. * * @param string standard isolation level * READ UNCOMMITTED (allows dirty reads) * READ COMMITTED (prevents dirty reads) * REPEATABLE READ (prevents nonrepeatable reads) * SERIALIZABLE (prevents phantom reads) * @param array some transaction options: * 'wait' => 'WAIT' | 'NO WAIT' * 'rw' => 'READ WRITE' | 'READ ONLY' * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public * @since 2.1.1 */ function setTransactionIsolation($isolation, $options = array()) { $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true)); switch ($isolation) { case 'READ UNCOMMITTED': case 'READ COMMITTED': case 'REPEATABLE READ': case 'SERIALIZABLE': break; default: return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'isolation level is not supported: '.$isolation, __FUNCTION__); } $query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL $isolation"; return $this->_doQuery($query, true); } // }}} // {{{ _doConnect() /** * Do the grunt work of connecting to the database * * @return mixed connection resource on success, MDB2 Error Object on failure * @access protected */ function _doConnect($username, $password, $database_name, $persistent = false) { if (!extension_loaded($this->phptype)) { return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__); } if ($database_name == '') { $database_name = 'template1'; } $protocol = $this->dsn['protocol'] ? $this->dsn['protocol'] : 'tcp'; $params = array(''); if ($protocol == 'tcp') { if ($this->dsn['hostspec']) { $params[0].= 'host=' . $this->dsn['hostspec']; } if ($this->dsn['port']) { $params[0].= ' port=' . $this->dsn['port']; } } elseif ($protocol == 'unix') { // Allow for pg socket in non-standard locations. if ($this->dsn['socket']) { $params[0].= 'host=' . $this->dsn['socket']; } if ($this->dsn['port']) { $params[0].= ' port=' . $this->dsn['port']; } } if ($database_name) { $params[0].= ' dbname=\'' . addslashes($database_name) . '\''; } if ($username) { $params[0].= ' user=\'' . addslashes($username) . '\''; } if ($password) { $params[0].= ' password=\'' . addslashes($password) . '\''; } if (!empty($this->dsn['options'])) { $params[0].= ' options=' . $this->dsn['options']; } if (!empty($this->dsn['tty'])) { $params[0].= ' tty=' . $this->dsn['tty']; } if (!empty($this->dsn['connect_timeout'])) { $params[0].= ' connect_timeout=' . $this->dsn['connect_timeout']; } if (!empty($this->dsn['sslmode'])) { $params[0].= ' sslmode=' . $this->dsn['sslmode']; } if (!empty($this->dsn['service'])) { $params[0].= ' service=' . $this->dsn['service']; } if ($this->_isNewLinkSet()) { if (version_compare(phpversion(), '4.3.0', '>=')) { $params[] = PGSQL_CONNECT_FORCE_NEW; } } $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect'; $connection = @call_user_func_array($connect_function, $params); if (!$connection) { return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null, 'unable to establish a connection', __FUNCTION__); } if (empty($this->dsn['disable_iso_date'])) { if (!@pg_query($connection, "SET SESSION DATESTYLE = 'ISO'")) { return $this->raiseError(null, null, null, 'Unable to set date style to iso', __FUNCTION__); } } if (!empty($this->dsn['charset'])) { $result = $this->setCharset($this->dsn['charset'], $connection); if (MDB2::isError($result)) { return $result; } } // Enable extra compatibility settings on 8.2 and later if (function_exists('pg_parameter_status')) { $version = pg_parameter_status($connection, 'server_version'); if ($version == false) { return $this->raiseError(null, null, null, 'Unable to retrieve server version', __FUNCTION__); } $version = explode ('.', $version); if ( $version['0'] > 8 || ($version['0'] == 8 && $version['1'] >= 2) ) { if (!@pg_query($connection, "SET SESSION STANDARD_CONFORMING_STRINGS = OFF")) { return $this->raiseError(null, null, null, 'Unable to set standard_conforming_strings to off', __FUNCTION__); } if (!@pg_query($connection, "SET SESSION ESCAPE_STRING_WARNING = OFF")) { return $this->raiseError(null, null, null, 'Unable to set escape_string_warning to off', __FUNCTION__); } } } return $connection; } // }}} // {{{ connect() /** * Connect to the database * * @return true on success, MDB2 Error Object on failure * @access public */ function connect() { if (is_resource($this->connection)) { //if (count(array_diff($this->connected_dsn, $this->dsn)) == 0 if (MDB2::areEquals($this->connected_dsn, $this->dsn) && $this->connected_database_name == $this->database_name && ($this->opened_persistent == $this->options['persistent']) ) { return MDB2_OK; } $this->disconnect(false); } if ($this->database_name) { $connection = $this->_doConnect($this->dsn['username'], $this->dsn['password'], $this->database_name, $this->options['persistent']); if (MDB2::isError($connection)) { return $connection; } $this->connection = $connection; $this->connected_dsn = $this->dsn; $this->connected_database_name = $this->database_name; $this->opened_persistent = $this->options['persistent']; $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype; } return MDB2_OK; } // }}} // {{{ setCharset() /** * Set the charset on the current connection * * @param string charset * @param resource connection handle * * @return true on success, MDB2 Error Object on failure */ function setCharset($charset, $connection = null) { if (null === $connection) { $connection = $this->getConnection(); if (MDB2::isError($connection)) { return $connection; } } if (is_array($charset)) { $charset = array_shift($charset); $this->warnings[] = 'postgresql does not support setting client collation'; } $result = @pg_set_client_encoding($connection, $charset); if ($result == -1) { return $this->raiseError(null, null, null, 'Unable to set client charset: '.$charset, __FUNCTION__); } return MDB2_OK; } // }}} // {{{ databaseExists() /** * check if given database name is exists? * * @param string $name name of the database that should be checked * * @return mixed true/false on success, a MDB2 error on failure * @access public */ function databaseExists($name) { $res = $this->_doConnect($this->dsn['username'], $this->dsn['password'], $this->escape($name), $this->options['persistent']); if (!MDB2::isError($res)) { return true; } return false; } // }}} // {{{ disconnect() /** * Log out and disconnect from the database. * * @param boolean $force if the disconnect should be forced even if the * connection is opened persistently * @return mixed true on success, false if not connected and error * object on error * @access public */ function disconnect($force = true) { if (is_resource($this->connection)) { if ($this->in_transaction) { $dsn = $this->dsn; $database_name = $this->database_name; $persistent = $this->options['persistent']; $this->dsn = $this->connected_dsn; $this->database_name = $this->connected_database_name; $this->options['persistent'] = $this->opened_persistent; $this->rollback(); $this->dsn = $dsn; $this->database_name = $database_name; $this->options['persistent'] = $persistent; } if (!$this->opened_persistent || $force) { $ok = @pg_close($this->connection); if (!$ok) { return $this->raiseError(MDB2_ERROR_DISCONNECT_FAILED, null, null, null, __FUNCTION__); } } } else { return false; } return parent::disconnect($force); } // }}} // {{{ standaloneQuery() /** * execute a query as DBA * * @param string $query the SQL query * @param mixed $types array that contains the types of the columns in * the result set * @param boolean $is_manip if the query is a manipulation query * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function standaloneQuery($query, $types = null, $is_manip = false) { $user = $this->options['DBA_username']? $this->options['DBA_username'] : $this->dsn['username']; $pass = $this->options['DBA_password']? $this->options['DBA_password'] : $this->dsn['password']; $connection = $this->_doConnect($user, $pass, $this->database_name, $this->options['persistent']); if (MDB2::isError($connection)) { return $connection; } $offset = $this->offset; $limit = $this->limit; $this->offset = $this->limit = 0; $query = $this->_modifyQuery($query, $is_manip, $limit, $offset); $result = $this->_doQuery($query, $is_manip, $connection, $this->database_name); if (!MDB2::isError($result)) { if ($is_manip) { $result = $this->_affectedRows($connection, $result); } else { $result = $this->_wrapResult($result, $types, true, true, $limit, $offset); } } @pg_close($connection); return $result; } // }}} // {{{ _doQuery() /** * Execute a query * @param string $query query * @param boolean $is_manip if the query is a manipulation query * @param resource $connection * @param string $database_name * @return result or error object * @access protected */ function _doQuery($query, $is_manip = false, $connection = null, $database_name = null) { $this->last_query = $query; $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre')); if ($result) { if (MDB2::isError($result)) { return $result; } $query = $result; } if ($this->options['disable_query']) { $result = $is_manip ? 0 : null; return $result; } if (null === $connection) { $connection = $this->getConnection(); if (MDB2::isError($connection)) { return $connection; } } $function = $this->options['multi_query'] ? 'pg_send_query' : 'pg_query'; $result = @$function($connection, $query); if (!$result) { $err = $this->raiseError(null, null, null, 'Could not execute statement', __FUNCTION__); return $err; } elseif ($this->options['multi_query']) { if (!($result = @pg_get_result($connection))) { $err = $this->raiseError(null, null, null, 'Could not get the first result from a multi query', __FUNCTION__); return $err; } } $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result)); return $result; } // }}} // {{{ _affectedRows() /** * Returns the number of rows affected * * @param resource $result * @param resource $connection * @return mixed MDB2 Error Object or the number of rows affected * @access private */ function _affectedRows($connection, $result = null) { if (null === $connection) { $connection = $this->getConnection(); if (MDB2::isError($connection)) { return $connection; } } return @pg_affected_rows($result); } // }}} // {{{ _modifyQuery() /** * Changes a query string for various DBMS specific reasons * * @param string $query query to modify * @param boolean $is_manip if it is a DML query * @param integer $limit limit the number of rows * @param integer $offset start reading from given offset * @return string modified query * @access protected */ function _modifyQuery($query, $is_manip, $limit, $offset) { if ($limit > 0 && !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\)]*)?$/i', $query) ) { $query = rtrim($query); if (substr($query, -1) == ';') { $query = substr($query, 0, -1); } if ($is_manip) { $query = $this->_modifyManipQuery($query, $limit); } else { $query.= " LIMIT $limit OFFSET $offset"; } } return $query; } // }}} // {{{ _modifyManipQuery() /** * Changes a manip query string for various DBMS specific reasons * * @param string $query query to modify * @param integer $limit limit the number of rows * @return string modified query * @access protected */ function _modifyManipQuery($query, $limit) { $pos = strpos(strtolower($query), 'where'); $where = $pos ? substr($query, $pos) : ''; $manip_clause = '(\bDELETE\b\s+(?:\*\s+)?\bFROM\b|\bUPDATE\b)'; $from_clause = '([\w\.]+)'; $where_clause = '(?:(.*)\bWHERE\b\s+(.*))|(.*)'; $pattern = '/^'. $manip_clause . '\s+' . $from_clause .'(?:\s)*(?:'. $where_clause .')?$/i'; $matches = preg_match($pattern, $query, $match); if ($matches) { $manip = $match[1]; $from = $match[2]; $what = (count($matches) == 6) ? $match[5] : $match[3]; return $manip.' '.$from.' '.$what.' WHERE ctid=(SELECT ctid FROM '.$from.' '.$where.' LIMIT '.$limit.')'; } //return error? return $query; } // }}} // {{{ getServerVersion() /** * return version information about the server * * @param bool $native determines if the raw version string should be returned * @return mixed array/string with version information or MDB2 error object * @access public */ function getServerVersion($native = false) { $query = 'SHOW SERVER_VERSION'; if ($this->connected_server_info) { $server_info = $this->connected_server_info; } else { $server_info = $this->queryOne($query, 'text'); if (MDB2::isError($server_info)) { return $server_info; } } // cache server_info $this->connected_server_info = $server_info; if (!$native && !MDB2::isError($server_info)) { $tmp = explode('.', $server_info, 3); if (empty($tmp[2]) && isset($tmp[1]) && preg_match('/(\d+)(.*)/', $tmp[1], $tmp2) ) { $server_info = array( 'major' => $tmp[0], 'minor' => $tmp2[1], 'patch' => null, 'extra' => $tmp2[2], 'native' => $server_info, ); } else { $server_info = array( 'major' => isset($tmp[0]) ? $tmp[0] : null, 'minor' => isset($tmp[1]) ? $tmp[1] : null, 'patch' => isset($tmp[2]) ? $tmp[2] : null, 'extra' => null, 'native' => $server_info, ); } } return $server_info; } // }}} // {{{ prepare() /** * Prepares a query for multiple execution with execute(). * With some database backends, this is emulated. * prepare() requires a generic query as string like * 'INSERT INTO numbers VALUES(?,?)' or * 'INSERT INTO numbers VALUES(:foo,:bar)'. * The ? and :name and are placeholders which can be set using * bindParam() and the query can be sent off using the execute() method. * The allowed format for :name can be set with the 'bindname_format' option. * * @param string $query the query to prepare * @param mixed $types array that contains the types of the placeholders * @param mixed $result_types array that contains the types of the columns in * the result set or MDB2_PREPARE_RESULT, if set to * MDB2_PREPARE_MANIP the query is handled as a manipulation query * @param mixed $lobs key (field) value (parameter) pair for all lob placeholders * @return mixed resource handle for the prepared query on success, a MDB2 * error on failure * @access public * @see bindParam, execute */ function prepare($query, $types = null, $result_types = null, $lobs = array()) { if ($this->options['emulate_prepared']) { return parent::prepare($query, $types, $result_types, $lobs); } $is_manip = ($result_types === MDB2_PREPARE_MANIP); $offset = $this->offset; $limit = $this->limit; $this->offset = $this->limit = 0; $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre')); if ($result) { if (MDB2::isError($result)) { return $result; } $query = $result; } $pgtypes = function_exists('pg_prepare') ? false : array(); if ($pgtypes !== false && !empty($types)) { $this->loadModule('Datatype', null, true); } $query = $this->_modifyQuery($query, $is_manip, $limit, $offset); $placeholder_type_guess = $placeholder_type = null; $question = '?'; $colon = ':'; $positions = array(); $position = $parameter = 0; while ($position < strlen($query)) { $q_position = strpos($query, $question, $position); $c_position = strpos($query, $colon, $position); //skip "::type" cast ("select id::varchar(20) from sometable where name=?") $doublecolon_position = strpos($query, '::', $position); if ($doublecolon_position !== false && $doublecolon_position == $c_position) { $c_position = strpos($query, $colon, $position+2); } if ($q_position && $c_position) { $p_position = min($q_position, $c_position); } elseif ($q_position) { $p_position = $q_position; } elseif ($c_position) { $p_position = $c_position; } else { break; } if (null === $placeholder_type) { $placeholder_type_guess = $query[$p_position]; } $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position); if (MDB2::isError($new_pos)) { return $new_pos; } if ($new_pos != $position) { $position = $new_pos; continue; //evaluate again starting from the new position } if ($query[$position] == $placeholder_type_guess) { if (null === $placeholder_type) { $placeholder_type = $query[$p_position]; $question = $colon = $placeholder_type; if (!empty($types) && is_array($types)) { if ($placeholder_type == ':') { } else { $types = array_values($types); } } } if ($placeholder_type_guess == '?') { $length = 1; $name = $parameter; } else { $regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s'; $param = preg_replace($regexp, '\\1', $query); if ($param === '') { $err = $this->raiseError(MDB2_ERROR_SYNTAX, null, null, 'named parameter name must match "bindname_format" option', __FUNCTION__); return $err; } $length = strlen($param) + 1; $name = $param; } if ($pgtypes !== false) { if (is_array($types) && array_key_exists($name, $types)) { $pgtypes[] = $this->datatype->mapPrepareDatatype($types[$name]); } elseif (is_array($types) && array_key_exists($parameter, $types)) { $pgtypes[] = $this->datatype->mapPrepareDatatype($types[$parameter]); } else { $pgtypes[] = 'text'; } } if (($key_parameter = array_search($name, $positions)) !== false) { //$next_parameter = 1; $parameter = $key_parameter + 1; //foreach ($positions as $key => $value) { // if ($key_parameter == $key) { // break; // } // ++$next_parameter; //} } else { ++$parameter; //$next_parameter = $parameter; $positions[] = $name; } $query = substr_replace($query, '$'.$parameter, $position, $length); $position = $p_position + strlen($parameter); } else { $position = $p_position; } } $connection = $this->getConnection(); if (MDB2::isError($connection)) { return $connection; } static $prep_statement_counter = 1; $statement_name = sprintf($this->options['statement_format'], $this->phptype, $prep_statement_counter++ . sha1(microtime() + mt_rand())); $statement_name = substr(strtolower($statement_name), 0, $this->options['max_identifiers_length']); if (false === $pgtypes) { $result = @pg_prepare($connection, $statement_name, $query); if (!$result) { $err = $this->raiseError(null, null, null, 'Unable to create prepared statement handle', __FUNCTION__); return $err; } } else { $types_string = ''; if ($pgtypes) { $types_string = ' ('.implode(', ', $pgtypes).') '; } $query = 'PREPARE '.$statement_name.$types_string.' AS '.$query; $statement = $this->_doQuery($query, true, $connection); if (MDB2::isError($statement)) { return $statement; } } $class_name = 'MDB2_Statement_'.$this->phptype; $obj = new $class_name($this, $statement_name, $positions, $query, $types, $result_types, $is_manip, $limit, $offset); $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj)); return $obj; } // }}} // {{{ function getSequenceName($sqn) /** * adds sequence name formatting to a sequence name * * @param string name of the sequence * * @return string formatted sequence name * * @access public */ function getSequenceName($sqn) { if (false === $this->options['disable_smart_seqname']) { if (strpos($sqn, '_') !== false) { list($table, $field) = explode('_', $sqn, 2); } $schema_list = $this->queryOne("SELECT array_to_string(current_schemas(false), ',')"); if (MDB2::isError($schema_list) || empty($schema_list) || count($schema_list) < 2) { $order_by = ' a.attnum'; $schema_clause = ' AND n.nspname=current_schema()'; } else { $schemas = explode(',', $schema_list); $schema_clause = ' AND n.nspname IN ('.$schema_list.')'; $counter = 1; $order_by = ' CASE '; foreach ($schemas as $schema) { $order_by .= ' WHEN n.nspname='.$schema.' THEN '.$counter++; } $order_by .= ' ELSE '.$counter.' END, a.attnum'; } $query = "SELECT substring((SELECT substring(pg_get_expr(d.adbin, d.adrelid) for 128) FROM pg_attrdef d WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef ) FROM 'nextval[^'']*''([^'']*)') FROM pg_attribute a LEFT JOIN pg_class c ON c.oid = a.attrelid LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef LEFT JOIN pg_namespace n ON c.relnamespace = n.oid WHERE (c.relname = ".$this->quote($sqn, 'text'); if (!empty($field)) { $query .= " OR (c.relname = ".$this->quote($table, 'text')." AND a.attname = ".$this->quote($field, 'text').")"; } $query .= " )" .$schema_clause." AND NOT a.attisdropped AND a.attnum > 0 AND pg_get_expr(d.adbin, d.adrelid) LIKE 'nextval%' ORDER BY ".$order_by; $seqname = $this->queryOne($query); if (!MDB2::isError($seqname) && !empty($seqname) && is_string($seqname)) { return $seqname; } } return parent::getSequenceName($sqn); } // }}} // {{{ nextID() /** * Returns the next free id of a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true the sequence is * automatic created, if it * not exists * @return mixed MDB2 Error Object or id * @access public */ function nextID($seq_name, $ondemand = true) { $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true); $query = "SELECT NEXTVAL('$sequence_name')"; $this->pushErrorHandling(PEAR_ERROR_RETURN); $this->expectError(MDB2_ERROR_NOSUCHTABLE); $result = $this->queryOne($query, 'integer'); $this->popExpect(); $this->popErrorHandling(); if (MDB2::isError($result)) { if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) { $this->loadModule('Manager', null, true); $result = $this->manager->createSequence($seq_name); if (MDB2::isError($result)) { return $this->raiseError($result, null, null, 'on demand sequence could not be created', __FUNCTION__); } return $this->nextId($seq_name, false); } } return $result; } // }}} // {{{ lastInsertID() /** * Returns the autoincrement ID if supported or $id or fetches the current * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field) * * @param string $table name of the table into which a new row was inserted * @param string $field name of the field into which a new row was inserted * @return mixed MDB2 Error Object or id * @access public */ function lastInsertID($table = null, $field = null) { if (empty($table) && empty($field)) { return $this->queryOne('SELECT lastval()', 'integer'); } $seq = $table.(empty($field) ? '' : '_'.$field); $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq), true); return $this->queryOne("SELECT currval('$sequence_name')", 'integer'); } // }}} // {{{ currID() /** * Returns the current id of a sequence * * @param string $seq_name name of the sequence * @return mixed MDB2 Error Object or id * @access public */ function currID($seq_name) { $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq_name), true); return $this->queryOne("SELECT last_value FROM $sequence_name", 'integer'); } } /** * MDB2 PostGreSQL result driver * * @package MDB2 * @category Database * @author Paul Cooper */ class MDB2_Result_pgsql extends MDB2_Result_Common { // }}} // {{{ fetchRow() /** * Fetch a row and insert the data into an existing array. * * @param int $fetchmode how the array data should be indexed * @param int $rownum number of the row where the data can be found * @return int data array on success, a MDB2 error on failure * @access public */ function fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) { if (null !== $rownum) { $seek = $this->seek($rownum); if (MDB2::isError($seek)) { return $seek; } } if ($fetchmode == MDB2_FETCHMODE_DEFAULT) { $fetchmode = $this->db->fetchmode; } if ( $fetchmode == MDB2_FETCHMODE_ASSOC || $fetchmode == MDB2_FETCHMODE_OBJECT ) { $row = @pg_fetch_array($this->result, null, PGSQL_ASSOC); if (is_array($row) && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE ) { $row = array_change_key_case($row, $this->db->options['field_case']); } } else { $row = @pg_fetch_row($this->result); } if (!$row) { if (false === $this->result) { $err = $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'resultset has already been freed', __FUNCTION__); return $err; } return null; } $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL; $rtrim = false; if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) { if (empty($this->types)) { $mode += MDB2_PORTABILITY_RTRIM; } else { $rtrim = true; } } if ($mode) { $this->db->_fixResultArrayValues($row, $mode); } if ( ( $fetchmode != MDB2_FETCHMODE_ASSOC && $fetchmode != MDB2_FETCHMODE_OBJECT) && !empty($this->types) ) { $row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim); } elseif (($fetchmode == MDB2_FETCHMODE_ASSOC || $fetchmode == MDB2_FETCHMODE_OBJECT) && !empty($this->types_assoc) ) { $row = $this->db->datatype->convertResultRow($this->types_assoc, $row, $rtrim); } if (!empty($this->values)) { $this->_assignBindColumns($row); } if ($fetchmode === MDB2_FETCHMODE_OBJECT) { $object_class = $this->db->options['fetch_class']; if ($object_class == 'stdClass') { $row = (object) $row; } else { $rowObj = new $object_class($row); $row = $rowObj; } } ++$this->rownum; return $row; } // }}} // {{{ _getColumnNames() /** * Retrieve the names of columns returned by the DBMS in a query result. * * @return mixed Array variable that holds the names of columns as keys * or an MDB2 error on failure. * Some DBMS may not return any columns when the result set * does not contain any rows. * @access private */ function _getColumnNames() { $columns = array(); $numcols = $this->numCols(); if (MDB2::isError($numcols)) { return $numcols; } for ($column = 0; $column < $numcols; $column++) { $column_name = @pg_field_name($this->result, $column); $columns[$column_name] = $column; } if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { $columns = array_change_key_case($columns, $this->db->options['field_case']); } return $columns; } // }}} // {{{ numCols() /** * Count the number of columns returned by the DBMS in a query result. * * @access public * @return mixed integer value with the number of columns, a MDB2 error * on failure */ function numCols() { $cols = @pg_num_fields($this->result); if (null === $cols) { if (false === $this->result) { return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'resultset has already been freed', __FUNCTION__); } if (null === $this->result) { return count($this->types); } return $this->db->raiseError(null, null, null, 'Could not get column count', __FUNCTION__); } return $cols; } // }}} // {{{ nextResult() /** * Move the internal result pointer to the next available result * * @return true on success, false if there is no more result set or an error object on failure * @access public */ function nextResult() { $connection = $this->db->getConnection(); if (MDB2::isError($connection)) { return $connection; } if (!($this->result = @pg_get_result($connection))) { return false; } return MDB2_OK; } // }}} // {{{ free() /** * Free the internal resources associated with result. * * @return boolean true on success, false if result is invalid * @access public */ function free() { if (is_resource($this->result) && $this->db->connection) { $free = @pg_free_result($this->result); if (false === $free) { return $this->db->raiseError(null, null, null, 'Could not free result', __FUNCTION__); } } $this->result = false; return MDB2_OK; } } /** * MDB2 PostGreSQL buffered result driver * * @package MDB2 * @category Database * @author Paul Cooper */ class MDB2_BufferedResult_pgsql extends MDB2_Result_pgsql { // {{{ seek() /** * Seek to a specific row in a result set * * @param int $rownum number of the row where the data can be found * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function seek($rownum = 0) { if ($this->rownum != ($rownum - 1) && !@pg_result_seek($this->result, $rownum)) { if (false === $this->result) { return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'resultset has already been freed', __FUNCTION__); } if (null === $this->result) { return MDB2_OK; } return $this->db->raiseError(MDB2_ERROR_INVALID, null, null, 'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__); } $this->rownum = $rownum - 1; return MDB2_OK; } // }}} // {{{ valid() /** * Check if the end of the result set has been reached * * @return mixed true or false on sucess, a MDB2 error on failure * @access public */ function valid() { $numrows = $this->numRows(); if (MDB2::isError($numrows)) { return $numrows; } return $this->rownum < ($numrows - 1); } // }}} // {{{ numRows() /** * Returns the number of rows in a result object * * @return mixed MDB2 Error Object or the number of rows * @access public */ function numRows() { $rows = @pg_num_rows($this->result); if (null === $rows) { if (false === $this->result) { return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'resultset has already been freed', __FUNCTION__); } if (null === $this->result) { return 0; } return $this->db->raiseError(null, null, null, 'Could not get row count', __FUNCTION__); } return $rows; } } /** * MDB2 PostGreSQL statement driver * * @package MDB2 * @category Database * @author Paul Cooper */ class MDB2_Statement_pgsql extends MDB2_Statement_Common { // {{{ _execute() /** * Execute a prepared query statement helper method. * * @param mixed $result_class string which specifies which result class to use * @param mixed $result_wrap_class string which specifies which class to wrap results in * * @return mixed MDB2_Result or integer (affected rows) on success, * a MDB2 error on failure * @access private */ function _execute($result_class = true, $result_wrap_class = true) { if (null === $this->statement) { return parent::_execute($result_class, $result_wrap_class); } $this->db->last_query = $this->query; $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'pre', 'parameters' => $this->values)); if ($this->db->getOption('disable_query')) { $result = $this->is_manip ? 0 : null; return $result; } $connection = $this->db->getConnection(); if (MDB2::isError($connection)) { return $connection; } $query = false; $parameters = array(); // todo: disabled until pg_execute() bytea issues are cleared up if (true || !function_exists('pg_execute')) { $query = 'EXECUTE '.$this->statement; } if (!empty($this->positions)) { foreach ($this->positions as $parameter) { if (!array_key_exists($parameter, $this->values)) { return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__); } $value = $this->values[$parameter]; $type = array_key_exists($parameter, $this->types) ? $this->types[$parameter] : null; if (is_resource($value) || $type == 'clob' || $type == 'blob' || $this->db->options['lob_allow_url_include']) { if (!is_resource($value) && preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) { if ($match[1] == 'file://') { $value = $match[2]; } $value = @fopen($value, 'r'); $close = true; } if (is_resource($value)) { $data = ''; while (!@feof($value)) { $data.= @fread($value, $this->db->options['lob_buffer_length']); } if ($close) { @fclose($value); } $value = $data; } } $quoted = $this->db->quote($value, $type, $query); if (MDB2::isError($quoted)) { return $quoted; } $parameters[] = $quoted; } if ($query) { $query.= ' ('.implode(', ', $parameters).')'; } } if (!$query) { $result = @pg_execute($connection, $this->statement, $parameters); if (!$result) { $err = $this->db->raiseError(null, null, null, 'Unable to execute statement', __FUNCTION__); return $err; } } else { $result = $this->db->_doQuery($query, $this->is_manip, $connection); if (MDB2::isError($result)) { return $result; } } if ($this->is_manip) { $affected_rows = $this->db->_affectedRows($connection, $result); return $affected_rows; } $result = $this->db->_wrapResult($result, $this->result_types, $result_class, $result_wrap_class, $this->limit, $this->offset); $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result)); return $result; } // }}} // {{{ free() /** * Release resources allocated for the specified prepared query. * * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function free() { if (null === $this->positions) { return $this->db->raiseError(MDB2_ERROR, null, null, 'Prepared statement has already been freed', __FUNCTION__); } $result = MDB2_OK; if (null !== $this->statement) { $connection = $this->db->getConnection(); if (MDB2::isError($connection)) { return $connection; } $query = 'DEALLOCATE PREPARE '.$this->statement; $result = $this->db->_doQuery($query, true, $connection); } parent::free(); return $result; } /** * drop an existing table * * @param string $name name of the table that should be dropped * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function dropTable($name) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($name, true); $result = $db->exec("DROP TABLE $name"); if (MDB2::isError($result)) { $result = $db->exec("DROP TABLE $name CASCADE"); } return $result; } } ?> php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/package_pgsql.xml000066400000000000000000000764431213614046500254260ustar00rootroot00000000000000 MDB2_Driver_pgsql pear.php.net pgsql MDB2 driver This is the PostgreSQL MDB2 driver. Lukas Kahwe Smith lsmith smith@pooteeweet.org no Lorenzo Alberton quipo l.alberton@quipo.it yes Nathan Fredrickson nrf nathan@silverorange.com yes Ali Fazelzadeh afz afz@dev-code.com yes 2012-10-23 1.5.0b4 1.5.0b4 beta beta BSD License - Remove assignment by reference, Bug #19585. - PEAR::isError() -> MDB2::isError(), Bug #19491. - PEAR::loadExtension() -> extension_loaded(), Bug #19583. - Fixed boolean type conversion for non-boolean types - Fix Bug #19262. Updates conditional stagements to use logical operators to include MDB2_FETCHMODE_OBJECT where appropriate. Was broken in r321197. - Request #12931 Add cascading to dropTable? - Have truncateTable() return MDB2_OK on success, as documented (bug 19201) - Have vacuum() return MDB2_OK on success, as documented (bug 19196) - Have dropConstraint() return MDB2_OK on success, as documented (bug 19194) - Have createSequence() return MDB2_OK on success, as documented (bug 19192) - Have dropSequence() return MDB2_OK on success, as documented (bug 19191). - Make setOption('result_wrap_class') actually useful by changing the default value of $result_wrap_class parameters from false to true. - Have pgsql only read LOB from file if lob_allow_url_include (bringing it in line with other drivers). - FETCHMODE constants are NOT bitwise. - fixed bug #18203: Type introspection breaks with associative arrays if names are identical (patch by Peter Bex) - fixed bug #17890: Improper use of array_search in prepare function [fletcherj] - fixed bug #17890: Improper use of array_search in prepare function [fletcherj] open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 5.2.0 1.9.1 MDB2 pear.php.net 2.5.0b4 pgsql 1.5.0b4 1.5.0b4 beta beta 2012-10-23 BSD License - Remove assignment by reference, Bug #19585. - PEAR::isError() -> MDB2::isError(), Bug #19491. - PEAR::loadExtension() -> extension_loaded(), Bug #19583. - Fixed boolean type conversion for non-boolean types - Fix Bug #19262. Updates conditional stagements to use logical operators to include MDB2_FETCHMODE_OBJECT where appropriate. Was broken in r321197. - Request #12931 Add cascading to dropTable? - Have truncateTable() return MDB2_OK on success, as documented (bug 19201) - Have vacuum() return MDB2_OK on success, as documented (bug 19196) - Have dropConstraint() return MDB2_OK on success, as documented (bug 19194) - Have createSequence() return MDB2_OK on success, as documented (bug 19192) - Have dropSequence() return MDB2_OK on success, as documented (bug 19191). - Make setOption('result_wrap_class') actually useful by changing the default value of $result_wrap_class parameters from false to true. - Have pgsql only read LOB from file if lob_allow_url_include (bringing it in line with other drivers). - FETCHMODE constants are NOT bitwise. - fixed bug #18203: Type introspection breaks with associative arrays if names are identical (patch by Peter Bex) - fixed bug #17890: Improper use of array_search in prepare function [fletcherj] - fixed bug #17890: Improper use of array_search in prepare function [fletcherj] open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.5.0b3 1.5.0b3 beta beta 2010-08-29 BSD License - fixed bug #16281: getTableFieldDefinition() does not recognize NULL defaults with datatype [Holger Schletz] - fixed bug #16384: alterTable() does not remove NOT NULL constraints [Holger Schletz] - fixed bug #16405: Compatibility issues with escaped strings [hschletz] open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.5.0b2 1.5.0b2 beta beta 2009-01-14 BSD License - fixed bug #12117: disconnect() does not work as documented - fixed bug #13481: getTableConstraintDefinition() may return incomplete results - fixed bug #13877: UNIQUE index not always recognized as constraint - fixed bug #14292: alterTable() cannot change column type when no implicit cast is defined - fixed bug #14510: getTableFieldDefinition() does not unquote defaults - fixed bug #14828: unixtimestamp() generates invalid SQL for non-constant argument - fixed bug #15056: tableInfo does not work with pgsql schemas (added support to table schemas to Manager module) Thanks to Holger Schletz for all the above patches! open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html Extended Query) 1.5.0b1 1.5.0b1 alpha alpha 2008-03-15 BSD License - request #12731: added truncateTable() in the Manager module - request #12732: added vacuum() in the Manager module for OPTIMIZE/VACUUM TABLE abstraction - request #12800: added alterDatabase() in the Manager module - fixed bug #12846: missing escape in getSequenceName() on PostgreSQL 8.2.4 [thanks to Stephane Berthelot] - fixed bug #12920: added new error info and fixed escape method if connection doesn't exist [afz] - fixed bug #12922: use standaloneQuery() in alterDatabase() [afz] - fixed bug #12924: correctly handle internal expected errors even with custom error handling - added standaloneQuery() and databaseExists() - fixed bug #13112: the Reverse module does not know the timestamptz data type - request #13106: added unixtimestamp() in the Function module - fixed bug #13281: list FOREIGN KEY constraints in listTableConstraints() in the Manager module - fixed bug #13356: added float4 to _mapNativeDatatype() - fixed query in getTableConstraintDefinition() for FK constraints in the Reverse module (thanks to Andre' Restivo) open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.5.0a2 1.5.0a2 alpha alpha 2007-12-06 BSD License - fixed bug #12376: getTableConstraintDefinition() in the Reverse module uses incorrect column number in certain cases (thanks to Dennis Birne) - request #12338: added 'disable_smart_seqname' option to make improved getSequenceName() behaviour optional (thanks to Dennis Birne) (@see bug #9106) - request #12012: added collation support in createDatabase() open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.5.0a1 1.5.0a1 alpha alpha 2007-10-28 BSD License - initial support for FOREIGN KEY and CHECK constraints in the Reverse and Manager modules - fixed bug #10986: Using more random statement names (request #11625) - request #11297: added support for "schema.table" (or "owner.table") notation in the Reverse module (related to bug #11207) - fixed bug #11428: propagate quote() errors with invalid data types - fixed bug in MDB2_Statement_pgsql::_execute(), called property of invalid object - fixed bug #11624: getSequenceName() returning incorrect results with multiple schemas and 'SET search_path' - fixed bug #11652: failed prepared queries containing the "::type" style of casting - fixed bug #11694: remove data type cast from DEFAULT value in getTableFieldDefinition() in the Reverse module - fixed bug #11753: NOTICEs in alterTable() in the Manager module - fixed bug #11790: avoid array_diff() because it has a memory leak in PHP 5.1.x - fixed some E_STRICT errors with PHP5 - fixed bug #12083: createTable() in the Manager module now returns MDB2_OK on success, as documented - fixed bug #12269: tableInfo() in the Reverse module detect 'clob' data type as first option open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.4.1 1.4.1 stable stable 2007-05-03 BSD License - return length as "precision,scale" for NUMERIC and DECIMAL fields in mapNativeDatatype() - more accurate field size information in getTableFieldDefinition() in the Reverse module - in getTableIndexDefinition() and getTableConstraintDefinition() in the Reverse module, also return the field position in the index/constraint - Request #9106: more accurate sequence name retrieval routine in getSequenceName() - fixed bug #10895: setLimit() does not work properly when a subquery uses LIMIT open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.4.0 1.4.0 stable stable 2007-03-13 BSD License - propagate errors in getTableFieldDefinition() in the Reverse module - implemented getTriggerDefinition() in the Reverse module [experimental] - implemented listTableTriggers() in the Manager module - don't show catalog views in listViews() in the Manager module - implemented a fallback mechanism within getTableIndexDefinition() and getTableConstraintDefinition() in the Reverse module to ignore the 'idxname_format' option and use the index name as provided in case of failure before returning an error - added a 'nativetype_map_callback' option to map native data declarations back to custom data types (thanks to Andrew Hill). - lastInsertID() without parameters now returns the last used ID (request #9989) - phpdoc fixes open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.3.0 2.3.0 stable stable 2006-11-03 BSD License - fixed _modifyQuery() for manipulation queries - fixed inheritance structure of convertResult() - added support for new 'disable_iso_date' date DSN option (Request #8739) - added support for getting the table name in tableInfo() - added connection to pg_escape_*() calls - fixed _modifyQuery() for manipulation queries - enable detection of proper size of integers in mapNativeDatatype - migrated to package.xml version 2 open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.2.2 1.2.2 stable stable 2006-09-03 BSD License - flip positions property array in prepared statement objects to make it possible to optionally use the same named placeholder in multiple places inside a single prepared statement open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) 1.2.1 1.2.1 stable stable 2006-08-21 BSD License - return an error if a named placeholder name is used twice inside a single statement - do not list empty contraints and indexes - do not set a default if type is a LOB (Request #8074) - fixed handling return values when disable_query is set in _doQuery() and _execute() - increased MDB2 dependency too 2.2.1 open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) 1.2.0 1.2.0 stable stable 2006-07-23 BSD License - performance tweaks for pg_prepare() enabled installations - MDB2_PORTABILITY_RTRIM ignored by driver (Bug #8239) - added ability to escape wildcard characters in escape() and quote() - added setTransactionIsolation() - added savepoint support to beginTransaction(), commit() and rollback() - added debug() call at the end of a query/prepare/execute calling (Request #7933) - added context array parameter to debug() and make use of it whereever sensible - added optional method name parameter to raiseError() and use whereever possible - added ability to escape wildcard characters in escape() and quote() - added debug() call at the end of a query/prepare/execute calling (Request #7933) - added 'nativetype' output to tableInfo() and getTableFieldDefinition() - added 'mdb2type' output to getTableFieldDefinition() - reworked tableInfo() to use a common implementation based on getTableFieldDefinition() when a table name is passed (Bug #8124) - fixed incorrect regex in mapNativeDatatype() (Bug #8256) (thx ioz at ionosfera dot com) - disconnect after changing database/DSN (otherwise transactions may be left open) - use old dsn when rolling back open transactions in disconnect() open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) 1.1.0 1.1.0 stable stable 2006-06-15 BSD License - added setCharset() - use setCharset() in connect()/_doConnect() - generalized quoteIdentifier() with a property - switched most array_key_exists() calls to !empty() to improve readability and performance - fixed a few edge cases and potential warnings - return error when changing datatypes in alterTable() on pgsql version lower than 8.x (Bug #7731) - added ability to rewrite queries for query(), exec() and prepare() using a debug handler callback - fixed missing error handling in getTableFieldDefinition() (Bug #7791) - pass limit and offset to the result object constructor in _execute() for read statements - use pg_prepare() if available so that we do not need to define the types anymore (Request #7797) - added code to use pg_execute() but disabled due to issues with bytea fields - check if result/connection has not yet been freed/dicsonnected before attempting to free a result set(Bug #7790) - revert change that would prefer 'clob' over 'text' for TEXT fields (this was breaking runtime instrospection) open todo items: - enable pg_execute() once issues with bytea column are resolved 1.0.3 1.0.3 stable stable 2006-05-24 BSD License SECURITY FIX (http://www.postgresql.org/docs/techdocs.50) - use pg_escape_string() - use "'" as the escape character - limit fetch to 1 row in listTableFields() 1.0.2 1.0.2 stable stable 2006-05-23 BSD License - added "emulate_prepared" option to force prepared statement emulation - tweaked handling of free() for prepared statements - return error if a prepared statement is attempted to be freed twice - fixed notice when using prepare() without placeholders 1.0.1 1.0.1 stable stable 2006-05-14 BSD License - added listTableViews() (request #6773) - explicitly set is_manip parameter to false for transaction debug calls - pass parameter array as debug() all with scope "parameters" in every execute() call (bug #4119) - typo fixes in phpdoc (thx Stoyan) - added support for fixed and variable types for 'text' in declarations, as well as in reverse engineering (Request #1523) - made _doQuery() return a reference - added userinfo's to all raiseError calls that previously had none - calling PREPARE is always a manip query - added 'prepared_statements' supported meta data setting - strip of file:// prefix in writeLOBToFile() - typo fix ressource/resource in LOB array - added missing supported parameter to prepare() signature - properly handle explicit error codes in errorInfo() - moved from OID to TEXT and BYTEA (OID handling was broken, its also deprecated) 1.0.0 1.0.0 stable stable 2006-02-09 BSD License - handle null as resource when disable_query option is enabled in result object open todo items: - considering migrating away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects, however there is no streaming API for bytea columns .. this needs to be done using manually chunked reads/writes 0.2.4 0.2.4 beta beta 2006-02-05 BSD License - added support for length in integer and decimal columns - removed ugly hack for quote parameter in quote() since it was insufficient (escaping also needs to be prevented) - added support for out of order parameter binding in prepared queries - reset row_limit and row_offset after calling prepare() just like we do for query() and exec() - cosmetic fix (removed "row_" prefix from "row_limit" and "row_offset") - improved parsing in getServerInfo() (bug #6550) open todo items: - considering migrating away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects, however there is no streaming API for bytea columns .. this needs to be done using manually chunked reads/writes 0.2.3 0.2.3 beta beta 2006-01-13 BSD License - explicitly pass if the module is phptype specific in all loadModule calls (bug #6226) - fixed signature of executeStoredProc() - typo fixes in error handling of nextResult() and numRows() calls - _fixIndexName() now just attempts to remove possible formatting - renamed _isSequenceName() to _fixSequenceName() - _fixSequenceName() now just attempts to remove possible formatting, and only returns a boolean if no formatting was applied when the new "check" parameter is set to true - fixed bug that caused unique indexes from not being seen (bug #6476) open todo items: - considering migrating away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects, however there is no streaming API for bytea columns .. this needs to be done using manually chunked reads/writes 0.2.2 0.2.2 beta beta 2005-12-30 BSD License - types can now always be keyed by name or by order open todo items: - considering migrating away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects, however there is no streaming API for bytea columns .. this needs to be done using manually chunked reads/writes 0.2.1 0.2.1 beta beta 2005-12-28 BSD License - fixed datatype conversion for "time" values - fixed getTableFieldDefinition() default handling (though still a bit shaky) - reverse module: ensure that parameters in queries are properly quoted - fixed bug in alterTable() where "rename" would be skipped if no other alteration is done - do not use multiple commands in ALTER TABLE (only supported since 8.0) - implemented native prepared queries - use proper error code in alterTable() - renamed table as the last step in alterTable() - proper quote new table name in alterTable() open todo items: - considering migrating away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects, however there is no streaming API for bytea columns .. this needs to be done using manually chunked reads/writes 0.2.0 0.2.0 beta beta 2005-12-21 BSD License - do not fix case in listUsers() - unified case fixing in the list*() methods - tweaked text handling in mapNativeDatatype() - use getConnection() to access connection property - split index and constraint handling - quote identifiers where possible inside the manager methods depending on the new 'quote_identifier' option (defaults to off) - refactored get*Declaration() methods to use getTypeDeclaration() - added support for table and column renaming as well as default and nullability changing in alterTable() - setting in_transaction to false on disconnect - added new Function modules to handle difference in SQL functions - force rollback() with open transactions on disconnect - escape floats to make sure they do not contain evil characters (bug #5608) - split off manipulation queries into exec() method from the query() method *BC BREAK* - if result_types is set to false in prepare() method the query will be handled as a DML statement *BC BREAK* - use a proper default value if a field is set to not null in _getDeclaration*() (bug #5930) - added ability to determine unsigned in mapNativeDatatype() (only really implemented in the mysql(i) drivers) (bug #6054) - use MDB2_ERROR_NOT_FOUND in getTableConstraintDefinition() and getTableIndexDefinition() (bug #6055) - added getServerVersion() - unified array structure in mapNativeDatatype() *BC BREAK* - added 'mdbtype' to tableInfo() output that is generated from mapNativeDatatype() - changed 'len' to 'length' in tableInfo() output *BC BREAK* open todo items: - implement native prepared queries - migrate away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects - testgettablefielddefinition and testnow test cases fail 0.1.1 0.1.1 beta beta 2005-10-10 BSD License Warning: this release features numerous BC breaks! There have been considerable improvements to the datatype, manager and reverse modules. Furthermore preliminary support for auto increment and primary keys has been added. Please note that making a field auto increment implies a single column primary key on this field. - increased php dependency to 4.3.0 due to the usage of the streams API since beta5 - fixed typo in _getTextDeclaration() - alterTable now needs the full definition to work (use getTableFieldDefinition from Reverse module if you do not have a definition at hand) this eliminates the need of the declaration part in the alterTable array. - fix PHP4.4 breakage - improved error mapping (ported from DB) - better detect if the query returned a result set or not in _doQuery() - minor tweaks to LOB handling - use !empty() instead of isset() in fetchRow to determine if result cols were bound or result types were set - moved all private fetch mode fix methods into _fixResultArrayValues() for performance reasons - renamed MDB2_PORTABILITY_LOWERCASE to MDB2_PORTABILITY_FIX_CASE and use 'field_case' option to determine if to upper- or lowercase (CASE_LOWER/CASE_UPPER) - use array_key_exists() instead of isset() where possible - changed structure of field add/remove/change in alterTable() to match MDB2_Schema - added createIndex() - return 0 for manipulation queries when disable_query is enabled - tweaked getTableFieldDefinition() in reverse module - added getTypeDeclaration() in the datatype module - tweaked field changing in alterTable() - fixed alterTable() signature open todo items: - implement native prepared queries php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/tests/000077500000000000000000000000001213614046500232275ustar00rootroot00000000000000php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/tests/Nonstandard/000077500000000000000000000000001213614046500255025ustar00rootroot00000000000000php-mdb2-driver-pgsql-1.5.0b4/MDB2_Driver_pgsql-1.5.0b4/tests/Nonstandard/PgsqlHelper.php000066400000000000000000000116061213614046500304450ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: PgsqlHelper.php 322038 2012-01-10 21:54:04Z danielc $ class Nonstandard_PgsqlHelper extends Nonstandard_Base { public $trigger_body = ''; public function createTrigger($trigger_name, $table_name) { // Ensure plpgsql is loaded. $res = $this->db->exec('CREATE LANGUAGE plpgsql'); $this->trigger_body = 'EXECUTE PROCEDURE '.$trigger_name.'_func();'; $table_name = $this->db->quoteIdentifier($table_name); $sql = 'CREATE OR REPLACE FUNCTION '.$trigger_name.'_func() RETURNS trigger AS \' DECLARE id_number INTEGER; BEGIN SELECT INTO id_number id FROM '. $table_name .' WHERE id = NEW.id; RETURN NEW; END; \' LANGUAGE \'plpgsql\';'; $res = $this->db->exec($sql); if (MDB2::isError($res)) { return $res; } $query = 'CREATE TRIGGER '. $trigger_name .' AFTER UPDATE ON '. $table_name .' FOR EACH ROW ' .$this->trigger_body; return $this->db->exec($query); } public function checkTrigger($trigger_name, $table_name, $def) { parent::checkTrigger($trigger_name, $table_name, $def); $this->test->assertEquals($this->trigger_body, $def['trigger_body']); } public function dropTrigger($trigger_name, $table_name) { return $this->db->exec('DROP TRIGGER '.$trigger_name .' ON '. $table_name); } public function createFunction($name) { $query = "CREATE FUNCTION $name (Decimal(6,2), Decimal(6,2)) RETURNS Decimal(6,2) AS 'select $1 + $2;' LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT"; return $this->db->exec($query); } public function dropFunction($name) { return $this->db->exec('DROP FUNCTION '.$name.' (Decimal(6,2), Decimal(6,2))'); } } php-mdb2-driver-pgsql-1.5.0b4/package.xml000066400000000000000000000764351213614046500201270ustar00rootroot00000000000000 MDB2_Driver_pgsql pear.php.net pgsql MDB2 driver This is the PostgreSQL MDB2 driver. Lukas Kahwe Smith lsmith smith@pooteeweet.org no Lorenzo Alberton quipo l.alberton@quipo.it yes Nathan Fredrickson nrf nathan@silverorange.com yes Ali Fazelzadeh afz afz@dev-code.com yes 2012-10-23 1.5.0b4 1.5.0b4 beta beta BSD License - Remove assignment by reference, Bug #19585. - PEAR::isError() -> MDB2::isError(), Bug #19491. - PEAR::loadExtension() -> extension_loaded(), Bug #19583. - Fixed boolean type conversion for non-boolean types - Fix Bug #19262. Updates conditional stagements to use logical operators to include MDB2_FETCHMODE_OBJECT where appropriate. Was broken in r321197. - Request #12931 Add cascading to dropTable? - Have truncateTable() return MDB2_OK on success, as documented (bug 19201) - Have vacuum() return MDB2_OK on success, as documented (bug 19196) - Have dropConstraint() return MDB2_OK on success, as documented (bug 19194) - Have createSequence() return MDB2_OK on success, as documented (bug 19192) - Have dropSequence() return MDB2_OK on success, as documented (bug 19191). - Make setOption('result_wrap_class') actually useful by changing the default value of $result_wrap_class parameters from false to true. - Have pgsql only read LOB from file if lob_allow_url_include (bringing it in line with other drivers). - FETCHMODE constants are NOT bitwise. - fixed bug #18203: Type introspection breaks with associative arrays if names are identical (patch by Peter Bex) - fixed bug #17890: Improper use of array_search in prepare function [fletcherj] - fixed bug #17890: Improper use of array_search in prepare function [fletcherj] open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 5.2.0 1.9.1 MDB2 pear.php.net 2.5.0b4 pgsql 1.5.0b4 1.5.0b4 beta beta 2012-10-23 BSD License - Remove assignment by reference, Bug #19585. - PEAR::isError() -> MDB2::isError(), Bug #19491. - PEAR::loadExtension() -> extension_loaded(), Bug #19583. - Fixed boolean type conversion for non-boolean types - Fix Bug #19262. Updates conditional stagements to use logical operators to include MDB2_FETCHMODE_OBJECT where appropriate. Was broken in r321197. - Request #12931 Add cascading to dropTable? - Have truncateTable() return MDB2_OK on success, as documented (bug 19201) - Have vacuum() return MDB2_OK on success, as documented (bug 19196) - Have dropConstraint() return MDB2_OK on success, as documented (bug 19194) - Have createSequence() return MDB2_OK on success, as documented (bug 19192) - Have dropSequence() return MDB2_OK on success, as documented (bug 19191). - Make setOption('result_wrap_class') actually useful by changing the default value of $result_wrap_class parameters from false to true. - Have pgsql only read LOB from file if lob_allow_url_include (bringing it in line with other drivers). - FETCHMODE constants are NOT bitwise. - fixed bug #18203: Type introspection breaks with associative arrays if names are identical (patch by Peter Bex) - fixed bug #17890: Improper use of array_search in prepare function [fletcherj] - fixed bug #17890: Improper use of array_search in prepare function [fletcherj] open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.5.0b3 1.5.0b3 beta beta 2010-08-29 BSD License - fixed bug #16281: getTableFieldDefinition() does not recognize NULL defaults with datatype [Holger Schletz] - fixed bug #16384: alterTable() does not remove NOT NULL constraints [Holger Schletz] - fixed bug #16405: Compatibility issues with escaped strings [hschletz] open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.5.0b2 1.5.0b2 beta beta 2009-01-14 BSD License - fixed bug #12117: disconnect() does not work as documented - fixed bug #13481: getTableConstraintDefinition() may return incomplete results - fixed bug #13877: UNIQUE index not always recognized as constraint - fixed bug #14292: alterTable() cannot change column type when no implicit cast is defined - fixed bug #14510: getTableFieldDefinition() does not unquote defaults - fixed bug #14828: unixtimestamp() generates invalid SQL for non-constant argument - fixed bug #15056: tableInfo does not work with pgsql schemas (added support to table schemas to Manager module) Thanks to Holger Schletz for all the above patches! open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html Extended Query) 1.5.0b1 1.5.0b1 alpha alpha 2008-03-15 BSD License - request #12731: added truncateTable() in the Manager module - request #12732: added vacuum() in the Manager module for OPTIMIZE/VACUUM TABLE abstraction - request #12800: added alterDatabase() in the Manager module - fixed bug #12846: missing escape in getSequenceName() on PostgreSQL 8.2.4 [thanks to Stephane Berthelot] - fixed bug #12920: added new error info and fixed escape method if connection doesn't exist [afz] - fixed bug #12922: use standaloneQuery() in alterDatabase() [afz] - fixed bug #12924: correctly handle internal expected errors even with custom error handling - added standaloneQuery() and databaseExists() - fixed bug #13112: the Reverse module does not know the timestamptz data type - request #13106: added unixtimestamp() in the Function module - fixed bug #13281: list FOREIGN KEY constraints in listTableConstraints() in the Manager module - fixed bug #13356: added float4 to _mapNativeDatatype() - fixed query in getTableConstraintDefinition() for FK constraints in the Reverse module (thanks to Andre' Restivo) open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.5.0a2 1.5.0a2 alpha alpha 2007-12-06 BSD License - fixed bug #12376: getTableConstraintDefinition() in the Reverse module uses incorrect column number in certain cases (thanks to Dennis Birne) - request #12338: added 'disable_smart_seqname' option to make improved getSequenceName() behaviour optional (thanks to Dennis Birne) (@see bug #9106) - request #12012: added collation support in createDatabase() open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.5.0a1 1.5.0a1 alpha alpha 2007-10-28 BSD License - initial support for FOREIGN KEY and CHECK constraints in the Reverse and Manager modules - fixed bug #10986: Using more random statement names (request #11625) - request #11297: added support for "schema.table" (or "owner.table") notation in the Reverse module (related to bug #11207) - fixed bug #11428: propagate quote() errors with invalid data types - fixed bug in MDB2_Statement_pgsql::_execute(), called property of invalid object - fixed bug #11624: getSequenceName() returning incorrect results with multiple schemas and 'SET search_path' - fixed bug #11652: failed prepared queries containing the "::type" style of casting - fixed bug #11694: remove data type cast from DEFAULT value in getTableFieldDefinition() in the Reverse module - fixed bug #11753: NOTICEs in alterTable() in the Manager module - fixed bug #11790: avoid array_diff() because it has a memory leak in PHP 5.1.x - fixed some E_STRICT errors with PHP5 - fixed bug #12083: createTable() in the Manager module now returns MDB2_OK on success, as documented - fixed bug #12269: tableInfo() in the Reverse module detect 'clob' data type as first option open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.4.1 1.4.1 stable stable 2007-05-03 BSD License - return length as "precision,scale" for NUMERIC and DECIMAL fields in mapNativeDatatype() - more accurate field size information in getTableFieldDefinition() in the Reverse module - in getTableIndexDefinition() and getTableConstraintDefinition() in the Reverse module, also return the field position in the index/constraint - Request #9106: more accurate sequence name retrieval routine in getSequenceName() - fixed bug #10895: setLimit() does not work properly when a subquery uses LIMIT open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.4.0 1.4.0 stable stable 2007-03-13 BSD License - propagate errors in getTableFieldDefinition() in the Reverse module - implemented getTriggerDefinition() in the Reverse module [experimental] - implemented listTableTriggers() in the Manager module - don't show catalog views in listViews() in the Manager module - implemented a fallback mechanism within getTableIndexDefinition() and getTableConstraintDefinition() in the Reverse module to ignore the 'idxname_format' option and use the index name as provided in case of failure before returning an error - added a 'nativetype_map_callback' option to map native data declarations back to custom data types (thanks to Andrew Hill). - lastInsertID() without parameters now returns the last used ID (request #9989) - phpdoc fixes open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.3.0 2.3.0 stable stable 2006-11-03 BSD License - fixed _modifyQuery() for manipulation queries - fixed inheritance structure of convertResult() - added support for new 'disable_iso_date' date DSN option (Request #8739) - added support for getting the table name in tableInfo() - added connection to pg_escape_*() calls - fixed _modifyQuery() for manipulation queries - enable detection of proper size of integers in mapNativeDatatype - migrated to package.xml version 2 open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) - add option to use unnamed prepared statements (see http://www.postgresql.org/docs/current/static/protocol-flow.html "Extended Query") 1.2.2 1.2.2 stable stable 2006-09-03 BSD License - flip positions property array in prepared statement objects to make it possible to optionally use the same named placeholder in multiple places inside a single prepared statement open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) 1.2.1 1.2.1 stable stable 2006-08-21 BSD License - return an error if a named placeholder name is used twice inside a single statement - do not list empty contraints and indexes - do not set a default if type is a LOB (Request #8074) - fixed handling return values when disable_query is set in _doQuery() and _execute() - increased MDB2 dependency too 2.2.1 open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) 1.2.0 1.2.0 stable stable 2006-07-23 BSD License - performance tweaks for pg_prepare() enabled installations - MDB2_PORTABILITY_RTRIM ignored by driver (Bug #8239) - added ability to escape wildcard characters in escape() and quote() - added setTransactionIsolation() - added savepoint support to beginTransaction(), commit() and rollback() - added debug() call at the end of a query/prepare/execute calling (Request #7933) - added context array parameter to debug() and make use of it whereever sensible - added optional method name parameter to raiseError() and use whereever possible - added ability to escape wildcard characters in escape() and quote() - added debug() call at the end of a query/prepare/execute calling (Request #7933) - added 'nativetype' output to tableInfo() and getTableFieldDefinition() - added 'mdb2type' output to getTableFieldDefinition() - reworked tableInfo() to use a common implementation based on getTableFieldDefinition() when a table name is passed (Bug #8124) - fixed incorrect regex in mapNativeDatatype() (Bug #8256) (thx ioz at ionosfera dot com) - disconnect after changing database/DSN (otherwise transactions may be left open) - use old dsn when rolling back open transactions in disconnect() open todo items: - enable pg_execute() once issues with bytea column are resolved - use pg_result_error_field() to handle localized error messages (Request #7059) 1.1.0 1.1.0 stable stable 2006-06-15 BSD License - added setCharset() - use setCharset() in connect()/_doConnect() - generalized quoteIdentifier() with a property - switched most array_key_exists() calls to !empty() to improve readability and performance - fixed a few edge cases and potential warnings - return error when changing datatypes in alterTable() on pgsql version lower than 8.x (Bug #7731) - added ability to rewrite queries for query(), exec() and prepare() using a debug handler callback - fixed missing error handling in getTableFieldDefinition() (Bug #7791) - pass limit and offset to the result object constructor in _execute() for read statements - use pg_prepare() if available so that we do not need to define the types anymore (Request #7797) - added code to use pg_execute() but disabled due to issues with bytea fields - check if result/connection has not yet been freed/dicsonnected before attempting to free a result set(Bug #7790) - revert change that would prefer 'clob' over 'text' for TEXT fields (this was breaking runtime instrospection) open todo items: - enable pg_execute() once issues with bytea column are resolved 1.0.3 1.0.3 stable stable 2006-05-24 BSD License SECURITY FIX (http://www.postgresql.org/docs/techdocs.50) - use pg_escape_string() - use "'" as the escape character - limit fetch to 1 row in listTableFields() 1.0.2 1.0.2 stable stable 2006-05-23 BSD License - added "emulate_prepared" option to force prepared statement emulation - tweaked handling of free() for prepared statements - return error if a prepared statement is attempted to be freed twice - fixed notice when using prepare() without placeholders 1.0.1 1.0.1 stable stable 2006-05-14 BSD License - added listTableViews() (request #6773) - explicitly set is_manip parameter to false for transaction debug calls - pass parameter array as debug() all with scope "parameters" in every execute() call (bug #4119) - typo fixes in phpdoc (thx Stoyan) - added support for fixed and variable types for 'text' in declarations, as well as in reverse engineering (Request #1523) - made _doQuery() return a reference - added userinfo's to all raiseError calls that previously had none - calling PREPARE is always a manip query - added 'prepared_statements' supported meta data setting - strip of file:// prefix in writeLOBToFile() - typo fix ressource/resource in LOB array - added missing supported parameter to prepare() signature - properly handle explicit error codes in errorInfo() - moved from OID to TEXT and BYTEA (OID handling was broken, its also deprecated) 1.0.0 1.0.0 stable stable 2006-02-09 BSD License - handle null as resource when disable_query option is enabled in result object open todo items: - considering migrating away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects, however there is no streaming API for bytea columns .. this needs to be done using manually chunked reads/writes 0.2.4 0.2.4 beta beta 2006-02-05 BSD License - added support for length in integer and decimal columns - removed ugly hack for quote parameter in quote() since it was insufficient (escaping also needs to be prevented) - added support for out of order parameter binding in prepared queries - reset row_limit and row_offset after calling prepare() just like we do for query() and exec() - cosmetic fix (removed "row_" prefix from "row_limit" and "row_offset") - improved parsing in getServerInfo() (bug #6550) open todo items: - considering migrating away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects, however there is no streaming API for bytea columns .. this needs to be done using manually chunked reads/writes 0.2.3 0.2.3 beta beta 2006-01-13 BSD License - explicitly pass if the module is phptype specific in all loadModule calls (bug #6226) - fixed signature of executeStoredProc() - typo fixes in error handling of nextResult() and numRows() calls - _fixIndexName() now just attempts to remove possible formatting - renamed _isSequenceName() to _fixSequenceName() - _fixSequenceName() now just attempts to remove possible formatting, and only returns a boolean if no formatting was applied when the new "check" parameter is set to true - fixed bug that caused unique indexes from not being seen (bug #6476) open todo items: - considering migrating away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects, however there is no streaming API for bytea columns .. this needs to be done using manually chunked reads/writes 0.2.2 0.2.2 beta beta 2005-12-30 BSD License - types can now always be keyed by name or by order open todo items: - considering migrating away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects, however there is no streaming API for bytea columns .. this needs to be done using manually chunked reads/writes 0.2.1 0.2.1 beta beta 2005-12-28 BSD License - fixed datatype conversion for "time" values - fixed getTableFieldDefinition() default handling (though still a bit shaky) - reverse module: ensure that parameters in queries are properly quoted - fixed bug in alterTable() where "rename" would be skipped if no other alteration is done - do not use multiple commands in ALTER TABLE (only supported since 8.0) - implemented native prepared queries - use proper error code in alterTable() - renamed table as the last step in alterTable() - proper quote new table name in alterTable() open todo items: - considering migrating away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects, however there is no streaming API for bytea columns .. this needs to be done using manually chunked reads/writes 0.2.0 0.2.0 beta beta 2005-12-21 BSD License - do not fix case in listUsers() - unified case fixing in the list*() methods - tweaked text handling in mapNativeDatatype() - use getConnection() to access connection property - split index and constraint handling - quote identifiers where possible inside the manager methods depending on the new 'quote_identifier' option (defaults to off) - refactored get*Declaration() methods to use getTypeDeclaration() - added support for table and column renaming as well as default and nullability changing in alterTable() - setting in_transaction to false on disconnect - added new Function modules to handle difference in SQL functions - force rollback() with open transactions on disconnect - escape floats to make sure they do not contain evil characters (bug #5608) - split off manipulation queries into exec() method from the query() method *BC BREAK* - if result_types is set to false in prepare() method the query will be handled as a DML statement *BC BREAK* - use a proper default value if a field is set to not null in _getDeclaration*() (bug #5930) - added ability to determine unsigned in mapNativeDatatype() (only really implemented in the mysql(i) drivers) (bug #6054) - use MDB2_ERROR_NOT_FOUND in getTableConstraintDefinition() and getTableIndexDefinition() (bug #6055) - added getServerVersion() - unified array structure in mapNativeDatatype() *BC BREAK* - added 'mdbtype' to tableInfo() output that is generated from mapNativeDatatype() - changed 'len' to 'length' in tableInfo() output *BC BREAK* open todo items: - implement native prepared queries - migrate away from OID's to bytea, since this is encourage since version 8 and is also what PDO expects - testgettablefielddefinition and testnow test cases fail 0.1.1 0.1.1 beta beta 2005-10-10 BSD License Warning: this release features numerous BC breaks! There have been considerable improvements to the datatype, manager and reverse modules. Furthermore preliminary support for auto increment and primary keys has been added. Please note that making a field auto increment implies a single column primary key on this field. - increased php dependency to 4.3.0 due to the usage of the streams API since beta5 - fixed typo in _getTextDeclaration() - alterTable now needs the full definition to work (use getTableFieldDefinition from Reverse module if you do not have a definition at hand) this eliminates the need of the declaration part in the alterTable array. - fix PHP4.4 breakage - improved error mapping (ported from DB) - better detect if the query returned a result set or not in _doQuery() - minor tweaks to LOB handling - use !empty() instead of isset() in fetchRow to determine if result cols were bound or result types were set - moved all private fetch mode fix methods into _fixResultArrayValues() for performance reasons - renamed MDB2_PORTABILITY_LOWERCASE to MDB2_PORTABILITY_FIX_CASE and use 'field_case' option to determine if to upper- or lowercase (CASE_LOWER/CASE_UPPER) - use array_key_exists() instead of isset() where possible - changed structure of field add/remove/change in alterTable() to match MDB2_Schema - added createIndex() - return 0 for manipulation queries when disable_query is enabled - tweaked getTableFieldDefinition() in reverse module - added getTypeDeclaration() in the datatype module - tweaked field changing in alterTable() - fixed alterTable() signature open todo items: - implement native prepared queries