blob: ea3203530882811aff4419642081107099395764 [file] [log] [blame]
<?php
/**
* File containing the ezcDbSchemaPgsqlReader class.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @package DatabaseSchema
* @version //autogentag//
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
*/
/**
* Handler for PostgreSQL connections representing a DB schema.
*
* @package DatabaseSchema
* @version //autogentag//
*/
class ezcDbSchemaPgsqlReader extends ezcDbSchemaCommonSqlReader implements ezcDbSchemaDbReader
{
/**
* Contains a type map from PostgreSQL native types to generic DbSchema types.
*
* @var array
*/
static private $typeMap = array(
'int' => 'integer',
'int2' => 'integer',
'int4' => 'integer',
'int8' => 'integer',
'integer' => 'integer',
'bool' => 'boolean',
'boolean' => 'boolean',
'float' => 'float',
'double' => 'float',
'dec' => 'decimal',
'decimal' => 'decimal',
'numeric' => 'decimal',
'fixed' => 'decimal',
'date' => 'date',
'datetime' => 'timestamp',
'timestamp' => 'timestamp',
'timestamp without time zone' => 'timestamp',
'time' => 'time',
'year' => 'integer',
'char' => 'text',
'varchar' => 'text',
'character' => 'text',
'binary' => 'blob',
'varbinary' => 'blob',
'tinyblob' => 'blob',
'blob' => 'blob',
'mediumblob' => 'blob',
'bytea' => 'blob',
'tinytext' => 'clob',
'text' => 'clob',
'mediumtext' => 'clob',
'longtext' => 'clob',
'character varying'=>'text',
'bigint' => 'integer',
'double precision' => 'float'
);
/**
* Loops over all the tables in the database and extracts schema information.
*
* This method extracts information about a database's schema from the
* database itself and returns this schema as an ezcDbSchema object.
*
* @return ezcDbSchema
*/
protected function fetchSchema()
{
$tables = $this->db->query( "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'" )->fetchAll();
return $this->processSchema( $tables );
}
/**
* Fetch fields definition for the table $tableName
*
* This method loops over all the fields in the table $tableName and
* returns an array with the field specification. The key in the returned
* array is the name of the field.
*
* @param string $tableName
* @return array(string=>ezcDbSchemaField)
*/
protected function fetchTableFields( $tableName )
{
$fields = array();
// fetching fields info from PostgreSQL
$resultArray = $this->db->query(
"SELECT a.attnum, a.attname AS field, t.typname AS type,
format_type(a.atttypid, a.atttypmod) AS fulltype,
( SELECT substring(d.adsrc for 128) FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef
) AS default,
a.attlen AS length, a.atttypmod AS lengthvar, a.attnotnull AS notnull
FROM pg_class c, pg_attribute a, pg_type t
WHERE c.relname = '$tableName' AND
a.attnum > 0 AND
a.attrelid = c.oid AND
a.atttypid = t.oid
ORDER BY a.attnum" );
$resultArray->setFetchMode( PDO::FETCH_ASSOC );
foreach ( $resultArray as $row )
{
$fieldLength = false;
$fieldType = self::convertToGenericType( $row['fulltype'], $fieldLength, $fieldPrecision );
if ( !$fieldLength )
{
$fieldLength = false;
}
$fieldNotNull = $row['notnull'];
$fieldDefault = null;
$fieldAutoIncrement = false;
if ( $row['default'] != '' )
{
// detecting autoincrement field by string like "nextval('public.TableName_FieldName_seq'::text)"
// in "default"
if ( strstr( $row['default'], $row['field'].'_seq' ) != false )
{
$fieldAutoIncrement = true;
}
else
{
// try to cut off single quotes and "::Typename" that postgreSQL
// adds to default value string for some types.
// we should do it to get clean value for default clause.
if ( preg_match( "@'(.*)('::[a-z ]*)$@", $row['default'], $matches ) == 1 )
{
$fieldDefault = $matches[1];
}
else
{
$fieldDefault = $row['default'];
if ( $fieldType == 'boolean' )
{
( $fieldDefault == 'true' )? $fieldDefault = 'true': $fieldDefault = 'false';
}
}
}
}
// FIXME: unsigned needs to be implemented
$fieldUnsigned = false;
$fields[$row['field']] = ezcDbSchema::createNewField( $fieldType, $fieldLength, $fieldNotNull, $fieldDefault, $fieldAutoIncrement, $fieldUnsigned );
}
return $fields;
}
/**
* Converts the native PostgreSQL type in $typeString to a generic DbSchema type.
*
* This method converts a string like "float(5,10)" to the generic DbSchema
* type and uses the by-reference parameters $typeLength and $typePrecision
* to communicate the optional length and precision of the field's type.
*
* @param string $typeString
* @param int &$typeLength
* @param int &$typePrecision
* @return string
*/
static function convertToGenericType( $typeString, &$typeLength, &$typePrecision )
{
preg_match( "@([a-z ]*)(\((\d*)(,(\d+))?\))?@", $typeString, $matches );
if ( !isset( self::$typeMap[$matches[1]] ) )
{
throw new ezcDbSchemaUnsupportedTypeException( 'PostGreSQL', $matches[1] );
}
$genericType = self::$typeMap[$matches[1]];
if ( in_array( $genericType, array( 'text', 'decimal', 'float' ) ) && isset( $matches[3] ) )
{
$typeLength = $matches[3];
if ( is_numeric( $typeLength ) )
{
$typeLength = (int) $typeLength;
}
}
if ( in_array( $genericType, array( 'decimal', 'float' ) ) && isset( $matches[5] ) )
{
$typePrecision = $matches[5];
}
return $genericType;
}
/**
* Returns whether the type $type is a numeric type
*
* @param string $type
* @return bool
*/
private function isNumericType( $type )
{
$types = array( 'float', 'int' );
return in_array( $type, $types );
}
/**
* Returns whether the type $type is a string type
*
* @param string $type
* @return bool
*/
private function isStringType( $type )
{
$types = array( 'tinytext', 'text', 'mediumtext', 'longtext' );
return in_array( $type, $types );
}
/**
* Returns whether the type $type is a blob type
*
* @param string $type
* @return bool
*/
private function isBlobType( $type )
{
$types = array( 'varchar', 'char' );
return in_array( $type, $types );
}
/**
* Loops over all the indexes in the table $table and extracts information.
*
* This method extracts information about the table $tableName's indexes
* from the database and returns this schema as an array of
* ezcDbSchemaIndex objects. The key in the array is the index' name.
*
* @param string $tableName
* @return array(string=>ezcDbSchemaIndex)
*/
protected function fetchTableIndexes( $tableName )
{
$indexBuffer = array();
$resultArray = array();
// fetching index info from PostgreSQL
$getIndexSQL = "SELECT relname, pg_index.indisunique, pg_index.indisprimary,
pg_index.indkey, pg_index.indrelid
FROM pg_class, pg_index
WHERE oid IN (
SELECT indexrelid
FROM pg_index, pg_class
WHERE pg_class.relname='$tableName' AND pg_class.oid=pg_index.indrelid
)
AND pg_index.indexrelid = oid";
$indexesArray = $this->db->query( $getIndexSQL )->fetchAll();
// getting columns to which each index related.
foreach ( $indexesArray as $row )
{
$myIndex[]=$row['relname'];
$colNumbers = explode( ' ', $row['indkey'] );
$colNumbersSQL = 'IN (' . join( ' ,', $colNumbers ) . ' )';
$indexColumns = $this->db->query( "SELECT attname
FROM pg_attribute
WHERE attrelid={$row['indrelid']}
AND attnum $colNumbersSQL;" );
foreach ( $indexColumns as $colRow )
{
$resultArray[] = array( 'key_name' => $row['relname'],
'column_name' => $colRow['attname'],
'non_unique' => !$row['indisunique'],
'primary' => !$row['indisprimary']
);
$indexColumnNames[] = $colRow['attname'];
}
}
foreach ( $resultArray as $row )
{
$keyName = $row['key_name'];
if ( substr( $keyName, -5) == '_pkey' )
{
$keyName = 'primary';
}
$indexBuffer[$keyName]['primary'] = false;
$indexBuffer[$keyName]['unique'] = true;
if ( $keyName == 'primary' )
{
$indexBuffer[$keyName]['primary'] = true;
$indexBuffer[$keyName]['unique'] = true;
}
else
{
$indexBuffer[$keyName]['unique'] = $row['non_unique'] ? false : true;
}
$indexBuffer[$keyName]['fields'][$row['column_name']] = ezcDbSchema::createNewIndexField();
}
$indexes = array();
foreach ( $indexBuffer as $indexName => $indexInfo )
{
$indexes[$indexName] = ezcDbSchema::createNewIndex( $indexInfo['fields'], $indexInfo['primary'], $indexInfo['unique'] );
}
return $indexes;
}
}
?>