Remove deprecated statement classes

parent 10357097
...@@ -6,15 +6,20 @@ ...@@ -6,15 +6,20 @@
- `DriverException` - `DriverException`
- `PDOConnection` - `PDOConnection`
- `PDOException` - `PDOException`
- `PDOStatement`
- `IBMDB2\DB2Connection` - `IBMDB2\DB2Connection`
- `IBMDB2\DB2Driver` - `IBMDB2\DB2Driver`
- `IBMDB2\DB2Exception` - `IBMDB2\DB2Exception`
- `IBMDB2\DB2Statement`
- `Mysqli\MysqliConnection` - `Mysqli\MysqliConnection`
- `Mysqli\MysqliException` - `Mysqli\MysqliException`
- `Mysqli\MysqliStatement`
- `OCI8\OCI8Connection` - `OCI8\OCI8Connection`
- `OCI8\OCI8Exception` - `OCI8\OCI8Exception`
- `OCI8\OCI8Statement`
- `SQLSrv\SQLSrvConnection` - `SQLSrv\SQLSrvConnection`
- `SQLSrv\SQLSrvException` - `SQLSrv\SQLSrvException`
- `SQLSrv\SQLSrvStatement`
## BC BREAK: `ServerInfoAwareConnection::requiresQueryForServerVersion()` is removed. ## BC BREAK: `ServerInfoAwareConnection::requiresQueryForServerVersion()` is removed.
......
...@@ -114,6 +114,6 @@ ...@@ -114,6 +114,6 @@
<!-- The SQLSRV_* functions are defined in the upper case by the sqlsrv extension and violate the standard <!-- The SQLSRV_* functions are defined in the upper case by the sqlsrv extension and violate the standard
see https://docs.microsoft.com/en-us/sql/connect/php/constants-microsoft-drivers-for-php-for-sql-server --> see https://docs.microsoft.com/en-us/sql/connect/php/constants-microsoft-drivers-for-php-for-sql-server -->
<rule ref="Squiz.PHP.LowercasePHPFunctions"> <rule ref="Squiz.PHP.LowercasePHPFunctions">
<exclude-pattern>src/Driver/SQLSrv/SQLSrvStatement.php</exclude-pattern> <exclude-pattern>src/Driver/SQLSrv/Statement.php</exclude-pattern>
</rule> </rule>
</ruleset> </ruleset>
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
Requires a release of Requires a release of
https://github.com/JetBrains/phpstorm-stubs/pull/727 https://github.com/JetBrains/phpstorm-stubs/pull/727
--> -->
<file name="src/Driver/SQLSrv/SQLSrvStatement.php"/> <file name="src/Driver/SQLSrv/Statement.php"/>
</errorLevel> </errorLevel>
</TooFewArguments> </TooFewArguments>
<UndefinedConstant> <UndefinedConstant>
......
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCopyStreamToStream;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCreateTemporaryFile;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotWriteToTemporaryFile;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use function assert;
use function db2_bind_param;
use function db2_execute;
use function error_get_last;
use function fclose;
use function fwrite;
use function is_int;
use function is_resource;
use function ksort;
use function stream_copy_to_stream;
use function stream_get_meta_data;
use function tmpfile;
use const DB2_BINARY;
use const DB2_CHAR;
use const DB2_LONG;
use const DB2_PARAM_FILE;
use const DB2_PARAM_IN;
/**
* @deprecated Use {@link Statement} instead
*/
class DB2Statement implements StatementInterface
{
/** @var resource */
private $stmt;
/** @var mixed[] */
private $bindParam = [];
/**
* Map of LOB parameter positions to the tuples containing reference to the variable bound to the driver statement
* and the temporary file handle bound to the underlying statement
*
* @var mixed[][]
*/
private $lobs = [];
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $stmt
*/
public function __construct($stmt)
{
$this->stmt = $stmt;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
assert(is_int($param));
return $this->bindParam($param, $value, $type);
}
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{
assert(is_int($column));
switch ($type) {
case ParameterType::INTEGER:
$this->bind($column, $variable, DB2_PARAM_IN, DB2_LONG);
break;
case ParameterType::LARGE_OBJECT:
if (isset($this->lobs[$column])) {
[, $handle] = $this->lobs[$column];
fclose($handle);
}
$handle = $this->createTemporaryFile();
$path = stream_get_meta_data($handle)['uri'];
$this->bind($column, $path, DB2_PARAM_FILE, DB2_BINARY);
$this->lobs[$column] = [&$variable, $handle];
break;
default:
$this->bind($column, $variable, DB2_PARAM_IN, DB2_CHAR);
break;
}
return true;
}
/**
* @param int $position Parameter position
* @param mixed $variable
*
* @throws Exception
*/
private function bind($position, &$variable, int $parameterType, int $dataType): void
{
$this->bindParam[$position] =& $variable;
if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) {
throw StatementError::new($this->stmt);
}
}
/**
* {@inheritdoc}
*/
public function execute($params = null): ResultInterface
{
if ($params === null) {
ksort($this->bindParam);
$params = [];
foreach ($this->bindParam as $column => $value) {
$params[] = $value;
}
}
foreach ($this->lobs as [$source, $target]) {
if (is_resource($source)) {
$this->copyStreamToStream($source, $target);
continue;
}
$this->writeStringToStream($source, $target);
}
$result = db2_execute($this->stmt, $params);
foreach ($this->lobs as [, $handle]) {
fclose($handle);
}
$this->lobs = [];
if ($result === false) {
throw StatementError::new($this->stmt);
}
return new Result($this->stmt);
}
/**
* @return resource
*
* @throws Exception
*/
private function createTemporaryFile()
{
$handle = @tmpfile();
if ($handle === false) {
throw CannotCreateTemporaryFile::new(error_get_last()['message']);
}
return $handle;
}
/**
* @param resource $source
* @param resource $target
*
* @throws Exception
*/
private function copyStreamToStream($source, $target): void
{
if (@stream_copy_to_stream($source, $target) === false) {
throw CannotCopyStreamToStream::new(error_get_last()['message']);
}
}
/**
* @param resource $target
*
* @throws Exception
*/
private function writeStringToStream(string $string, $target): void
{
if (@fwrite($target, $string) === false) {
throw CannotWriteToTemporaryFile::new(error_get_last()['message']);
}
}
}
...@@ -2,6 +2,197 @@ ...@@ -2,6 +2,197 @@
namespace Doctrine\DBAL\Driver\IBMDB2; namespace Doctrine\DBAL\Driver\IBMDB2;
final class Statement extends DB2Statement use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCopyStreamToStream;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCreateTemporaryFile;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotWriteToTemporaryFile;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use function assert;
use function db2_bind_param;
use function db2_execute;
use function error_get_last;
use function fclose;
use function fwrite;
use function is_int;
use function is_resource;
use function ksort;
use function stream_copy_to_stream;
use function stream_get_meta_data;
use function tmpfile;
use const DB2_BINARY;
use const DB2_CHAR;
use const DB2_LONG;
use const DB2_PARAM_FILE;
use const DB2_PARAM_IN;
final class Statement implements StatementInterface
{ {
/** @var resource */
private $stmt;
/** @var mixed[] */
private $bindParam = [];
/**
* Map of LOB parameter positions to the tuples containing reference to the variable bound to the driver statement
* and the temporary file handle bound to the underlying statement
*
* @var mixed[][]
*/
private $lobs = [];
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $stmt
*/
public function __construct($stmt)
{
$this->stmt = $stmt;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
assert(is_int($param));
return $this->bindParam($param, $value, $type);
}
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{
assert(is_int($column));
switch ($type) {
case ParameterType::INTEGER:
$this->bind($column, $variable, DB2_PARAM_IN, DB2_LONG);
break;
case ParameterType::LARGE_OBJECT:
if (isset($this->lobs[$column])) {
[, $handle] = $this->lobs[$column];
fclose($handle);
}
$handle = $this->createTemporaryFile();
$path = stream_get_meta_data($handle)['uri'];
$this->bind($column, $path, DB2_PARAM_FILE, DB2_BINARY);
$this->lobs[$column] = [&$variable, $handle];
break;
default:
$this->bind($column, $variable, DB2_PARAM_IN, DB2_CHAR);
break;
}
return true;
}
/**
* @param int $position Parameter position
* @param mixed $variable
*
* @throws Exception
*/
private function bind($position, &$variable, int $parameterType, int $dataType): void
{
$this->bindParam[$position] =& $variable;
if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) {
throw StatementError::new($this->stmt);
}
}
/**
* {@inheritdoc}
*/
public function execute($params = null): ResultInterface
{
if ($params === null) {
ksort($this->bindParam);
$params = [];
foreach ($this->bindParam as $column => $value) {
$params[] = $value;
}
}
foreach ($this->lobs as [$source, $target]) {
if (is_resource($source)) {
$this->copyStreamToStream($source, $target);
continue;
}
$this->writeStringToStream($source, $target);
}
$result = db2_execute($this->stmt, $params);
foreach ($this->lobs as [, $handle]) {
fclose($handle);
}
$this->lobs = [];
if ($result === false) {
throw StatementError::new($this->stmt);
}
return new Result($this->stmt);
}
/**
* @return resource
*
* @throws Exception
*/
private function createTemporaryFile()
{
$handle = @tmpfile();
if ($handle === false) {
throw CannotCreateTemporaryFile::new(error_get_last()['message']);
}
return $handle;
}
/**
* @param resource $source
* @param resource $target
*
* @throws Exception
*/
private function copyStreamToStream($source, $target): void
{
if (@stream_copy_to_stream($source, $target) === false) {
throw CannotCopyStreamToStream::new(error_get_last()['message']);
}
}
/**
* @param resource $target
*
* @throws Exception
*/
private function writeStringToStream(string $string, $target): void
{
if (@fwrite($target, $string) === false) {
throw CannotWriteToTemporaryFile::new(error_get_last()['message']);
}
}
} }
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError;
use Doctrine\DBAL\Driver\Mysqli\Exception\FailedReadingStreamOffset;
use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError;
use Doctrine\DBAL\Driver\Mysqli\Exception\UnknownType;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\ParameterType;
use mysqli;
use mysqli_stmt;
use function array_fill;
use function assert;
use function count;
use function feof;
use function fread;
use function get_resource_type;
use function is_int;
use function is_resource;
use function str_repeat;
/**
* @deprecated Use {@link Statement} instead
*/
class MysqliStatement implements StatementInterface
{
/** @var string[] */
protected static $_paramTypeMap = [
ParameterType::STRING => 's',
ParameterType::BINARY => 's',
ParameterType::BOOLEAN => 'i',
ParameterType::NULL => 's',
ParameterType::INTEGER => 'i',
ParameterType::LARGE_OBJECT => 'b',
];
/** @var mysqli */
protected $_conn;
/** @var mysqli_stmt */
protected $_stmt;
/** @var mixed[] */
protected $_bindedValues;
/** @var string */
protected $types;
/**
* Contains ref values for bindValue().
*
* @var mixed[]
*/
protected $_values = [];
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param string $prepareString
*
* @throws Exception
*/
public function __construct(mysqli $conn, $prepareString)
{
$this->_conn = $conn;
$stmt = $conn->prepare($prepareString);
if ($stmt === false) {
throw ConnectionError::new($this->_conn);
}
$this->_stmt = $stmt;
$paramCount = $this->_stmt->param_count;
if (0 >= $paramCount) {
return;
}
$this->types = str_repeat('s', $paramCount);
$this->_bindedValues = array_fill(1, $paramCount, null);
}
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{
assert(is_int($column));
if (! isset(self::$_paramTypeMap[$type])) {
throw UnknownType::new($type);
}
$this->_bindedValues[$column] =& $variable;
$this->types[$column - 1] = self::$_paramTypeMap[$type];
return true;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
assert(is_int($param));
if (! isset(self::$_paramTypeMap[$type])) {
throw UnknownType::new($type);
}
$this->_values[$param] = $value;
$this->_bindedValues[$param] =& $this->_values[$param];
$this->types[$param - 1] = self::$_paramTypeMap[$type];
return true;
}
/**
* {@inheritdoc}
*/
public function execute($params = null): ResultInterface
{
if ($this->_bindedValues !== null) {
if ($params !== null) {
if (! $this->bindUntypedValues($params)) {
throw StatementError::new($this->_stmt);
}
} else {
$this->bindTypedParameters();
}
}
if (! $this->_stmt->execute()) {
throw StatementError::new($this->_stmt);
}
return new Result($this->_stmt);
}
/**
* Binds parameters with known types previously bound to the statement
*/
private function bindTypedParameters(): void
{
$streams = $values = [];
$types = $this->types;
foreach ($this->_bindedValues as $parameter => $value) {
assert(is_int($parameter));
if (! isset($types[$parameter - 1])) {
$types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING];
}
if ($types[$parameter - 1] === static::$_paramTypeMap[ParameterType::LARGE_OBJECT]) {
if (is_resource($value)) {
if (get_resource_type($value) !== 'stream') {
throw new InvalidArgumentException('Resources passed with the LARGE_OBJECT parameter type must be stream resources.');
}
$streams[$parameter] = $value;
$values[$parameter] = null;
continue;
}
$types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING];
}
$values[$parameter] = $value;
}
if (! $this->_stmt->bind_param($types, ...$values)) {
throw StatementError::new($this->_stmt);
}
$this->sendLongData($streams);
}
/**
* Handle $this->_longData after regular query parameters have been bound
*
* @param array<int, resource> $streams
*
* @throws Exception
*/
private function sendLongData(array $streams): void
{
foreach ($streams as $paramNr => $stream) {
while (! feof($stream)) {
$chunk = fread($stream, 8192);
if ($chunk === false) {
throw FailedReadingStreamOffset::new($paramNr);
}
if (! $this->_stmt->send_long_data($paramNr - 1, $chunk)) {
throw StatementError::new($this->_stmt);
}
}
}
}
/**
* Binds a array of values to bound parameters.
*
* @param mixed[] $values
*
* @return bool
*/
private function bindUntypedValues(array $values)
{
$params = [];
$types = str_repeat('s', count($values));
foreach ($values as &$v) {
$params[] =& $v;
}
return $this->_stmt->bind_param($types, ...$params);
}
}
...@@ -2,6 +2,223 @@ ...@@ -2,6 +2,223 @@
namespace Doctrine\DBAL\Driver\Mysqli; namespace Doctrine\DBAL\Driver\Mysqli;
final class Statement extends MysqliStatement use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError;
use Doctrine\DBAL\Driver\Mysqli\Exception\FailedReadingStreamOffset;
use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError;
use Doctrine\DBAL\Driver\Mysqli\Exception\UnknownType;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\ParameterType;
use mysqli;
use mysqli_stmt;
use function array_fill;
use function assert;
use function count;
use function feof;
use function fread;
use function get_resource_type;
use function is_int;
use function is_resource;
use function str_repeat;
final class Statement implements StatementInterface
{ {
/** @var string[] */
protected static $_paramTypeMap = [
ParameterType::STRING => 's',
ParameterType::BINARY => 's',
ParameterType::BOOLEAN => 'i',
ParameterType::NULL => 's',
ParameterType::INTEGER => 'i',
ParameterType::LARGE_OBJECT => 'b',
];
/** @var mysqli */
protected $_conn;
/** @var mysqli_stmt */
protected $_stmt;
/** @var mixed[] */
protected $_bindedValues;
/** @var string */
protected $types;
/**
* Contains ref values for bindValue().
*
* @var mixed[]
*/
protected $_values = [];
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param string $prepareString
*
* @throws Exception
*/
public function __construct(mysqli $conn, $prepareString)
{
$this->_conn = $conn;
$stmt = $conn->prepare($prepareString);
if ($stmt === false) {
throw ConnectionError::new($this->_conn);
}
$this->_stmt = $stmt;
$paramCount = $this->_stmt->param_count;
if (0 >= $paramCount) {
return;
}
$this->types = str_repeat('s', $paramCount);
$this->_bindedValues = array_fill(1, $paramCount, null);
}
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{
assert(is_int($column));
if (! isset(self::$_paramTypeMap[$type])) {
throw UnknownType::new($type);
}
$this->_bindedValues[$column] =& $variable;
$this->types[$column - 1] = self::$_paramTypeMap[$type];
return true;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
assert(is_int($param));
if (! isset(self::$_paramTypeMap[$type])) {
throw UnknownType::new($type);
}
$this->_values[$param] = $value;
$this->_bindedValues[$param] =& $this->_values[$param];
$this->types[$param - 1] = self::$_paramTypeMap[$type];
return true;
}
/**
* {@inheritdoc}
*/
public function execute($params = null): ResultInterface
{
if ($this->_bindedValues !== null) {
if ($params !== null) {
if (! $this->bindUntypedValues($params)) {
throw StatementError::new($this->_stmt);
}
} else {
$this->bindTypedParameters();
}
}
if (! $this->_stmt->execute()) {
throw StatementError::new($this->_stmt);
}
return new Result($this->_stmt);
}
/**
* Binds parameters with known types previously bound to the statement
*/
private function bindTypedParameters(): void
{
$streams = $values = [];
$types = $this->types;
foreach ($this->_bindedValues as $parameter => $value) {
assert(is_int($parameter));
if (! isset($types[$parameter - 1])) {
$types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING];
}
if ($types[$parameter - 1] === static::$_paramTypeMap[ParameterType::LARGE_OBJECT]) {
if (is_resource($value)) {
if (get_resource_type($value) !== 'stream') {
throw new InvalidArgumentException('Resources passed with the LARGE_OBJECT parameter type must be stream resources.');
}
$streams[$parameter] = $value;
$values[$parameter] = null;
continue;
}
$types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING];
}
$values[$parameter] = $value;
}
if (! $this->_stmt->bind_param($types, ...$values)) {
throw StatementError::new($this->_stmt);
}
$this->sendLongData($streams);
}
/**
* Handle $this->_longData after regular query parameters have been bound
*
* @param array<int, resource> $streams
*
* @throws Exception
*/
private function sendLongData(array $streams): void
{
foreach ($streams as $paramNr => $stream) {
while (! feof($stream)) {
$chunk = fread($stream, 8192);
if ($chunk === false) {
throw FailedReadingStreamOffset::new($paramNr);
}
if (! $this->_stmt->send_long_data($paramNr - 1, $chunk)) {
throw StatementError::new($this->_stmt);
}
}
}
}
/**
* Binds a array of values to bound parameters.
*
* @param mixed[] $values
*
* @return bool
*/
private function bindUntypedValues(array $values)
{
$params = [];
$types = str_repeat('s', count($values));
foreach ($values as &$v) {
$params[] =& $v;
}
return $this->_stmt->bind_param($types, ...$params);
}
} }
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\OCI8\Exception\Error;
use Doctrine\DBAL\Driver\OCI8\Exception\UnknownParameterIndex;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use function assert;
use function is_int;
use function is_resource;
use function oci_bind_by_name;
use function oci_execute;
use function oci_new_descriptor;
use function oci_parse;
use const OCI_B_BIN;
use const OCI_B_BLOB;
use const OCI_COMMIT_ON_SUCCESS;
use const OCI_D_LOB;
use const OCI_NO_AUTO_COMMIT;
use const OCI_TEMP_BLOB;
use const SQLT_CHR;
/**
* The OCI8 implementation of the Statement interface.
*
* @deprecated Use {@link Statement} instead
*/
class OCI8Statement implements StatementInterface
{
/** @var resource */
protected $_dbh;
/** @var resource */
protected $_sth;
/** @var ExecutionMode */
private $executionMode;
/** @var string[] */
protected $_paramMap = [];
/**
* Holds references to bound parameter values.
*
* This is a new requirement for PHP7's oci8 extension that prevents bound values from being garbage collected.
*
* @var mixed[]
*/
private $boundValues = [];
/**
* Creates a new OCI8Statement that uses the given connection handle and SQL statement.
*
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $dbh The connection handle.
* @param string $query The SQL query.
*/
public function __construct($dbh, $query, ExecutionMode $executionMode)
{
[$query, $paramMap] = (new ConvertPositionalToNamedPlaceholders())($query);
$stmt = oci_parse($dbh, $query);
assert(is_resource($stmt));
$this->_sth = $stmt;
$this->_dbh = $dbh;
$this->_paramMap = $paramMap;
$this->executionMode = $executionMode;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
return $this->bindParam($param, $value, $type, null);
}
/**
* {@inheritdoc}
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null)
{
if (is_int($param)) {
if (! isset($this->_paramMap[$param])) {
throw UnknownParameterIndex::new($param);
}
$param = $this->_paramMap[$param];
}
if ($type === ParameterType::LARGE_OBJECT) {
$lob = oci_new_descriptor($this->_dbh, OCI_D_LOB);
$class = 'OCI-Lob';
assert($lob instanceof $class);
$lob->writetemporary($variable, OCI_TEMP_BLOB);
$variable =& $lob;
}
$this->boundValues[$param] =& $variable;
return oci_bind_by_name(
$this->_sth,
$param,
$variable,
$length ?? -1,
$this->convertParameterType($type)
);
}
/**
* Converts DBAL parameter type to oci8 parameter type
*/
private function convertParameterType(int $type): int
{
switch ($type) {
case ParameterType::BINARY:
return OCI_B_BIN;
case ParameterType::LARGE_OBJECT:
return OCI_B_BLOB;
default:
return SQLT_CHR;
}
}
/**
* {@inheritdoc}
*/
public function execute($params = null): ResultInterface
{
if ($params !== null) {
foreach ($params as $key => $val) {
if (is_int($key)) {
$this->bindValue($key + 1, $val);
} else {
$this->bindValue($key, $val);
}
}
}
if ($this->executionMode->isAutoCommitEnabled()) {
$mode = OCI_COMMIT_ON_SUCCESS;
} else {
$mode = OCI_NO_AUTO_COMMIT;
}
$ret = @oci_execute($this->_sth, $mode);
if (! $ret) {
throw Error::new($this->_sth);
}
return new Result($this->_sth);
}
}
...@@ -2,6 +2,158 @@ ...@@ -2,6 +2,158 @@
namespace Doctrine\DBAL\Driver\OCI8; namespace Doctrine\DBAL\Driver\OCI8;
final class Statement extends OCI8Statement use Doctrine\DBAL\Driver\OCI8\Exception\Error;
use Doctrine\DBAL\Driver\OCI8\Exception\UnknownParameterIndex;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use function assert;
use function is_int;
use function is_resource;
use function oci_bind_by_name;
use function oci_execute;
use function oci_new_descriptor;
use function oci_parse;
use const OCI_B_BIN;
use const OCI_B_BLOB;
use const OCI_COMMIT_ON_SUCCESS;
use const OCI_D_LOB;
use const OCI_NO_AUTO_COMMIT;
use const OCI_TEMP_BLOB;
use const SQLT_CHR;
final class Statement implements StatementInterface
{ {
/** @var resource */
protected $_dbh;
/** @var resource */
protected $_sth;
/** @var ExecutionMode */
private $executionMode;
/** @var string[] */
protected $_paramMap = [];
/**
* Holds references to bound parameter values.
*
* This is a new requirement for PHP7's oci8 extension that prevents bound values from being garbage collected.
*
* @var mixed[]
*/
private $boundValues = [];
/**
* Creates a new OCI8Statement that uses the given connection handle and SQL statement.
*
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $dbh The connection handle.
* @param string $query The SQL query.
*/
public function __construct($dbh, $query, ExecutionMode $executionMode)
{
[$query, $paramMap] = (new ConvertPositionalToNamedPlaceholders())($query);
$stmt = oci_parse($dbh, $query);
assert(is_resource($stmt));
$this->_sth = $stmt;
$this->_dbh = $dbh;
$this->_paramMap = $paramMap;
$this->executionMode = $executionMode;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
return $this->bindParam($param, $value, $type, null);
}
/**
* {@inheritdoc}
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null)
{
if (is_int($param)) {
if (! isset($this->_paramMap[$param])) {
throw UnknownParameterIndex::new($param);
}
$param = $this->_paramMap[$param];
}
if ($type === ParameterType::LARGE_OBJECT) {
$lob = oci_new_descriptor($this->_dbh, OCI_D_LOB);
$class = 'OCI-Lob';
assert($lob instanceof $class);
$lob->writetemporary($variable, OCI_TEMP_BLOB);
$variable =& $lob;
}
$this->boundValues[$param] =& $variable;
return oci_bind_by_name(
$this->_sth,
$param,
$variable,
$length ?? -1,
$this->convertParameterType($type)
);
}
/**
* Converts DBAL parameter type to oci8 parameter type
*/
private function convertParameterType(int $type): int
{
switch ($type) {
case ParameterType::BINARY:
return OCI_B_BIN;
case ParameterType::LARGE_OBJECT:
return OCI_B_BLOB;
default:
return SQLT_CHR;
}
}
/**
* {@inheritdoc}
*/
public function execute($params = null): ResultInterface
{
if ($params !== null) {
foreach ($params as $key => $val) {
if (is_int($key)) {
$this->bindValue($key + 1, $val);
} else {
$this->bindValue($key, $val);
}
}
}
if ($this->executionMode->isAutoCommitEnabled()) {
$mode = OCI_COMMIT_ON_SUCCESS;
} else {
$mode = OCI_NO_AUTO_COMMIT;
}
$ret = @oci_execute($this->_sth, $mode);
if (! $ret) {
throw Error::new($this->_sth);
}
return new Result($this->_sth);
}
} }
...@@ -2,8 +2,98 @@ ...@@ -2,8 +2,98 @@
namespace Doctrine\DBAL\Driver\PDO; namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\PDOStatement; use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use InvalidArgumentException;
use PDO;
use PDOException;
use PDOStatement;
class Statement extends PDOStatement use function array_slice;
use function func_get_args;
class Statement implements StatementInterface
{ {
private const PARAM_TYPE_MAP = [
ParameterType::NULL => PDO::PARAM_NULL,
ParameterType::INTEGER => PDO::PARAM_INT,
ParameterType::STRING => PDO::PARAM_STR,
ParameterType::BINARY => PDO::PARAM_LOB,
ParameterType::LARGE_OBJECT => PDO::PARAM_LOB,
ParameterType::BOOLEAN => PDO::PARAM_BOOL,
];
/** @var PDOStatement */
private $stmt;
/**
* @internal The statement can be only instantiated by its driver connection.
*/
public function __construct(PDOStatement $stmt)
{
$this->stmt = $stmt;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
$type = $this->convertParamType($type);
try {
return $this->stmt->bindValue($param, $value, $type);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* @param mixed $column
* @param mixed $variable
* @param int $type
* @param int|null $length
* @param mixed $driverOptions
*
* @return bool
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null)
{
$type = $this->convertParamType($type);
try {
return $this->stmt->bindParam($column, $variable, $type, ...array_slice(func_get_args(), 3));
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritdoc}
*/
public function execute($params = null): ResultInterface
{
try {
$this->stmt->execute($params);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
return new Result($this->stmt);
}
/**
* Converts DBAL parameter type to PDO parameter type
*
* @param int $type Parameter type
*/
private function convertParamType(int $type): int
{
if (! isset(self::PARAM_TYPE_MAP[$type])) {
throw new InvalidArgumentException('Invalid parameter type: ' . $type);
}
return self::PARAM_TYPE_MAP[$type];
}
} }
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\PDO\Exception;
use Doctrine\DBAL\Driver\PDO\Result;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use InvalidArgumentException;
use PDO;
use PDOException;
use function array_slice;
use function func_get_args;
/**
* The PDO implementation of the Statement interface.
* Used by all PDO-based drivers.
*
* @deprecated Use {@link Statement} instead
*/
class PDOStatement implements StatementInterface
{
private const PARAM_TYPE_MAP = [
ParameterType::NULL => PDO::PARAM_NULL,
ParameterType::INTEGER => PDO::PARAM_INT,
ParameterType::STRING => PDO::PARAM_STR,
ParameterType::BINARY => PDO::PARAM_LOB,
ParameterType::LARGE_OBJECT => PDO::PARAM_LOB,
ParameterType::BOOLEAN => PDO::PARAM_BOOL,
];
/** @var \PDOStatement */
private $stmt;
/**
* @internal The statement can be only instantiated by its driver connection.
*/
public function __construct(\PDOStatement $stmt)
{
$this->stmt = $stmt;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
$type = $this->convertParamType($type);
try {
return $this->stmt->bindValue($param, $value, $type);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* @param mixed $column
* @param mixed $variable
* @param int $type
* @param int|null $length
* @param mixed $driverOptions
*
* @return bool
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null)
{
$type = $this->convertParamType($type);
try {
return $this->stmt->bindParam($column, $variable, $type, ...array_slice(func_get_args(), 3));
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritdoc}
*/
public function execute($params = null): ResultInterface
{
try {
$this->stmt->execute($params);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
return new Result($this->stmt);
}
/**
* Converts DBAL parameter type to PDO parameter type
*
* @param int $type Parameter type
*/
private function convertParamType(int $type): int
{
if (! isset(self::PARAM_TYPE_MAP[$type])) {
throw new InvalidArgumentException('Invalid parameter type: ' . $type);
}
return self::PARAM_TYPE_MAP[$type];
}
}
<?php
namespace Doctrine\DBAL\Driver\SQLSrv;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\SQLSrv\Exception\Error;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use function assert;
use function is_int;
use function sqlsrv_execute;
use function sqlsrv_fetch;
use function sqlsrv_get_field;
use function sqlsrv_next_result;
use function SQLSRV_PHPTYPE_STREAM;
use function SQLSRV_PHPTYPE_STRING;
use function sqlsrv_prepare;
use function SQLSRV_SQLTYPE_VARBINARY;
use function stripos;
use const SQLSRV_ENC_BINARY;
use const SQLSRV_PARAM_IN;
/**
* SQL Server Statement.
*
* @deprecated Use {@link Statement} instead
*/
class SQLSrvStatement implements StatementInterface
{
/**
* The SQLSRV Resource.
*
* @var resource
*/
private $conn;
/**
* The SQL statement to execute.
*
* @var string
*/
private $sql;
/**
* The SQLSRV statement resource.
*
* @var resource|null
*/
private $stmt;
/**
* References to the variables bound as statement parameters.
*
* @var mixed
*/
private $variables = [];
/**
* Bound parameter types.
*
* @var int[]
*/
private $types = [];
/**
* The last insert ID.
*
* @var LastInsertId|null
*/
private $lastInsertId;
/**
* Append to any INSERT query to retrieve the last insert id.
*/
private const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;';
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $conn
* @param string $sql
*/
public function __construct($conn, $sql, ?LastInsertId $lastInsertId = null)
{
$this->conn = $conn;
$this->sql = $sql;
if (stripos($sql, 'INSERT INTO ') !== 0) {
return;
}
$this->sql .= self::LAST_INSERT_ID_SQL;
$this->lastInsertId = $lastInsertId;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
assert(is_int($param));
$this->variables[$param] = $value;
$this->types[$param] = $type;
return true;
}
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{
assert(is_int($column));
$this->variables[$column] =& $variable;
$this->types[$column] = $type;
// unset the statement resource if it exists as the new one will need to be bound to the new variable
$this->stmt = null;
return true;
}
/**
* {@inheritdoc}
*/
public function execute($params = null): ResultInterface
{
if ($params !== null) {
foreach ($params as $key => $val) {
if (is_int($key)) {
$this->bindValue($key + 1, $val);
} else {
$this->bindValue($key, $val);
}
}
}
if ($this->stmt === null) {
$this->stmt = $this->prepare();
}
if (! sqlsrv_execute($this->stmt)) {
throw Error::new();
}
if ($this->lastInsertId !== null) {
sqlsrv_next_result($this->stmt);
sqlsrv_fetch($this->stmt);
$this->lastInsertId->setId(sqlsrv_get_field($this->stmt, 0));
}
return new Result($this->stmt);
}
/**
* Prepares SQL Server statement resource
*
* @return resource
*
* @throws Exception
*/
private function prepare()
{
$params = [];
foreach ($this->variables as $column => &$variable) {
switch ($this->types[$column]) {
case ParameterType::LARGE_OBJECT:
$params[$column - 1] = [
&$variable,
SQLSRV_PARAM_IN,
SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY),
SQLSRV_SQLTYPE_VARBINARY('max'),
];
break;
case ParameterType::BINARY:
$params[$column - 1] = [
&$variable,
SQLSRV_PARAM_IN,
SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY),
];
break;
default:
$params[$column - 1] =& $variable;
break;
}
}
$stmt = sqlsrv_prepare($this->conn, $this->sql, $params);
if ($stmt === false) {
throw Error::new();
}
return $stmt;
}
}
...@@ -2,6 +2,198 @@ ...@@ -2,6 +2,198 @@
namespace Doctrine\DBAL\Driver\SQLSrv; namespace Doctrine\DBAL\Driver\SQLSrv;
final class Statement extends SQLSrvStatement use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\Result as ResultInterface;
use Doctrine\DBAL\Driver\SQLSrv\Exception\Error;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;
use function assert;
use function is_int;
use function sqlsrv_execute;
use function sqlsrv_fetch;
use function sqlsrv_get_field;
use function sqlsrv_next_result;
use function SQLSRV_PHPTYPE_STREAM;
use function SQLSRV_PHPTYPE_STRING;
use function sqlsrv_prepare;
use function SQLSRV_SQLTYPE_VARBINARY;
use function stripos;
use const SQLSRV_ENC_BINARY;
use const SQLSRV_PARAM_IN;
final class Statement implements StatementInterface
{ {
/**
* The SQLSRV Resource.
*
* @var resource
*/
private $conn;
/**
* The SQL statement to execute.
*
* @var string
*/
private $sql;
/**
* The SQLSRV statement resource.
*
* @var resource|null
*/
private $stmt;
/**
* References to the variables bound as statement parameters.
*
* @var mixed
*/
private $variables = [];
/**
* Bound parameter types.
*
* @var int[]
*/
private $types = [];
/**
* The last insert ID.
*
* @var LastInsertId|null
*/
private $lastInsertId;
/**
* Append to any INSERT query to retrieve the last insert id.
*/
private const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;';
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $conn
* @param string $sql
*/
public function __construct($conn, $sql, ?LastInsertId $lastInsertId = null)
{
$this->conn = $conn;
$this->sql = $sql;
if (stripos($sql, 'INSERT INTO ') !== 0) {
return;
}
$this->sql .= self::LAST_INSERT_ID_SQL;
$this->lastInsertId = $lastInsertId;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
assert(is_int($param));
$this->variables[$param] = $value;
$this->types[$param] = $type;
return true;
}
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{
assert(is_int($column));
$this->variables[$column] =& $variable;
$this->types[$column] = $type;
// unset the statement resource if it exists as the new one will need to be bound to the new variable
$this->stmt = null;
return true;
}
/**
* {@inheritdoc}
*/
public function execute($params = null): ResultInterface
{
if ($params !== null) {
foreach ($params as $key => $val) {
if (is_int($key)) {
$this->bindValue($key + 1, $val);
} else {
$this->bindValue($key, $val);
}
}
}
if ($this->stmt === null) {
$this->stmt = $this->prepare();
}
if (! sqlsrv_execute($this->stmt)) {
throw Error::new();
}
if ($this->lastInsertId !== null) {
sqlsrv_next_result($this->stmt);
sqlsrv_fetch($this->stmt);
$this->lastInsertId->setId(sqlsrv_get_field($this->stmt, 0));
}
return new Result($this->stmt);
}
/**
* Prepares SQL Server statement resource
*
* @return resource
*
* @throws Exception
*/
private function prepare()
{
$params = [];
foreach ($this->variables as $column => &$variable) {
switch ($this->types[$column]) {
case ParameterType::LARGE_OBJECT:
$params[$column - 1] = [
&$variable,
SQLSRV_PARAM_IN,
SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY),
SQLSRV_SQLTYPE_VARBINARY('max'),
];
break;
case ParameterType::BINARY:
$params[$column - 1] = [
&$variable,
SQLSRV_PARAM_IN,
SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY),
];
break;
default:
$params[$column - 1] =& $variable;
break;
}
}
$stmt = sqlsrv_prepare($this->conn, $this->sql, $params);
if ($stmt === false) {
throw Error::new();
}
return $stmt;
}
} }
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