00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 require_once 'creole/metadata/TableInfo.php';
00023
00040 class PgSQLTableInfo extends TableInfo {
00041
00046 private $version;
00047
00052 private $oid;
00053
00059 function __construct(DatabaseInfo $database, $name, $version, $intOID) {
00060 parent::__construct ($database, $name);
00061 $this->version = $version;
00062 $this->oid = $intOID;
00063 }
00064
00066 protected function initColumns () {
00067
00068 include_once ('creole/metadata/ColumnInfo.php');
00069 include_once ('creole/drivers/pgsql/PgSQLTypes.php');
00070
00071
00072
00073 $result = pg_query ($this->conn->getResource(), sprintf ("SELECT
00074 att.attname,
00075 att.atttypmod,
00076 att.atthasdef,
00077 att.attnotnull,
00078 def.adsrc,
00079 CASE WHEN att.attndims > 0 THEN 1 ELSE 0 END AS isarray,
00080 CASE
00081 WHEN ty.typname = 'bpchar'
00082 THEN 'char'
00083 WHEN ty.typname = '_bpchar'
00084 THEN '_char'
00085 ELSE
00086 ty.typname
00087 END AS typname,
00088 ty.typtype
00089 FROM pg_attribute att
00090 JOIN pg_type ty ON ty.oid=att.atttypid
00091 LEFT OUTER JOIN pg_attrdef def ON adrelid=att.attrelid AND adnum=att.attnum
00092 WHERE att.attrelid = %d AND att.attnum > 0
00093 AND att.attisdropped IS FALSE
00094 ORDER BY att.attnum", $this->oid));
00095
00096 if (!$result) {
00097 throw new SQLException("Could not list fields for table: " . $this->name, pg_last_error($this->conn->getResource()));
00098 }
00099 while($row = pg_fetch_assoc($result)) {
00100
00101 $size = null;
00102 $precision = null;
00103 $scale = null;
00104
00105
00106 if (((int) $row['isarray']) === 1)
00107 {
00108 throw new SQLException (sprintf ("Array datatypes are not currently supported [%s.%s]", $this->name, $row['attname']));
00109 }
00110 $name = $row['attname'];
00111
00112 if (strtolower ($row['typtype']) == 'd')
00113 {
00114 $arrDomain = $this->processDomain ($row['typname']);
00115 $type = $arrDomain['type'];
00116 $size = $arrDomain['length'];
00117 $precision = $size;
00118 $scale = $arrDomain['scale'];
00119 $boolHasDefault = (strlen (trim ($row['atthasdef'])) > 0) ? $row['atthasdef'] : $arrDomain['hasdefault'];
00120 $default = (strlen (trim ($row['adsrc'])) > 0) ? $row['adsrc'] : $arrDomain['default'];
00121 $is_nullable = (strlen (trim ($row['attnotnull'])) > 0) ? $row['attnotnull'] : $arrDomain['notnull'];
00122 $is_nullable = (($is_nullable == 't') ? false : true);
00123 }
00124 else
00125 {
00126 $type = $row['typname'];
00127 $arrLengthPrecision = $this->processLengthScale ($row['atttypmod'], $type);
00128 $size = $arrLengthPrecision['length'];
00129 $precision = $size;
00130 $scale = $arrLengthPrecision['scale'];
00131 $boolHasDefault = $row['atthasdef'];
00132 $default = $row['adsrc'];
00133 $is_nullable = (($row['attnotnull'] == 't') ? false : true);
00134 }
00135
00136 $autoincrement = null;
00137
00138
00139 if (($boolHasDefault == 't') && (strlen (trim ($default)) > 0))
00140 {
00141 if (!preg_match('/^nextval\(/', $default))
00142 {
00143 $strDefault= preg_replace ('/::[\W\D]*/', '', $default);
00144 $default = str_replace ("'", '', $strDefault);
00145 }
00146 else
00147 {
00148 $autoincrement = true;
00149 $default = null;
00150 }
00151 }
00152 else
00153 {
00154 $default = null;
00155 }
00156
00157 $this->columns[$name] = new ColumnInfo($this, $name, PgSQLTypes::getType($type), $type, $size, $precision, $scale, $is_nullable, $default, $autoincrement);
00158 }
00159
00160 $this->colsLoaded = true;
00161 }
00162
00163 private function processLengthScale ($intTypmod, $strName)
00164 {
00165
00166 $arrRetVal = array ('length'=>null, 'scale'=>null);
00167
00168
00169 if ($intTypmod == -1)
00170 {
00171 return $arrRetVal;
00172 }
00173
00174
00175 if ($strName == PgSQLTypes::getNativeType (CreoleTypes::NUMERIC))
00176 {
00177 $intLen = ($intTypmod - 4) >> 16;
00178 $intPrec = ($intTypmod - 4) & 0xffff;
00179 $intLen = sprintf ("%ld", $intLen);
00180 if ($intPrec)
00181 {
00182 $intPrec = sprintf ("%ld", $intPrec);
00183 }
00184 $arrRetVal['length'] = $intLen;
00185 $arrRetVal['scale'] = $intPrec;
00186 }
00187 elseif ($strName == PgSQLTypes::getNativeType (CreoleTypes::TIME) || $strName == 'timetz'
00188 || $strName == PgSQLTypes::getNativeType (CreoleTypes::TIMESTAMP) || $strName == 'timestamptz'
00189 || $strName == 'interval' || $strName == 'bit')
00190 {
00191 $arrRetVal['length'] = sprintf ("%ld", $intTypmod);
00192 }
00193 else
00194 {
00195 $arrRetVal['length'] = sprintf ("%ld", ($intTypmod - 4));
00196 }
00197 return $arrRetVal;
00198 }
00199
00200 private function processDomain ($strDomain)
00201 {
00202 if (strlen (trim ($strDomain)) < 1)
00203 {
00204 throw new SQLException ("Invalid domain name [" . $strDomain . "]");
00205 }
00206 $result = pg_query ($this->conn->getResource(), sprintf ("SELECT
00207 d.typname as domname,
00208 b.typname as basetype,
00209 d.typlen,
00210 d.typtypmod,
00211 d.typnotnull,
00212 d.typdefault
00213 FROM pg_type d
00214 INNER JOIN pg_type b ON b.oid = CASE WHEN d.typndims > 0 then d.typelem ELSE d.typbasetype END
00215 WHERE
00216 d.typtype = 'd'
00217 AND d.typname = '%s'
00218 ORDER BY d.typname", $strDomain));
00219
00220 if (!$result) {
00221 throw new SQLException("Query for domain [" . $strDomain . "] failed.", pg_last_error($this->conn->getResource()));
00222 }
00223
00224 $row = pg_fetch_assoc ($result);
00225 if (!$row)
00226 {
00227 throw new SQLException ("Domain [" . $strDomain . "] not found.");
00228 }
00229 $arrDomain = array ();
00230 $arrDomain['type'] = $row['basetype'];
00231 $arrLengthPrecision = $this->processLengthScale ($row['typtypmod'], $row['basetype']);
00232 $arrDomain['length'] = $arrLengthPrecision['length'];
00233 $arrDomain['scale'] = $arrLengthPrecision['scale'];
00234 $arrDomain['notnull'] = $row['typnotnull'];
00235 $arrDomain['default'] = $row['typdefault'];
00236 $arrDomain['hasdefault'] = (strlen (trim ($row['typdefault'])) > 0) ? 't' : 'f';
00237
00238 pg_free_result ($result);
00239 return $arrDomain;
00240 }
00241
00243 protected function initForeignKeys()
00244 {
00245 include_once 'creole/metadata/ForeignKeyInfo.php';
00246
00247 $result = pg_query ($this->conn->getResource(), sprintf ("SELECT
00248 conname,
00249 confupdtype,
00250 confdeltype,
00251 cl.relname as fktab,
00252 a2.attname as fkcol,
00253 cr.relname as reftab,
00254 a1.attname as refcol
00255 FROM pg_constraint ct
00256 JOIN pg_class cl ON cl.oid=conrelid
00257 JOIN pg_class cr ON cr.oid=confrelid
00258 LEFT JOIN pg_catalog.pg_attribute a1 ON a1.attrelid = ct.confrelid
00259 LEFT JOIN pg_catalog.pg_attribute a2 ON a2.attrelid = ct.conrelid
00260 WHERE
00261 contype='f'
00262 AND conrelid = %d
00263 AND a2.attnum = ct.conkey[1]
00264 AND a1.attnum = ct.confkey[1]
00265 ORDER BY conname", $this->oid));
00266 if (!$result) {
00267 throw new SQLException("Could not list foreign keys for table: " . $this->name, pg_last_error($this->conn->getResource()));
00268 }
00269
00270 while($row = pg_fetch_assoc($result)) {
00271 $name = $row['conname'];
00272 $local_table = $row['fktab'];
00273 $local_column = $row['fkcol'];
00274 $foreign_table = $row['reftab'];
00275 $foreign_column = $row['refcol'];
00276
00277
00278 switch ($row['confupdtype']) {
00279 case 'c':
00280 $onupdate = ForeignKeyInfo::CASCADE; break;
00281 case 'd':
00282 $onupdate = ForeignKeyInfo::SETDEFAULT; break;
00283 case 'n':
00284 $onupdate = ForeignKeyInfo::SETNULL; break;
00285 case 'r':
00286 $onupdate = ForeignKeyInfo::RESTRICT; break;
00287 default:
00288 case 'a':
00289
00290 $onupdate = ForeignKeyInfo::NONE; break;
00291 }
00292
00293 switch ($row['confdeltype']) {
00294 case 'c':
00295 $ondelete = ForeignKeyInfo::CASCADE; break;
00296 case 'd':
00297 $ondelete = ForeignKeyInfo::SETDEFAULT; break;
00298 case 'n':
00299 $ondelete = ForeignKeyInfo::SETNULL; break;
00300 case 'r':
00301 $ondelete = ForeignKeyInfo::RESTRICT; break;
00302 default:
00303 case 'a':
00304
00305 $ondelete = ForeignKeyInfo::NONE; break;
00306 }
00307
00308
00309 $foreignTable = $this->database->getTable($foreign_table);
00310 $foreignColumn = $foreignTable->getColumn($foreign_column);
00311
00312 $localTable = $this->database->getTable($local_table);
00313 $localColumn = $localTable->getColumn($local_column);
00314
00315 if (!isset($this->foreignKeys[$name])) {
00316 $this->foreignKeys[$name] = new ForeignKeyInfo($name);
00317 }
00318 $this->foreignKeys[$name]->addReference($localColumn, $foreignColumn, $ondelete, $onupdate);
00319 }
00320
00321 $this->fksLoaded = true;
00322 }
00323
00325 protected function initIndexes()
00326 {
00327 include_once 'creole/metadata/IndexInfo.php';
00328
00329
00330 if (!$this->colsLoaded) $this->initColumns();
00331
00332 $result = pg_query ($this->conn->getResource(), sprintf ("SELECT
00333 DISTINCT ON(cls.relname)
00334 cls.relname as idxname,
00335 indkey,
00336 indisunique
00337 FROM pg_index idx
00338 JOIN pg_class cls ON cls.oid=indexrelid
00339 WHERE indrelid = %d AND NOT indisprimary
00340 ORDER BY cls.relname", $this->oid));
00341
00342
00343 if (!$result) {
00344 throw new SQLException("Could not list indexes keys for table: " . $this->name, pg_last_error($this->conn->getResource()));
00345 }
00346
00347 while($row = pg_fetch_assoc($result)) {
00348 $name = $row["idxname"];
00349 $unique = ($row["indisunique"] == 't') ? true : false;
00350 if (!isset($this->indexes[$name])) {
00351 $this->indexes[$name] = new IndexInfo($name, $unique);
00352 }
00353 $arrColumns = explode (' ', $row['indkey']);
00354 foreach ($arrColumns as $intColNum)
00355 {
00356 $result2 = pg_query ($this->conn->getResource(), sprintf ("SELECT a.attname
00357 FROM pg_catalog.pg_class c JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
00358 WHERE c.oid = '%s' AND a.attnum = %d AND NOT a.attisdropped
00359 ORDER BY a.attnum", $this->oid, $intColNum));
00360 if (!$result2)
00361 {
00362 throw new SQLException("Could not list indexes keys for table: " . $this->name, pg_last_error($this->conn->getResource()));
00363 }
00364 $row2 = pg_fetch_assoc($result2);
00365 $this->indexes[$name]->addColumn($this->columns[ $row2['attname'] ]);
00366 }
00367 }
00368
00369 $this->indexesLoaded = true;
00370 }
00371
00373 protected function initPrimaryKey() {
00374
00375 include_once 'creole/metadata/PrimaryKeyInfo.php';
00376
00377
00378
00379 if (!$this->colsLoaded) $this->initColumns();
00380
00381
00382
00383 $result = pg_query($this->conn->getResource(), sprintf ("SELECT
00384 DISTINCT ON(cls.relname)
00385 cls.relname as idxname,
00386 indkey,
00387 indisunique
00388 FROM pg_index idx
00389 JOIN pg_class cls ON cls.oid=indexrelid
00390 WHERE indrelid = %s AND indisprimary
00391 ORDER BY cls.relname", $this->oid));
00392 if (!$result) {
00393 throw new SQLException("Could not list primary keys for table: " . $this->name, pg_last_error($this->conn->getResource()));
00394 }
00395
00396
00397
00398
00399 while($row = pg_fetch_assoc($result)) {
00400 $arrColumns = explode (' ', $row['indkey']);
00401 foreach ($arrColumns as $intColNum)
00402 {
00403 $result2 = pg_query ($this->conn->getResource(), sprintf ("SELECT a.attname
00404 FROM pg_catalog.pg_class c JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
00405 WHERE c.oid = '%s' AND a.attnum = %d AND NOT a.attisdropped
00406 ORDER BY a.attnum", $this->oid, $intColNum));
00407 if (!$result2)
00408 {
00409 throw new SQLException("Could not list indexes keys for table: " . $this->name, pg_last_error($this->conn->getResource()));
00410 }
00411 $row2 = pg_fetch_assoc($result2);
00412 if (!isset($this->primaryKey)) {
00413 $this->primaryKey = new PrimaryKeyInfo($row2['attname']);
00414 }
00415 $this->primaryKey->addColumn($this->columns[ $row2['attname'] ]);
00416 }
00417 }
00418 $this->pkLoaded = true;
00419 }
00420
00421
00422
00423 }