00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 class DboPostgres extends DboSource {
00040
00041
00042
00043
00044
00045
00046
00047 var $description = "PostgreSQL DBO Driver";
00048
00049
00050
00051
00052
00053
00054 var $_commands = array(
00055 'begin' => 'BEGIN',
00056 'commit' => 'COMMIT',
00057 'rollback' => 'ROLLBACK'
00058 );
00059
00060
00061
00062
00063
00064
00065 var $_baseConfig = array(
00066 'connect' => 'pg_pconnect',
00067 'persistent' => true,
00068 'host' => 'localhost',
00069 'login' => 'root',
00070 'password' => '',
00071 'database' => 'cake',
00072 'schema' => 'public',
00073 'port' => 5432,
00074 'encoding' => ''
00075 );
00076
00077 var $columns = array(
00078 'primary_key' => array('name' => 'serial NOT NULL'),
00079 'string' => array('name' => 'varchar', 'limit' => '255'),
00080 'text' => array('name' => 'text'),
00081 'integer' => array('name' => 'integer', 'formatter' => 'intval'),
00082 'float' => array('name' => 'float', 'formatter' => 'floatval'),
00083 'datetime' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
00084 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
00085 'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
00086 'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
00087 'binary' => array('name' => 'bytea'),
00088 'boolean' => array('name' => 'boolean'),
00089 'number' => array('name' => 'numeric'),
00090 'inet' => array('name' => 'inet')
00091 );
00092
00093 var $startQuote = '"';
00094
00095 var $endQuote = '"';
00096
00097
00098
00099
00100
00101
00102 var $_sequenceMap = array();
00103
00104
00105
00106
00107
00108 function connect() {
00109 $config = $this->config;
00110 $conn = "host='{$config['host']}' port='{$config['port']}' dbname='{$config['database']}' ";
00111 $conn .= "user='{$config['login']}' password='{$config['password']}'";
00112
00113 if (!$config['persistent']) {
00114 $this->connection = pg_connect($conn, PGSQL_CONNECT_FORCE_NEW);
00115 } else {
00116 $this->connection = pg_pconnect($conn);
00117 }
00118 $this->connected = false;
00119
00120 if ($this->connection) {
00121 $this->connected = true;
00122 $this->_execute("SET search_path TO " . $config['schema']);
00123 }
00124 if (!empty($config['encoding'])) {
00125 $this->setEncoding($config['encoding']);
00126 }
00127 return $this->connected;
00128 }
00129
00130
00131
00132
00133
00134 function disconnect() {
00135 if ($this->hasResult()) {
00136 pg_free_result($this->_result);
00137 }
00138 if (is_resource($this->connection)) {
00139 $this->connected = !pg_close($this->connection);
00140 } else {
00141 $this->connected = false;
00142 }
00143 return !$this->connected;
00144 }
00145
00146
00147
00148
00149
00150
00151 function _execute($sql) {
00152 return pg_query($this->connection, $sql);
00153 }
00154
00155
00156
00157
00158
00159 function listSources() {
00160 $cache = parent::listSources();
00161
00162 if ($cache != null) {
00163 return $cache;
00164 }
00165
00166 $schema = $this->config['schema'];
00167 $sql = "SELECT table_name as name FROM INFORMATION_SCHEMA.tables WHERE table_schema = '{$schema}';";
00168 $result = $this->fetchAll($sql, false);
00169
00170 if (!$result) {
00171 return array();
00172 } else {
00173 $tables = array();
00174
00175 foreach ($result as $item) {
00176 $tables[] = $item[0]['name'];
00177 }
00178
00179 parent::listSources($tables);
00180 return $tables;
00181 }
00182 }
00183
00184
00185
00186
00187
00188
00189
00190 function &describe(&$model) {
00191 $fields = parent::describe($model);
00192 $table = $this->fullTableName($model, false);
00193 $this->_sequenceMap[$table] = array();
00194
00195 if ($fields === null) {
00196 $cols = $this->fetchAll("SELECT DISTINCT column_name AS name, data_type AS type, is_nullable AS null, column_default AS default, ordinal_position AS position, character_maximum_length AS char_length, character_octet_length AS oct_length FROM information_schema.columns WHERE table_name =" . $this->value($table) . " ORDER BY position", false);
00197
00198 foreach ($cols as $column) {
00199 $colKey = array_keys($column);
00200
00201 if (isset($column[$colKey[0]]) && !isset($column[0])) {
00202 $column[0] = $column[$colKey[0]];
00203 }
00204
00205 if (isset($column[0])) {
00206 $c = $column[0];
00207
00208 if (!empty($c['char_length'])) {
00209 $length = intval($c['char_length']);
00210 } elseif (!empty($c['oct_length'])) {
00211 $length = intval($c['oct_length']);
00212 } else {
00213 $length = $this->length($c['type']);
00214 }
00215 $fields[$c['name']] = array(
00216 'type' => $this->column($c['type']),
00217 'null' => ($c['null'] == 'NO' ? false : true),
00218 'default' => preg_replace("/^'(.*)'$/", "$1", preg_replace('/::.*/', '', $c['default'])),
00219 'length' => $length
00220 );
00221 if ($c['name'] == $model->primaryKey) {
00222 $fields[$c['name']]['key'] = 'primary';
00223 if ($fields[$c['name']]['type'] !== 'string') {
00224 $fields[$c['name']]['length'] = 11;
00225 }
00226 }
00227 if ($fields[$c['name']]['default'] == 'NULL' || preg_match('/nextval\([\'"]?(\w+)/', $c['default'], $seq)) {
00228 $fields[$c['name']]['default'] = null;
00229 if (!empty($seq) && isset($seq[1])) {
00230 $this->_sequenceMap[$table][$c['name']] = $seq[1];
00231 }
00232 }
00233 }
00234 }
00235 $this->__cacheDescription($table, $fields);
00236 }
00237 if (isset($model->sequence)) {
00238 $this->_sequenceMap[$table][$model->primaryKey] = $model->sequence;
00239 }
00240 return $fields;
00241 }
00242
00243
00244
00245
00246
00247
00248
00249
00250 function value($data, $column = null) {
00251
00252 $parent = parent::value($data, $column);
00253 if ($parent != null) {
00254 return $parent;
00255 }
00256
00257 if ($data === null) {
00258 return 'NULL';
00259 }
00260 if (empty($column)) {
00261 $column = $this->introspectType($data);
00262 }
00263
00264 switch($column) {
00265 case 'inet':
00266 case 'float':
00267 case 'integer':
00268 if ($data === '') {
00269 return 'DEFAULT';
00270 }
00271 case 'binary':
00272 $data = pg_escape_bytea($data);
00273 break;
00274 case 'boolean':
00275 if ($data === true || $data === 't') {
00276 return 'TRUE';
00277 } elseif ($data === false || $data === 'f') {
00278 return 'FALSE';
00279 }
00280 return (!empty($data) ? 'TRUE' : 'FALSE');
00281 break;
00282 default:
00283 $data = pg_escape_string($data);
00284 break;
00285 }
00286 return "'" . $data . "'";
00287 }
00288
00289
00290
00291
00292
00293 function lastError() {
00294 $error = pg_last_error($this->connection);
00295 if ($error) {
00296 return $error;
00297 }
00298 return null;
00299 }
00300
00301
00302
00303
00304
00305
00306 function lastAffected() {
00307 if ($this->_result) {
00308 $return = pg_affected_rows($this->_result);
00309 return $return;
00310 }
00311 return false;
00312 }
00313
00314
00315
00316
00317
00318
00319 function lastNumRows() {
00320 if ($this->_result) {
00321 $return = pg_num_rows($this->_result);
00322 return $return;
00323 }
00324 return false;
00325 }
00326
00327
00328
00329
00330
00331
00332
00333 function lastInsertId($source, $field = 'id') {
00334 $seq = $this->getSequence($source, $field);
00335 $data = $this->fetchRow("SELECT currval('{$seq}') as max");
00336 return $data[0]['max'];
00337 }
00338
00339
00340
00341
00342
00343
00344
00345 function getSequence($table, $field = 'id') {
00346 if (is_object($table)) {
00347 $table = $this->fullTableName($table, false);
00348 }
00349 if (isset($this->_sequenceMap[$table]) && isset($this->_sequenceMap[$table][$field])) {
00350 return $this->_sequenceMap[$table][$field];
00351 } else {
00352 return "{$table}_{$field}_seq";
00353 }
00354 }
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364 function truncate($table, $reset = 0) {
00365 if (parent::truncate($table)) {
00366 $table = $this->fullTableName($table, false);
00367 if (isset($this->_sequenceMap[$table]) && $reset !== 1) {
00368 foreach ($this->_sequenceMap[$table] as $field => $sequence) {
00369 if ($reset === 0) {
00370 $this->execute("ALTER SEQUENCE \"{$sequence}\" RESTART WITH 1");
00371 } elseif ($reset === -1) {
00372 $this->execute("DROP SEQUENCE IF EXISTS \"{$sequence}\"");
00373 }
00374 }
00375 }
00376 return true;
00377 }
00378 return false;
00379 }
00380
00381
00382
00383
00384
00385
00386 function name($data) {
00387 return parent::name(str_replace('"__"', '__', $data));
00388 }
00389
00390
00391
00392
00393
00394
00395
00396
00397 function fields(&$model, $alias = null, $fields = array(), $quote = true) {
00398 if (empty($alias)) {
00399 $alias = $model->alias;
00400 }
00401 $fields = parent::fields($model, $alias, $fields, false);
00402
00403 if (!$quote) {
00404 return $fields;
00405 }
00406 $count = count($fields);
00407
00408 if ($count >= 1 && $fields[0] != '*' && strpos($fields[0], 'COUNT(*)') === false) {
00409 for ($i = 0; $i < $count; $i++) {
00410 if (!preg_match('/^.+\\(.*\\)/', $fields[$i]) && !preg_match('/\s+AS\s+/', $fields[$i])) {
00411 $prepend = '';
00412 if (strpos($fields[$i], 'DISTINCT') !== false) {
00413 $prepend = 'DISTINCT ';
00414 $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
00415 }
00416
00417 if (strrpos($fields[$i], '.') === false) {
00418 $fields[$i] = $prepend . $this->name($alias) . '.' . $this->name($fields[$i]) . ' AS ' . $this->name($alias . '__' . $fields[$i]);
00419 } else {
00420 $build = explode('.', $fields[$i]);
00421 $fields[$i] = $prepend . $this->name($build[0]) . '.' . $this->name($build[1]) . ' AS ' . $this->name($build[0] . '__' . $build[1]);
00422 }
00423 }
00424 }
00425 }
00426 return $fields;
00427 }
00428
00429
00430
00431
00432
00433
00434
00435 function limit($limit, $offset = null) {
00436 if ($limit) {
00437 $rt = '';
00438 if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
00439 $rt = ' LIMIT';
00440 }
00441
00442 $rt .= ' ' . $limit;
00443 if ($offset) {
00444 $rt .= ' OFFSET ' . $offset;
00445 }
00446
00447 return $rt;
00448 }
00449 return null;
00450 }
00451
00452
00453
00454
00455
00456
00457 function column($real) {
00458 if (is_array($real)) {
00459 $col = $real['name'];
00460 if (isset($real['limit'])) {
00461 $col .= '(' . $real['limit'] . ')';
00462 }
00463 return $col;
00464 }
00465
00466 $col = str_replace(')', '', $real);
00467 $limit = null;
00468 if (strpos($col, '(') !== false) {
00469 list($col, $limit) = explode('(', $col);
00470 }
00471
00472 if (in_array($col, array('date', 'time'))) {
00473 return $col;
00474 }
00475 if (strpos($col, 'timestamp') !== false) {
00476 return 'datetime';
00477 }
00478 if (strpos($col, 'time') === 0) {
00479 return 'time';
00480 }
00481 if ($col == 'inet') {
00482 return('inet');
00483 }
00484 if ($col == 'boolean') {
00485 return 'boolean';
00486 }
00487 if (strpos($col, 'int') !== false && $col != 'interval') {
00488 return 'integer';
00489 }
00490 if (strpos($col, 'char') !== false) {
00491 return 'string';
00492 }
00493 if (strpos($col, 'text') !== false) {
00494 return 'text';
00495 }
00496 if (strpos($col, 'bytea') !== false) {
00497 return 'binary';
00498 }
00499 if (in_array($col, array('float', 'float4', 'float8', 'double', 'double precision', 'decimal', 'real', 'numeric'))) {
00500 return 'float';
00501 }
00502 return 'text';
00503 }
00504
00505
00506
00507
00508
00509
00510 function length($real) {
00511 $col = str_replace(array(')', 'unsigned'), '', $real);
00512 $limit = null;
00513
00514 if (strpos($col, '(') !== false) {
00515 list($col, $limit) = explode('(', $col);
00516 }
00517
00518 if ($limit != null) {
00519 return intval($limit);
00520 }
00521 return null;
00522 }
00523
00524
00525
00526
00527
00528 function resultSet(&$results) {
00529 $this->results =& $results;
00530 $this->map = array();
00531 $num_fields = pg_num_fields($results);
00532 $index = 0;
00533 $j = 0;
00534
00535 while ($j < $num_fields) {
00536 $columnName = pg_field_name($results, $j);
00537
00538 if (strpos($columnName, '__')) {
00539 $parts = explode('__', $columnName);
00540 $this->map[$index++] = array($parts[0], $parts[1]);
00541 } else {
00542 $this->map[$index++] = array(0, $columnName);
00543 }
00544 $j++;
00545 }
00546 }
00547
00548
00549
00550
00551
00552 function fetchResult() {
00553 if ($row = pg_fetch_row($this->results)) {
00554 $resultRow = array();
00555 $i = 0;
00556
00557 foreach ($row as $index => $field) {
00558 list($table, $column) = $this->map[$index];
00559 $resultRow[$table][$column] = $row[$index];
00560 $i++;
00561 }
00562 return $resultRow;
00563 } else {
00564 return false;
00565 }
00566 }
00567
00568
00569
00570
00571
00572
00573
00574 function boolean($data, $quote = true) {
00575 switch (true) {
00576 case ($data === true || $data === false):
00577 return $data;
00578 case ($data === 't' || $data === 'f'):
00579 return ($data === 't');
00580 default:
00581 return (bool)$data;
00582 break;
00583 }
00584 }
00585
00586
00587
00588
00589
00590
00591 function setEncoding($enc) {
00592 return pg_set_client_encoding($this->connection, $enc) == 0;
00593 }
00594
00595
00596
00597
00598
00599 function getEncoding() {
00600 return pg_client_encoding($this->connection);
00601 }
00602
00603
00604
00605
00606
00607
00608
00609 function buildColumn($column) {
00610 $out = preg_replace('/integer\([0-9]+\)/', 'integer', parent::buildColumn($column));
00611 $out = str_replace('integer serial', 'serial', $out);
00612
00613 if (strpos($out, 'DEFAULT DEFAULT')) {
00614 if (isset($column['null']) && $column['null']) {
00615 $out = str_replace('DEFAULT DEFAULT', 'DEFAULT NULL', $out);
00616 } elseif (in_array($column['type'], array('integer', 'float'))) {
00617 $out = str_replace('DEFAULT DEFAULT', 'DEFAULT 0', $out);
00618 } elseif ($column['type'] == 'boolean') {
00619 $out = str_replace('DEFAULT DEFAULT', 'DEFAULT FALSE', $out);
00620 }
00621 }
00622 return $out;
00623 }
00624
00625
00626
00627
00628
00629
00630
00631 function buildIndex($indexes, $table = null) {
00632 $join = array();
00633
00634 foreach ($indexes as $name => $value) {
00635 if ($name == 'PRIMARY') {
00636 $out = 'PRIMARY KEY (' . $this->name($value['column']) . ')';
00637 } else {
00638 $out = 'CREATE ';
00639 if (!empty($value['unique'])) {
00640 $out .= 'UNIQUE ';
00641 }
00642 if (is_array($value['column'])) {
00643 $value['column'] = join(', ', array_map(array(&$this, 'name'), $value['column']));
00644 } else {
00645 $value['column'] = $this->name($value['column']);
00646 }
00647 $out .= "INDEX {$name} ON {$table}({$value['column']});";
00648 }
00649 $join[] = $out;
00650 }
00651 return $join;
00652 }
00653
00654
00655
00656
00657
00658
00659
00660 function renderStatement($type, $data) {
00661 switch (strtolower($type)) {
00662 case 'schema':
00663 extract($data);
00664
00665 foreach ($indexes as $i => $index) {
00666 if (preg_match('/PRIMARY KEY/', $index)) {
00667 unset($indexes[$i]);
00668 $columns[] = $index;
00669 break;
00670 }
00671 }
00672
00673 foreach (array('columns', 'indexes') as $var) {
00674 if (is_array(${$var})) {
00675 ${$var} = "\t" . join(",\n\t", array_filter(${$var}));
00676 }
00677 }
00678 return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}";
00679 break;
00680 default:
00681 return parent::renderStatement($type, $data);
00682 break;
00683 }
00684 }
00685 }
00686
00687 ?>