From d18688e4f0fd9adc744e8c9efa98aa8bfd1d21fd Mon Sep 17 00:00:00 2001 From: zYne Date: Fri, 20 Oct 2006 21:11:50 +0000 Subject: [PATCH] Added pgsql export driver and updated pgsql datadict driver --- lib/Doctrine/DataDict/Pgsql.php | 201 ++++++++++++++++++++++++++++- lib/Doctrine/Export/Pgsql.php | 219 ++++++++++++++++++++++++++++++++ 2 files changed, 419 insertions(+), 1 deletion(-) create mode 100644 lib/Doctrine/Export/Pgsql.php diff --git a/lib/Doctrine/DataDict/Pgsql.php b/lib/Doctrine/DataDict/Pgsql.php index ebc550080..b786439c1 100644 --- a/lib/Doctrine/DataDict/Pgsql.php +++ b/lib/Doctrine/DataDict/Pgsql.php @@ -24,10 +24,209 @@ * @url http://www.phpdoctrine.com * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @author Konsta Vesterinen + * @author Paul Cooper + * @author Lukas Smith (PEAR MDB2 library) * @version $Id$ */ - + class Doctrine_DataDict_Mysql extends Doctrine_DataDict { + /** + * 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 + */ + public function getTypeDeclaration(array $field) { + switch ($field['type']) { + case 'string': + case 'array': + case 'object': + $length = !empty($field['length']) + ? $field['length'] : $db->options['default_text_field_length']; + + $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; + return 'NUMERIC('.$length.','.$db->options['decimal_places'].')'; + default: + throw new Doctrine_DataDict_Pgsql_Exception('Unknown field type '. $field['type']); + } + } + /** + * 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 = preg_replace('/\d/','', strtolower($field['type']) ); + $length = $field['length']; + if ($length == '-1' && !empty($field['atttypmod'])) { + $length = $field['atttypmod'] - 4; + } + if ((int)$length <= 0) { + $length = null; + } + $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'; + } + if ($fixed !== false) { + $fixed = true; + } + break; + case 'date': + $type[] = 'date'; + $length = null; + break; + case 'datetime': + case 'timestamp': + $type[] = 'timestamp'; + $length = null; + break; + case 'time': + $type[] = 'time'; + $length = null; + break; + case 'float': + case 'double': + case 'real': + $type[] = 'float'; + break; + case 'decimal': + case 'money': + case 'numeric': + $type[] = 'decimal'; + 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: + throw new Doctrine_DataDict_Pgsql_Exception('unknown database attribute type: '.$db_type); + } + + return array($type, $length, $unsigned, $fixed); + } /** * lists all databases * diff --git a/lib/Doctrine/Export/Pgsql.php b/lib/Doctrine/Export/Pgsql.php new file mode 100644 index 000000000..2274a12d9 --- /dev/null +++ b/lib/Doctrine/Export/Pgsql.php @@ -0,0 +1,219 @@ +. + */ +Doctrine::autoload('Doctrine_Export'); +/** + * Doctrine_Export_Pgsql + * + * @package Doctrine + * @author Konsta Vesterinen + * @author Lukas Smith (PEAR MDB2 library) + * @license LGPL + */ +class Doctrine_Export_Pgsql extends Doctrine_Export { + /** + * create a new database + * + * @param string $name name of the database that should be created + * @throws PDOException + * @return void + */ + public function createDatabase($name) { + $query = 'CREATE DATABASE ' . $this->conn->quoteIdentifier($name); + $result = $this->dbh->query($query); + } + /** + * drop an existing database + * + * @param string $name name of the database that should be dropped + * @throws PDOException + * @access public + */ + public function dropDatabase($name) { + $query = 'DROP DATABASE ' . $this->conn->quoteIdentifier($name); + $this->dbh->query($query); + } + /** + * 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 Metabase 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 Metabase 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. + * @throws PDOException + * @return boolean + */ + function alterTable($name, $changes, $check) + { + $db =& $this->getDBInstance(); + if (PEAR::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: + throw new Doctrine_Export_Pgsql_Exception('change type "'.$change_name.'\" not yet supported'); + } + } + + if ($check) { + return true; + } + + if (!empty($changes['add']) && is_array($changes['add'])) { + foreach ($changes['add'] as $field_name => $field) { + $query = 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field); + $this->dbh->query("ALTER TABLE $name $query"); + } + } + + if (!empty($changes['remove']) && is_array($changes['remove'])) { + foreach ($changes['remove'] as $field_name => $field) { + $field_name = $this->conn->quoteIdentifier($field_name, true); + $query = 'DROP ' . $field_name; + $this->dbh->query("ALTER TABLE $name $query"); + } + } + + if (!empty($changes['change']) && is_array($changes['change'])) { + foreach ($changes['change'] as $field_name => $field) { + $field_name = $this->conn->quoteIdentifier($field_name, true); + if (!empty($field['type'])) { + $server_info = $db->getServerVersion(); + + if (is_array($server_info) && $server_info['major'] < 8) + throw new Doctrine_Export_Pgsql_Exception('changing column type for "'.$change_name.'\" requires PostgreSQL 8.0 or above'); + + $query = "ALTER $field_name TYPE ".$db->datatype->getTypeDeclaration($field['definition']); + $this->dbh->query("ALTER TABLE $name $query"); + } + if (array_key_exists('default', $field)) { + $query = "ALTER $field_name SET DEFAULT ".$db->quote($field['definition']['default'], $field['definition']['type']); + $this->dbh->query("ALTER TABLE $name $query"); + } + if (!empty($field['notnull'])) { + $query = "ALTER $field_name ".($field['definition']['notnull'] ? "SET" : "DROP").' NOT NULL'; + $this->dbh->query("ALTER TABLE $name $query"); + + } + } + } + + if (!empty($changes['rename']) && is_array($changes['rename'])) { + foreach ($changes['rename'] as $field_name => $field) { + $field_name = $this->conn->quoteIdentifier($field_name, true); + $this->dbh->query("ALTER TABLE $name RENAME COLUMN $field_name TO ".$this->conn->quoteIdentifier($field['name'], true)); + } + } + + $name = $this->conn->quoteIdentifier($name, true); + if (!empty($changes['name'])) { + $change_name = $this->conn->quoteIdentifier($changes['name'], true); + $this->dbh->query("ALTER TABLE $name RENAME TO ".$change_name); + } + } +} +?>