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