Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
D
doctrine-dbal
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Tomáš Trávníček
doctrine-dbal
Commits
b713ba74
Commit
b713ba74
authored
Feb 04, 2017
by
Marco Pivetta
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'fix/#2546-prepared-statement-close-cursor-invalidation-2.5' into 2.5
Backport #2546 to 2.5.x
parents
fc376f7a
658e3257
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
499 additions
and
37 deletions
+499
-37
DB2Statement.php
lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php
+29
-11
MysqliStatement.php
lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php
+45
-13
OCI8Statement.php
lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php
+41
-1
Connection.php
lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php
+9
-0
Statement.php
lib/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php
+49
-0
SQLAnywhereStatement.php
...Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php
+1
-1
SQLSrvStatement.php
lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php
+77
-11
PDOConnectionTest.php
...ctrine/Tests/DBAL/Functional/Driver/PDOConnectionTest.php
+7
-0
StatementTest.php
tests/Doctrine/Tests/DBAL/Functional/StatementTest.php
+241
-0
No files found.
lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php
View file @
b713ba74
...
...
@@ -48,6 +48,13 @@ class DB2Statement implements \IteratorAggregate, Statement
*/
private
$_defaultFetchMode
=
\PDO
::
FETCH_BOTH
;
/**
* Indicates whether the statement is in the state when fetching results is possible
*
* @var bool
*/
private
$result
=
false
;
/**
* DB2_BINARY, DB2_CHAR, DB2_DOUBLE, or DB2_LONG
*
...
...
@@ -104,11 +111,14 @@ class DB2Statement implements \IteratorAggregate, Statement
}
$this
->
_bindParam
=
array
();
db2_free_result
(
$this
->
_stmt
);
$ret
=
db2_free_stmt
(
$this
->
_stmt
);
$this
->
_stmt
=
false
;
return
$ret
;
if
(
!
db2_free_result
(
$this
->
_stmt
))
{
return
false
;
}
$this
->
result
=
false
;
return
true
;
}
/**
...
...
@@ -151,22 +161,24 @@ class DB2Statement implements \IteratorAggregate, Statement
return
false
;
}
/*$retval = true;
if ($params !== null) {
$retval = @db2_execute($this->_stmt, $params);
} else {
$retval = @db2_execute($this->_stmt);
}*/
if
(
$params
===
null
)
{
ksort
(
$this
->
_bindParam
);
$params
=
array_values
(
$this
->
_bindParam
);
$params
=
array
();
foreach
(
$this
->
_bindParam
as
$column
=>
$value
)
{
$params
[]
=
$value
;
}
}
$retval
=
@
db2_execute
(
$this
->
_stmt
,
$params
);
if
(
$retval
===
false
)
{
throw
new
DB2Exception
(
db2_stmt_errormsg
());
}
$this
->
result
=
true
;
return
$retval
;
}
...
...
@@ -197,6 +209,12 @@ class DB2Statement implements \IteratorAggregate, Statement
*/
public
function
fetch
(
$fetchMode
=
null
)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
if
(
!
$this
->
result
)
{
return
false
;
}
$fetchMode
=
$fetchMode
?:
$this
->
_defaultFetchMode
;
switch
(
$fetchMode
)
{
case
\PDO
::
FETCH_BOTH
:
...
...
lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php
View file @
b713ba74
...
...
@@ -80,6 +80,13 @@ class MysqliStatement implements \IteratorAggregate, Statement
*/
protected
$_defaultFetchMode
=
PDO
::
FETCH_BOTH
;
/**
* Indicates whether the statement is in the state when fetching results is possible
*
* @var bool
*/
private
$result
=
false
;
/**
* @param \mysqli $conn
* @param string $prepareString
...
...
@@ -168,9 +175,6 @@ class MysqliStatement implements \IteratorAggregate, Statement
if
(
null
===
$this
->
_columnNames
)
{
$meta
=
$this
->
_stmt
->
result_metadata
();
if
(
false
!==
$meta
)
{
// We have a result.
$this
->
_stmt
->
store_result
();
$columnNames
=
array
();
foreach
(
$meta
->
fetch_fields
()
as
$col
)
{
$columnNames
[]
=
$col
->
name
;
...
...
@@ -178,21 +182,42 @@ class MysqliStatement implements \IteratorAggregate, Statement
$meta
->
free
();
$this
->
_columnNames
=
$columnNames
;
$this
->
_rowBindedValues
=
array_fill
(
0
,
count
(
$columnNames
),
null
);
$refs
=
array
();
foreach
(
$this
->
_rowBindedValues
as
$key
=>
&
$value
)
{
$refs
[
$key
]
=&
$value
;
}
if
(
!
call_user_func_array
(
array
(
$this
->
_stmt
,
'bind_result'
),
$refs
))
{
throw
new
MysqliException
(
$this
->
_stmt
->
error
,
$this
->
_stmt
->
sqlstate
,
$this
->
_stmt
->
errno
);
}
}
else
{
$this
->
_columnNames
=
false
;
}
}
if
(
false
!==
$this
->
_columnNames
)
{
// Store result of every execution which has it. Otherwise it will be impossible
// to execute a new statement in case if the previous one has non-fetched rows
// @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html
$this
->
_stmt
->
store_result
();
// Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql,
// it will have to allocate as much memory as it may be needed for the given column type
// (e.g. for a LONGBLOB field it's 4 gigabytes)
// @link https://bugs.php.net/bug.php?id=51386#1270673122
//
// Make sure that the values are bound after each execution. Otherwise, if closeCursor() has been
// previously called on the statement, the values are unbound making the statement unusable.
//
// It's also important that row values are bound after _each_ call to store_result(). Otherwise,
// if mysqli is compiled with libmysql, subsequently fetched string values will get truncated
// to the length of the ones fetched during the previous execution.
$this
->
_rowBindedValues
=
array_fill
(
0
,
count
(
$this
->
_columnNames
),
null
);
$refs
=
array
();
foreach
(
$this
->
_rowBindedValues
as
$key
=>
&
$value
)
{
$refs
[
$key
]
=&
$value
;
}
if
(
!
call_user_func_array
(
array
(
$this
->
_stmt
,
'bind_result'
),
$refs
))
{
throw
new
MysqliException
(
$this
->
_stmt
->
error
,
$this
->
_stmt
->
sqlstate
,
$this
->
_stmt
->
errno
);
}
}
$this
->
result
=
true
;
return
true
;
}
...
...
@@ -240,6 +265,12 @@ class MysqliStatement implements \IteratorAggregate, Statement
*/
public
function
fetch
(
$fetchMode
=
null
)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
if
(
!
$this
->
result
)
{
return
false
;
}
$values
=
$this
->
_fetch
();
if
(
null
===
$values
)
{
return
false
;
...
...
@@ -325,6 +356,7 @@ class MysqliStatement implements \IteratorAggregate, Statement
public
function
closeCursor
()
{
$this
->
_stmt
->
free_result
();
$this
->
result
=
false
;
return
true
;
}
...
...
lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php
View file @
b713ba74
...
...
@@ -80,6 +80,13 @@ class OCI8Statement implements \IteratorAggregate, Statement
*/
private
$boundValues
=
array
();
/**
* Indicates whether the statement is in the state when fetching results is possible
*
* @var bool
*/
private
$result
=
false
;
/**
* Creates a new OCI8Statement that uses the given connection handle and SQL statement.
*
...
...
@@ -176,7 +183,20 @@ class OCI8Statement implements \IteratorAggregate, Statement
*/
public
function
closeCursor
()
{
return
oci_free_statement
(
$this
->
_sth
);
// not having the result means there's nothing to close
if
(
!
$this
->
result
)
{
return
true
;
}
// emulate it by fetching and discarding rows, similarly to what PDO does in this case
// @link http://php.net/manual/en/pdostatement.closecursor.php
// @link https://github.com/php/php-src/blob/php-7.0.11/ext/pdo/pdo_stmt.c#L2075
// deliberately do not consider multiple result sets, since doctrine/dbal doesn't support them
while
(
oci_fetch
(
$this
->
_sth
));
$this
->
result
=
false
;
return
true
;
}
/**
...
...
@@ -229,6 +249,8 @@ class OCI8Statement implements \IteratorAggregate, Statement
throw
OCI8Exception
::
fromErrorInfo
(
$this
->
errorInfo
());
}
$this
->
result
=
true
;
return
$ret
;
}
...
...
@@ -257,6 +279,12 @@ class OCI8Statement implements \IteratorAggregate, Statement
*/
public
function
fetch
(
$fetchMode
=
null
)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
if
(
!
$this
->
result
)
{
return
false
;
}
$fetchMode
=
$fetchMode
?:
$this
->
_defaultFetchMode
;
if
(
!
isset
(
self
::
$fetchModeMap
[
$fetchMode
]))
{
throw
new
\InvalidArgumentException
(
"Invalid fetch style: "
.
$fetchMode
);
...
...
@@ -286,6 +314,12 @@ class OCI8Statement implements \IteratorAggregate, Statement
$fetchStructure
=
OCI_FETCHSTATEMENT_BY_COLUMN
;
}
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
if
(
!
$this
->
result
)
{
return
array
();
}
oci_fetch_all
(
$this
->
_sth
,
$result
,
0
,
-
1
,
self
::
$fetchModeMap
[
$fetchMode
]
|
OCI_RETURN_NULLS
|
$fetchStructure
|
OCI_RETURN_LOBS
);
...
...
@@ -302,6 +336,12 @@ class OCI8Statement implements \IteratorAggregate, Statement
*/
public
function
fetchColumn
(
$columnIndex
=
0
)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
if
(
!
$this
->
result
)
{
return
false
;
}
$row
=
oci_fetch_array
(
$this
->
_sth
,
OCI_NUM
|
OCI_RETURN_NULLS
|
OCI_RETURN_LOBS
);
if
(
false
===
$row
)
{
...
...
lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php
View file @
b713ba74
...
...
@@ -28,6 +28,15 @@ use Doctrine\DBAL\Driver\PDOConnection;
*/
class
Connection
extends
PDOConnection
implements
\Doctrine\DBAL\Driver\Connection
{
/**
* {@inheritdoc}
*/
public
function
__construct
(
$dsn
,
$user
=
null
,
$password
=
null
,
array
$options
=
null
)
{
parent
::
__construct
(
$dsn
,
$user
,
$password
,
$options
);
$this
->
setAttribute
(
\PDO
::
ATTR_STATEMENT_CLASS
,
array
(
Statement
::
class
,
array
()));
}
/**
* @override
*/
...
...
lib/Doctrine/DBAL/Driver/PDOSqlsrv/Statement.php
0 → 100644
View file @
b713ba74
<?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 MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace
Doctrine\DBAL\Driver\PDOSqlsrv
;
use
Doctrine\DBAL\Driver\PDOStatement
;
use
PDO
;
/**
* PDO SQL Server Statement
*/
class
Statement
extends
PDOStatement
{
/**
* {@inheritdoc}
*/
public
function
bindParam
(
$column
,
&
$variable
,
$type
=
PDO
::
PARAM_STR
,
$length
=
null
,
$driverOptions
=
null
)
{
if
(
$type
===
PDO
::
PARAM_LOB
&&
$driverOptions
===
null
)
{
$driverOptions
=
PDO
::
SQLSRV_ENCODING_BINARY
;
}
return
parent
::
bindParam
(
$column
,
$variable
,
$type
,
$length
,
$driverOptions
);
}
/**
* {@inheritdoc}
*/
public
function
bindValue
(
$param
,
$value
,
$type
=
PDO
::
PARAM_STR
)
{
return
$this
->
bindParam
(
$param
,
$value
,
$type
);
}
}
lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php
View file @
b713ba74
...
...
@@ -131,7 +131,7 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement
*/
public
function
closeCursor
()
{
if
(
!
sasql_stmt_free_resul
t
(
$this
->
stmt
))
{
if
(
!
sasql_stmt_rese
t
(
$this
->
stmt
))
{
throw
SQLAnywhereException
::
fromSQLAnywhereError
(
$this
->
conn
,
$this
->
stmt
);
}
...
...
lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php
View file @
b713ba74
...
...
@@ -53,11 +53,18 @@ class SQLSrvStatement implements IteratorAggregate, Statement
private
$stmt
;
/**
*
Parameters to bind
.
*
References to the variables bound as statement parameters
.
*
* @var array
*/
private
$params
=
array
();
private
$variables
=
array
();
/**
* Bound parameter types.
*
* @var array
*/
private
$types
=
array
();
/**
* Translations.
...
...
@@ -98,6 +105,13 @@ class SQLSrvStatement implements IteratorAggregate, Statement
*/
private
$lastInsertId
;
/**
* Indicates whether the statement is in the state when fetching results is possible
*
* @var bool
*/
private
$result
=
false
;
/**
* Append to any INSERT query to retrieve the last insert id.
*
...
...
@@ -138,11 +152,8 @@ class SQLSrvStatement implements IteratorAggregate, Statement
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
;
}
$this
->
variables
[
$column
]
=&
$variable
;
$this
->
types
[
$column
]
=
$type
;
}
/**
...
...
@@ -150,9 +161,20 @@ class SQLSrvStatement implements IteratorAggregate, Statement
*/
public
function
closeCursor
()
{
if
(
$this
->
stmt
)
{
sqlsrv_free_stmt
(
$this
->
stmt
);
// not having the result means there's nothing to close
if
(
!
$this
->
result
)
{
return
true
;
}
// emulate it by fetching and discarding rows, similarly to what PDO does in this case
// @link http://php.net/manual/en/pdostatement.closecursor.php
// @link https://github.com/php/php-src/blob/php-7.0.11/ext/pdo/pdo_stmt.c#L2075
// deliberately do not consider multiple result sets, since doctrine/dbal doesn't support them
while
(
sqlsrv_fetch
(
$this
->
stmt
));
$this
->
result
=
false
;
return
true
;
}
/**
...
...
@@ -193,12 +215,16 @@ class SQLSrvStatement implements IteratorAggregate, Statement
$hasZeroIndex
=
array_key_exists
(
0
,
$params
);
foreach
(
$params
as
$key
=>
$val
)
{
$key
=
(
$hasZeroIndex
&&
is_numeric
(
$key
))
?
$key
+
1
:
$key
;
$this
->
bindValue
(
$key
,
$val
);
$this
->
variables
[
$key
]
=
$val
;
$this
->
types
[
$key
]
=
null
;
}
}
$this
->
stmt
=
sqlsrv_query
(
$this
->
conn
,
$this
->
sql
,
$this
->
params
);
if
(
!
$this
->
stmt
)
{
$this
->
stmt
=
$this
->
prepare
();
}
if
(
!
sqlsrv_execute
(
$this
->
stmt
))
{
throw
SQLSrvException
::
fromSqlSrvErrors
();
}
...
...
@@ -207,6 +233,40 @@ class SQLSrvStatement implements IteratorAggregate, Statement
sqlsrv_fetch
(
$this
->
stmt
);
$this
->
lastInsertId
->
setId
(
sqlsrv_get_field
(
$this
->
stmt
,
0
));
}
$this
->
result
=
true
;
}
/**
* Prepares SQL Server statement resource
*
* @return resource
* @throws SQLSrvException
*/
private
function
prepare
()
{
$params
=
array
();
foreach
(
$this
->
variables
as
$column
=>
&
$variable
)
{
if
(
$this
->
types
[
$column
]
===
\PDO
::
PARAM_LOB
)
{
$params
[
$column
-
1
]
=
array
(
&
$variable
,
SQLSRV_PARAM_IN
,
SQLSRV_PHPTYPE_STREAM
(
SQLSRV_ENC_BINARY
),
SQLSRV_SQLTYPE_VARBINARY
(
'max'
),
);
}
else
{
$params
[
$column
-
1
]
=&
$variable
;
}
}
$stmt
=
sqlsrv_prepare
(
$this
->
conn
,
$this
->
sql
,
$params
);
if
(
!
$stmt
)
{
throw
SQLSrvException
::
fromSqlSrvErrors
();
}
return
$stmt
;
}
/**
...
...
@@ -236,6 +296,12 @@ class SQLSrvStatement implements IteratorAggregate, Statement
*/
public
function
fetch
(
$fetchMode
=
null
)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
if
(
!
$this
->
result
)
{
return
false
;
}
$args
=
func_get_args
();
$fetchMode
=
$fetchMode
?:
$this
->
defaultFetchMode
;
...
...
tests/Doctrine/Tests/DBAL/Functional/Driver/PDOConnectionTest.php
View file @
b713ba74
...
...
@@ -29,6 +29,13 @@ class PDOConnectionTest extends DbalFunctionalTestCase
}
}
protected
function
tearDown
()
{
$this
->
resetSharedConn
();
parent
::
tearDown
();
}
public
function
testDoesNotRequireQueryForServerVersion
()
{
$this
->
assertFalse
(
$this
->
driverConnection
->
requiresQueryForServerVersion
());
...
...
tests/Doctrine/Tests/DBAL/Functional/StatementTest.php
0 → 100644
View file @
b713ba74
<?php
namespace
Doctrine\Tests\DBAL\Functional
;
use
Doctrine\DBAL\Driver\Statement
;
use
Doctrine\DBAL\Schema\Table
;
use
Doctrine\DBAL\Types\Type
;
class
StatementTest
extends
\Doctrine\Tests\DbalFunctionalTestCase
{
protected
function
setUp
()
{
parent
::
setUp
();
$table
=
new
Table
(
'stmt_test'
);
$table
->
addColumn
(
'id'
,
'integer'
);
$this
->
_conn
->
getSchemaManager
()
->
dropAndCreateTable
(
$table
);
}
public
function
testStatementIsReusableAfterClosingCursor
()
{
$this
->
_conn
->
insert
(
'stmt_test'
,
array
(
'id'
=>
1
));
$this
->
_conn
->
insert
(
'stmt_test'
,
array
(
'id'
=>
2
));
$stmt
=
$this
->
_conn
->
prepare
(
'SELECT id FROM stmt_test ORDER BY id'
);
$stmt
->
execute
();
$id
=
$stmt
->
fetchColumn
();
$this
->
assertEquals
(
1
,
$id
);
$stmt
->
closeCursor
();
$stmt
->
execute
();
$id
=
$stmt
->
fetchColumn
();
$this
->
assertEquals
(
1
,
$id
);
$id
=
$stmt
->
fetchColumn
();
$this
->
assertEquals
(
2
,
$id
);
}
public
function
testReuseStatementWithLongerResults
()
{
$sm
=
$this
->
_conn
->
getSchemaManager
();
$table
=
new
Table
(
'stmt_longer_results'
);
$table
->
addColumn
(
'param'
,
'string'
);
$table
->
addColumn
(
'val'
,
'text'
);
$sm
->
createTable
(
$table
);
$row1
=
array
(
'param'
=>
'param1'
,
'val'
=>
'X'
,
);
$this
->
_conn
->
insert
(
'stmt_longer_results'
,
$row1
);
$stmt
=
$this
->
_conn
->
prepare
(
'SELECT param, val FROM stmt_longer_results ORDER BY param'
);
$stmt
->
execute
();
$this
->
assertArraySubset
(
array
(
array
(
'param1'
,
'X'
),
),
$stmt
->
fetchAll
(
\PDO
::
FETCH_NUM
));
$row2
=
array
(
'param'
=>
'param2'
,
'val'
=>
'A bit longer value'
,
);
$this
->
_conn
->
insert
(
'stmt_longer_results'
,
$row2
);
$stmt
->
execute
();
$this
->
assertArraySubset
(
array
(
array
(
'param1'
,
'X'
),
array
(
'param2'
,
'A bit longer value'
),
),
$stmt
->
fetchAll
(
\PDO
::
FETCH_NUM
));
}
public
function
testFetchLongBlob
()
{
// make sure memory limit is large enough to not cause false positives,
// but is still not enough to store a LONGBLOB of the max possible size
$this
->
iniSet
(
'memory_limit'
,
'4G'
);
$sm
=
$this
->
_conn
->
getSchemaManager
();
$table
=
new
Table
(
'stmt_long_blob'
);
$table
->
addColumn
(
'contents'
,
'blob'
,
array
(
'length'
=>
0xFFFFFFFF
,
));
$sm
->
createTable
(
$table
);
$contents
=
base64_decode
(
<<<EOF
H4sICJRACVgCA2RvY3RyaW5lLmljbwDtVNtLFHEU/ia1i9fVzVWxvJSrZmoXS6pd0zK7QhdNc03z
lrpppq1pWqJCFERZkUFEDybYBQqJhB6iUOqhh+whgl4qkF6MfGh+s87O7GVmO6OlBfUfdIZvznxn
fpzznW9gAI4unQ50XwirH2AAkEygEuIwU58ODnPBzXGv14sEq4BrwzKKL4sY++SGTz6PodcutN5x
IPvsFCa+K9CXMfS/cOL5OxesN0Wceygho0WAXVLwcUJBdDVDaqOAij4Rrz640XlXQmAxQ16PHU63
iqdvXbg4JOHLpILBUSdM7XZEVDDcfuZEbI2ASaYguUGAroSh97GMngcSeFFFerMdI+/dyGy1o+GW
Ax5FxfAbFwoviajuc+DCIwn+RTwGRmRIThXxdQJyu+z4/NUDYz2DKCsILuERWsoQfoQhqpLhyhMZ
XfcknBmU0NLvQArpTm0SsI5mqKqKuFoGc8cUcjrtqLohom1AgtujQnapmJJU+BbwCLIwhJXyiKlh
MB4TkFgvIK3JjrRmAefJm+77Eiqvi+SvCq/qJahQyWuVuEpcIa7QLh7Kbsourb9b66/pZdAd1voz
fCNfwsp46OnZQPojSX9UFcNy+mYJNDeJPHtJfqeR/nSaPTzmwlXar5dQ1adpd+B//I9/hi0xuCPQ
Nkvb5um37Wtc+auQXZsVxEVYD5hnCilxTaYYjsuxLlsxXUitzd2hs3GWHLM5UOM7Fy8t3xiat4fb
sneNxmNb/POO1pRXc7vnF2nc13Rq0cFWiyXkuHmzxuOtzUYfC7fEmK/3mx4QZd5u4E7XJWz6+dey
Za4tXHUiPyB8Vm781oaT+3fN6Y/eUFDfPkcNWetNxb+tlxEZsPqPdZMOzS4rxwJ8CDC+ABj1+Tu0
d+N0hqezcjblboJ3Bj8ARJilHX4FAAA=
EOF
);
$this
->
_conn
->
insert
(
'stmt_long_blob'
,
array
(
'contents'
=>
$contents
,
),
array
(
\PDO
::
PARAM_LOB
));
$stmt
=
$this
->
_conn
->
prepare
(
'SELECT contents FROM stmt_long_blob'
);
$stmt
->
execute
();
$stream
=
Type
::
getType
(
'blob'
)
->
convertToPHPValue
(
$stmt
->
fetchColumn
(),
$this
->
_conn
->
getDatabasePlatform
()
);
if
(
$this
->
_conn
->
getDriver
()
->
getName
()
===
'pdo_sqlsrv'
)
{
$this
->
markTestSkipped
(
'Skipping on pdo_sqlsrv due to https://github.com/Microsoft/msphpsql/issues/270'
);
}
$this
->
assertSame
(
$contents
,
stream_get_contents
(
$stream
));
}
public
function
testIncompletelyFetchedStatementDoesNotBlockConnection
()
{
$this
->
_conn
->
insert
(
'stmt_test'
,
array
(
'id'
=>
1
));
$this
->
_conn
->
insert
(
'stmt_test'
,
array
(
'id'
=>
2
));
$stmt1
=
$this
->
_conn
->
prepare
(
'SELECT id FROM stmt_test'
);
$stmt1
->
execute
();
$stmt1
->
fetch
();
$stmt1
->
execute
();
// fetching only one record out of two
$stmt1
->
fetch
();
$stmt2
=
$this
->
_conn
->
prepare
(
'SELECT id FROM stmt_test WHERE id = ?'
);
$stmt2
->
execute
(
array
(
1
));
$this
->
assertEquals
(
1
,
$stmt2
->
fetchColumn
());
}
public
function
testReuseStatementAfterClosingCursor
()
{
$this
->
_conn
->
insert
(
'stmt_test'
,
array
(
'id'
=>
1
));
$this
->
_conn
->
insert
(
'stmt_test'
,
array
(
'id'
=>
2
));
$stmt
=
$this
->
_conn
->
prepare
(
'SELECT id FROM stmt_test WHERE id = ?'
);
$stmt
->
execute
(
array
(
1
));
$id
=
$stmt
->
fetchColumn
();
$this
->
assertEquals
(
1
,
$id
);
$stmt
->
closeCursor
();
$stmt
->
execute
(
array
(
2
));
$id
=
$stmt
->
fetchColumn
();
$this
->
assertEquals
(
2
,
$id
);
}
public
function
testReuseStatementWithParameterBoundByReference
()
{
$this
->
_conn
->
insert
(
'stmt_test'
,
array
(
'id'
=>
1
));
$this
->
_conn
->
insert
(
'stmt_test'
,
array
(
'id'
=>
2
));
$stmt
=
$this
->
_conn
->
prepare
(
'SELECT id FROM stmt_test WHERE id = ?'
);
$stmt
->
bindParam
(
1
,
$id
);
$id
=
1
;
$stmt
->
execute
();
$this
->
assertEquals
(
1
,
$stmt
->
fetchColumn
());
$id
=
2
;
$stmt
->
execute
();
$this
->
assertEquals
(
2
,
$stmt
->
fetchColumn
());
}
/**
* @dataProvider emptyFetchProvider
*/
public
function
testFetchFromNonExecutedStatement
(
callable
$fetch
,
$expected
)
{
$stmt
=
$this
->
_conn
->
prepare
(
'SELECT id FROM stmt_test'
);
$this
->
assertSame
(
$expected
,
$fetch
(
$stmt
));
}
public
function
testCloseCursorOnNonExecutedStatement
()
{
$stmt
=
$this
->
_conn
->
prepare
(
'SELECT id FROM stmt_test'
);
$this
->
assertTrue
(
$stmt
->
closeCursor
());
}
/**
* @dataProvider emptyFetchProvider
*/
public
function
testFetchFromNonExecutedStatementWithClosedCursor
(
callable
$fetch
,
$expected
)
{
$stmt
=
$this
->
_conn
->
prepare
(
'SELECT id FROM stmt_test'
);
$stmt
->
closeCursor
();
$this
->
assertSame
(
$expected
,
$fetch
(
$stmt
));
}
/**
* @dataProvider emptyFetchProvider
*/
public
function
testFetchFromExecutedStatementWithClosedCursor
(
callable
$fetch
,
$expected
)
{
$this
->
_conn
->
insert
(
'stmt_test'
,
array
(
'id'
=>
1
));
$stmt
=
$this
->
_conn
->
prepare
(
'SELECT id FROM stmt_test'
);
$stmt
->
execute
();
$stmt
->
closeCursor
();
$this
->
assertSame
(
$expected
,
$fetch
(
$stmt
));
}
public
static
function
emptyFetchProvider
()
{
return
array
(
'fetch'
=>
array
(
function
(
Statement
$stmt
)
{
return
$stmt
->
fetch
();
},
false
,
),
'fetch-column'
=>
array
(
function
(
Statement
$stmt
)
{
return
$stmt
->
fetchColumn
();
},
false
,
),
'fetch-all'
=>
array
(
function
(
Statement
$stmt
)
{
return
$stmt
->
fetchAll
();
},
array
(),
),
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment