|
eZ Publish
[4.0]
|
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 ?>