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