1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16:
17: App::uses('DboSource', 'Model/Datasource');
18:
19: 20: 21: 22: 23:
24: class Postgres extends DboSource {
25:
26: 27: 28: 29: 30:
31: public $description = "PostgreSQL DBO Driver";
32:
33: 34: 35: 36: 37:
38: protected $_baseConfig = array(
39: 'persistent' => true,
40: 'host' => 'localhost',
41: 'login' => 'root',
42: 'password' => '',
43: 'database' => 'cake',
44: 'schema' => 'public',
45: 'port' => 5432,
46: 'encoding' => '',
47: 'sslmode' => 'allow',
48: 'flags' => array()
49: );
50:
51: 52: 53: 54: 55:
56: public $columns = array(
57: 'primary_key' => array('name' => 'serial NOT NULL'),
58: 'string' => array('name' => 'varchar', 'limit' => '255'),
59: 'text' => array('name' => 'text'),
60: 'integer' => array('name' => 'integer', 'formatter' => 'intval'),
61: 'biginteger' => array('name' => 'bigint', 'limit' => '20'),
62: 'float' => array('name' => 'float', 'formatter' => 'floatval'),
63: 'decimal' => array('name' => 'decimal', 'formatter' => 'floatval'),
64: 'datetime' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
65: 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
66: 'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
67: 'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
68: 'binary' => array('name' => 'bytea'),
69: 'boolean' => array('name' => 'boolean'),
70: 'number' => array('name' => 'numeric'),
71: 'inet' => array('name' => 'inet')
72: );
73:
74: 75: 76: 77: 78:
79: public $startQuote = '"';
80:
81: 82: 83: 84: 85:
86: public $endQuote = '"';
87:
88: 89: 90: 91: 92: 93:
94: protected $_sequenceMap = array();
95:
96: 97: 98: 99: 100:
101: protected $_sqlOps = array('like', 'ilike', 'or', 'not', 'in', 'between', '~', '~\*', '\!~', '\!~\*', 'similar to');
102:
103: 104: 105: 106: 107: 108:
109: public function connect() {
110: $config = $this->config;
111: $this->connected = false;
112:
113: $flags = $config['flags'] + array(
114: PDO::ATTR_PERSISTENT => $config['persistent'],
115: PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
116: );
117:
118: try {
119: $this->_connection = new PDO(
120: "pgsql:host={$config['host']};port={$config['port']};dbname={$config['database']};sslmode={$config['sslmode']}",
121: $config['login'],
122: $config['password'],
123: $flags
124: );
125:
126: $this->connected = true;
127: if (!empty($config['encoding'])) {
128: $this->setEncoding($config['encoding']);
129: }
130: if (!empty($config['schema'])) {
131: $this->_execute('SET search_path TO "' . $config['schema'] . '"');
132: }
133: if (!empty($config['settings'])) {
134: foreach ($config['settings'] as $key => $value) {
135: $this->_execute("SET $key TO $value");
136: }
137: }
138: } catch (PDOException $e) {
139: throw new MissingConnectionException(array(
140: 'class' => get_class($this),
141: 'message' => $e->getMessage()
142: ));
143: }
144:
145: return $this->connected;
146: }
147:
148: 149: 150: 151: 152:
153: public function enabled() {
154: return in_array('pgsql', PDO::getAvailableDrivers());
155: }
156:
157: 158: 159: 160: 161: 162:
163: public function listSources($data = null) {
164: $cache = parent::listSources();
165:
166: if ($cache) {
167: return $cache;
168: }
169:
170: $schema = $this->config['schema'];
171: $sql = "SELECT table_name as name FROM INFORMATION_SCHEMA.tables WHERE table_schema = ?";
172: $result = $this->_execute($sql, array($schema));
173:
174: if (!$result) {
175: return array();
176: }
177:
178: $tables = array();
179:
180: foreach ($result as $item) {
181: $tables[] = $item->name;
182: }
183:
184: $result->closeCursor();
185: parent::listSources($tables);
186: return $tables;
187: }
188:
189: 190: 191: 192: 193: 194:
195: public function describe($model) {
196: $table = $this->fullTableName($model, false, false);
197: $fields = parent::describe($table);
198: $this->_sequenceMap[$table] = array();
199: $cols = null;
200:
201: if ($fields === null) {
202: $cols = $this->_execute(
203: "SELECT DISTINCT table_schema AS schema, column_name AS name, data_type AS type, is_nullable AS null,
204: column_default AS default, ordinal_position AS position, character_maximum_length AS char_length,
205: character_octet_length AS oct_length FROM information_schema.columns
206: WHERE table_name = ? AND table_schema = ? ORDER BY position",
207: array($table, $this->config['schema'])
208: );
209:
210:
211:
212: foreach ($cols as $c) {
213: $type = $c->type;
214: if (!empty($c->oct_length) && $c->char_length === null) {
215: if ($c->type === 'character varying') {
216: $length = null;
217: $type = 'text';
218: } elseif ($c->type === 'uuid') {
219: $length = 36;
220: } else {
221: $length = (int)$c->oct_length;
222: }
223: } elseif (!empty($c->char_length)) {
224: $length = (int)$c->char_length;
225: } else {
226: $length = $this->length($c->type);
227: }
228: if (empty($length)) {
229: $length = null;
230: }
231: $fields[$c->name] = array(
232: 'type' => $this->column($type),
233: 'null' => ($c->null === 'NO' ? false : true),
234: 'default' => preg_replace(
235: "/^'(.*)'$/",
236: "$1",
237: preg_replace('/::.*/', '', $c->default)
238: ),
239: 'length' => $length
240: );
241: if ($model instanceof Model) {
242: if ($c->name === $model->primaryKey) {
243: $fields[$c->name]['key'] = 'primary';
244: if ($fields[$c->name]['type'] !== 'string') {
245: $fields[$c->name]['length'] = 11;
246: }
247: }
248: }
249: if (
250: $fields[$c->name]['default'] === 'NULL' ||
251: $c->default === null ||
252: preg_match('/nextval\([\'"]?([\w.]+)/', $c->default, $seq)
253: ) {
254: $fields[$c->name]['default'] = null;
255: if (!empty($seq) && isset($seq[1])) {
256: if (strpos($seq[1], '.') === false) {
257: $sequenceName = $c->schema . '.' . $seq[1];
258: } else {
259: $sequenceName = $seq[1];
260: }
261: $this->_sequenceMap[$table][$c->name] = $sequenceName;
262: }
263: }
264: if ($fields[$c->name]['type'] === 'timestamp' && $fields[$c->name]['default'] === '') {
265: $fields[$c->name]['default'] = null;
266: }
267: if ($fields[$c->name]['type'] === 'boolean' && !empty($fields[$c->name]['default'])) {
268: $fields[$c->name]['default'] = constant($fields[$c->name]['default']);
269: }
270: }
271: $this->_cacheDescription($table, $fields);
272: }
273:
274:
275: if (isset($model->sequence)) {
276: $this->_sequenceMap[$table][$model->primaryKey] = $model->sequence;
277: }
278:
279: if ($cols) {
280: $cols->closeCursor();
281: }
282: return $fields;
283: }
284:
285: 286: 287: 288: 289: 290: 291:
292: public function lastInsertId($source = null, $field = 'id') {
293: $seq = $this->getSequence($source, $field);
294: return $this->_connection->lastInsertId($seq);
295: }
296:
297: 298: 299: 300: 301: 302: 303:
304: public function getSequence($table, $field = 'id') {
305: if (is_object($table)) {
306: $table = $this->fullTableName($table, false, false);
307: }
308: if (!isset($this->_sequenceMap[$table])) {
309: $this->describe($table);
310: }
311: if (isset($this->_sequenceMap[$table][$field])) {
312: return $this->_sequenceMap[$table][$field];
313: }
314: return "{$table}_{$field}_seq";
315: }
316:
317: 318: 319: 320: 321: 322: 323: 324: 325:
326: public function resetSequence($table, $column) {
327: $tableName = $this->fullTableName($table, false, false);
328: $fullTable = $this->fullTableName($table);
329:
330: $sequence = $this->value($this->getSequence($tableName, $column));
331: $column = $this->name($column);
332: $this->execute("SELECT setval($sequence, (SELECT MAX($column) FROM $fullTable))");
333: return true;
334: }
335:
336: 337: 338: 339: 340: 341: 342: 343:
344: public function truncate($table, $reset = false) {
345: $table = $this->fullTableName($table, false, false);
346: if (!isset($this->_sequenceMap[$table])) {
347: $cache = $this->cacheSources;
348: $this->cacheSources = false;
349: $this->describe($table);
350: $this->cacheSources = $cache;
351: }
352: if ($this->execute('DELETE FROM ' . $this->fullTableName($table))) {
353: if (isset($this->_sequenceMap[$table]) && $reset != true) {
354: foreach ($this->_sequenceMap[$table] as $sequence) {
355: $quoted = $this->name($sequence);
356: $this->_execute("ALTER SEQUENCE {$quoted} RESTART WITH 1");
357: }
358: }
359: return true;
360: }
361: return false;
362: }
363:
364: 365: 366: 367: 368: 369:
370: public function name($data) {
371: if (is_string($data)) {
372: $data = str_replace('"__"', '__', $data);
373: }
374: return parent::name($data);
375: }
376:
377: 378: 379: 380: 381: 382: 383: 384: 385:
386: public function fields(Model $model, $alias = null, $fields = array(), $quote = true) {
387: if (empty($alias)) {
388: $alias = $model->alias;
389: }
390: $fields = parent::fields($model, $alias, $fields, false);
391:
392: if (!$quote) {
393: return $fields;
394: }
395: $count = count($fields);
396:
397: if ($count >= 1 && !preg_match('/^\s*COUNT\(\*/', $fields[0])) {
398: $result = array();
399: for ($i = 0; $i < $count; $i++) {
400: if (!preg_match('/^.+\\(.*\\)/', $fields[$i]) && !preg_match('/\s+AS\s+/', $fields[$i])) {
401: if (substr($fields[$i], -1) === '*') {
402: if (strpos($fields[$i], '.') !== false && $fields[$i] != $alias . '.*') {
403: $build = explode('.', $fields[$i]);
404: $AssociatedModel = $model->{$build[0]};
405: } else {
406: $AssociatedModel = $model;
407: }
408:
409: $_fields = $this->fields($AssociatedModel, $AssociatedModel->alias, array_keys($AssociatedModel->schema()));
410: $result = array_merge($result, $_fields);
411: continue;
412: }
413:
414: $prepend = '';
415: if (strpos($fields[$i], 'DISTINCT') !== false) {
416: $prepend = 'DISTINCT ';
417: $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
418: }
419:
420: if (strrpos($fields[$i], '.') === false) {
421: $fields[$i] = $prepend . $this->name($alias) . '.' . $this->name($fields[$i]) . ' AS ' . $this->name($alias . '__' . $fields[$i]);
422: } else {
423: $build = explode('.', $fields[$i]);
424: $fields[$i] = $prepend . $this->name($build[0]) . '.' . $this->name($build[1]) . ' AS ' . $this->name($build[0] . '__' . $build[1]);
425: }
426: } else {
427: $fields[$i] = preg_replace_callback('/\(([\s\.\w]+)\)/', array(&$this, '_quoteFunctionField'), $fields[$i]);
428: }
429: $result[] = $fields[$i];
430: }
431: return $result;
432: }
433: return $fields;
434: }
435:
436: 437: 438: 439: 440: 441: 442:
443: protected function _quoteFunctionField($match) {
444: $prepend = '';
445: if (strpos($match[1], 'DISTINCT') !== false) {
446: $prepend = 'DISTINCT ';
447: $match[1] = trim(str_replace('DISTINCT', '', $match[1]));
448: }
449: $constant = preg_match('/^\d+|NULL|FALSE|TRUE$/i', $match[1]);
450:
451: if (!$constant && strpos($match[1], '.') === false) {
452: $match[1] = $this->name($match[1]);
453: } elseif (!$constant) {
454: $parts = explode('.', $match[1]);
455: if (!Hash::numeric($parts)) {
456: $match[1] = $this->name($match[1]);
457: }
458: }
459: return '(' . $prepend . $match[1] . ')';
460: }
461:
462: 463: 464: 465: 466: 467:
468: public function index($model) {
469: $index = array();
470: $table = $this->fullTableName($model, false, false);
471: if ($table) {
472: $indexes = $this->query("SELECT c2.relname, i.indisprimary, i.indisunique, i.indisclustered, i.indisvalid, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) as statement, c2.reltablespace
473: FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
474: WHERE c.oid = (
475: SELECT c.oid
476: FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
477: WHERE c.relname ~ '^(" . $table . ")$'
478: AND pg_catalog.pg_table_is_visible(c.oid)
479: AND n.nspname ~ '^(" . $this->config['schema'] . ")$'
480: )
481: AND c.oid = i.indrelid AND i.indexrelid = c2.oid
482: ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname", false);
483: foreach ($indexes as $info) {
484: $key = array_pop($info);
485: if ($key['indisprimary']) {
486: $key['relname'] = 'PRIMARY';
487: }
488: preg_match('/\(([^\)]+)\)/', $key['statement'], $indexColumns);
489: $parsedColumn = $indexColumns[1];
490: if (strpos($indexColumns[1], ',') !== false) {
491: $parsedColumn = explode(', ', $indexColumns[1]);
492: }
493: $index[$key['relname']]['unique'] = $key['indisunique'];
494: $index[$key['relname']]['column'] = $parsedColumn;
495: }
496: }
497: return $index;
498: }
499:
500: 501: 502: 503: 504: 505: 506:
507: public function alterSchema($compare, $table = null) {
508: if (!is_array($compare)) {
509: return false;
510: }
511: $out = '';
512: $colList = array();
513: foreach ($compare as $curTable => $types) {
514: $indexes = $colList = array();
515: if (!$table || $table === $curTable) {
516: $out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
517: foreach ($types as $type => $column) {
518: if (isset($column['indexes'])) {
519: $indexes[$type] = $column['indexes'];
520: unset($column['indexes']);
521: }
522: switch ($type) {
523: case 'add':
524: foreach ($column as $field => $col) {
525: $col['name'] = $field;
526: $colList[] = 'ADD COLUMN ' . $this->buildColumn($col);
527: }
528: break;
529: case 'drop':
530: foreach ($column as $field => $col) {
531: $col['name'] = $field;
532: $colList[] = 'DROP COLUMN ' . $this->name($field);
533: }
534: break;
535: case 'change':
536: $schema = $this->describe($curTable);
537: foreach ($column as $field => $col) {
538: if (!isset($col['name'])) {
539: $col['name'] = $field;
540: }
541: $original = $schema[$field];
542: $fieldName = $this->name($field);
543:
544: $default = isset($col['default']) ? $col['default'] : null;
545: $nullable = isset($col['null']) ? $col['null'] : null;
546: $boolToInt = $original['type'] === 'boolean' && $col['type'] === 'integer';
547: unset($col['default'], $col['null']);
548: if ($field !== $col['name']) {
549: $newName = $this->name($col['name']);
550: $out .= "\tRENAME {$fieldName} TO {$newName};\n";
551: $out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
552: $fieldName = $newName;
553: }
554:
555: if ($boolToInt) {
556: $colList[] = 'ALTER COLUMN ' . $fieldName . ' SET DEFAULT NULL';
557: $colList[] = 'ALTER COLUMN ' . $fieldName . ' TYPE ' . str_replace(array($fieldName, 'NOT NULL'), '', $this->buildColumn($col)) . ' USING CASE WHEN TRUE THEN 1 ELSE 0 END';
558: } else {
559: if ($original['type'] === 'text' && $col['type'] === 'integer') {
560: $colList[] = 'ALTER COLUMN ' . $fieldName . ' TYPE ' . str_replace(array($fieldName, 'NOT NULL'), '', $this->buildColumn($col)) . " USING cast({$fieldName} as INTEGER)";
561: } else {
562: $colList[] = 'ALTER COLUMN ' . $fieldName . ' TYPE ' . str_replace(array($fieldName, 'NOT NULL'), '', $this->buildColumn($col));
563: }
564: }
565:
566: if (isset($nullable)) {
567: $nullable = ($nullable) ? 'DROP NOT NULL' : 'SET NOT NULL';
568: $colList[] = 'ALTER COLUMN ' . $fieldName . ' ' . $nullable;
569: }
570:
571: if (isset($default)) {
572: if (!$boolToInt) {
573: $colList[] = 'ALTER COLUMN ' . $fieldName . ' SET DEFAULT ' . $this->value($default, $col['type']);
574: }
575: } else {
576: $colList[] = 'ALTER COLUMN ' . $fieldName . ' DROP DEFAULT';
577: }
578:
579: }
580: break;
581: }
582: }
583: if (isset($indexes['drop']['PRIMARY'])) {
584: $colList[] = 'DROP CONSTRAINT ' . $curTable . '_pkey';
585: }
586: if (isset($indexes['add']['PRIMARY'])) {
587: $cols = $indexes['add']['PRIMARY']['column'];
588: if (is_array($cols)) {
589: $cols = implode(', ', $cols);
590: }
591: $colList[] = 'ADD PRIMARY KEY (' . $cols . ')';
592: }
593:
594: if (!empty($colList)) {
595: $out .= "\t" . implode(",\n\t", $colList) . ";\n\n";
596: } else {
597: $out = '';
598: }
599: $out .= implode(";\n\t", $this->_alterIndexes($curTable, $indexes));
600: }
601: }
602: return $out;
603: }
604:
605: 606: 607: 608: 609: 610: 611:
612: protected function _alterIndexes($table, $indexes) {
613: $alter = array();
614: if (isset($indexes['drop'])) {
615: foreach ($indexes['drop'] as $name => $value) {
616: $out = 'DROP ';
617: if ($name === 'PRIMARY') {
618: continue;
619: } else {
620: $out .= 'INDEX ' . $name;
621: }
622: $alter[] = $out;
623: }
624: }
625: if (isset($indexes['add'])) {
626: foreach ($indexes['add'] as $name => $value) {
627: $out = 'CREATE ';
628: if ($name === 'PRIMARY') {
629: continue;
630: } else {
631: if (!empty($value['unique'])) {
632: $out .= 'UNIQUE ';
633: }
634: $out .= 'INDEX ';
635: }
636: if (is_array($value['column'])) {
637: $out .= $name . ' ON ' . $table . ' (' . implode(', ', array_map(array(&$this, 'name'), $value['column'])) . ')';
638: } else {
639: $out .= $name . ' ON ' . $table . ' (' . $this->name($value['column']) . ')';
640: }
641: $alter[] = $out;
642: }
643: }
644: return $alter;
645: }
646:
647: 648: 649: 650: 651: 652: 653:
654: public function limit($limit, $offset = null) {
655: if ($limit) {
656: $rt = sprintf(' LIMIT %u', $limit);
657: if ($offset) {
658: $rt .= sprintf(' OFFSET %u', $offset);
659: }
660: return $rt;
661: }
662: return null;
663: }
664:
665: 666: 667: 668: 669: 670:
671: public function column($real) {
672: if (is_array($real)) {
673: $col = $real['name'];
674: if (isset($real['limit'])) {
675: $col .= '(' . $real['limit'] . ')';
676: }
677: return $col;
678: }
679:
680: $col = str_replace(')', '', $real);
681:
682: if (strpos($col, '(') !== false) {
683: list($col, $limit) = explode('(', $col);
684: }
685:
686: $floats = array(
687: 'float', 'float4', 'float8', 'double', 'double precision', 'real'
688: );
689:
690: switch (true) {
691: case (in_array($col, array('date', 'time', 'inet', 'boolean'))):
692: return $col;
693: case (strpos($col, 'timestamp') !== false):
694: return 'datetime';
695: case (strpos($col, 'time') === 0):
696: return 'time';
697: case ($col === 'bigint'):
698: return 'biginteger';
699: case (strpos($col, 'int') !== false && $col !== 'interval'):
700: return 'integer';
701: case (strpos($col, 'char') !== false || $col === 'uuid'):
702: return 'string';
703: case (strpos($col, 'text') !== false):
704: return 'text';
705: case (strpos($col, 'bytea') !== false):
706: return 'binary';
707: case ($col === 'decimal' || $col === 'numeric'):
708: return 'decimal';
709: case (in_array($col, $floats)):
710: return 'float';
711: default:
712: return 'text';
713: }
714: }
715:
716: 717: 718: 719: 720: 721:
722: public function length($real) {
723: $col = str_replace(array(')', 'unsigned'), '', $real);
724: $limit = null;
725:
726: if (strpos($col, '(') !== false) {
727: list($col, $limit) = explode('(', $col);
728: }
729: if ($col === 'uuid') {
730: return 36;
731: }
732: if ($limit) {
733: return (int)$limit;
734: }
735: return null;
736: }
737:
738: 739: 740: 741: 742: 743:
744: public function resultSet(&$results) {
745: $this->map = array();
746: $numFields = $results->columnCount();
747: $index = 0;
748: $j = 0;
749:
750: while ($j < $numFields) {
751: $column = $results->getColumnMeta($j);
752: if (strpos($column['name'], '__')) {
753: list($table, $name) = explode('__', $column['name']);
754: $this->map[$index++] = array($table, $name, $column['native_type']);
755: } else {
756: $this->map[$index++] = array(0, $column['name'], $column['native_type']);
757: }
758: $j++;
759: }
760: }
761:
762: 763: 764: 765: 766:
767: public function fetchResult() {
768: if ($row = $this->_result->fetch(PDO::FETCH_NUM)) {
769: $resultRow = array();
770:
771: foreach ($this->map as $index => $meta) {
772: list($table, $column, $type) = $meta;
773:
774: switch ($type) {
775: case 'bool':
776: $resultRow[$table][$column] = $row[$index] === null ? null : $this->boolean($row[$index]);
777: break;
778: case 'binary':
779: case 'bytea':
780: $resultRow[$table][$column] = $row[$index] === null ? null : stream_get_contents($row[$index]);
781: break;
782: default:
783: $resultRow[$table][$column] = $row[$index];
784: }
785: }
786: return $resultRow;
787: }
788: $this->_result->closeCursor();
789: return false;
790: }
791:
792: 793: 794: 795: 796: 797: 798:
799: public function boolean($data, $quote = false) {
800: switch (true) {
801: case ($data === true || $data === false):
802: $result = $data;
803: break;
804: case ($data === 't' || $data === 'f'):
805: $result = ($data === 't');
806: break;
807: case ($data === 'true' || $data === 'false'):
808: $result = ($data === 'true');
809: break;
810: case ($data === 'TRUE' || $data === 'FALSE'):
811: $result = ($data === 'TRUE');
812: break;
813: default:
814: $result = (bool)$data;
815: }
816:
817: if ($quote) {
818: return ($result) ? 'TRUE' : 'FALSE';
819: }
820: return (bool)$result;
821: }
822:
823: 824: 825: 826: 827: 828:
829: public function setEncoding($enc) {
830: return $this->_execute('SET NAMES ' . $this->value($enc)) !== false;
831: }
832:
833: 834: 835: 836: 837:
838: public function getEncoding() {
839: $result = $this->_execute('SHOW client_encoding')->fetch();
840: if ($result === false) {
841: return false;
842: }
843: return (isset($result['client_encoding'])) ? $result['client_encoding'] : false;
844: }
845:
846: 847: 848: 849: 850: 851: 852: 853:
854: public function buildColumn($column) {
855: $col = $this->columns[$column['type']];
856: if (!isset($col['length']) && !isset($col['limit'])) {
857: unset($column['length']);
858: }
859: $out = parent::buildColumn($column);
860:
861: $out = preg_replace(
862: '/integer\([0-9]+\)/',
863: 'integer',
864: $out
865: );
866: $out = preg_replace(
867: '/bigint\([0-9]+\)/',
868: 'bigint',
869: $out
870: );
871:
872: $out = str_replace('integer serial', 'serial', $out);
873: $out = str_replace('bigint serial', 'bigserial', $out);
874: if (strpos($out, 'timestamp DEFAULT')) {
875: if (isset($column['null']) && $column['null']) {
876: $out = str_replace('DEFAULT NULL', '', $out);
877: } else {
878: $out = str_replace('DEFAULT NOT NULL', '', $out);
879: }
880: }
881: if (strpos($out, 'DEFAULT DEFAULT')) {
882: if (isset($column['null']) && $column['null']) {
883: $out = str_replace('DEFAULT DEFAULT', 'DEFAULT NULL', $out);
884: } elseif (in_array($column['type'], array('integer', 'float'))) {
885: $out = str_replace('DEFAULT DEFAULT', 'DEFAULT 0', $out);
886: } elseif ($column['type'] === 'boolean') {
887: $out = str_replace('DEFAULT DEFAULT', 'DEFAULT FALSE', $out);
888: }
889: }
890: return $out;
891: }
892:
893: 894: 895: 896: 897: 898: 899:
900: public function buildIndex($indexes, $table = null) {
901: $join = array();
902: if (!is_array($indexes)) {
903: return array();
904: }
905: foreach ($indexes as $name => $value) {
906: if ($name === 'PRIMARY') {
907: $out = 'PRIMARY KEY (' . $this->name($value['column']) . ')';
908: } else {
909: $out = 'CREATE ';
910: if (!empty($value['unique'])) {
911: $out .= 'UNIQUE ';
912: }
913: if (is_array($value['column'])) {
914: $value['column'] = implode(', ', array_map(array(&$this, 'name'), $value['column']));
915: } else {
916: $value['column'] = $this->name($value['column']);
917: }
918: $out .= "INDEX {$name} ON {$table}({$value['column']});";
919: }
920: $join[] = $out;
921: }
922: return $join;
923: }
924:
925: 926: 927: 928: 929: 930: 931:
932: public function renderStatement($type, $data) {
933: switch (strtolower($type)) {
934: case 'schema':
935: extract($data);
936:
937: foreach ($indexes as $i => $index) {
938: if (preg_match('/PRIMARY KEY/', $index)) {
939: unset($indexes[$i]);
940: $columns[] = $index;
941: break;
942: }
943: }
944: $join = array('columns' => ",\n\t", 'indexes' => "\n");
945:
946: foreach (array('columns', 'indexes') as $var) {
947: if (is_array(${$var})) {
948: ${$var} = implode($join[$var], array_filter(${$var}));
949: }
950: }
951: return "CREATE TABLE {$table} (\n\t{$columns}\n);\n{$indexes}";
952: default:
953: return parent::renderStatement($type, $data);
954: }
955: }
956:
957: 958: 959: 960: 961:
962: public function getSchemaName() {
963: return $this->config['schema'];
964: }
965:
966: 967: 968: 969: 970:
971: public function nestedTransactionSupported() {
972: return $this->useNestedTransactions && version_compare($this->getVersion(), '8.0', '>=');
973: }
974:
975: }
976: