. */ namespace Doctrine\DBAL\Platforms; /** * PostgreSqlPlatform. * * @since 2.0 * @author Roman Borschel * @author Lukas Smith (PEAR MDB2 library) */ class PostgreSqlPlatform extends AbstractPlatform { /** * Constructor. * Creates a new PostgreSqlPlatform. */ public function __construct() { parent::__construct(); } /** * 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. * @override */ public function getNativeDeclaration(array $field) { if ( ! isset($field['type'])) { throw DoctrineException::updateMe('Missing column type.'); } switch ($field['type']) { case 'char': case 'string': case 'array': case 'object': case 'varchar': case 'gzip': // TODO: what is the maximum VARCHAR length in pgsql ? $length = (isset($field['length']) && $field['length'] && $field['length'] < 10000) ? $field['length'] : null; $fixed = ((isset($field['fixed']) && $field['fixed']) || $field['type'] == 'char') ? true : false; return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR('.$this->conn->options['default_text_field_length'].')') : ($length ? 'VARCHAR(' .$length . ')' : 'TEXT'); case 'clob': return 'TEXT'; case 'blob': return 'BYTEA'; case 'enum': case 'integer': case 'int': if ( ! empty($field['autoincrement'])) { if ( ! empty($field['length'])) { $length = $field['length']; if ($length > 4) { return 'BIGSERIAL'; } } return 'SERIAL'; } 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': case 'double': return 'FLOAT'; case 'decimal': $length = !empty($field['length']) ? $field['length'] : 18; $scale = !empty($field['scale']) ? $field['scale'] : $this->conn->getAttribute(Doctrine::ATTR_DECIMAL_PLACES); return 'NUMERIC('.$length.','.$scale.')'; } throw DoctrineException::updateMe('Unknown field type \'' . $field['type'] . '\'.'); } /** * Maps a native array description of a field to a portable Doctrine datatype and length * * @param array $field native field description * * @return array containing the various possible types, length, sign, fixed * @override */ public function getPortableDeclaration(array $field) { $length = (isset($field['length'])) ? $field['length'] : null; if ($length == '-1' && isset($field['atttypmod'])) { $length = $field['atttypmod'] - 4; } if ((int)$length <= 0) { $length = null; } $type = array(); $unsigned = $fixed = null; if ( ! isset($field['name'])) { $field['name'] = ''; } $dbType = strtolower($field['type']); switch ($dbType) { 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 = 1; break; case 'text': case 'varchar': case 'interval': case '_varchar': $fixed = false; case 'unknown': case 'char': case 'bpchar': $type[] = 'string'; if ($length == '1') { $type[] = 'boolean'; if (preg_match('/^(is|has)/', $field['name'])) { $type = array_reverse($type); } } elseif (strstr($dbType, 'text')) { $type[] = 'clob'; } 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 'double precision': 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 DoctrineException::updateMe('unknown database attribute type: '.$dbType); } return array('type' => $type, 'length' => $length, 'unsigned' => $unsigned, 'fixed' => $fixed); } /** * Returns the md5 sum of a field. * * Note: Not SQL92, but common functionality * * md5() works with the default PostgreSQL 8 versions. * * If you are using PostgreSQL 7.x or older you need * to make sure that the digest procedure is installed. * If you use RPMS (Redhat and Mandrake) install the postgresql-contrib * package. You must then install the procedure by running this shell command: * * psql [dbname] < /usr/share/pgsql/contrib/pgcrypto.sql * * You should make sure you run this as the postgres user. * * @return string * @override */ public function getMd5Expression($column) { $column = $this->quoteIdentifier($column); if ($this->_version > 7) { return 'MD5(' . $column . ')'; } else { return 'encode(digest(' . $column .', md5), hex)'; } } /** * Returns part of a string. * * Note: Not SQL92, but common functionality. * * @param string $value the target $value the string or the string column. * @param int $from extract from this characeter. * @param int $len extract this amount of characters. * @return string sql that extracts part of a string. * @override */ public function getSubstringExpression($value, $from, $len = null) { $value = $this->quoteIdentifier($value); if ($len === null) { $len = $this->quoteIdentifier($len); return 'SUBSTR(' . $value . ', ' . $from . ')'; } else { return 'SUBSTR(' . $value . ', ' . $from . ', ' . $len . ')'; } } /** * PostgreSQLs AGE( [, ]) function. * * @param string $timestamp1 timestamp to subtract from NOW() * @param string $timestamp2 optional; if given: subtract arguments * @return string */ public function getAgeExpression($timestamp1, $timestamp2 = null) { if ( $timestamp2 == null ) { return 'AGE(' . $timestamp1 . ')'; } return 'AGE(' . $timestamp1 . ', ' . $timestamp2 . ')'; } /** * PostgreSQLs DATE_PART( ,