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