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