eZ Publish  [trunk]
ezpgsqlschema.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZPgsqlSchema class.
00004  *
00005  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00006  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00007  * @version //autogentag//
00008  * @package lib
00009  */
00010 
00011 /*!
00012   \class eZPgsqlSchema ezpgsqlschema.php
00013   \ingroup eZDbSchema
00014   \brief Handles schemas for PostgreSQL
00015 
00016 */
00017 
00018 class eZPgsqlSchema extends eZDBSchemaInterface
00019 {
00020     const SHOW_TABLES_QUERY = '
00021         SELECT n.nspname as "Schema",
00022                c.relname as "Name",
00023                CASE c.relkind
00024                     WHEN \'r\' THEN \'table\'
00025                     WHEN \'v\' THEN \'view\'
00026                     WHEN \'i\' THEN \'index\'
00027                     WHEN \'S\' THEN \'sequence\'
00028                     WHEN \'s\' THEN \'special\'
00029                END as "Type",
00030                u.usename as "Owner"
00031         FROM pg_catalog.pg_class c
00032              LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
00033              LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
00034         WHERE c.relkind IN (\'r\',\'\')
00035               AND n.nspname NOT IN (\'pg_catalog\', \'pg_toast\')
00036               AND pg_catalog.pg_table_is_visible(c.oid)
00037         ORDER BY 1, 2';
00038 
00039     const FETCH_TABLE_OID_QUERY = '
00040         SELECT c.oid,
00041                n.nspname,
00042                c.relname
00043         FROM pg_catalog.pg_class c
00044              LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
00045         WHERE pg_catalog.pg_table_is_visible(c.oid)
00046               AND c.relname ~ \'^<<tablename>>$\'
00047         ORDER BY 2, 3';
00048 
00049     const FETCH_TABLE_DEF_QUERY = '
00050         SELECT a.attname,
00051                pg_catalog.format_type(a.atttypid, a.atttypmod),
00052                (SELECT substring(d.adsrc for 128) FROM pg_catalog.pg_attrdef d
00053                 WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) as default,
00054                a.attnotnull, a.attnum
00055         FROM pg_catalog.pg_attribute a
00056         WHERE a.attrelid = \'<<oid>>\' AND a.attnum > 0 AND NOT a.attisdropped
00057         ORDER BY a.attnum';
00058 
00059     const FETCH_INDEX_DEF_QUERY = '
00060         SELECT c.relname, i.*
00061         FROM pg_catalog.pg_index i, pg_catalog.pg_class c
00062         WHERE indrelid = \'<<oid>>\'
00063               AND i.indexrelid = c.oid';
00064 
00065     const FETCH_INDEX_COL_NAMES_QUERY = '
00066         SELECT a.attnum, a.attname
00067         FROM pg_catalog.pg_attribute a
00068         WHERE a.attrelid = \'<<indexrelid>>\' AND a.attnum IN (<<attids>>) AND NOT a.attisdropped
00069         ORDER BY a.attnum';
00070 
00071     /*!
00072      Constructor
00073 
00074      \param db instance
00075     */
00076     function eZPgsqlSchema( $db )
00077     {
00078         $this->eZDBSchemaInterface( $db );
00079     }
00080 
00081     function schema( $params = array() )
00082     {
00083         $params = array_merge( array( 'meta_data' => false,
00084                                       'format' => 'generic',
00085                                       'sort_columns' => true,
00086                                       'sort_indexes' => true ),
00087                                $params );
00088         $schema = array();
00089 
00090         if ( $this->Schema === false )
00091         {
00092             $resultArray = $this->DBInstance->arrayQuery( eZPgsqlSchema::SHOW_TABLES_QUERY );
00093 
00094             foreach( $resultArray as $row )
00095             {
00096                 $table_name = $row['Name'];
00097                 if ( !isset( $params['table_include'] ) or
00098                      ( is_array( $params['table_include'] ) and
00099                        in_array( $table_name, $params['table_include'] ) ) )
00100                 {
00101                     $schema_table['name'] = $table_name;
00102                     $schema_table['fields'] = $this->fetchTableFields( $table_name, $params );
00103                     $schema_table['indexes'] = $this->fetchTableIndexes( $table_name, $params );
00104 
00105                     $schema[$table_name] = $schema_table;
00106                 }
00107             }
00108             $this->transformSchema( $schema, $params['format'] == 'local' );
00109             ksort( $schema );
00110             $this->Schema = $schema;
00111         }
00112         else
00113         {
00114             $this->transformSchema( $this->Schema, $params['format'] == 'local' );
00115             $schema = $this->Schema;
00116         }
00117         return $schema;
00118     }
00119 
00120     /*!
00121      * \private
00122      */
00123     function fetchTableFields( $table, $params )
00124     {
00125         $fields = array();
00126 
00127         $resultArray = $this->DBInstance->arrayQuery( str_replace( '<<tablename>>', $table, eZPgsqlSchema::FETCH_TABLE_OID_QUERY ) );
00128         $row = $resultArray[0];
00129         $oid = $row['oid'];
00130 
00131         $resultArray = $this->DBInstance->arrayQuery( str_replace( '<<oid>>', $oid, eZPgsqlSchema::FETCH_TABLE_DEF_QUERY ) );
00132         foreach( $resultArray as $row )
00133         {
00134             $field = array();
00135             $autoinc = false;
00136             $field['type'] = $this->parseType( $row['format_type'], $field['length'] );
00137             if ( !$field['length'] )
00138             {
00139                 unset( $field['length'] );
00140             }
00141 
00142             $field['not_null'] = 0;
00143             if ( $row['attnotnull'] == 't' )
00144             {
00145                 $field['not_null'] = '1';
00146             }
00147 
00148             $field['default'] = false;
00149             if ( !$field['not_null'] )
00150             {
00151                 if ( $row['default'] === null )
00152                     $field['default'] = null;
00153                 else
00154                     $field['default'] = $this->parseDefault ( $row['default'], $autoinc );
00155             }
00156             else
00157             {
00158                 $field['default'] = (string)$this->parseDefault ( $row['default'], $autoinc );
00159             }
00160 
00161             $numericTypes = array( 'float', 'int' );
00162             $blobTypes = array( 'tinytext', 'text', 'mediumtext', 'longtext' );
00163             $charTypes = array( 'varchar', 'char' );
00164             if ( in_array( $field['type'], $charTypes ) )
00165             {
00166                 if ( !$field['not_null'] )
00167                 {
00168                     if ( $field['default'] === null )
00169                     {
00170                         $field['default'] = null;
00171                     }
00172                     else if ( $field['default'] === false )
00173                     {
00174                         $field['default'] = '';
00175                     }
00176                 }
00177             }
00178             else if ( in_array( $field['type'], $numericTypes ) )
00179             {
00180                 if ( $field['default'] === false )
00181                 {
00182                     if ( $field['not_null'] )
00183                     {
00184                         $field['default'] = 0;
00185                     }
00186                 }
00187                 else if ( $field['type'] == 'int' )
00188                 {
00189                     if ( $field['not_null'] or
00190                          is_numeric( $field['default'] ) )
00191                     {
00192                         $field['default'] = (int)$field['default'];
00193                     }
00194                 }
00195                 else if ( $field['type'] == 'float' )
00196                 {
00197                     if ( $field['not_null'] or
00198                          is_numeric( $field['default'] ) )
00199                     {
00200                         $field['default'] = (float)$field['default'];
00201                     }
00202                 }
00203             }
00204             else if ( in_array( $field['type'], $blobTypes ) )
00205             {
00206                 // We do not want default for blobs.
00207                 $field['default'] = false;
00208             }
00209 
00210             if ( $autoinc )
00211             {
00212                 unset( $field['length'] );
00213                 $field['not_null'] = 0;
00214                 $field['default'] = false;
00215                 $field['type'] = 'auto_increment';
00216             }
00217 
00218             if ( !$field['not_null'] )
00219                 unset( $field['not_null'] );
00220 
00221             $fields[$row['attname']] = $field;
00222         }
00223         if ( $params['sort_columns'] )
00224         {
00225             ksort( $fields );
00226         }
00227 
00228         return $fields;
00229     }
00230 
00231     /*!
00232      * \private
00233      */
00234     function fetchTableIndexes( $table, $params )
00235     {
00236         $metaData = false;
00237         if ( isset( $params['meta_data'] ) )
00238         {
00239             $metaData = $params['meta_data'];
00240         }
00241 
00242         $indexes = array();
00243 
00244         $resultArray = $this->DBInstance->arrayQuery( str_replace( '<<tablename>>', $table, eZPgsqlSchema::FETCH_TABLE_OID_QUERY ) );
00245         $row = $resultArray[0];
00246         $oid = $row['oid'];
00247 
00248         $resultArray = $this->DBInstance->arrayQuery( str_replace( '<<oid>>', $oid, eZPgsqlSchema::FETCH_INDEX_DEF_QUERY ) );
00249 
00250         foreach( $resultArray as $row )
00251         {
00252             $fields = array();
00253             $kn = $row['relname'];
00254 
00255             $column_id_array = explode( ' ', $row['indkey'] );
00256             if ( $row['indisprimary'] == 't' )
00257             {
00258                 // If the name of the key matches our primary key naming standard
00259                 // we change the name to PRIMARY, this makes it 100% similar to
00260                 // primary keys in MySQL
00261                 $correctName = $this->primaryKeyIndexName( $table, $kn, $column_id_array );
00262                 if ( strlen( $correctName ) > 63 )
00263                 {
00264                     eZDebug::writeError( "The index name '$correctName' (" . strlen( $correctName ) . ") exceeds 63 characters which is the PostgreSQL limit for names" );
00265                 }
00266                 if ( $kn == $correctName )
00267                 {
00268                     $kn = 'PRIMARY';
00269                 }
00270 
00271                 // Extra meta data:
00272                 // Include the name of the index that postgresql will use
00273                 if ( $metaData )
00274                 {
00275                     $indexes[$kn]['postgresql:name'] = $correctName;
00276                 }
00277 
00278                 $indexes[$kn]['type'] = 'primary';
00279             }
00280             else
00281             {
00282                 $indexes[$kn]['type'] = $row['indisunique'] == 't' ? 'unique' : 'non-unique';
00283             }
00284 
00285             /* getting fieldnames requires yet another query and it doesn't return it 'in order' either.
00286              * grumbl, stupid pgsql :) */
00287             $att_ids = join( ', ',  $column_id_array );
00288             $query = str_replace( '<<indexrelid>>', $row['indrelid'], eZPgsqlSchema::FETCH_INDEX_COL_NAMES_QUERY );
00289             $query = str_replace( '<<attids>>', $att_ids, $query );
00290 
00291             $fieldsArray = $this->DBInstance->arrayQuery( $query );
00292             foreach( $fieldsArray as $fields_row )
00293             {
00294                 $fields[$fields_row['attnum']] = $fields_row['attname'];
00295             }
00296             foreach ( $column_id_array as $rank => $id )
00297             {
00298                 $indexes[$kn]['fields'][$rank] = $fields[$id];
00299             }
00300         }
00301         if ( $params['sort_indexes'] )
00302         {
00303             ksort( $indexes );
00304         }
00305 
00306         return $indexes;
00307     }
00308 
00309     function parseType( $type_info, &$length_info )
00310     {
00311         preg_match ( "@([a-z ]*)(\(([0-9]*|[0-9]*,[0-9]*)\))?@", $type_info, $matches );
00312         if ( isset( $matches[3] ) )
00313         {
00314             $length_info = $matches[3];
00315             if ( is_numeric( $length_info ) )
00316                 $length_info = (int)$length_info;
00317         }
00318         $type = $this->convertToStandardType ( $matches[1], $length_info );
00319         return $type;
00320     }
00321 
00322     function isTypeLengthSupported( $pgType )
00323     {
00324         switch ( $pgType )
00325         {
00326             case 'integer':
00327             case 'double precision':
00328             case 'real':
00329             case 'bigint':
00330             {
00331                 return false;
00332             } break;
00333         }
00334         return true;
00335     }
00336 
00337     function convertFromStandardType( $type, &$length )
00338     {
00339         switch ( $type )
00340         {
00341             case 'char':
00342                 return 'character';
00343 
00344             case 'int':
00345                 return 'integer';
00346 
00347             case 'bigint':
00348                 return 'bigint';
00349 
00350             case 'varchar':
00351                 return 'character varying';
00352 
00353             case 'longtext':
00354                 return 'text';
00355 
00356             case 'mediumtext':
00357                 return 'text';
00358 
00359             case 'text':
00360                 return 'text';
00361 
00362             case 'float':
00363                 return 'real';
00364 
00365             case 'double':
00366                 return 'double precision';
00367 
00368             case 'decimal':
00369                 return 'numeric';
00370 
00371             default:
00372                 die ( "ERROR UNHANDLED TYPE: $type\n" );
00373         }
00374     }
00375 
00376     /*!
00377      \private
00378      The name will consist of the table name and _pkey, since it is only allowed
00379      to have one primary key pre table that shouldn't be a problem.
00380 
00381      \return A string representing the name of the primary key index.
00382     */
00383     function primaryKeyIndexName( $tableName, $indexName, $fields )
00384     {
00385         return $tableName . '_pkey';
00386     }
00387 
00388     function convertToStandardType( $type, &$length )
00389     {
00390         switch ( $type )
00391         {
00392             case 'bigint':
00393                 $length = 20;
00394                 return 'bigint';
00395 
00396             case 'integer':
00397                 $length = 11;
00398                 return 'int';
00399 
00400             case 'character varying':
00401                 return 'varchar';
00402 
00403             case 'text':
00404                 return 'longtext';
00405 
00406             case 'double precision':
00407                 return 'double';
00408                 
00409             case 'real':
00410                 return 'float';
00411 
00412             case 'character':
00413                 return 'char';
00414 
00415             case 'numeric':
00416                 return 'decimal';
00417 
00418             default:
00419                 die ( "ERROR UNHANDLED TYPE: $type\n" );
00420         }
00421     }
00422 
00423     function parseDefault( $default, &$autoinc )
00424     {
00425         if ( preg_match( "@^NULL::.*@", $default, $matches ) )
00426         {
00427             return null;
00428         }
00429 
00430         // postgresql 7.x: nextval('ezbasket_s'::text)
00431         // postgresql 8.x: nextval(('ezbasket_s'::text)::regclass)
00432         if ( preg_match( "@^nextval\(\(?'([a-z_]+_s)'::text\)@", $default, $matches ) )
00433         {
00434             $autoinc = 1;
00435             return '';
00436         }
00437 
00438         if ( preg_match( "@^\(?([^()]*)\)?::double precision@", $default, $matches ) )
00439         {
00440             return $matches[1];
00441         }
00442 
00443         if ( preg_match( "@^(.*)::bigint@", $default, $matches ) )
00444         {
00445             return $matches[1];
00446         }
00447 
00448         if ( preg_match( "@^'(.*)'::character\ varying$@", $default, $matches ) )
00449         {
00450             return $matches[1];
00451         }
00452 
00453         if ( preg_match( "@^'(.*)'::[a-zA-Z ]+$@", $default, $matches ) )
00454         {
00455             return $matches[1];
00456         }
00457 
00458         if ( preg_match( "@^'(.*)'$@", $default, $matches ) )
00459         {
00460             return $matches[1];
00461         }
00462 
00463         return $default;
00464     }
00465 
00466     /*!
00467      \private
00468      \param $table_name The table name
00469      \param $index_name The index name
00470      \param $def The index structure, see eZDBSchemaInterface for more details
00471      \param $params An associative array with optional parameters which controls the output of SQLs
00472      \param $withClosure If \c true then the SQLs will contain semi-colons to close them.
00473     */
00474     function generateAddIndexSql( $table_name, $index_name, $def, $params, $withClosure = true )
00475     {
00476         $diffFriendly = isset( $params['diff_friendly'] ) ? $params['diff_friendly'] : false;
00477         $postgresqlCompatible = isset( $params['compatible_sql'] ) ? $params['compatible_sql'] : false;
00478 
00479         $spacing = $postgresqlCompatible ? "\n    " : " ";
00480         switch ( $def['type'] )
00481         {
00482             case 'primary':
00483             {
00484                 $pkeyName = $this->primaryKeyIndexName( $table_name, $index_name, $def['fields'] );
00485                 if ( strlen( $pkeyName ) > 63 )
00486                 {
00487                     eZDebug::writeError( "The primary key '$pkeyName' (" . strlen( $pkeyName ) . ") exceeds 63 characters which is the PostgreSQL limit for names" );
00488                 }
00489                 $sql = "ALTER TABLE ONLY $table_name" . $spacing . "ADD CONSTRAINT $pkeyName PRIMARY KEY";
00490             } break;
00491 
00492             case 'non-unique':
00493             {
00494                 $sql = "CREATE INDEX $index_name ON $table_name USING btree";
00495             } break;
00496 
00497             case 'unique':
00498             {
00499                 $sql = "CREATE UNIQUE INDEX $index_name ON $table_name USING btree";
00500             } break;
00501         }
00502 
00503         $sql .= ( $diffFriendly ? " (\n    " : ( $postgresqlCompatible ? ' (' : ' ( ' ) );
00504         $i = 0;
00505         foreach ( $def['fields'] as $fieldDef )
00506         {
00507             if ( $i > 0 )
00508             {
00509                 $sql .= $diffFriendly ? ",\n    " : ', ';
00510             }
00511             if ( is_array( $fieldDef ) )
00512             {
00513                 $fieldName = $fieldDef['name'];
00514             }
00515             else
00516             {
00517                 $fieldName = $fieldDef;
00518             }
00519             if ( in_array( $fieldName, $this->reservedKeywordList() ) )
00520             {
00521                 $sql .= '"' . $fieldName . '"';
00522             }
00523             else
00524             {
00525                 $sql .= $fieldName;
00526             }
00527             ++$i;
00528         }
00529 
00530         $sql .= ( $diffFriendly ? "\n)" : ( $postgresqlCompatible ? ')' : ' )' ) );
00531 
00532         return $sql . ( $withClosure ? ";\n" : "" );
00533     }
00534 
00535     /*!
00536      * \private
00537      */
00538     function generateDropIndexSql( $table_name, $index_name, $def, $withClosure = true )
00539     {
00540         if ($def['type'] == 'primary' )
00541         {
00542             $sql = "ALTER TABLE $table_name DROP CONSTRAINT "
00543                  . $this->primaryKeyIndexName( $table_name, $index_name, $def['fields'] );
00544         }
00545         else
00546         {
00547             $sql = "DROP INDEX $index_name";
00548         }
00549         return $sql . ( $withClosure ? ";\n" : "" );
00550     }
00551 
00552     /*!
00553      * \private
00554      */
00555     function generateFieldDef( $table_name, $field_name, $def, $add_default_not_null = true, $params )
00556     {
00557         $diffFriendly = isset( $params['diff_friendly'] ) ? $params['diff_friendly'] : false;
00558 
00559         if ( in_array( $field_name, $this->reservedKeywordList() ) )
00560         {
00561             $sql_def = '"' . $field_name . '"';
00562         }
00563         else
00564         {
00565             $sql_def = $field_name;
00566         }
00567 
00568         $sql_def .= ( $diffFriendly ? "\n    " : " " );
00569         if ( $def['type'] != 'auto_increment' )
00570         {
00571             $pgType = eZPgsqlSchema::convertFromStandardType( $def['type'], $def['length'] );
00572             $sql_def .= $pgType;
00573             if ( eZPgsqlSchema::isTypeLengthSupported( $pgType ) and isset( $def['length'] ) && $def['length'] )
00574             {
00575                 $sql_def .= "({$def['length']})";
00576             }
00577             if ( $add_default_not_null )
00578             {
00579                 $defaultDef = eZPGSQLSchema::generateDefaultDef( false, false, $def, $params );
00580                 if ( $defaultDef )
00581                 {
00582                     $sql_def .= ( $diffFriendly ? "\n    " : " " );
00583                     $sql_def .= rtrim( $defaultDef );
00584                 }
00585                 $nullDef = eZPGSQLSchema::generateNullDef( false, false, $def, $params );
00586                 if ( $nullDef )
00587                 {
00588                     $sql_def .= ( $diffFriendly ? "\n    " : " " );
00589                     $sql_def .= trim( $nullDef );
00590                 }
00591             }
00592         }
00593         else
00594         {
00595             if ( $diffFriendly )
00596             {
00597                 $sql_def .= "integer\n    DEFAULT nextval('{$table_name}_s'::text)\n    NOT NULL";
00598             }
00599             else
00600             {
00601                 $sql_def .= "integer DEFAULT nextval('{$table_name}_s'::text) NOT NULL";
00602             }
00603         }
00604         return $sql_def;
00605     }
00606 
00607     /*!
00608      \private
00609     */
00610     function generateDefaultDef( $table_name, $field_name, $def, $params )
00611     {
00612         $postgresqlCompatible = isset( $params['compatible_sql'] ) ? $params['compatible_sql'] : false;
00613         $sql_def = '';
00614         if ( $table_name and $field_name )
00615         {
00616             $sql_def .= "ALTER TABLE $table_name ALTER $field_name SET ";
00617         }
00618         if ( array_key_exists( 'default', $def ) and
00619              $def['default'] !== false )
00620         {
00621             if ( $def['default'] === null )
00622             {
00623                 if ( !$postgresqlCompatible )
00624                     $sql_def .= "DEFAULT NULL ";
00625             }
00626             else if ( $def['default'] !== false )
00627             {
00628                 if ( $def['type'] == 'int' )
00629                 {
00630                     $sql_def .= "DEFAULT {$def['default']} ";
00631                 }
00632                 else if ( $def['type'] == 'float' )
00633                 {
00634                     $sql_def .= "DEFAULT {$def['default']}::real ";
00635                 }
00636                 else if ( $def['type'] == 'double' )
00637                 {
00638                     $sql_def .= "DEFAULT {$def['default']}::double precision ";
00639                 }
00640                 else if ( $def['type'] == 'varchar' )
00641                 {
00642                     $sql_def .= "DEFAULT '{$def['default']}'::character varying ";
00643                 }
00644                 else if ( $def['type'] == 'char' )
00645                 {
00646                     $sql_def .= "DEFAULT '{$def['default']}'::bpchar ";
00647                 }
00648                 else
00649                 {
00650                     $sql_def .= "DEFAULT '{$def['default']}' ";
00651                 }
00652             }
00653         }
00654         else if ( $table_name and $field_name )
00655         {
00656             return false;
00657         }
00658         return $sql_def;
00659     }
00660 
00661     /*!
00662      \private
00663     */
00664     function generateNullDef( $table_name, $field_name, $def, $params )
00665     {
00666         $sql_def = '';
00667         if ( $table_name and $field_name )
00668         {
00669             $sql_def .= "ALTER TABLE $table_name ALTER $field_name SET ";
00670         }
00671         if ( isset( $def['not_null'] ) && ( $def['not_null'] ) )
00672         {
00673             $sql_def .= 'NOT NULL ';
00674         }
00675         else if ( $table_name and $field_name )
00676         {
00677             return false;
00678         }
00679         return $sql_def;
00680     }
00681 
00682     /*!
00683      * \private
00684      */
00685     function generateAddFieldSql( $table_name, $field_name, $def, $params )
00686     {
00687         $sql = "ALTER TABLE $table_name ADD COLUMN ";
00688         $sql .= eZPgsqlSchema::generateFieldDef( $table_name, $field_name, $def, false, $params ) . ";\n";
00689         $defaultSQL = eZPGSQLSchema::generateDefaultDef( $table_name, $field_name, $def, $params );
00690         if ( $defaultSQL )
00691             $sql .= $defaultSQL . ";\n";
00692         $nullSQL = eZPGSQLSchema::generateNullDef( $table_name, $field_name, $def, $params );
00693         if ( $nullSQL )
00694             $sql .= $nullSQL . ";\n";
00695         $sql .= "\n";
00696         return $sql;
00697     }
00698 
00699     /*!
00700      \private
00701     */
00702     function generateDropFieldSql( $table_name, $field_name, $params )
00703     {
00704         if ( in_array( $field_name, $this->reservedKeywordList() ) )
00705         {
00706             $field_name = '"' . $field_name . '"';
00707         }
00708 
00709         $sql = "ALTER TABLE $table_name DROP COLUMN $field_name";
00710 
00711         return $sql . ";\n";
00712     }
00713 
00714     /*!
00715      * \private
00716      */
00717     function generateAlterFieldSql( $table_name, $field_name, $def, $params )
00718     {
00719         $sql = "ALTER TABLE $table_name RENAME COLUMN $field_name TO " . $field_name . "_tmp;\n";
00720         $sql .= "ALTER TABLE $table_name ADD COLUMN ";
00721         $sql .= eZPgsqlSchema::generateFieldDef( $table_name, $field_name, $def, false, $params ) . ";\n";
00722         $defaultSQL = eZPGSQLSchema::generateDefaultDef( $table_name, $field_name, $def, $params );
00723         if ( $defaultSQL )
00724             $sql .= $defaultSQL . ";\n";
00725         $nullSQL = eZPGSQLSchema::generateNullDef( $table_name, $field_name, $def, $params );
00726         if ( $nullSQL )
00727             $sql .= $nullSQL . ";\n";
00728         $sql .= "UPDATE $table_name SET $field_name=" . $field_name . "_tmp;\n";
00729         $sql .= "ALTER TABLE $table_name DROP COLUMN " . $field_name . "_tmp;\n\n";
00730         return $sql;
00731     }
00732 
00733     function generateTableSchema( $table, $table_def, $params )
00734     {
00735         $arrays = $this->generateTableArrays( $table, $table_def, $params, true );
00736         return ( join( "\n\n", $arrays['sequences'] ) . "\n" .
00737                  join( "\n\n", $arrays['tables'] ) . "\n" .
00738                  join( "\n\n", $arrays['indexes'] ) . "\n" .
00739                  join( "\n\n", $arrays['constraints'] ) . "\n" );
00740     }
00741 
00742     function generateTableSQLList( $table, $table_def, $params, $separateTypes )
00743     {
00744         $arrays = $this->generateTableArrays( $table, $table_def, $params, false );
00745 
00746         // If we have to separate the types the current array is sufficient
00747         if ( $separateTypes )
00748             return $arrays;
00749         return array_merge( $arrays['sequences'],
00750                             $arrays['tables'],
00751                             $arrays['indexes'],
00752                             $arrays['constraints'] );
00753     }
00754 
00755     /*!
00756      \private
00757      \param $table The table name
00758      \param $table_def The table structure, see eZDBSchemaInterface for more details
00759      \param $params An associative array with optional parameters which controls the output of SQLs
00760      \param $withClosure If \c true then the SQLs will contain semi-colons to close them.
00761     */
00762     function generateTableArrays( $table, $table_def, $params, $withClosure = true )
00763     {
00764         $diffFriendly = isset( $params['diff_friendly'] ) ? $params['diff_friendly'] : false;
00765         $postgresqlCompatible = isset( $params['compatible_sql'] ) ? $params['compatible_sql'] : false;
00766 
00767         $arrays = array( 'sequences' => array(),
00768                          'tables' => array(),
00769                          'indexes' => array(),
00770                          'constraints' => array() );
00771 
00772         $sql_fields = array();
00773 
00774         $spacing = $postgresqlCompatible ? '    ' : '  ';
00775 
00776         // First we need to check if we use auto increment fields as
00777         // sequences need to exist before we use them
00778         foreach ( $table_def['fields'] as $field_name => $field_def )
00779         {
00780             if ( $field_def['type'] == 'auto_increment' )
00781             {
00782                 $sequenceFields = array( "CREATE SEQUENCE {$table}_s",
00783                                          "START 1",
00784                                          "INCREMENT 1",
00785                                          "MAXVALUE 9223372036854775807",
00786                                          "MINVALUE 1",
00787                                          "CACHE 1" );
00788                 $arrays['sequences'][] = join( "\n$spacing", $sequenceFields ) . ( $withClosure ? ';' : '' );
00789             }
00790         }
00791 
00792         $sql = "CREATE TABLE $table (\n";
00793         $fields = $table_def['fields'];
00794         foreach ( $fields as $field_name => $field_def )
00795         {
00796             $sql_fields[] = $spacing . eZPgsqlSchema::generateFieldDef( $table, $field_name, $field_def, true, $params );
00797         }
00798         $sql .= join( ",\n", $sql_fields ) . ( $withClosure ? "\n);" : "\n)" );
00799         $arrays['tables'][] = $sql;
00800 
00801         foreach ( $table_def['indexes'] as $index_name => $index_def )
00802         {
00803             if ( $index_def['type'] != 'primary' )
00804             {
00805                 $arrays['indexes'][] = eZPgsqlSchema::generateAddIndexSql( $table, $index_name, $index_def, $params, $withClosure );
00806             }
00807         }
00808         foreach ( $table_def['indexes'] as $index_name => $index_def )
00809         {
00810             if ( $index_def['type'] == 'primary' )
00811             {
00812                 $arrays['constraints'][] = eZPgsqlSchema::generateAddIndexSql( $table, $index_name, $index_def, $params, $withClosure );
00813             }
00814         }
00815 
00816         return $arrays;
00817     }
00818 
00819 
00820     /*!
00821 
00822      This calls eZDBSchemaInterface::generateTableInsertSQLList() and adds a setval SQL if
00823      the table has auto increments.
00824     */
00825     function generateTableInsertSQLList( $tableName, $tableDef, $dataEntries, $params, $withClosure = true )
00826     {
00827         $sqlList = eZDBSchemaInterface::generateTableInsertSQLList( $tableName, $tableDef, $dataEntries, $params, $withClosure );
00828 
00829         foreach ( $tableDef['fields'] as $fieldName => $fieldDef )
00830         {
00831             if ( $fieldDef['type'] == 'auto_increment' )
00832             {
00833                 $sql = "SELECT setval('" . $tableName . "_s',max(" . $fieldName . ")+1) FROM " . $tableName;
00834                 if ( $withClosure )
00835                     $sql .= ";";
00836                 $sqlList[] = $sql;
00837             }
00838         }
00839         return $sqlList;
00840     }
00841 
00842     function generateSchemaFile( $schema, $params = array() )
00843     {
00844         $sql = '';
00845         $postgresqlCompatible = isset( $params['compatible_sql'] ) ? $params['compatible_sql'] : false;
00846 
00847         $i = 0;
00848         $allArrays = array( 'sequences' => array(),
00849                             'tables' => array(),
00850                             'indexes' => array(),
00851                             'constraints' => array() );
00852 
00853         foreach ( $schema as $table => $tableDef )
00854         {
00855             // Skip the info structure, this is not a table
00856             if ( $table == '_info' )
00857                 continue;
00858 
00859             $arrays = $this->generateTableArrays( $table, $tableDef, $params, true );
00860             if ( $postgresqlCompatible )
00861             {
00862                 $allArrays['sequences'] = array_merge( $allArrays['sequences'],
00863                                                        $arrays['sequences'] );
00864                 $allArrays['tables'] = array_merge( $allArrays['tables'],
00865                                                     $arrays['tables'] );
00866                 $allArrays['indexes'] = array_merge( $allArrays['indexes'],
00867                                                      $arrays['indexes'] );
00868                 $allArrays['constraints'] = array_merge( $allArrays['constraints'],
00869                                                          $arrays['constraints'] );
00870             }
00871             else
00872             {
00873                 if ( $i > 0 )
00874                     $sql .= "\n\n";
00875                 ++$i;
00876 
00877                 $sql .= ( join( "\n", $arrays['sequences'] ) . "\n" .
00878                           join( "\n", $arrays['tables'] ) . "\n" .
00879                           join( "\n", $arrays['indexes'] ) . "\n" .
00880                           join( "\n", $arrays['constraints'] ) . "\n" );
00881             }
00882         }
00883 
00884         if ( $postgresqlCompatible )
00885         {
00886             $sql = ( str_repeat( "\n", 11 ) .
00887                      join( str_repeat( "\n", 8 ), $allArrays['sequences'] ) . str_repeat( "\n", 8 ) .
00888                      join( str_repeat( "\n", 8 ), $allArrays['tables'] ) . str_repeat( "\n", 8 ) .
00889                      join( str_repeat( "\n", 7 ), $allArrays['indexes'] ) . str_repeat( "\n", 8 ) .
00890                      join( str_repeat( "\n", 7 ), $allArrays['constraints'] ) . str_repeat( "\n", 8 ) );
00891         }
00892 
00893         return $sql;
00894     }
00895 
00896     /*!
00897      * \private
00898      */
00899     function generateDropTable( $table )
00900     {
00901         return "DROP TABLE $table;\n";
00902     }
00903 
00904     function escapeSQLString( $value )
00905     {
00906         return pg_escape_string( $value );
00907     }
00908 
00909     function schemaType()
00910     {
00911         return 'postgresql';
00912     }
00913 
00914     function schemaName()
00915     {
00916         return 'PostgreSQL';
00917     }
00918 
00919     /*!
00920      \return An array with keywords that are reserved by PostgreSQL.
00921     */
00922     function reservedKeywordList()
00923     {
00924         return array( 'abort',
00925                       'absolute',
00926                       'access',
00927                       'action',
00928                       'add',
00929                       'after',
00930                       'aggregate',
00931                       'all',
00932                       'alter',
00933                       'analyse',
00934                       'analyze',
00935                       'and',
00936                       'any',
00937                       'as',
00938                       'asc',
00939                       'assertion',
00940                       'assignment',
00941                       'at',
00942                       'authorization',
00943                       'backward',
00944                       'before',
00945                       'begin',
00946                       'between',
00947                       'bigint',
00948                       'binary',
00949                       'bit',
00950                       'boolean',
00951                       'both',
00952                       'by',
00953                       'cache',
00954                       'called',
00955                       'cascade',
00956                       'case',
00957                       'cast',
00958                       'chain',
00959                       'char',
00960                       'character',
00961                       'characteristics',
00962                       'check',
00963                       'checkpoint',
00964                       'class',
00965                       'close',
00966                       'cluster',
00967                       'coalesce',
00968                       'collate',
00969                       'column',
00970                       'comment',
00971                       'commit',
00972                       'committed',
00973                       'constraint',
00974                       'constraints',
00975                       'conversion',
00976                       'convert',
00977                       'copy',
00978                       'create',
00979                       'createdb',
00980                       'createuser',
00981                       'cross',
00982                       'current_date',
00983                       'current_time',
00984                       'current_timestamp',
00985                       'current_user',
00986                       'cursor',
00987                       'cycle',
00988                       'database',
00989                       'day',
00990                       'deallocate',
00991                       'dec',
00992                       'decimal',
00993                       'declare',
00994                       'default',
00995                       'deferrable',
00996                       'deferred',
00997                       'definer',
00998                       'delete',
00999                       'delimiter',
01000                       'delimiters',
01001                       'desc',
01002                       'distinct',
01003                       'do',
01004                       'domain',
01005                       'double',
01006                       'drop',
01007                       'each',
01008                       'else',
01009                       'encoding',
01010                       'encrypted',
01011                       'end',
01012                       'escape',
01013                       'except',
01014                       'exclusive',
01015                       'execute',
01016                       'exists',
01017                       'explain',
01018                       'external',
01019                       'extract',
01020                       'false',
01021                       'fetch',
01022                       'float',
01023                       'for',
01024                       'force',
01025                       'foreign',
01026                       'forward',
01027                       'freeze',
01028                       'from',
01029                       'full',
01030                       'function',
01031                       'get',
01032                       'global',
01033                       'grant',
01034                       'group',
01035                       'handler',
01036                       'having',
01037                       'hour',
01038                       'ilike',
01039                       'immediate',
01040                       'immutable',
01041                       'implicit',
01042                       'in',
01043                       'increment',
01044                       'index',
01045                       'inherits',
01046                       'initially',
01047                       'inner',
01048                       'inout',
01049                       'input',
01050                       'insensitive',
01051                       'insert',
01052                       'instead',
01053                       'int',
01054                       'integer',
01055                       'intersect',
01056                       'interval',
01057                       'into',
01058                       'invoker',
01059                       'is',
01060                       'isnull',
01061                       'isolation',
01062                       'join',
01063                       'key',
01064                       'lancompiler',
01065                       'language',
01066                       'leading',
01067                       'left',
01068                       'level',
01069                       'like',
01070                       'limit',
01071                       'listen',
01072                       'load',
01073                       'local',
01074                       'localtime',
01075                       'localtimestamp',
01076                       'location',
01077                       'lock',
01078                       'match',
01079                       'maxvalue',
01080                       'minute',
01081                       'minvalue',
01082                       'mode',
01083                       'month',
01084                       'move',
01085                       'names',
01086                       'national',
01087                       'natural',
01088                       'nchar',
01089                       'new',
01090                       'next',
01091                       'no',
01092                       'nocreatedb',
01093                       'nocreateuser',
01094                       'none',
01095                       'not',
01096                       'nothing',
01097                       'notify',
01098                       'notnull',
01099                       'null',
01100                       'nullif',
01101                       'numeric',
01102                       'of',
01103                       'off',
01104                       'offset',
01105                       'oids',
01106                       'old',
01107                       'on',
01108                       'only',
01109                       'operator',
01110                       'option',
01111                       'or',
01112                       'order',
01113                       'out',
01114                       'outer',
01115                       'overlaps',
01116                       'overlay',
01117                       'owner',
01118                       'partial',
01119                       'password',
01120                       'path',
01121                       'pendant',
01122                       'placing',
01123                       'position',
01124                       'precision',
01125                       'prepare',
01126                       'primary',
01127                       'prior',
01128                       'privileges',
01129                       'procedural',
01130                       'procedure',
01131                       'read',
01132                       'real',
01133                       'recheck',
01134                       'references',
01135                       'reindex',
01136                       'relative',
01137                       'rename',
01138                       'replace',
01139                       'reset',
01140                       'restrict',
01141                       'returns',
01142                       'revoke',
01143                       'right',
01144                       'rollback',
01145                       'row',
01146                       'rule',
01147                       'schema',
01148                       'scroll',
01149                       'second',
01150                       'security',
01151                       'select',
01152                       'sequence',
01153                       'serializable',
01154                       'session',
01155                       'session_user',
01156                       'set',
01157                       'setof',
01158                       'share',
01159                       'show',
01160                       'similar',
01161                       'simple',
01162                       'smallint',
01163                       'some',
01164                       'stable',
01165                       'start',
01166                       'statement',
01167                       'statistics',
01168                       'stdin',
01169                       'stdout',
01170                       'storage',
01171                       'strict',
01172                       'substring',
01173                       'sysid',
01174                       'table',
01175                       'temp',
01176                       'template',
01177                       'temporary',
01178                       'then',
01179                       'time',
01180                       'timestamp',
01181                       'to',
01182                       'toast',
01183                       'trailing',
01184                       'transaction',
01185                       'treat',
01186                       'trigger',
01187                       'trim',
01188                       'true',
01189                       'truncate',
01190                       'trusted',
01191                       'type',
01192                       'unencrypted',
01193                       'union',
01194                       'unique',
01195                       'unknown',
01196                       'unlisten',
01197                       'until',
01198                       'update',
01199                       'usage',
01200                       'user',
01201                       'using',
01202                       'vacuum',
01203                       'valid',
01204                       'validator',
01205                       'values',
01206                       'varchar',
01207                       'varying',
01208                       'verbose',
01209                       'version',
01210                       'view',
01211                       'volatile',
01212                       'when',
01213                       'where',
01214                       'with',
01215                       'without',
01216                       'work',
01217                       'write',
01218                       'year',
01219                       'zone' );
01220     }
01221 }
01222 ?>