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