Commit 4c300758 authored by Benjamin Eberlei's avatar Benjamin Eberlei

[DBAL-216] Reintroduce ext/sqlsrv Driver.

This is necessary because pdo_sqlsrv still has some little bugs that
prevent it from being used in all use-cases. Additionally only sqlsrv
can make use of the Azure federations features.
parent 104f97e6
......@@ -60,7 +60,6 @@ class Driver implements \Doctrine\DBAL\Driver
return $dsn;
}
public function getDatabasePlatform()
{
return new \Doctrine\DBAL\Platforms\SQLServer2008Platform();
......
<?php
/*
* 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\SQLSrv;
/**
* Driver for ext/sqlsrv
*/
class Driver implements \Doctrine\DBAL\Driver
{
public function connect(array $params, $username = null, $password = null, array $driverOptions = array())
{
if (!isset($params['host'])) {
throw new SQLSrvException("Missing 'host' in configuration for sqlsrv driver.");
}
$serverName = $params['host'];
if (isset($params['port'])) {
$serverName .= ', ' . $params['port'];
}
if (!isset($params['dbname'])) {
throw new SQLSrvException("Missing 'dbname' in configuration for sqlsrv driver.");
}
$driverOptions['Database'] = $params['dbname'];
$driverOptions['UID'] = $username;
$driverOptions['PWD'] = $password;
if (!isset($driverOptions['ReturnDatesAsStrings'])) {
$driverOptions['ReturnDatesAsStrings'] = 1;
}
return new SQLSrvConnection($serverName, $driverOptions);
}
public function getDatabasePlatform()
{
return new \Doctrine\DBAL\Platforms\SQLServer2008Platform();
}
public function getSchemaManager(\Doctrine\DBAL\Connection $conn)
{
return new \Doctrine\DBAL\Schema\SQLServerSchemaManager($conn);
}
public function getName()
{
return 'sqlsrv';
}
public function getDatabase(\Doctrine\DBAL\Connection $conn)
{
$params = $conn->getParams();
return $params['dbname'];
}
}
<?php
/*
* 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\SQLSrv;
/**
* SQL Server implementation for the Connection interface.
*
* @since 2.3
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class SQLSrvConnection implements \Doctrine\DBAL\Driver\Connection
{
protected $conn;
public function __construct($serverName, $connectionOptions)
{
$this->conn = sqlsrv_connect($serverName, $connectionOptions);
if (!$this->conn) {
throw SQLSrvException::fromSqlSrvErrors();
}
}
/**
* {@inheritDoc}
*/
public function prepare($sql)
{
return new SQLSrvStatement($this->conn, $sql);
}
/**
* {@inheritDoc}
*/
public function query()
{
$args = func_get_args();
$sql = $args[0];
$stmt = $this->prepare($sql);
$stmt->execute();
return $stmt;
}
/**
* {@inheritDoc}
* @license New BSD, code from Zend Framework
*/
public function quote($value, $type=\PDO::PARAM_STR)
{
if (is_int($value)) {
return $value;
} else if (is_float($value)) {
return sprintf('%F', $value);
}
return "'" . str_replace("'", "''", $value) . "'";
}
/**
* {@inheritDoc}
*/
public function exec($statement)
{
$stmt = $this->prepare($statement);
$stmt->execute();
return $stmt->rowCount();
}
/**
* {@inheritDoc}
*/
public function lastInsertId($name = null)
{
if ($name === null) {
$sql = "SELECT SCOPE_IDENTITY() AS LastInsertId";
} else {
$sql = "SELECT IDENT_CURRENT(".$this->quote($name).") AS LastInsertId";
}
$stmt = $this->prepare($sql);
$stmt->execute();
return $stmt->fetchColumn();
}
/**
* {@inheritDoc}
*/
public function beginTransaction()
{
if ( ! sqlsrv_begin_transaction($this->conn)) {
throw SQLSrvException::fromSqlSrvErrors();
}
}
/**
* {@inheritDoc}
*/
public function commit()
{
if ( ! sqlsrv_commit($this->conn)) {
throw SQLSrvException::fromSqlSrvErrors();
}
}
/**
* {@inheritDoc}
*/
public function rollBack()
{
if ( ! sqlsrv_rollback($this->conn)) {
throw SQLSrvException::fromSqlSrvErrors();
}
}
/**
* {@inheritDoc}
*/
public function errorCode()
{
$errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
if ($errors) {
return $errors[0]['code'];
}
return false;
}
/**
* {@inheritDoc}
*/
public function errorInfo()
{
return sqlsrv_errors(SQLSRV_ERR_ERRORS);
}
}
<?php
/*
* 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\SQLSrv;
class SQLSrvException extends \Doctrine\DBAL\DBALException
{
/**
* Helper method to turn sql server errors into exception.
*
* @return SQLSrvException
*/
static public function fromSqlSrvErrors()
{
$errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
$message = "";
foreach ($errors as $error) {
$message .= "SQLSTATE [".$error['SQLSTATE'].", ".$error['code']."]: ". $error['message']."\n";
}
if (!$message) {
$message = "SQL Server error occured but no error message was retrieved from driver.";
}
return new self(rtrim($message));
}
}
<?php
/*
* 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\SQLSrv;
use PDO;
use IteratorAggregate;
use Doctrine\DBAL\Driver\Statement;
class SQLSrvStatement implements IteratorAggregate, Statement
{
/**
* SQLSRV Resource
*
* @var resource
*/
private $conn;
/**
* SQL Statement to execute
*
* @var string
*/
private $sql;
/**
* SQLSRV Statement Resource
*
* @var resource
*/
private $stmt;
/**
* Parameters to bind
*
* @var array
*/
private $params = array();
/**
* Translations
*
* @var array
*/
private static $fetchMap = array(
PDO::FETCH_BOTH => SQLSRV_FETCH_BOTH,
PDO::FETCH_ASSOC => SQLSRV_FETCH_ASSOC,
PDO::FETCH_NUM => SQLSRV_FETCH_NUMERIC,
);
/**
* Fetch Style
*
* @param int
*/
private $defaultFetchStyle = PDO::FETCH_BOTH;
public function __construct($conn, $sql)
{
$this->conn = $conn;
$this->sql = $sql;
}
public function bindValue($param, $value, $type = null)
{
return $this->bindParam($param, $value, $type);
}
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = null)
{
if (!is_numeric($column)) {
throw new SQLSrvException("sqlsrv does not support named parameters to queries, use question mark (?) placeholders instead.");
}
if ($type === \PDO::PARAM_LOB) {
$this->params[$column-1] = array($variable, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY), SQLSRV_SQLTYPE_VARBINARY('max'));
} else {
$this->params[$column-1] = $variable;
}
}
public function closeCursor()
{
if ($this->stmt) {
sqlsrv_free_stmt($this->stmt);
}
}
public function columnCount()
{
return sqlsrv_num_fields($this->stmt);
}
/**
* {@inheritDoc}
*/
public function errorCode()
{
$errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
if ($errors) {
return $errors[0]['code'];
}
return false;
}
/**
* {@inheritDoc}
*/
public function errorInfo()
{
return sqlsrv_errors(SQLSRV_ERR_ERRORS);
}
public function execute($params = null)
{
if ($params) {
$hasZeroIndex = array_key_exists(0, $params);
foreach ($params as $key => $val) {
if ($hasZeroIndex && is_numeric($key)) {
$this->bindValue($key + 1, $val);
} else {
$this->bindValue($key, $val);
}
}
}
$this->stmt = sqlsrv_query($this->conn, $this->sql, $this->params);
if (!$this->stmt) {
throw SQLSrvException::fromSqlSrvErrors();
}
}
public function setFetchMode($fetchStyle = PDO::FETCH_BOTH)
{
$this->defaultFetchStyle = $fetchStyle;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
$data = $this->fetchAll($this->defaultFetchStyle);
return new \ArrayIterator($data);
}
/**
* {@inheritdoc}
*/
public function fetch($fetchStyle = null)
{
$fetchStyle = ($fetchStyle)?:$this->defaultFetchStyle;
if (isset(self::$fetchMap[$fetchStyle])) {
return sqlsrv_fetch_array($this->stmt, self::$fetchMap[$fetchStyle]);
} else if ($fetchStyle == PDO::FETCH_OBJ || $fetchStyle == PDO::FETCH_CLASS) {
$className = null;
$ctorArgs = null;
if (func_num_args() >= 2) {
$args = func_get_args();
$className = $args[1];
$ctorArgs = (isset($args[2])) ? $args[2] : array();
}
return sqlsrv_fetch_object($this->stmt, $className, $ctorArgs);
} else {
throw new SQLSrvException("Fetch mode is not supported!");
}
}
/**
* {@inheritdoc}
*/
public function fetchAll($fetchStyle = null)
{
$className = null;
$ctorArgs = null;
if (func_num_args() >= 2) {
$args = func_get_args();
$className = $args[1];
$ctorArgs = (isset($args[2])) ? $args[2] : array();
}
$rows = array();
while ($row = $this->fetch($fetchStyle, $className, $ctorArgs)) {
$rows[] = $row;
}
return $rows;
}
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
{
$row = $this->fetch(PDO::FETCH_NUM);
return $row[$columnIndex];
}
/**
* {@inheritdoc}
*/
public function rowCount()
{
return sqlsrv_rows_affected($this->stmt);
}
}
......@@ -32,8 +32,10 @@ final class DriverManager
/**
* List of supported drivers and their mappings to the driver classes.
*
* To add your own driver use the 'driverClass' parameter to
* {@link DriverManager::getConnection()}.
*
* @var array
* @todo REMOVE. Users should directly supply class names instead.
*/
private static $_driverMap = array(
'pdo_mysql' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
......@@ -46,6 +48,7 @@ final class DriverManager
'pdo_sqlsrv' => 'Doctrine\DBAL\Driver\PDOSqlsrv\Driver',
'mysqli' => 'Doctrine\DBAL\Driver\Mysqli\Driver',
'drizzle_pdo_mysql' => 'Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver',
'sqlsrv' => 'Doctrine\DBAL\Driver\SQLSrv\Driver',
);
/** Private constructor. This class cannot be instantiated. */
......@@ -59,11 +62,18 @@ final class DriverManager
* $params must contain at least one of the following.
*
* Either 'driver' with one of the following values:
*
* pdo_mysql
* pdo_sqlite
* pdo_pgsql
* pdo_oracle
* pdo_oci (unstable)
* pdo_sqlsrv
* pdo_ibm (unstable)
* pdo_sqlsrv
* mysqli
* sqlsrv
* ibm_db2 (unstable)
* drizzle_pdo_mysql
*
* OR 'driverClass' that contains the full class name (with namespace) of the
* driver class to instantiate.
......
......@@ -38,6 +38,7 @@ class Connection extends \Doctrine\DBAL\Connection
const PORTABILITY_SQLITE = 13;
const PORTABILITY_OTHERVENDORS = 12;
const PORTABILITY_DRIZZLE = 13;
const PORTABILITY_SQLSRV = 13;
/**
* @var int
......@@ -63,6 +64,8 @@ class Connection extends \Doctrine\DBAL\Connection
$params['portability'] = $params['portability'] & self::PORTABILITY_SQLITE;
} else if ($this->_platform->getName() === "drizzle") {
$params['portability'] = self::PORTABILITY_DRIZZLE;
} else if ($this->_platform->getName() === 'sqlsrv') {
$params['portaility'] = $params['portabililty'] & self::PORTABILITY_SQLSRV;
} else {
$params['portability'] = $params['portability'] & self::PORTABILITY_OTHERVENDORS;
}
......
......@@ -21,7 +21,6 @@ namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Events;
use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs;
use Doctrine\DBAL\Events;
/**
* SQL Server Schema Manager
......
......@@ -411,7 +411,7 @@ class DataAccessTest extends \Doctrine\Tests\DbalFunctionalTestCase
$this->assertEquals(1, $results[0]->test_int);
$this->assertEquals('foo', $results[0]->test_string);
$this->assertEquals('2010-01-01 10:10:10', $results[0]->test_datetime);
$this->assertStringStartsWith('2010-01-01 10:10:10', $results[0]->test_datetime);
}
}
......
......@@ -125,6 +125,7 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest
$table->addColumn('baz1', 'datetime');
$table->addColumn('baz2', 'time');
$table->addColumn('baz3', 'date');
$table->setPrimaryKey(array('id'));
return $table;
}
......
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