diff --git a/lib/Doctrine/DBAL/Driver/AkibanSrv/Driver.php b/lib/Doctrine/DBAL/Driver/AkibanSrv/Driver.php new file mode 100644 index 00000000000..ecf2c29f769 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/AkibanSrv/Driver.php @@ -0,0 +1,102 @@ +. + */ + +namespace Doctrine\DBAL\Driver\AkibanSrv; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\Platforms\AkibanServerPlatform; +use Doctrine\DBAL\Schema\AkibanServerSchemaManager; + +/** + * Driver that connects to Akiban Server through pdo_pgsql. + * + * @author Padraig O'Sullivan + * @since 2.4 + */ +class Driver implements \Doctrine\DBAL\Driver +{ + /** + * {@inheritDoc} + */ + public function connect(array $params, $username = null, $password = null, array $driverOptions = array()) + { + return new PDOConnection( + $this->constructPdoDsn($params), + $username, + $password, + $driverOptions + ); + } + + /** + * Constructs the Akiban Server PDO DSN. + * + * @return string The DSN. + */ + private function constructPdoDsn(array $params) + { + $dsn = 'pgsql:'; + if (! empty($params['host'])) { + $dsn .= 'host=' . $params['host'] . ' '; + } + if (! empty($params['port'])) { + $dsn .= 'port=' . $params['port'] . ' '; + } + if (! empty($params['dbname'])) { + $dsn .= 'dbname=' . $params['dbname'] . ' '; + } + + return $dsn; + } + + /** + * {@inheritDoc} + */ + public function getDatabasePlatform() + { + return new AkibanServerPlatform(); + } + + /** + * {@inheritDoc} + */ + public function getSchemaManager(Connection $conn) + { + return new AkibanServerSchemaManager($conn); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return 'akibansrv'; + } + + /** + * {@inheritDoc} + */ + public function getDatabase(Connection $conn) + { + $params = $conn->getParams(); + return $params['dbname']; + } +} + diff --git a/lib/Doctrine/DBAL/DriverManager.php b/lib/Doctrine/DBAL/DriverManager.php index 0f196261fb5..b70947f50d9 100644 --- a/lib/Doctrine/DBAL/DriverManager.php +++ b/lib/Doctrine/DBAL/DriverManager.php @@ -49,6 +49,7 @@ final class DriverManager 'mysqli' => 'Doctrine\DBAL\Driver\Mysqli\Driver', 'drizzle_pdo_mysql' => 'Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver', 'sqlsrv' => 'Doctrine\DBAL\Driver\SQLSrv\Driver', + 'akibansrv' => 'Doctrine\DBAL\Driver\AkibanSrv\Driver', ); /** Private constructor. This class cannot be instantiated. */ @@ -74,6 +75,7 @@ private function __construct() { } * sqlsrv * ibm_db2 (unstable) * drizzle_pdo_mysql + * akibansrv * * OR 'driverClass' that contains the full class name (with namespace) of the * driver class to instantiate. diff --git a/lib/Doctrine/DBAL/Platforms/AkibanServerPlatform.php b/lib/Doctrine/DBAL/Platforms/AkibanServerPlatform.php new file mode 100644 index 00000000000..5370d31df8f --- /dev/null +++ b/lib/Doctrine/DBAL/Platforms/AkibanServerPlatform.php @@ -0,0 +1,624 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\Table; + +/** + * AkibanServerPlatform. + * + * @author Padraig O'Sullivan + * @since 2.4 + */ +class AkibanServerPlatform extends AbstractPlatform +{ + /** + * {@inheritdoc} + */ + public function getSubstringExpression($value, $from, $len = null) + { + if ($len === null) { + return "SUBSTR(" . $value . ", " . $from . ")"; + } + return "SUBSTR(" . $value . ", " . $from . ", " . $len . ")"; + } + + /** + * {@inheritdoc} + */ + 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"; + } + return "POSITION(" . $substr . " IN " . $str . ")"; + } + + /** + * {@inheritdoc} + */ + public function getDateDiffExpression($date1, $date2) + { + return "DATEDIFF(" . $date1 . ", " . $date2 . ")"; + } + + /** + * {@inheritdoc} + */ + public function getDateAddDaysExpression($date, $days) + { + return "DATE_ADD(" . $date . ", INTERVAL " . $days . " DAY)"; + } + + /** + * {@inheritdoc} + */ + public function getDateSubDaysExpression($date, $days) + { + return "DATE_SUB(" . $date . ", INTERVAL " . $days . " DAY)"; + } + + /** + * {@inheritdoc} + */ + public function getDateAddMonthExpression($date, $months) + { + return "DATE_ADD(" . $date . ", INTERVAL " . $months . " MONTH)"; + } + + /** + * {@inheritdoc} + */ + public function getDateSubMonthExpression($date, $months) + { + return "DATE_SUB(" . $date . ", INTERVAL " . $months . " MONTH)"; + } + + /** + * {@inheritdoc} + */ + public function getBitAndComparisonExpression($value1, $value2) + { + return "BITAND(" . $value1 . ", " . $value2 . ")"; + } + + /** + * {@inheritdoc} + */ + public function getBitOrComparisonExpression($value1, $value2) + { + return "BITOR(" . $value1 . ", " . $value2 . ")"; + } + + /** + * Akiban does not support this syntax in 1.4.0 release. + */ + public function getForUpdateSQL() + { + return ""; + } + + /** + * {@inheritdoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsSchemas() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsIdentityColumns() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsCommentOnStatement() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function prefersSequences() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function supportsSavepoints() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsReleaseSavepoints() + { + return $this->supportsSavepoints(); + } + + /** + * {@inheritdoc} + */ + public function supportsForeignKeyConstraints() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function supportsForeignKeyOnUpdate() + { + return $this->supportsForeignKeyConstraints(); + } + + + /** + * {@inheritdoc} + */ + public function getListDatabasesSQL() + { + return "SELECT schema_name FROM information_schema.schemata"; + } + + /** + * {@inheritdoc} + */ + public function getListSequencesSQL($database) + { + return "SELECT sequence_name, sequence_schema as schemaname, increment as increment_by, minimum_value as min_value " . + "FROM information_schema.sequences " . + "WHERE sequence_name != 'information_schema'"; + } + + /** + * {@inheritdoc} + */ + public function getListTablesSQL() + { + return "SELECT table_name, table_schema " . + "FROM information_schema.tables WHERE table_schema != 'information_schema'"; + } + + /** + * {@inheritdoc} + */ + public function getListViewsSQL($database) + { + return "SELECT table_name as viewname, view_definition as definition FROM information_schema.views"; + } + + /** + * {@inheritdoc} + */ + public function getCreateViewSQL($name, $sql) + { + return "CREATE VIEW " . $name . " AS " . $sql; + } + + /** + * {@inheritdoc} + */ + public function getDropViewSQL($name) + { + return "DROP VIEW " . $name; + } + + /** + * {@inheritdoc} + */ + public function getListTableConstraintsSQL($table) + { + // TODO - do we only want unique and primary key indexes here? + return "SELECT index_name " . + "FROM information_schema.indexes " . + "WHERE schema_name != 'information_schema' AND " . + "table_name = '" . $table . "'"; + } + + /** + * {@inheritdoc} + */ + public function getListTableIndexesSQL($table, $currentDatabase = null) + { + $schemaPredicate = ""; + if (null !== $currentDatabase) { + $schemaPredicate = "i.schema_name = '" . $currentDatabase . "' and "; + } + return "SELECT i.table_name as table_name, i.index_name as index_name, i.is_unique as is_unique, i.index_type as index_type, c.column_name as column_name " . + "FROM information_schema.indexes i join information_schema.index_columns c on i.index_name = c.index_name and i.table_name = c.index_table_name " . + "WHERE c.schema_name != 'information_schema' and " . $schemaPredicate . "i.table_name = '" . $table . "'"; + } + + /** + * {@inheritdoc} + */ + public function getListTableColumnsSQL($table, $database = null) + { + $schemaPredicate = ""; + if (null !== $database) { + $schemaPredicate = "c.schema_name = '" . $database . "' and "; + } + return "SELECT c.column_name as column_name, c.length as length, c.type as type, c.nullable as nullable, " . + "c.character_set_name as character_set_name, c.collation_name as collation_name, " . + "i.index_type as index_type " . + "FROM information_schema.columns c left outer join information_schema.indexes i on c.table_name = i.table_name " . + "WHERE c.schema_name != 'information_schema' and " . $schemaPredicate . + "c.table_name = '" . $table . "'"; + } + + /** + * {@inheritdoc} + */ + public function getCreateDatabaseSQL($name) + { + return "CREATE SCHEMA " . $name; + } + + /** + * {@inheritdoc} + */ + public function getDropDatabaseSQL($name) + { + return "DROP SCHEMA IF EXISTS " . $name . " CASCADE"; + } + + /** + * {@inheritdoc} + */ + public function getAlterTableSQL(TableDiff $diff) + { + $sql = array(); + $commentsSQL = array(); // Akiban Server does not support comments as of 1.4.0 + $columnSql = array(); + + foreach ($diff->addedColumns as $column) { + if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { + continue; + } + + $query = "ADD " . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + $sql[] = "ALTER TABLE " . $diff->name . " " . $query; + if ($comment = $this->getColumnComment($column)) { + // TODO - Akiban does not support comments on tables or columns in 1.4.0 + } + } + + foreach ($diff->removedColumns as $column) { + if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { + continue; + } + + $query = "DROP " . $column->getQuotedName($this); + $sql[] = "ALTER TABLE " . $diff->name . " " . $query; + } + + foreach ($diff->changedColumns as $columnDiff) { + if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { + continue; + } + + $oldColumnName = $columnDiff->oldColumnName; + $column = $columnDiff->column; + + if ($columnDiff->hasChanged('type')) { + $type = $column->getType(); + + $query = "ALTER " . $oldColumnName . " SET DATA TYPE " . $type->getSqlDeclaration($column->toArray(), $this); + $sql[] = "ALTER TABLE " . $diff->name . " " . $query; + } + if ($columnDiff->hasChanged('default')) { + $query = "ALTER " . $oldColumnName . $this->getDefaultValueDeclarationSQL($column->toArray()); + $sql[] = "ALTER TABLE " . $diff->name . " " . $query; + } + if ($columnDiff->hasChanged('notnull')) { + $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotNull() ? 'NOT NULL' : 'NULL'); + $sql[] = "ALTER TABLE " . $diff->name . " " . $query; + } + if ($columnDiff->hasChanged('autoincrement')) { + // TODO - Akiban does not support modifying sequences created with SERIAL in 1.4.0 + } + if ($columnDiff->hasChanged('comment') && $comment = $this->getColumnComment($column)) { + // TODO - Akiban does not support comments on tables or columns in 1.4.0 + } + } + + foreach ($diff->renamedColumns as $oldColumnName => $column) { + if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { + continue; + } + // TODO - Akiban does not support renaming columns with ALTER in 1.4.0 + } + + $tableSql = array(); + + if (! $this->onSchemaAlterTable($diff, $tableSql)) { + if ($diff->newName !== false) { + // TODO + } + // TODO - handle foreign keys alter statements + } + + return array_merge($sql, $tableSql, $columnSql); + } + + /** + * {@inheritdoc} + */ + public function getCreateSequenceSQL(\Doctrine\DBAL\Schema\Sequence $sequence) + { + return "CREATE SEQUENCE " . $sequence->getQuotedName($this) . + " START WITH " . $sequence->getInitialValue() . + " INCREMENT BY " . $sequence->getAllocationSize() . + " MINVALUE " . $sequence->getInitialValue(); + } + + /** + * {@inheritdoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof \Doctrine\DBAL\Schema\Sequence) { + $sequence = $sequence->getQuotedName($this); + } + return "DROP SEQUENCE " . $sequence . " RESTRICT"; + } + + /** + * {@inheritdoc} + */ + protected function _getCreateTableSQL($tableName, array $columns, array $options = array()) + { + $columnListSql = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $name => $definition) { + $columnListSql .= $this->getUniqueConstraintDeclarationSQL($name, $definition); + } + } + + if (isset($options['primary']) && ! empty($options['primary'])) { + $keyColumns = array_unique(array_values($options['primary'])); + $columnListSql .= ", PRIMARY KEY(" . implode(", ", $keyColumns) . ")"; + } + + $query = "CREATE TABLE " . $tableName . " (" . $columnListSql . ")"; + + $check = $this->getCheckDeclarationSQL($columns); + if (! empty($check)) { + // TODO - Akiban does not support CHECK constraints in 1.4.0 + } + + $sql[] = $query; + + foreach ($columns as $name => $column) { + if (isset($column['sequence'])) { + $sql[] = $this->getCreateSequenceSQL($column['sequence'], 1); + } + } + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableName); + } + } + + // TODO - foreign keys, Akiban does not support in 1.4.0 + + return $sql; + } + + /** + * {@inheritdoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return "SELECT NEXT VALUE FOR ". $sequenceName; + } + + /** + * {@inheritdoc} + */ + public function getBooleanTypeDeclarationSQL(array $field) + { + return "BOOLEAN"; + } + + /** + * {@inheritdoc} + */ + public function getIntegerTypeDeclarationSQL(array $field) + { + if (! empty($field['autoincrement'])) { + return "SERIAL"; + } + return "INT"; + } + + /** + * {@inheritdoc} + */ + public function getBigIntTypeDeclarationSQL(array $field) + { + if (! empty($field['autoincrement'])) { + return "BIGSERIAL"; + } + return "BIGINT"; + } + + /** + * {@inheritdoc} + */ + public function getSmallIntTypeDeclarationSQL(array $field) + { + return "SMALLINT"; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) + { + if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] === true) { + return "TIMESTAMP"; + } + return "DATETIME"; + } + + /** + * {@inheritdoc} + */ + public function getDateTypeDeclarationSQL(array $fieldDeclaration) + { + return "DATE"; + } + + /** + * {@inheritdoc} + */ + public function getTimeTypeDeclarationSQL(array $fieldDeclaration) + { + return "TIME"; + } + + /** + * {@inheritdoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) + { + return ""; + } + + /** + * {@inheritdoc} + */ + protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + { + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + } + + /** + * {@inheritdoc} + */ + public function getClobTypeDeclarationSQL(array $field) + { + return "BLOB"; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return "akibansrv"; + } + + /** + * {@inheritdoc} + */ + public function getSQLResultCasing($column) + { + return strtolower($column); + } + + /** + * {@inheritdoc} + */ + public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + { + return "INSERT INTO " . $quotedTableName . " (" . $quotedIdentifierColumnName . ") VALUES (DEFAULT)"; + } + + /** + * {@inheritdoc} + */ + public function getTruncateTableSQL($tableName, $cascade = false) + { + return "TRUNCATE TABLE " . $tableName . " " . (($cascade) ? "CASCADE" : ""); + } + + protected function initializeDoctrineTypeMappings() + { + $this->doctrineTypeMapping = array( + 'smallint' => 'smallint', + 'serial' => 'integer', + 'int' => 'integer', + 'integer' => 'integer', + 'bigserial' => 'bigint', + 'bigint' => 'bigint', + 'boolean' => 'boolean', + 'varchar' => 'string', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'time' => 'time', + 'float' => 'float', + 'double' => 'float', + 'real' => 'float', + 'decimal' => 'decimal', + 'numeric' => 'decimal', + 'year' => 'date', + 'blob' => 'blob', + 'longblob' => 'blob', + ); + } + + public function getVarcharMaxLength() + { + return 65535; + } + + protected function getReservedKeywordsClass() + { + return "Doctrine\DBAL\Platforms\Keywords\AkibanSrvKeywords"; + } + + /** + * {@inheritdoc} + */ + public function getBlobTypeDeclarationSQL(array $field) + { + return "BLOB"; + } +} + diff --git a/lib/Doctrine/DBAL/Platforms/Keywords/AkibanSrvKeywords.php b/lib/Doctrine/DBAL/Platforms/Keywords/AkibanSrvKeywords.php new file mode 100644 index 00000000000..6314b036a99 --- /dev/null +++ b/lib/Doctrine/DBAL/Platforms/Keywords/AkibanSrvKeywords.php @@ -0,0 +1,129 @@ +. + */ + + +namespace Doctrine\DBAL\Platforms\Keywords; + +/** + * Akiban Server Keywordlist + * + * @author Padraig O'Sullivan + * @since 2.4 + */ +class AkibanSrvKeywords extends KeywordList +{ + public function getName() + { + return 'Akiban Server'; + } + + protected function getKeywords() + { + return array( + 'ALL', + 'ANALYSE', + 'ANALYZE', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'AUTHORIZATION', + 'BETWEEN', + 'BINARY', + 'BOTH', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONSTRAINT', + 'CREATE', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'DEFAULT', + 'DEFERRABLE', + 'DESC', + 'DISTINCT', + 'DO', + 'ELSE', + 'END', + 'EXCEPT', + 'FALSE', + 'FOR', + 'FOREIGN', + 'FREEZE', + 'FROM', + 'FULL', + 'GRANT', + 'GROUP', + 'HAVING', + 'ILIKE', + 'IN', + 'INITIALLY', + 'INNER', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'LEADING', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'NATURAL', + 'NEW', + 'NOT', + 'NOTNULL', + 'NULL', + 'OFF', + 'OFFSET', + 'OLD', + 'ON', + 'ONLY', + 'OR', + 'ORDER', + 'OUTER', + 'OVERLAPS', + 'PLACING', + 'PRIMARY', + 'REFERENCES', + 'SELECT', + 'SESSION_USER', + 'SIMILAR', + 'SOME', + 'TABLE', + 'THEN', + 'TO', + 'TRAILING', + 'TRUE', + 'UNION', + 'UNIQUE', + 'USER', + 'USING', + 'VERBOSE', + 'WHEN', + 'WHERE' + ); + } +} + diff --git a/lib/Doctrine/DBAL/Schema/AkibanServerSchemaManager.php b/lib/Doctrine/DBAL/Schema/AkibanServerSchemaManager.php new file mode 100644 index 00000000000..5e2e6dca572 --- /dev/null +++ b/lib/Doctrine/DBAL/Schema/AkibanServerSchemaManager.php @@ -0,0 +1,231 @@ +. + */ + +namespace Doctrine\DBAL\Schema; + +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Types\Type; + +/** + * Akiban Server Schema Manager + * + * @author Padraig O'Sullivan + * @since 2.4 + */ +class AkibanServerSchemaManager extends AbstractSchemaManager +{ + + /** + * Get all the existing schema names. + * + * @return array + */ + public function getSchemaNames() + { + $rows = $this->_conn->fetchAll("SELECT schema_name FROM information_schema.schemata WHERE schema_name != 'information_schema'"); + return array_map(function($v) { return $v['schema_name']; }, $rows); + } + + /** + * {@inheritdoc} + */ + public function dropDatabase($database = null) + { + if (null === $database) { + $database = $this->_conn->getDatabase(); + } + + $params = $this->_conn->getParams(); + $params["dbname"] = "information_schema"; + $tmpPlatform = $this->_platform; + $tmpConn = $this->_conn; + + $this->_conn = DriverManager::getConnection($params); + $this->_platform = $this->_conn->getDatabasePlatform(); + + parent::dropDatabase($database); + + $this->_platform = $tmpPlatform; + $this->_conn = $tmpConn; + } + + /** + * {@inheritdoc} + */ + public function createDatabase($database = null) + { + if (null === $database) { + $database = $this->_conn->getDatabase(); + } + + $params = $this->_conn->getParams(); + $params["dbname"] = "information_schema"; + $tmpPlatform = $this->_platform; + $tmpConn = $this->_conn; + + $this->_conn = DriverManager::getConnection($params); + $this->_platform = $this->_conn->getDatabasePlatform(); + + parent::createDatabase($database); + + $this->_platform = $tmpPlatform; + $this->_conn = $tmpConn; + } + + protected function _getPortableViewDefinition($view) + { + return new View($view['viewname'], $view['definition']); + } + + protected function _getPortableUserDefinition($user) + { + return array( + 'user' => $user['usename'], + 'password' => $user['passwd'] + ); + } + + protected function _getPortableTableDefinition($table) + { + return $table['table_name']; + } + + /** + * @param array $tableIndexes + * @param string $tableName + * @return array + */ + protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) + { + $indexBuffer = array(); + foreach ($tableIndexes as $tableIndex) { + if ($tableIndex['index_type'] === "PRIMARY") { + $keyName = 'primary'; + $buffer['primary'] = true; + $buffer['non_unique'] = false; + } else { + $buffer['primary'] = false; + $buffer['non_unique'] = $tableIndex['is_unique'] === 'NO'; + } + $buffer['key_name'] = $tableIndex['index_name']; + $buffer['column_name'] = $tableIndex['column_name']; + $indexBuffer[] = $buffer; + } + return parent::_getPortableTableIndexesList($indexBuffer, $tableName); + } + + protected function _getPortableDatabaseDefinition($database) + { + return $database['schema_name']; + } + + protected function _getPortableSequenceDefinition($sequence) + { + return new Sequence($sequence['sequence_name'], $sequence['increment_by'], $sequence['min_value']); + } + + protected function _getPortableTableColumnDefinition($tableColumn) + { + $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); + + if (strtolower($tableColumn['type']) === 'varchar') { + // get length from varchar definition + $length = $tableColumn['length']; + } + + $matches = array(); + + $autoincrement = false; + $tableColumn['default'] = null; + + $length = (isset($tableColumn['length'])) ? $tableColumn['length'] : null; + if ((int) $length <= 0) { + $length = null; + } + $fixed = null; + + if (!isset($tableColumn['column_name'])) { + $tableColumn['name'] = ''; + } + + $precision = null; + $scale = null; + + $dbType = strtolower($tableColumn['type']); + $type = $this->_platform->getDoctrineTypeMapping($dbType); + + switch ($dbType) { + case 'smallint': + $length = null; + break; + case 'int': + case 'integer': + $length = null; + break; + case 'bigint': + $length = null; + break; + case 'boolean': + $length = null; + break; + case 'varchar': + case 'interval': + $fixed = false; + break; + case 'char': + $fixed = true; + break; + case 'float': + case 'double': + case 'double precision': + case 'real': + case 'decimal': + case 'numeric': + // TODO + break; + case 'year': + $length = null; + break; + } + + if ($tableColumn['default'] && preg_match("('([^']+)'::)", $tableColumn['default'], $match)) { + $tableColumn['default'] = $match[1]; + } + + $notNull = $tableColumn['nullable'] === 'NO'; + $primaryKey = $tableColumn['index_type'] === 'PRIMARY'; + + $options = array( + 'length' => $length, + 'notnull' => $notNull, + 'default' => $tableColumn['default'], + 'primary' => $primaryKey, + 'precision' => $precision, + 'scale' => $scale, + 'fixed' => $fixed, + 'unsigned' => false, + 'autoincrement' => $autoincrement, + 'comment' => NULL, + ); + + return new Column($tableColumn['column_name'], Type::getType($type), $options); + } + +} + diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/AkibanSrvSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/AkibanSrvSchemaManagerTest.php new file mode 100644 index 00000000000..c2c6599b7ad --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/AkibanSrvSchemaManagerTest.php @@ -0,0 +1,51 @@ +_conn) { + return; + } + + $this->_conn->getConfiguration()->setFilterSchemaAssetsExpression(null); + } + + public function testGetSchemaNames() + { + $names = $this->_sm->getSchemaNames(); + + $this->assertInternalType('array', $names); + $this->assertGreaterThan(0, count($names)); + } + + /** + * @group DBAL-204 + */ + public function testFilterSchemaExpression() + { + $testTable = new \Doctrine\DBAL\Schema\Table('dbal204_test_prefix'); + $column = $testTable->addColumn('id', 'integer'); + $this->_sm->createTable($testTable); + $testTable = new \Doctrine\DBAL\Schema\Table('dbal204_without_prefix'); + $column = $testTable->addColumn('id', 'integer'); + $this->_sm->createTable($testTable); + + $this->_conn->getConfiguration()->setFilterSchemaAssetsExpression('#^dbal204_#'); + $names = $this->_sm->listTableNames(); + $this->assertEquals(2, count($names)); + + $this->_conn->getConfiguration()->setFilterSchemaAssetsExpression('#^dbal204_test#'); + $names = $this->_sm->listTableNames(); + $this->assertEquals(1, count($names)); + } +} + diff --git a/tests/Doctrine/Tests/DBAL/Functional/TemporaryTableTest.php b/tests/Doctrine/Tests/DBAL/Functional/TemporaryTableTest.php index 9fa92c7a652..799cd2fa3a8 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/TemporaryTableTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/TemporaryTableTest.php @@ -35,6 +35,9 @@ public function tearDown() public function testDropTemporaryTableNotAutoCommitTransaction() { $platform = $this->_conn->getDatabasePlatform(); + if ($platform->getName() === 'akibansrv') { + $this->markTestSkipped('Akiban does not support temporary tables'); + } $columnDefinitions = array("id" => array("type" => Type::getType("integer"), "notnull" => true)); $tempTable = $platform->getTemporaryTableName("temporary"); @@ -68,6 +71,9 @@ public function testDropTemporaryTableNotAutoCommitTransaction() public function testCreateTemporaryTableNotAutoCommitTransaction() { $platform = $this->_conn->getDatabasePlatform(); + if ($platform->getName() === 'akibansrv') { + $this->markTestSkipped('Akiban does not support temporary tables'); + } $columnDefinitions = array("id" => array("type" => Type::getType("integer"), "notnull" => true)); $tempTable = $platform->getTemporaryTableName("temporary"); @@ -99,4 +105,4 @@ public function testCreateTemporaryTableNotAutoCommitTransaction() $rows = $this->_conn->fetchAll('SELECT * FROM nontemporary'); $this->assertEquals(array(), $rows, "In an event of an error this result has one row, because of an implicit commit."); } -} \ No newline at end of file +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php b/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php index f1a3a5983eb..0c23cabd122 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php @@ -140,6 +140,10 @@ public function testLastInsertId() $this->markTestSkipped('Test only works on platforms with identity columns.'); } + if ($this->_conn->getDatabasePlatform()->getName() === 'akibansrv') { + $this->markTestSkipped('Akiban 1.4.0 does not support lastInsertId'); + } + $this->assertEquals(1, $this->_conn->insert('write_table', array('test_int' => 2, 'test_string' => 'bar'))); $num = $this->_conn->lastInsertId(); @@ -153,6 +157,10 @@ public function testLastInsertIdSequence() $this->markTestSkipped('Test only works on platforms with sequences.'); } + if ($this->_conn->getDatabasePlatform()->getName() === 'akibansrv') { + $this->markTestSkipped('Akiban 1.4.0 does not support lastInsertId'); + } + $sequence = new \Doctrine\DBAL\Schema\Sequence('write_table_id_seq'); try { $this->_conn->getSchemaManager()->createSequence($sequence); diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AkibanSrvPlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/AkibanSrvPlatformTest.php new file mode 100644 index 00000000000..2736713d0da --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Platforms/AkibanSrvPlatformTest.php @@ -0,0 +1,180 @@ +assertEquals('"', $this->_platform->getIdentifierQuoteCharacter(), 'Identifier quote character is not correct'); + $this->assertEquals('column1 || column2 || column3', $this->_platform->getConcatExpression('column1', 'column2', 'column3'), 'Concatenation expression is not correct'); + $this->assertEquals('SUBSTR(column, 5)', $this->_platform->getSubstringExpression('column', 5), 'Substring expression without length is not correct'); + $this->assertEquals('SUBSTR(column, 0, 5)', $this->_platform->getSubstringExpression('column', 0, 5), 'Substring expression with length is not correct'); + } + + public function testGeneratesDDLSnippets() + { + $this->assertEquals('CREATE SCHEMA foobar', $this->_platform->getCreateDatabaseSQL('foobar')); + $this->assertEquals('DROP SCHEMA IF EXISTS foobar CASCADE', $this->_platform->getDropDatabaseSQL('foobar')); + $this->assertEquals('DROP TABLE foobar', $this->_platform->getDropTableSQL('foobar')); + } + + public function testGenerateTableWithAutoincrement() + { + $table = new \Doctrine\DBAL\Schema\Table('autoinc_table'); + $column = $table->addColumn('id', 'integer'); + $column->setAutoincrement(true); + + $this->assertEquals(array('CREATE TABLE autoinc_table (id SERIAL NOT NULL)'), $this->_platform->getCreateTableSQL($table)); + } + + public function testGeneratesTypeDeclarationForIntegers() + { + $this->assertEquals( + 'INT', + $this->_platform->getIntegerTypeDeclarationSQL(array()) + ); + $this->assertEquals( + 'SERIAL', + $this->_platform->getIntegerTypeDeclarationSQL(array('autoincrement' => true) + )); + $this->assertEquals( + 'SERIAL', + $this->_platform->getIntegerTypeDeclarationSQL( + array('autoincrement' => true, 'primary' => true) + )); + } + + public function testGeneratesTypeDeclarationForStrings() + { + $this->assertEquals( + 'CHAR(10)', + $this->_platform->getVarcharTypeDeclarationSQL( + array('length' => 10, 'fixed' => true)) + ); + $this->assertEquals( + 'VARCHAR(50)', + $this->_platform->getVarcharTypeDeclarationSQL(array('length' => 50)), + 'Variable string declaration is not correct' + ); + $this->assertEquals( + 'VARCHAR(255)', + $this->_platform->getVarcharTypeDeclarationSQL(array()), + 'Long string declaration is not correct' + ); + } + + public function getGenerateUniqueIndexSql() + { + return 'CREATE UNIQUE INDEX index_name ON test (test, test2)'; + } + + public function testGeneratesSequenceSqlCommands() + { + $sequence = new \Doctrine\DBAL\Schema\Sequence('myseq', 20, 1); + $this->assertEquals( + 'CREATE SEQUENCE myseq START WITH 1 INCREMENT BY 20 MINVALUE 1', + $this->_platform->getCreateSequenceSQL($sequence) + ); + $this->assertEquals( + 'DROP SEQUENCE myseq RESTRICT', + $this->_platform->getDropSequenceSQL('myseq') + ); + $this->assertEquals( + "SELECT NEXT VALUE FOR myseq", + $this->_platform->getSequenceNextValSQL('myseq') + ); + } + + public function testDoesNotPreferIdentityColumns() + { + $this->assertFalse($this->_platform->prefersIdentityColumns()); + } + + public function testPrefersSequences() + { + $this->assertTrue($this->_platform->prefersSequences()); + } + + public function testSupportsIdentityColumns() + { + $this->assertTrue($this->_platform->supportsIdentityColumns()); + } + + public function testSupportsSavePoints() + { + $this->assertFalse($this->_platform->supportsSavepoints()); + } + + public function testSupportsSequences() + { + $this->assertTrue($this->_platform->supportsSequences()); + } + + public function testModifyLimitQuery() + { + $sql = $this->_platform->modifyLimitQuery('SELECT * FROM user', 10, 0); + $this->assertEquals('SELECT * FROM user LIMIT 10 OFFSET 0', $sql); + } + + public function testModifyLimitQueryWithEmptyOffset() + { + $sql = $this->_platform->modifyLimitQuery('SELECT * FROM user', 10); + $this->assertEquals('SELECT * FROM user LIMIT 10', $sql); + } + + public function getGenerateForeignKeySql() + { + return 'ALTER TABLE test ADD FOREIGN KEY (fk_name_id) REFERENCES other_table (id)'; + } + + protected function getBitAndComparisonExpressionSql($value1, $value2) + { + return 'BITAND(' . $value1 . ', ' . $value2 . ')'; + } + + protected function getBitOrComparisonExpressionSql($value1, $value2) + { + return 'BITOR(' . $value1 . ', ' . $value2 . ')'; + } +}