PostgreSqlPlatform.php 22.3 KB
Newer Older
1
<?php
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.doctrine-project.org>.
 */
19

20
namespace Doctrine\DBAL\Platforms;
21

22 23
use Doctrine\DBAL\Schema\TableDiff,
    Doctrine\DBAL\Schema\Table;
24

25 26 27 28 29 30
/**
 * PostgreSqlPlatform.
 *
 * @since 2.0
 * @author Roman Borschel <roman@code-factory.org>
 * @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
31
 * @author Benjamin Eberlei <kontakt@beberlei.de>
32
 * @todo Rename: PostgreSQLPlatform
33
 */
34
class PostgreSqlPlatform extends AbstractPlatform
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
{
    /**
     * Returns part of a string.
     *
     * Note: Not SQL92, but common functionality.
     *
     * @param string $value the target $value the string or the string column.
     * @param int $from extract from this characeter.
     * @param int $len extract this amount of characters.
     * @return string sql that extracts part of a string.
     * @override
     */
    public function getSubstringExpression($value, $from, $len = null)
    {
        if ($len === null) {
            return 'SUBSTR(' . $value . ', ' . $from . ')';
        } else {
            return 'SUBSTR(' . $value . ', ' . $from . ', ' . $len . ')';
        }
    }

    /**
     * Returns the SQL string to return the current system date and time.
     *
     * @return string
     */
    public function getNowExpression()
    {
        return 'LOCALTIMESTAMP(0)';
    }

    /**
     * regexp
     *
     * @return string           the regular expression operator
     * @override
     */
    public function getRegexpExpression()
    {
        return 'SIMILAR TO';
    }
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

    /**
     * returns the position of the first occurrence of substring $substr in string $str
     *
     * @param string $substr    literal string to find
     * @param string $str       literal string
     * @param int    $pos       position to start at, beginning of string by default
     * @return integer
     */
    public function getLocateExpression($str, $substr, $startPos = false)
    {
        if ($startPos !== false) {
            $str = $this->getSubstringExpression($str, $startPos);
            return 'CASE WHEN (POSITION('.$substr.' IN '.$str.') = 0) THEN 0 ELSE (POSITION('.$substr.' IN '.$str.') + '.($startPos-1).') END';
        } else {
            return 'POSITION('.$substr.' IN '.$str.')';
        }
    }
94 95 96 97 98 99 100 101
    
    /**
     * parses a literal boolean value and returns
     * proper sql equivalent
     *
     * @param string $value     boolean value to be parsed
     * @return string           parsed boolean value
     */
102
    /*public function parseBoolean($value)
103 104
    {
        return $value;
105
    }*/
romanb's avatar
romanb committed
106 107 108 109 110 111 112 113 114 115 116 117
    
    /**
     * Whether the platform supports sequences.
     * Postgres has native support for sequences.
     *
     * @return boolean
     */
    public function supportsSequences()
    {
        return true;
    }
    
118 119 120 121 122 123 124 125 126 127
    /**
     * Whether the platform supports database schemas.
     * 
     * @return boolean
     */
    public function supportsSchemas()
    {
        return true;
    }
    
romanb's avatar
romanb committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
    /**
     * Whether the platform supports identity columns.
     * Postgres supports these through the SERIAL keyword.
     *
     * @return boolean
     */
    public function supportsIdentityColumns()
    {
        return true;
    }
    
    /**
     * Whether the platform prefers sequences for ID generation.
     *
     * @return boolean
     */
    public function prefersSequences()
    {
        return true;
    }
148

149
    public function getListDatabasesSQL()
150 151 152
    {
        return 'SELECT datname FROM pg_database';
    }
153

154
    public function getListSequencesSQL($database)
155 156
    {
        return "SELECT
157
                    c.relname, n.nspname AS schemaname
158
                FROM
159 160 161
                   pg_class c, pg_namespace n
                WHERE relkind = 'S' AND n.oid = c.relnamespace AND 
                    (n.nspname NOT LIKE 'pg_%' AND n.nspname != 'information_schema')";
162
    }
163

164
    public function getListTablesSQL()
165
    {
166 167
        return "SELECT tablename AS table_name, schemaname AS schema_name
                FROM pg_tables WHERE schemaname NOT LIKE 'pg_%' AND schemaname != 'information_schema'";
168
    }
169

170
    public function getListViewsSQL($database)
171
    {
172
        return 'SELECT viewname, definition FROM pg_views';
173
    }
174

175
    public function getListTableForeignKeysSQL($table, $database = null)
176
    {
177
        return "SELECT r.conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef
178 179 180 181
                  FROM pg_catalog.pg_constraint r
                  WHERE r.conrelid =
                  (
                      SELECT c.oid
182 183 184
                      FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n
                      WHERE " .$this->getTableWhereClause($table) ."
                        AND n.oid = c.relnamespace
185 186 187 188
                  )
                  AND r.contype = 'f'";
    }

189
    public function getCreateViewSQL($name, $sql)
190 191 192 193
    {
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
    }

194
    public function getDropViewSQL($name)
195 196 197 198
    {
        return 'DROP VIEW '. $name;
    }

199
    public function getListTableConstraintsSQL($table)
200 201 202 203 204 205 206 207
    {
        return "SELECT
                    relname
                FROM
                    pg_class
                WHERE oid IN (
                    SELECT indexrelid
                    FROM pg_index, pg_class
208
                    WHERE pg_class.relname = '$table'
209 210 211 212
                        AND pg_class.oid = pg_index.indrelid
                        AND (indisunique = 't' OR indisprimary = 't')
                        )";
    }
213

214 215 216 217 218 219
    /**
     * @license New BSD License
     * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
     * @param  string $table
     * @return string
     */
220
    public function getListTableIndexesSQL($table, $currentDatabase = null)
221
    {
222 223 224 225
        return "SELECT relname, pg_index.indisunique, pg_index.indisprimary,
                       pg_index.indkey, pg_index.indrelid
                 FROM pg_class, pg_index
                 WHERE oid IN (
226
                    SELECT indexrelid
227 228
                    FROM pg_index si, pg_class sc, pg_namespace sn
                    WHERE " . $this->getTableWhereClause($table, 'sc', 'sn')." AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid
229
                 ) AND pg_index.indexrelid = oid";
230
    }
231

232 233 234 235 236 237 238 239 240 241 242 243
    private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n')
    {
        $whereClause = "";
        if (strpos($table, ".") !== false) {
            list($schema, $table) = explode(".", $table);
            $whereClause = "$classAlias.relname = '" . $table . "' AND $namespaceAlias.nspname = '" . $schema . "'";
        } else {
            $whereClause = "$classAlias.relname = '" . $table . "'";
        }
        return $whereClause;
    }

244
    public function getListTableColumnsSQL($table)
245 246 247 248 249 250
    {
        return "SELECT
                    a.attnum,
                    a.attname AS field,
                    t.typname AS type,
                    format_type(a.atttypid, a.atttypmod) AS complete_type,
251 252 253
                    (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type,
                    (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM pg_catalog.pg_type t2
                     WHERE t2.typtype = 'd' AND t2.typname = format_type(a.atttypid, a.atttypmod)) AS domain_complete_type,
254 255 256 257 258 259 260 261 262 263 264 265
                    a.attnotnull AS isnotnull,
                    (SELECT 't'
                     FROM pg_index
                     WHERE c.oid = pg_index.indrelid
                        AND pg_index.indkey[0] = a.attnum
                        AND pg_index.indisprimary = 't'
                    ) AS pri,
                    (SELECT pg_attrdef.adsrc
                     FROM pg_attrdef
                     WHERE c.oid = pg_attrdef.adrelid
                        AND pg_attrdef.adnum=a.attnum
                    ) AS default
266 267
                    FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n 
                    WHERE ".$this->getTableWhereClause($table, 'c', 'n') ."
268 269 270
                        AND a.attnum > 0
                        AND a.attrelid = c.oid
                        AND a.atttypid = t.oid
271
                        AND n.oid = c.relnamespace
272 273 274 275 276 277 278 279 280 281 282
                    ORDER BY a.attnum";
    }
    
    /**
     * create a new database
     *
     * @param string $name name of the database that should be created
     * @throws PDOException
     * @return void
     * @override
     */
283
    public function getCreateDatabaseSQL($name)
284
    {
285
        return 'CREATE DATABASE ' . $name;
286
    }
287

288 289 290 291 292 293 294
    /**
     * drop an existing database
     *
     * @param string $name name of the database that should be dropped
     * @throws PDOException
     * @access public
     */
295
    public function getDropDatabaseSQL($name)
296
    {
297
        return 'DROP DATABASE ' . $name;
298
    }
299

300 301 302 303
    /**
     * Return the FOREIGN KEY query section dealing with non-standard options
     * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
     *
304
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey         foreign key definition
305 306 307
     * @return string
     * @override
     */
308
    public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey)
309 310
    {
        $query = '';
311 312
        if ($foreignKey->hasOption('match')) {
            $query .= ' MATCH ' . $foreignKey->getOption('match');
313
        }
314
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
315
        if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) {
316 317 318 319
            $query .= ' DEFERRABLE';
        } else {
            $query .= ' NOT DEFERRABLE';
        }
320
        if ($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false) {
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
            $query .= ' INITIALLY DEFERRED';
        } else {
            $query .= ' INITIALLY IMMEDIATE';
        }
        return $query;
    }
    
    /**
     * generates the sql for altering an existing table on postgresql
     *
     * @param string $name          name of the table that is intended to be changed.
     * @param array $changes        associative array that contains the details of each type      *
     * @param boolean $check        indicates whether the function should just check if the DBMS driver
     *                              can perform the requested table alterations if the value is true or
     *                              actually perform them otherwise.
     * @see Doctrine_Export::alterTable()
     * @return array
     * @override
     */
340
    public function getAlterTableSQL(TableDiff $diff)
341
    {
342
        $sql = array();
343 344

        foreach ($diff->addedColumns as $column) {
345
            $query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
346
            $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query;
347 348
        }

349
        foreach ($diff->removedColumns as $column) {
350
            $query = 'DROP ' . $column->getQuotedName($this);
351
            $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query;
352 353
        }

354 355 356 357 358 359
        foreach ($diff->changedColumns AS $columnDiff) {
            $oldColumnName = $columnDiff->oldColumnName;
            $column = $columnDiff->column;
            
            if ($columnDiff->hasChanged('type')) {
                $type = $column->getType();
360

361 362 363
                // here was a server version check before, but DBAL API does not support this anymore.
                $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSqlDeclaration($column->toArray(), $this);
                $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query;
364
            }
365
            if ($columnDiff->hasChanged('default')) {
366
                $query = 'ALTER ' . $oldColumnName . ' SET ' . $this->getDefaultValueDeclarationSQL($column->toArray());
367
                $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query;
368
            }
369 370 371
            if ($columnDiff->hasChanged('notnull')) {
                $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotNull() ? 'SET' : 'DROP') . ' NOT NULL';
                $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . $query;
372
            }
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
            if ($columnDiff->hasChanged('autoincrement')) {
                if ($column->getAutoincrement()) {
                    // add autoincrement
                    $seqName = $diff->name . '_' . $oldColumnName . '_seq';

                    $sql[] = "CREATE SEQUENCE " . $seqName;
                    $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ") FROM " . $diff->name . "))";
                    $query = "ALTER " . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')";
                    $sql[] = "ALTER TABLE " . $diff->name . " " . $query;
                } else {
                    // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have
                    $query = "ALTER " . $oldColumnName . " " . "DROP DEFAULT";
                    $sql[] = "ALTER TABLE " . $diff->name . " " . $query;
                }
            }
388 389
        }

390
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
391
            $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME COLUMN ' . $oldColumnName . ' TO ' . $column->getQuotedName($this);
392 393
        }

394 395
        if ($diff->newName !== false) {
            $sql[] = 'ALTER TABLE ' . $diff->name . ' RENAME TO ' . $diff->newName;
396 397
        }

398
        $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff));
399

400 401 402 403
        return $sql;
    }
    
    /**
404 405 406
     * Gets the SQL to create a sequence on this platform.
     *
     * @param \Doctrine\DBAL\Schema\Sequence $sequence
407
     * @return string
408
     */
409
    public function getCreateSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence)
410
    {
411
        return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
412
               ' INCREMENT BY ' . $sequence->getAllocationSize() .
413
               ' MINVALUE ' . $sequence->getInitialValue() .
414
               ' START ' . $sequence->getInitialValue();
415 416 417
    }
    
    /**
418 419 420
     * Drop existing sequence
     * @param  \Doctrine\DBAL\Schema\Sequence $sequence
     * @return string
421
     */
422
    public function getDropSequenceSQL($sequence)
423
    {
424
        if ($sequence instanceof \Doctrine\DBAL\Schema\Sequence) {
425
            $sequence = $sequence->getQuotedName($this);
426 427
        }
        return 'DROP SEQUENCE ' . $sequence;
428
    }
429 430 431 432 433 434

    /**
     * @param  ForeignKeyConstraint|string $foreignKey
     * @param  Table|string $table
     * @return string
     */
435
    public function getDropForeignKeySQL($foreignKey, $table)
436
    {
437
        return $this->getDropConstraintSQL($foreignKey, $table);
438
    }
439 440 441 442
    
    /**
     * Gets the SQL used to create a table.
     *
443 444
     * @param unknown_type $tableName
     * @param array $columns
445 446 447
     * @param array $options
     * @return unknown
     */
448
    protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
449
    {
450
        $queryFields = $this->getColumnDeclarationListSQL($columns);
451 452

        if (isset($options['primary']) && ! empty($options['primary'])) {
453
            $keyColumns = array_unique(array_values($options['primary']));
454 455 456
            $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
        }

457
        $query = 'CREATE TABLE ' . $tableName . ' (' . $queryFields . ')';
458 459 460 461

        $sql[] = $query;

        if (isset($options['indexes']) && ! empty($options['indexes'])) {
462
            foreach ($options['indexes'] AS $index) {
463
                $sql[] = $this->getCreateIndexSQL($index, $tableName);
464 465 466 467
            }
        }

        if (isset($options['foreignKeys'])) {
468
            foreach ((array) $options['foreignKeys'] as $definition) {
469
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
            }
        }

        return $sql;
    }
    
    /**
     * Postgres wants boolean values converted to the strings 'true'/'false'.
     *
     * @param array $item
     * @override
     */
    public function convertBooleans($item)
    {
        if (is_array($item)) {
            foreach ($item as $key => $value) {
                if (is_bool($value) || is_numeric($item)) {
                    $item[$key] = ($value) ? 'true' : 'false';
                }
            }
        } else {
           if (is_bool($item) || is_numeric($item)) {
               $item = ($item) ? 'true' : 'false';
           }
        }
        return $item;
    }
497

498
    public function getSequenceNextValSQL($sequenceName)
499 500 501
    {
        return "SELECT NEXTVAL('" . $sequenceName . "')";
    }
502

503
    public function getSetTransactionIsolationSQL($level)
romanb's avatar
romanb committed
504 505
    {
        return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL '
506
                . $this->_getTransactionIsolationLevelSQL($level);
romanb's avatar
romanb committed
507
    }
508 509 510 511
    
    /**
     * @override
     */
512
    public function getBooleanTypeDeclarationSQL(array $field)
513 514 515
    {
        return 'BOOLEAN';
    }
516 517 518 519

    /**
     * @override
     */
520
    public function getIntegerTypeDeclarationSQL(array $field)
521 522 523 524
    {
        if ( ! empty($field['autoincrement'])) {
            return 'SERIAL';
        }
525
        
526 527 528 529 530 531
        return 'INT';
    }

    /**
     * @override
     */
532
    public function getBigIntTypeDeclarationSQL(array $field)
533 534 535 536 537 538 539 540 541 542
    {
        if ( ! empty($field['autoincrement'])) {
            return 'BIGSERIAL';
        }
        return 'BIGINT';
    }

    /**
     * @override
     */
543
    public function getSmallIntTypeDeclarationSQL(array $field)
544 545 546 547
    {
        return 'SMALLINT';
    }

548 549 550
    /**
     * @override
     */
551
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
552 553 554 555 556 557 558
    {
        return 'TIMESTAMP(0) WITHOUT TIME ZONE';
    }

    /**
     * @override
     */
559
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
560
    {
561 562 563 564 565 566
        return 'TIMESTAMP(0) WITH TIME ZONE';
    }
    
    /**
     * @override
     */
567
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
568 569
    {
        return 'DATE';
570 571
    }

572 573 574
    /**
     * @override
     */
575
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
576
    {
577
        return 'TIME(0) WITHOUT TIME ZONE';
578 579
    }

580 581 582
    /**
     * @override
     */
583
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
584 585 586 587 588 589 590 591 592 593
    {
        return '';
    }

    /**
     * Gets the SQL snippet used to declare a VARCHAR column on the MySql platform.
     *
     * @params array $field
     * @override
     */
594
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
595 596
    {
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
597
                : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
598
    }
599 600
    
    /** @override */
601
    public function getClobTypeDeclarationSQL(array $field)
602 603 604
    {
        return 'TEXT';
    }
605 606 607 608 609 610 611 612 613 614

    /**
     * Get the platform name for this instance
     *
     * @return string
     */
    public function getName()
    {
        return 'postgresql';
    }
615
    
616 617 618 619 620 621 622 623
    /**
     * Gets the character casing of a column in an SQL result set.
     * 
     * PostgreSQL returns all column names in SQL result sets in lowercase.
     * 
     * @param string $column The column name for which to get the correct character casing.
     * @return string The column name in the character casing used in SQL result sets.
     */
624
    public function getSQLResultCasing($column)
625 626 627
    {
        return strtolower($column);
    }
628
    
629
    public function getDateTimeTzFormatString()
630
    {
631 632
        return 'Y-m-d H:i:sO';
    }
633 634 635 636 637 638 639 640

    /**
     * Get the insert sql for an empty insert statement
     *
     * @param string $tableName 
     * @param string $identifierColumnName 
     * @return string $sql
     */
641
    public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName)
642 643 644
    {
        return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)';
    }
645 646 647 648

    /**
     * @inheritdoc
     */
649
    public function getTruncateTableSQL($tableName, $cascade = false)
650 651 652
    {
        return 'TRUNCATE '.$tableName.' '.($cascade)?'CASCADE':'';
    }
653 654 655 656 657

    public function getReadLockSQL()
    {
        return 'FOR SHARE';
    }
658 659 660 661

    protected function initializeDoctrineTypeMappings()
    {
        $this->doctrineTypeMapping = array(
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
            'smallint'      => 'smallint',
            'int2'          => 'smallint',
            'serial'        => 'integer',
            'serial4'       => 'integer',
            'int'           => 'integer',
            'int4'          => 'integer',
            'integer'       => 'integer',
            'bigserial'     => 'bigint',
            'serial8'       => 'bigint',
            'bigint'        => 'bigint',
            'int8'          => 'bigint',
            'bool'          => 'boolean',
            'boolean'       => 'boolean',
            'text'          => 'text',
            'varchar'       => 'string',
            'interval'      => 'string',
            '_varchar'      => 'string',
            'char'          => 'string',
            'bpchar'        => 'string',
            'date'          => 'date',
            'datetime'      => 'datetime',
            'timestamp'     => 'datetime',
            'timestamptz'   => 'datetimetz',
            'time'          => 'time',
            'timetz'        => 'time',
            'float'         => 'float',
            'float4'        => 'float',
            'float8'        => 'float',
            'double'        => 'float',
            'double precision' => 'float',
            'real'          => 'float',
            'decimal'       => 'decimal',
            'money'         => 'decimal',
            'numeric'       => 'decimal',
            'year'          => 'date',
697 698
        );
    }
699 700 701 702 703

    public function getVarcharMaxLength()
    {
        return 65535;
    }
704
}