Commit e620471c authored by Benjamin Eberlei's avatar Benjamin Eberlei

Merge remote branch 'origin/DBAL-8'

parents ac910457 ce5b0569
<?php
/*
* $Id$
*
* 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
......@@ -19,17 +21,17 @@
namespace Doctrine\DBAL\Driver\PDOMsSql;
use PDO, Doctrine\DBAL\Driver\Connection as DriverConnection;
/**
* MsSql Connection implementation.
*
* @since 2.0
*/
class Connection extends PDO implements DriverConnection
class Connection extends \PDO implements \Doctrine\DBAL\Driver\Connection
{
/**
* {@inheritdoc}
* Performs the rollback.
*
* @override
*/
public function rollback()
{
......@@ -37,7 +39,9 @@ class Connection extends PDO implements DriverConnection
}
/**
* {@inheritdoc}
* Performs the commit.
*
* @override
*/
public function commit()
{
......@@ -45,21 +49,12 @@ class Connection extends PDO implements DriverConnection
}
/**
* {@inheritdoc}
* Begins a database transaction.
*
* @override
*/
public function beginTransaction()
{
$this->exec('BEGIN TRANSACTION');
}
/**
* {@inheritdoc}
*/
public function lastInsertId($name = null)
{
$stmt = $this->query('SELECT SCOPE_IDENTITY()');
$id = $stmt->fetchColumn();
$stmt->closeCursor();
return $id;
}
}
\ No newline at end of file
<?php
/*
* $Id$
*
* 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>.
*/
namespace Doctrine\DBAL\Driver\PDOSqlsrv;
/**
* The PDO-based Sqlsrv driver.
*
* @since 2.0
*/
class Driver implements \Doctrine\DBAL\Driver
{
public function connect(array $params, $username = null, $password = null, array $driverOptions = array())
{
if (isset($params['dbname'])) {
$driverOptions['Database'] = $params['dbname'];
}
return new \Doctrine\DBAL\Driver\PDOConnection(
$this->_constructPdoDsn($params),
$username,
$password,
$driverOptions
);
}
/**
* Constructs the Sqlsrv PDO DSN.
*
* @return string The DSN.
*/
private function _constructPdoDsn(array $params)
{
// TODO: This might need to be revisted once we have access to a sql server
$dsn = 'sqlsrv:(';
if (isset($params['host'])) {
$dsn .= $params['host'];
}
$dsn .= ')';
if (stripos($dsn, '\sqlexpress') !== false)
{
$dsn = str_ireplace('\sqlexpress', '', $dsn);
$dsn .= '\sqlexpress';
}
$dsn = str_ireplace('localhost', 'local', $dsn);
if (isset($params['port']) && !empty($params['port'])) {
$dsn .= ',' . $params['port'];
}
return $dsn;
}
public function getDatabasePlatform()
{
return new \Doctrine\DBAL\Platforms\SqlsrvPlatform();
}
public function getSchemaManager(\Doctrine\DBAL\Connection $conn)
{
return new \Doctrine\DBAL\Schema\SqlsrvSchemaManager($conn);
}
public function getName()
{
return 'pdo_sqlsrv';
}
public function getDatabase(\Doctrine\DBAL\Connection $conn)
{
$params = $conn->getParams();
return $params['dbname'];
}
}
\ No newline at end of file
......@@ -44,6 +44,7 @@ final class DriverManager
'oci8' => 'Doctrine\DBAL\Driver\OCI8\Driver',
'ibm_db2' => 'Doctrine\DBAL\Driver\IBMDB2\DB2Driver',
'pdo_ibm' => 'Doctrine\DBAL\Driver\PDOIbm\Driver',
'pdo_sqlsrv' => 'Doctrine\DBAL\Driver\PDOSqlsrv\Driver',
);
/** Private constructor. This class cannot be instantiated. */
......
......@@ -21,6 +21,7 @@ namespace Doctrine\DBAL\Platforms;
use Doctrine\DBAL\Schema\TableDiff;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Schema\Index;
/**
* The MsSqlPlatform provides the behavior, features and SQL dialect of the
......@@ -35,50 +36,87 @@ use Doctrine\DBAL\DBALException;
class MsSqlPlatform extends AbstractPlatform
{
/**
* Adds an adapter-specific LIMIT clause to the SELECT statement.
* [ borrowed from Zend Framework ]
* Whether the platform prefers identity columns for ID generation.
* MsSql prefers "autoincrement" identity columns since sequences can only
* be emulated with a table.
*
* @param string $query
* @param mixed $limit
* @param mixed $offset
* @link http://lists.bestpractical.com/pipermail/rt-devel/2005-June/007339.html
* @return boolean
* @override
*/
public function prefersIdentityColumns()
{
return true;
}
/**
* Whether the platform supports identity columns.
* MsSql supports this through AUTO_INCREMENT columns.
*
* @return boolean
* @override
*/
public function supportsIdentityColumns()
{
return true;
}
/**
* Whether the platform supports savepoints. MsSql does not.
*
* @return boolean
* @override
*/
public function supportsSavepoints()
{
return false;
}
/**
* create a new database
*
* @param string $name name of the database that should be created
* @return string
* @override
*/
public function writeLimitClause($query, $limit = false, $offset = false)
public function getCreateDatabaseSQL($name)
{
if ($limit > 0) {
$count = intval($limit);
return 'CREATE DATABASE ' . $name;
}
$offset = intval($offset);
if ($offset < 0) {
throw DBALException::limitOffsetInvalid($offset);
}
$orderby = stristr($query, 'ORDER BY');
if ($orderby !== false) {
$sort = (stripos($orderby, 'desc') !== false) ? 'desc' : 'asc';
$order = str_ireplace('ORDER BY', '', $orderby);
$order = trim(preg_replace('/ASC|DESC/i', '', $order));
}
$query = preg_replace('/^SELECT\s/i', 'SELECT TOP ' . ($count+$offset) . ' ', $query);
$query = 'SELECT * FROM (SELECT TOP ' . $count . ' * FROM (' . $query . ') AS inner_tbl';
if ($orderby !== false) {
$query .= ' ORDER BY ' . $order . ' ';
$query .= (stripos($sort, 'asc') !== false) ? 'DESC' : 'ASC';
}
$query .= ') AS outer_tbl';
if ($orderby !== false) {
$query .= ' ORDER BY ' . $order . ' ' . $sort;
}
return $query;
/**
* drop an existing database
*
* @param string $name name of the database that should be dropped
* @return string
* @override
*/
public function getDropDatabaseSQL($name)
{
return 'DROP DATABASE ' . $name;
}
/**
* @override
*/
public function quoteIdentifier($str)
{
return '[' . $str . ']';
}
/**
* @override
*/
public function getDropForeignKeySQL($foreignKey, $table)
{
if ($foreignKey instanceof \Doctrine\DBAL\Schema\ForeignKeyConstraint) {
$foreignKey = $foreignKey->getName();
}
return $query;
if ($table instanceof \Doctrine\DBAL\Schema\Table) {
$table = $table->getName();
}
return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey;
}
/**
......@@ -123,66 +161,77 @@ class MsSqlPlatform extends AbstractPlatform
$sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff));
return $sql;
}
/**
* Returns the regular expression operator.
*
* @return string
* @override
*/
public function getRegexpExpression()
public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName)
{
return 'RLIKE';
return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES';
}
/**
* Return string to call a variable with the current timestamp inside an SQL statement
* There are three special variables for current date and time:
* - CURRENT_TIMESTAMP (date and time, TIMESTAMP type)
* - CURRENT_DATE (date, DATE type)
* - CURRENT_TIME (time, TIME type)
*
* @return string to call a variable with the current timestamp
* @override
*/
public function getNowExpression($type = 'timestamp')
public function getShowDatabasesSQL()
{
switch ($type) {
case 'time':
case 'date':
case 'timestamp':
default:
return 'GETDATE()';
}
return 'SHOW DATABASES';
}
/**
* return string to call a function to get a substring inside an SQL statement
*
* @return string to call a function to get a substring
* @override
*/
public function getSubstringExpression($value, $position, $length = null)
public function getListTablesSQL()
{
if ( ! is_null($length)) {
return 'SUBSTRING(' . $value . ', ' . $position . ', ' . $length . ')';
}
return 'SUBSTRING(' . $value . ', ' . $position . ', LEN(' . $value . ') - ' . $position . ' + 1)';
return "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
}
/**
* @override
*/
public function getListTableColumnsSQL($table)
{
return 'exec sp_columns @table_name = ' . $table;
}
/**
* @override
*/
public function getListTableForeignKeysSQL($table, $database = null)
{
return "SELECT f.name AS ForeignKey,
SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName,
OBJECT_NAME (f.parent_object_id) AS TableName,
COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName,
SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName,
OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName,
COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName,
f.delete_referential_action_desc,
f.update_referential_action_desc
FROM sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS fc
INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id
ON f.OBJECT_ID = fc.constraint_object_id
WHERE OBJECT_NAME (f.parent_object_id) = '" . $table . "'";
}
/**
* Returns string to concatenate two or more string parameters
* @override
*/
public function getListTableIndexesSQL($table)
{
return "exec sp_helpindex '" . $table . "'";
}
/**
* Returns the regular expression operator.
*
* @param string $arg1
* @param string $arg2
* @param string $values...
* @return string to concatenate two strings
* @return string
* @override
*/
public function getConcatExpression()
public function getRegexpExpression()
{
$args = func_get_args();
return '(' . implode(' + ', $args) . ')';
return 'RLIKE';
}
/**
......@@ -193,86 +242,87 @@ class MsSqlPlatform extends AbstractPlatform
*/
public function getGuidExpression()
{
return 'NEWID()';
return 'UUID()';
}
/**
* Whether the platform prefers identity columns for ID generation.
* MsSql prefers "autoincrement" identity columns since sequences can only
* be emulated with a table.
*
* @return boolean
* @override
*/
public function prefersIdentityColumns()
public function getLocateExpression($str, $substr, $startPos = false)
{
return true;
if ($startPos == false) {
return 'CHARINDEX(' . $substr . ', ' . $str . ')';
} else {
return 'CHARINDEX(' . $substr . ', ' . $str . ', '.$startPos.')';
}
}
/**
* Whether the platform supports identity columns.
* MsSql supports this through AUTO_INCREMENT columns.
*
* @return boolean
* @override
*/
public function supportsIdentityColumns()
public function getModExpression($expression1, $expression2)
{
return true;
return $expression1 . ' % ' . $expression2;
}
/**
* Whether the platform supports savepoints. MsSql does not.
*
* @return boolean
* @override
*/
public function supportsSavepoints()
public function getTrimExpression($str, $pos = self::TRIM_UNSPECIFIED, $char = false)
{
return false;
}
// @todo
$trimFn = '';
$trimChar = ($char != false) ? (', ' . $char) : '';
public function getShowDatabasesSQL()
{
return 'SHOW DATABASES';
if ($pos == self::TRIM_LEADING) {
$trimFn = 'LTRIM';
} else if($pos == self::TRIM_TRAILING) {
$trimFn = 'RTRIM';
} else {
return 'LTRIM(RTRIM(' . $str . '))';
}
return $trimFn . '(' . $str . ')';
}
public function getListTablesSQL()
/**
* @override
*/
public function getConcatExpression()
{
return 'SHOW TABLES';
$args = func_get_args();
return '(' . implode(' + ', $args) . ')';
}
/**
* create a new database
*
* @param string $name name of the database that should be created
* @return string
* @override
*/
public function getCreateDatabaseSQL($name)
public function getSubstringExpression($value, $from, $len = null)
{
return 'CREATE DATABASE ' . $name;
if ( ! is_null($len)) {
return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $len . ')';
}
return 'SUBSTRING(' . $value . ', ' . $from . ', LEN(' . $value . ') - ' . $from . ' + 1)';
}
/**
* drop an existing database
*
* @param string $name name of the database that should be dropped
* @return string
* @override
*/
public function getDropDatabaseSQL($name)
public function getLengthExpression($column)
{
return 'DROP DATABASE ' . $name;
return 'LEN(' . $column . ')';
}
/**
* @override
*/
public function getSetTransactionIsolationSQL($level)
{
return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
}
/**
* @override
/**
* @override
*/
public function getIntegerTypeDeclarationSQL(array $field)
{
......@@ -280,21 +330,22 @@ class MsSqlPlatform extends AbstractPlatform
}
/**
* @override
* @override
*/
public function getBigIntTypeDeclarationSQL(array $field)
{
return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
}
/**
* @override
/**
* @override
*/
public function getSmallIntTypeDeclarationSQL(array $field)
{
return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($field);
}
/** @override */
public function getVarcharTypeDeclarationSQL(array $field)
{
if ( ! isset($field['length'])) {
......@@ -311,21 +362,21 @@ class MsSqlPlatform extends AbstractPlatform
return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
: ($length ? 'VARCHAR(' . $length . ')' : 'TEXT');
}
/** @override */
public function getClobTypeDeclarationSQL(array $field)
{
return 'TEXT';
}
/**
* @override
/**
* @override
*/
protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
{
$autoinc = '';
if ( ! empty($columnDef['autoincrement'])) {
$autoinc = ' AUTO_INCREMENT';
$autoinc = ' IDENTITY';
}
$unsigned = (isset($columnDef['unsigned']) && $columnDef['unsigned']) ? ' UNSIGNED' : '';
......@@ -337,7 +388,8 @@ class MsSqlPlatform extends AbstractPlatform
*/
public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
{
return 'CHAR(' . strlen('YYYY-MM-DD HH:MM:SS') . ')';
// 6 - microseconds precision length
return 'DATETIME2(6)';
}
/**
......@@ -345,15 +397,15 @@ class MsSqlPlatform extends AbstractPlatform
*/
public function getDateTypeDeclarationSQL(array $fieldDeclaration)
{
return 'CHAR(' . strlen('YYYY-MM-DD') . ')';
}
return 'DATE';
}
/**
* @override
*/
public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
{
return 'CHAR(' . strlen('HH:MM:SS') . ')';
return 'TIME';
}
/**
......@@ -364,16 +416,6 @@ class MsSqlPlatform extends AbstractPlatform
return 'BIT';
}
/**
* Get the platform name for this instance
*
* @return string
*/
public function getName()
{
return 'mssql';
}
/**
* Adds an adapter-specific LIMIT clause to the SELECT statement.
*
......@@ -432,14 +474,14 @@ class MsSqlPlatform extends AbstractPlatform
$query = 'SELECT * FROM (SELECT TOP ' . $count . ' * FROM (' . $query . ') AS ' . 'inner_tbl';
if ($orderby !== false) {
$query .= ' ORDER BY ';
$query .= ' ORDER BY ';
for ($i = 0, $l = count($orders); $i < $l; $i++) {
if ($i > 0) { // not first order clause
$query .= ', ';
}
for ($i = 0, $l = count($orders); $i < $l; $i++) {
if ($i > 0) { // not first order clause
$query .= ', ';
}
$query .= 'inner_tbl' . '.' . $aliases[$i] . ' ';
$query .= 'inner_tbl' . '.' . $aliases[$i] . ' ';
$query .= (stripos($sorts[$i], 'ASC') !== false) ? 'DESC' : 'ASC';
}
}
......@@ -447,12 +489,12 @@ class MsSqlPlatform extends AbstractPlatform
$query .= ') AS ' . 'outer_tbl';
if ($orderby !== false) {
$query .= ' ORDER BY ';
$query .= ' ORDER BY ';
for ($i = 0, $l = count($orders); $i < $l; $i++) {
if ($i > 0) { // not first order clause
$query .= ', ';
}
for ($i = 0, $l = count($orders); $i < $l; $i++) {
if ($i > 0) { // not first order clause
$query .= ', ';
}
$query .= 'outer_tbl' . '.' . $aliases[$i] . ' ' . $sorts[$i];
}
......@@ -463,51 +505,65 @@ class MsSqlPlatform extends AbstractPlatform
}
/**
* Get the insert sql for an empty insert statement
*
* @param string $tableName
* @param string $identifierColumnName
* @return string $sql
* @override
*/
public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName)
public function convertBooleans($item)
{
return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES';
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;
}
/**
* @inheritdoc
* @override
*/
public function getTruncateTableSQL($tableName, $cascade = false)
public function getCreateTemporaryTableSnippetSQL()
{
return 'TRUNCATE TABLE '.$tableName;
return "CREATE TABLE";
}
/**
* MsSql uses Table Hints for locking strategies instead of the ANSI SQL FOR UPDATE like hints.
*
* @return string
* @override
*/
public function getTemporaryTableName($tableName)
{
return '#' . $tableName;
}
/**
* @override
*/
public function getDateTimeFormatString()
{
return 'Y-m-d H:i:s.u';
}
/**
* @override
*/
public function getForUpdateSQL()
public function getIndexDeclarationSQL($name, Index $index)
{
return '';
// @todo
return $this->getUniqueConstraintDeclarationSQL($name, $index);
}
/**
* @license LGPL
* @author Hibernate
* @param string $fromClause
* @param int $lockMode
* Get the platform name for this instance
*
* @return string
*/
public function appendLockHint($fromClause, $lockMode)
public function getName()
{
if ($lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE) {
return $fromClause . " WITH (UPDLOCK, ROWLOCK)";
} else if ( $lockMode == \Doctrine\DBAL\LockMode::PESSIMISTIC_READ ) {
return $fromClause . " WITH (HOLDLOCK, ROWLOCK)";
} else {
return $fromClause;
}
return 'mssql';
}
protected function initializeDoctrineTypeMappings()
......
<?php
/*
* $Id$
*
* 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>.
*/
namespace Doctrine\DBAL\Platforms;
use Doctrine\DBAL\Schema\TableDiff;
use Doctrine\DBAL\DBALException;
/**
* The SqlsrvPlatform provides the behavior, features and SQL dialect of the
* Microsoft SQL database platform.
*
* @since 2.0
* @author Juozas Kaziukenas <juozas@juokaz.com>
*/
class SqlsrvPlatform extends MsSqlPlatform
{
/**
* Get the platform name for this instance
*
* @return string
*/
public function getName()
{
return 'sqlsrv';
}
}
......@@ -27,373 +27,207 @@ namespace Doctrine\DBAL\Schema;
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
* @author Juozas Kaziukenas <juozas@juokaz.com>
* @version $Revision$
* @since 2.0
*/
class MsSqlSchemaManager extends AbstractSchemaManager
{
/**
* create a new database
*
* @param string $name name of the database that should be created
* @return void
* @override
*/
public function createDatabase($name)
protected function _getPortableTableColumnDefinition($tableColumn)
{
$query = "CREATE DATABASE $name";
if ($this->conn->options['database_device']) {
$query.= ' ON '.$this->conn->options['database_device'];
$query.= $this->conn->options['database_size'] ? '=' .
$this->conn->options['database_size'] : '';
$dbType = strtolower($tableColumn['TYPE_NAME']);
if (stripos($dbType, 'identity')) {
$dbType = trim(str_ireplace('identity', '', $dbType));
}
return $this->conn->standaloneQuery($query, null, true);
}
/**
* drop an existing database
*
* @param string $name name of the database that should be dropped
* @return void
*/
public function dropDatabase($name)
{
return $this->conn->standaloneQuery('DROP DATABASE ' . $name, null, true);
}
$type = array();
$unsigned = $fixed = null;
/**
* alter an existing table
*
* @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
* of change that is intended to be performed. The types of
* changes that are currently supported are defined as follows:
*
* name
*
* New name for the table.
*
* add
*
* Associative array with the names of fields to be added as
* indexes of the array. The value of each entry of the array
* should be set to another associative array with the properties
* of the fields to be added. The properties of the fields should
* be the same as defined by the Metabase parser.
*
*
* remove
*
* Associative array with the names of fields to be removed as indexes
* of the array. Currently the values assigned to each entry are ignored.
* An empty array should be used for future compatibility.
*
* rename
*
* Associative array with the names of fields to be renamed as indexes
* of the array. The value of each entry of the array should be set to
* another associative array with the entry named name with the new
* field name and the entry named Declaration that is expected to contain
* the portion of the field declaration already in DBMS specific SQL code
* as it is used in the CREATE TABLE statement.
*
* change
*
* Associative array with the names of the fields to be changed as indexes
* of the array. Keep in mind that if it is intended to change either the
* name of a field and any other properties, the change array entries
* should have the new names of the fields as array indexes.
*
* The value of each entry of the array should be set to another associative
* array with the properties of the fields to that are meant to be changed as
* array entries. These entries should be assigned to the new values of the
* respective properties. The properties of the fields should be the same
* as defined by the Metabase parser.
*
* Example
* array(
* 'name' => 'userlist',
* 'add' => array(
* 'quota' => array(
* 'type' => 'integer',
* 'unsigned' => 1
* )
* ),
* 'remove' => array(
* 'file_limit' => array(),
* 'time_limit' => array()
* ),
* 'change' => array(
* 'name' => array(
* 'length' => '20',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 20,
* ),
* )
* ),
* 'rename' => array(
* 'sex' => array(
* 'name' => 'gender',
* 'definition' => array(
* 'type' => 'text',
* 'length' => 1,
* 'default' => 'M',
* ),
* )
* )
* )
*
* @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.
* @return void
*/
public function alterTable($name, array $changes, $check = false)
{
foreach ($changes as $changeName => $change) {
switch ($changeName) {
case 'add':
break;
case 'remove':
break;
case 'name':
case 'rename':
case 'change':
default:
throw SchemaException::alterTableChangeNotSupported($changeName);
}
if ( ! isset($tableColumn['name'])) {
$tableColumn['name'] = '';
}
$query = '';
if ( ! empty($changes['add']) && is_array($changes['add'])) {
foreach ($changes['add'] as $fieldName => $field) {
if ($query) {
$query .= ', ';
// Map db type to Doctrine mapping type
switch ($dbType) {
case 'tinyint':
$type = 'boolean';
break;
case 'smallint':
$type = 'smallint';
break;
case 'mediumint':
$type = 'integer';
break;
case 'int':
case 'integer':
$type = 'integer';
break;
case 'bigint':
$type = 'bigint';
break;
case 'tinytext':
case 'mediumtext':
case 'longtext':
case 'text':
$type = 'text';
$fixed = false;
break;
case 'varchar':
$fixed = false;
case 'string':
case 'char':
$type = 'string';
if ($tableColumn['LENGTH'] == '1') {
$type = 'boolean';
if (preg_match('/^(is|has)/', $tableColumn['name'])) {
$type = array_reverse($type);
}
} else if (strstr($dbType, 'text')) {
$type = 'text';
if ($decimal == 'binary') {
$type = 'blob';
}
}
$query .= 'ADD ' . $this->getDeclaration($fieldName, $field);
}
}
if ( ! empty($changes['remove']) && is_array($changes['remove'])) {
foreach ($changes['remove'] as $fieldName => $field) {
if ($query) {
$query .= ', ';
if ($fixed !== false) {
$fixed = true;
}
$query .= 'DROP COLUMN ' . $fieldName;
}
break;
case 'set':
$fixed = false;
$type = 'text';
$type = 'integer'; //FIXME:???
break;
case 'date':
$type = 'date';
break;
case 'datetime':
case 'timestamp':
$type = 'datetime';
break;
case 'time':
$type = 'time';
break;
case 'float':
case 'double':
case 'real':
case 'numeric':
case 'decimal':
$type = 'decimal';
break;
case 'tinyblob':
case 'mediumblob':
case 'longblob':
case 'blob':
case 'binary':
case 'varbinary':
$type = 'blob';
break;
case 'year':
$type = 'integer';
$type = 'date';
break;
case 'geometry':
case 'geometrycollection':
case 'point':
case 'multipoint':
case 'linestring':
case 'multilinestring':
case 'polygon':
case 'multipolygon':
$type = 'blob';
break;
default:
$type = 'string';
}
$def = array(
'type' => $type,
'length' => ((int) $tableColumn['LENGTH'] == 0) ? null : (int) $tableColumn['LENGTH'],
'unsigned' => (bool) $unsigned,
'fixed' => (bool) $fixed
);
if ( ! $query) {
return false;
}
return $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query);
}
$default = $tableColumn['COLUMN_DEF'];
/**
* {@inheritdoc}
*/
public function createSequence($seqName, $start = 1, $allocationSize = 1)
{
$seqcolName = 'seq_col';
$query = 'CREATE TABLE ' . $seqName . ' (' . $seqcolName .
' INT PRIMARY KEY CLUSTERED IDENTITY(' . $start . ', 1) NOT NULL)';
$res = $this->conn->exec($query);
if ($start == 1) {
return true;
while($default != ($default2 = preg_replace("/^\((.*)\)$/", '$1', $default))) {
$default = $default2;
}
try {
$query = 'SET IDENTITY_INSERT ' . $sequenceName . ' ON ' .
'INSERT INTO ' . $sequenceName . ' (' . $seqcolName . ') VALUES ( ' . $start . ')';
$res = $this->conn->exec($query);
} catch (Exception $e) {
$result = $this->conn->exec('DROP TABLE ' . $sequenceName);
}
return true;
}
/**
* This function drops an existing sequence
*
* @param string $seqName name of the sequence to be dropped
* @return void
*/
public function dropSequenceSql($seqName)
{
return 'DROP TABLE ' . $seqName;
}
/**
* lists all database sequences
*
* @param string|null $database
* @return array
*/
public function listSequences($database = null)
{
$query = "SELECT name FROM sysobjects WHERE xtype = 'U'";
$tableNames = $this->conn->fetchColumn($query);
return array_map(array($this->conn->formatter, 'fixSequenceName'), $tableNames);
$options = array(
'length' => ((int) $tableColumn['LENGTH'] == 0) ? null : (int) $tableColumn['LENGTH'],
'unsigned' => (bool)$unsigned,
'fixed' => (bool)$fixed,
'default' => $default !== 'NULL' ? $default : null,
'notnull' => (bool) ($tableColumn['IS_NULLABLE'] != 'YES'),
'scale' => $tableColumn['SCALE'],
'precision' => $tableColumn['PRECISION'],
'platformOptions' => array(
// @todo
'primary' => false,
'unique' => false,
'autoincrement' => false,
),
);
return new Column($tableColumn['COLUMN_NAME'], \Doctrine\DBAL\Types\Type::getType($type), $options);
}
/**
* lists table constraints
*
* @param string $table database table name
* @return array
* @override
*/
public function listTableColumns($table)
protected function _getPortableTableIndexesList($tableIndexRows, $tableName=null)
{
$sql = 'EXEC sp_columns @table_name = ' . $table;
$result = $this->conn->fetchAssoc($sql);
$columns = array();
foreach ($result as $key => $val) {
$val = array_change_key_case($val, CASE_LOWER);
if (strstr($val['type_name'], ' ')) {
list($type, $identity) = explode(' ', $val['type_name']);
} else {
$type = $val['type_name'];
$identity = '';
}
if ($type == 'varchar') {
$type .= '(' . $val['length'] . ')';
$result = array();
foreach($tableIndexRows AS $tableIndex) {
$indexName = $keyName = $tableIndex['index_name'];
if(strpos($tableIndex['index_description'], 'primary key') !== false) {
$keyName = 'primary';
}
$keyName = strtolower($keyName);
$val['type'] = $type;
$val['identity'] = $identity;
$decl = $this->conn->getDatabasePlatform()->getPortableDeclaration($val);
$description = array(
'name' => $val['column_name'],
'ntype' => $type,
'type' => $decl['type'][0],
'alltypes' => $decl['type'],
'length' => $decl['length'],
'fixed' => $decl['fixed'],
'unsigned' => $decl['unsigned'],
'notnull' => (bool) (trim($val['is_nullable']) === 'NO'),
'default' => $val['column_def'],
'primary' => (strtolower($identity) == 'identity'),
$result[$keyName] = array(
'name' => $indexName,
'columns' => explode(', ', $tableIndex['index_keys']),
'unique' => strpos($tableIndex['index_description'], 'unique') !== false,
'primary' => strpos($tableIndex['index_description'], 'primary key') !== false,
);
$columns[$val['column_name']] = $description;
}
return $columns;
}
/**
* lists table constraints
*
* @param string $table database table name
* @return array
*/
public function listTableIndexes($table)
{
}
/**
* lists tables
*
* @param string|null $database
* @return array
*/
public function listTables($database = null)
{
$sql = "SELECT name FROM sysobjects WHERE type = 'U' AND name <> 'dtproperties' ORDER BY name";
return $this->conn->fetchColumn($sql);
}
/**
* lists all triggers
*
* @return array
*/
public function listTriggers($database = null)
{
$query = "SELECT name FROM sysobjects WHERE xtype = 'TR'";
$result = $this->conn->fetchColumn($query);
return $result;
}
/**
* lists table triggers
*
* @param string $table database table name
* @return array
*/
public function listTableTriggers($table)
{
$table = $this->conn->quote($table, 'text');
$query = "SELECT name FROM sysobjects WHERE xtype = 'TR' AND object_name(parent_obj) = " . $table;
$result = $this->conn->fetchColumn($query);
$indexes = array();
foreach($result AS $indexKey => $data) {
$indexes[$indexKey] = new Index($data['name'], $data['columns'], $data['unique'], $data['primary']);
}
return $result;
return $indexes;
}
/**
* lists table views
*
* @param string $table database table name
* @return array
* @override
*/
public function listTableViews($table)
public function _getPortableTableForeignKeyDefinition($tableForeignKey)
{
$keyName = 'INDEX_NAME';
$pkName = 'PK_NAME';
if ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_FIX_CASE) {
if ($this->conn->getAttribute(Doctrine::ATTR_FIELD_CASE) == CASE_LOWER) {
$keyName = strtolower($keyName);
$pkName = strtolower($pkName);
} else {
$keyName = strtoupper($keyName);
$pkName = strtoupper($pkName);
}
}
$table = $this->conn->quote($table, 'text');
$query = 'EXEC sp_statistics @table_name = ' . $table;
$indexes = $this->conn->fetchColumn($query, $keyName);
$query = 'EXEC sp_pkeys @table_name = ' . $table;
$pkAll = $this->conn->fetchColumn($query, $pkName);
$result = array();
foreach ($indexes as $index) {
if ( ! in_array($index, $pkAll) && $index != null) {
$result[] = $this->conn->formatter->fixIndexName($index);
}
}
return $result;
return new ForeignKeyConstraint(
(array)$tableForeignKey['ColumnName'],
$tableForeignKey['ReferenceTableName'],
(array)$tableForeignKey['ReferenceColumnName'],
$tableForeignKey['ForeignKey'],
array(
'onUpdate' => str_replace('_', ' ', $tableForeignKey['update_referential_action_desc']),
'onDelete' => str_replace('_', ' ', $tableForeignKey['delete_referential_action_desc']),
)
);
}
/**
* lists database views
*
* @param string|null $database
* @return array
* @override
*/
public function listViews($database = null)
{
$query = "SELECT name FROM sysobjects WHERE xtype = 'V'";
return $this->conn->fetchColumn($query);
}
protected function _getPortableTableColumnDefinition($tableColumn)
protected function _getPortableTableDefinition($table)
{
return $column;
return $table['name'];
}
}
\ No newline at end of file
<?php
/*
* $Id$
*
* 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.phpdoctrine.org>.
*/
namespace Doctrine\DBAL\Schema;
/**
* xxx
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Juozas Kaziukenas <juozas@juokaz.com>
* @version $Revision$
* @since 2.0
*/
class SqlsrvSchemaManager extends MsSqlSchemaManager
{
}
\ No newline at end of file
......@@ -16,13 +16,13 @@ class MsSqlPlatformTest extends AbstractPlatformTestCase
public function getGenerateTableSql()
{
return 'CREATE TABLE test (id INT AUTO_INCREMENT NOT NULL, test VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))';
return 'CREATE TABLE test (id INT IDENTITY NOT NULL, test VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))';
}
public function getGenerateTableWithMultiColumnUniqueIndexSql()
{
return array(
'CREATE TABLE test (foo VARCHAR(255) DEFAULT NULL, bar VARCHAR(255) DEFAULT NULL, UNIQUE INDEX test_foo_bar_uniq (foo, bar))'
'CREATE TABLE test (foo VARCHAR(255) DEFAULT NULL, bar VARCHAR(255) DEFAULT NULL, CONSTRAINT test_foo_bar_uniq UNIQUE (foo, bar))'
);
}
......@@ -75,11 +75,11 @@ class MsSqlPlatformTest extends AbstractPlatformTestCase
$this->_platform->getIntegerTypeDeclarationSQL(array())
);
$this->assertEquals(
'INT AUTO_INCREMENT',
'INT IDENTITY',
$this->_platform->getIntegerTypeDeclarationSQL(array('autoincrement' => true)
));
$this->assertEquals(
'INT AUTO_INCREMENT',
'INT IDENTITY',
$this->_platform->getIntegerTypeDeclarationSQL(
array('autoincrement' => true, 'primary' => true)
));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment