PDO::ATTR_EMULATE_PREPARES at the connection level (#1324)
This commit is contained in:
parent
e3042e1ed4
commit
3826f1a522
|
@ -542,7 +542,8 @@ pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ vo
|
|||
fetch_datetime( false ),
|
||||
format_decimals( false ),
|
||||
decimal_places( NO_CHANGE_DECIMAL_PLACES ),
|
||||
use_national_characters(CHARSET_PREFERENCE_NOT_SPECIFIED)
|
||||
use_national_characters(CHARSET_PREFERENCE_NOT_SPECIFIED),
|
||||
emulate_prepare(false)
|
||||
{
|
||||
if( client_buffer_max_size < 0 ) {
|
||||
client_buffer_max_size = sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_DEFAULT;
|
||||
|
@ -734,7 +735,8 @@ bool pdo_sqlsrv_dbh_prepare(_Inout_ pdo_dbh_t *dbh, _In_ zend_string *sql_zstr,
|
|||
// assign the methods for the statement object. This is necessary even if the
|
||||
// statement fails so the user can retrieve the error information.
|
||||
stmt->methods = &pdo_sqlsrv_stmt_methods;
|
||||
stmt->supports_placeholders = PDO_PLACEHOLDER_POSITIONAL; // we support parameterized queries with ?, not names
|
||||
// if not emulate_prepare, we support parameterized queries with ?, not names
|
||||
stmt->supports_placeholders = (driver_dbh->emulate_prepare) ? PDO_PLACEHOLDER_NONE : PDO_PLACEHOLDER_POSITIONAL; // the statement options may override this later
|
||||
|
||||
// Initialize the options array to be passed to the core layer
|
||||
ALLOC_HASHTABLE( pdo_stmt_options_ht );
|
||||
|
@ -1288,8 +1290,15 @@ bool pdo_sqlsrv_dbh_set_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
|
|||
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR );
|
||||
}
|
||||
|
||||
// Statement level only
|
||||
case PDO_ATTR_EMULATE_PREPARES:
|
||||
{
|
||||
driver_dbh->emulate_prepare = zend_is_true(val);
|
||||
if (driver_dbh->emulate_prepare && driver_dbh->ce_option.enabled) {
|
||||
THROW_PDO_ERROR(driver_dbh, PDO_SQLSRV_ERROR_CE_EMULATE_PREPARE_UNSUPPORTED);
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Statement level only
|
||||
case PDO_ATTR_CURSOR:
|
||||
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
|
||||
case SQLSRV_ATTR_DATA_CLASSIFICATION:
|
||||
|
@ -1362,8 +1371,13 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
|
|||
#endif
|
||||
}
|
||||
|
||||
// Statement level only
|
||||
case PDO_ATTR_EMULATE_PREPARES:
|
||||
{
|
||||
ZVAL_BOOL(return_value, driver_dbh->emulate_prepare);
|
||||
break;
|
||||
}
|
||||
|
||||
// Statement level only
|
||||
case PDO_ATTR_CURSOR:
|
||||
case SQLSRV_ATTR_CURSOR_SCROLL_TYPE:
|
||||
case SQLSRV_ATTR_DATA_CLASSIFICATION:
|
||||
|
|
|
@ -194,6 +194,7 @@ struct pdo_sqlsrv_dbh : public sqlsrv_conn {
|
|||
bool format_decimals;
|
||||
short decimal_places;
|
||||
short use_national_characters;
|
||||
bool emulate_prepare;
|
||||
|
||||
pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* driver );
|
||||
};
|
||||
|
|
|
@ -1,49 +1,44 @@
|
|||
--TEST--
|
||||
Tests error returned when binding output parameter with emulate prepare
|
||||
--DESCRIPTION--
|
||||
The test shows that the option sets in prepared statements overrides the
|
||||
connection setting of PDO::ATTR_EMULATE_PREPARES
|
||||
--SKIPIF--
|
||||
<?php require_once('skipif_mid-refactor.inc'); ?>
|
||||
<?php require('skipif_azure_dw.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
require_once("MsSetup.inc");
|
||||
|
||||
try {
|
||||
$conn = connect();
|
||||
// Do not connect with AE enabled because otherwise this would have thrown a different exception
|
||||
$conn = new PDO("sqlsrv:server=$server; Database = $databaseName;", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
|
||||
|
||||
$count = 0;
|
||||
|
||||
$query = "select ? = count(* ) from cd_info";
|
||||
$stmt = $conn->prepare($query, array(PDO::ATTR_EMULATE_PREPARES => true));
|
||||
$stmt->bindParam(1, $count, PDO::PARAM_STR, 10);
|
||||
$stmt->execute();
|
||||
echo "Result: ".$count."\n";
|
||||
$stmt = $conn->prepare($query);
|
||||
} catch (PDOException $e) {
|
||||
print("Error: " . $e->getMessage() . "\n");
|
||||
}
|
||||
|
||||
$query = "select bigint_type, int_type, money_type from [test_types] where int_type < 0";
|
||||
$stmt1 = $conn->prepare($query);
|
||||
$stmt1->execute();
|
||||
$row = $stmt1->fetch(PDO::FETCH_ASSOC);
|
||||
print_r($row);
|
||||
try {
|
||||
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
||||
|
||||
$int = 0;
|
||||
$bigint = 100;
|
||||
$query = "select ? = bigint_type, ? = int_type, ? = money_type from [test_types] where int_type < 0";
|
||||
$stmt2 = $conn->prepare($query, array(PDO::ATTR_EMULATE_PREPARES => true));
|
||||
$stmt2->bindparam(1, $bigint, PDO::PARAM_STR, 256);
|
||||
$stmt2->bindParam(2, $int, PDO::PARAM_INT, 4);
|
||||
$stmt2->bindParam(3, $money, PDO::PARAM_STR, 1024);
|
||||
$stmt2->execute();
|
||||
echo "Big integer: ".$bigint."\n";
|
||||
echo "Integer: ".$int."\n";
|
||||
echo "Money: ".$money."\n";
|
||||
|
||||
//free the statement and connection
|
||||
unset($stmt);
|
||||
unset($stmt1);
|
||||
unset($stmt2);
|
||||
unset($conn);
|
||||
$stmt = $conn->prepare($query, array(PDO::ATTR_EMULATE_PREPARES => true));
|
||||
} catch (PDOException $e) {
|
||||
print("Error: " . $e->getMessage() . "\n");
|
||||
}
|
||||
|
||||
// free the statement and connection
|
||||
unset($stmt);
|
||||
unset($conn);
|
||||
?>
|
||||
--EXPECT--
|
||||
Error: SQLSTATE[IMSSP]: Statement with emulate prepare on does not support output or input_output parameters.
|
||||
Error: SQLSTATE[IMSSP]: Statement with emulate prepare on does not support output or input_output parameters.
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
GitHub issue 1310 - bind null field as varchar(max) if not binary
|
||||
--DESCRIPTION--
|
||||
The test shows null fields are no longer bound as char(1) if not binary such that it solves both issues 1310 and 1102.
|
||||
Note that this test does not connect with AE enabled because SQLDescribeParam() does not work with these queries.
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
|
@ -9,10 +10,9 @@ PHPT_EXEC=true
|
|||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
|
||||
try {
|
||||
$conn = connect();
|
||||
$conn = new PDO("sqlsrv:server=$server; Database = $databaseName;", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
// Issue 1310
|
||||
|
|
62
test/functional/pdo_sqlsrv/pdo_1320_pdo_emulate_prepare.phpt
Normal file
62
test/functional/pdo_sqlsrv/pdo_1320_pdo_emulate_prepare.phpt
Normal file
|
@ -0,0 +1,62 @@
|
|||
--TEST--
|
||||
GitHub issue 1320 - support PDO::ATTR_EMULATE_PREPARES at the connection level
|
||||
--DESCRIPTION--
|
||||
Supports PDO::ATTR_EMULATE_PREPARES at the connection level but setting it to true with column
|
||||
encryption enabled will fail with an exception. Also, the options in the prepared statement will
|
||||
override the connection setting.
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif_mid-refactor.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
|
||||
try {
|
||||
// Connection with column encryption enabled
|
||||
$connectionInfo = "ColumnEncryption = Enabled;";
|
||||
$conn = new PDO("sqlsrv:server = $server; database=$databaseName; $connectionInfo", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
|
||||
echo "setAttribute should have failed because column encryption is enabled.\n\n";
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
unset($conn);
|
||||
|
||||
try {
|
||||
// Connection with column encryption enabled
|
||||
$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => true);
|
||||
$connectionInfo = "ColumnEncryption = Enabled;";
|
||||
$conn = new PDO("sqlsrv:server = $server; database=$databaseName; $connectionInfo", $uid, $pwd, $options);
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
unset($conn);
|
||||
|
||||
try {
|
||||
// Connection with column encryption enabled - PDO::ATTR_EMULATE_PREPARES is false by default
|
||||
$connectionInfo = "ColumnEncryption = Enabled;";
|
||||
$conn = new PDO("sqlsrv:server = $server; database=$databaseName; $connectionInfo", $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
echo "Connected successfully with column encryption enabled.\n";
|
||||
$enabled = $conn->getAttribute(PDO::ATTR_EMULATE_PREPARES);
|
||||
echo "By default, the emulation of prepared statements is:\n";
|
||||
var_dump($enabled);
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
--EXPECT--
|
||||
SQLSTATE[IMSSP]: Parameterized statement with attribute PDO::ATTR_EMULATE_PREPARES is not supported in a Column Encryption enabled Connection.
|
||||
SQLSTATE[IMSSP]: Parameterized statement with attribute PDO::ATTR_EMULATE_PREPARES is not supported in a Column Encryption enabled Connection.
|
||||
Connected successfully with column encryption enabled.
|
||||
By default, the emulation of prepared statements is:
|
||||
bool(false)
|
|
@ -0,0 +1,137 @@
|
|||
--TEST--
|
||||
Prepare with emulate prepare and binding uft8 characters
|
||||
--DESCRIPTION--
|
||||
This is the same as pdo_prepare_emulatePrepare_unicode.phpt except that
|
||||
PDO::ATTR_EMULATE_PREPARES at the connection level
|
||||
--SKIPIF--
|
||||
<?php require('skipif_mid-refactor.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once('MsCommon_mid-refactor.inc');
|
||||
|
||||
function prepareStmt($conn, $query, $prepareOptions = array(), $dataType = PDO::PARAM_STR, $length = 0, $driverOptions = null)
|
||||
{
|
||||
$name = "가각";
|
||||
if (!isColEncrypted()) {
|
||||
$stmt = $conn->prepare($query, $prepareOptions);
|
||||
$stmt->bindParam(':name', $name, $dataType, $length, $driverOptions);
|
||||
} else {
|
||||
$status = 1;
|
||||
$stmt = $conn->prepare($query, $prepareOptions);
|
||||
$stmt->bindParam(':name', $name, $dataType, $length, $driverOptions);
|
||||
$stmt->bindParam(':status', $status);
|
||||
}
|
||||
$stmt->execute();
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
try {
|
||||
$conn = connect("", array(), PDO::ERRMODE_SILENT);
|
||||
|
||||
$tableName = "users";
|
||||
createTable($conn, $tableName, array("name" => "nvarchar(max)", "status" => "int", "age" => "int"));
|
||||
|
||||
if (!isColEncrypted()) {
|
||||
$conn->exec("INSERT INTO [$tableName] (name, status, age) VALUES (N'Belle', 1, 34)");
|
||||
$conn->exec("INSERT INTO [$tableName] (name, status, age) VALUES (N'Абрам', 1, 40)");
|
||||
$conn->exec("INSERT INTO [$tableName] (name, status, age) VALUES (N'가각', 1, 30)");
|
||||
$query = "SELECT * FROM [$tableName] WHERE name = :name AND status = 1";
|
||||
} else {
|
||||
insertRow($conn, $tableName, array("name" => "Belle", "status" => 1, "age" => 34));
|
||||
insertRow($conn, $tableName, array("name" => "Абрам", "status" => 1, "age" => 40));
|
||||
insertRow($conn, $tableName, array("name" => "가각", "status" => 1, "age" => 30));
|
||||
$query = "SELECT * FROM [$tableName] WHERE name = :name AND status = :status";
|
||||
}
|
||||
|
||||
//without emulate prepare
|
||||
print_r("Prepare without emulate prepare:\n");
|
||||
$stmt = prepareStmt($conn, $query, array(), PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_UTF8);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
print_r($row);
|
||||
|
||||
if (!isAEConnected()) {
|
||||
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
|
||||
} else {
|
||||
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
||||
}
|
||||
|
||||
//with emulate prepare and no bind param options
|
||||
print_r("Prepare with emulate prepare and no bindParam options:\n");
|
||||
// This test only makes sense without AE because the default encoding is PDO::SQLSRV_ENCODING_UTF8
|
||||
if (!isAEConnected()) {
|
||||
$stmt = prepareStmt($conn, $query, array());
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($stmt->rowCount() != 0) {
|
||||
print_r("Do not expect results for this query!\n");
|
||||
print_r($row);
|
||||
}
|
||||
}
|
||||
|
||||
//with emulate prepare and SQLSRV_ENCODING_UTF8
|
||||
print_r("Prepare with emulate prepare and SQLSRV_ENCODING_UTF8:\n");
|
||||
$stmt = prepareStmt($conn, $query, array(), PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_UTF8);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
print_r($row);
|
||||
|
||||
//with emulate prepare and SQLSRV_ENCODING_SYSTEM
|
||||
print_r("Prepare with emulate prepare and and SQLSRV_ENCODING_SYSTEM:\n");
|
||||
$stmt = prepareStmt($conn, $query, array(), PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_SYSTEM);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// The combination of Column Encryption and Unix platforms support SQLSRV_ENCODING_SYSTEM because:
|
||||
// With Column Encryption enabled, binding parameters uses exact datatypes as the column definition
|
||||
// the default encoding in Linux and Mac is UTF8
|
||||
$success = true;
|
||||
if (!(strtoupper( substr( php_uname( 's' ),0,3 ) ) === 'WIN') && isAEConnected()) {
|
||||
if ($row['name'] != "가각" || $row['status'] != 1 || $row['age'] != 30) {
|
||||
print_r("Incorrect results retrieved.\n");
|
||||
$success = false;
|
||||
}
|
||||
} else {
|
||||
// the default encoding in Windows is non-UTF8, thus binding UTF8 parameters does not work
|
||||
if ($stmt->rowCount() != 0) {
|
||||
print_r("Binding UTF8 data when encoding is SQLSRV_ENCODING_SYSTEM should not work.\n");
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
if ($success) {
|
||||
print_r("Binding UTF8 data with SQLSRV_ENCODING_SYSTEM is tested successfully.\n");
|
||||
}
|
||||
|
||||
//with emulate prepare and encoding SQLSRV_ENCODING_BINARY
|
||||
print_r("Prepare with emulate prepare and encoding SQLSRV_ENCODING_BINARY:\n");
|
||||
$stmt = prepareStmt($conn, $query, array(), PDO::PARAM_STR, 0, PDO::SQLSRV_ENCODING_BINARY);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
print_r($row);
|
||||
if ($stmt->rowCount() == 0) {
|
||||
print_r("No results for this query\n");
|
||||
}
|
||||
|
||||
dropTable($conn, $tableName);
|
||||
unset($stmt);
|
||||
unset($conn);
|
||||
} catch (PDOException $e) {
|
||||
var_dump($e->errorInfo);
|
||||
}
|
||||
?>
|
||||
|
||||
--EXPECT--
|
||||
Prepare without emulate prepare:
|
||||
Array
|
||||
(
|
||||
[name] => 가각
|
||||
[status] => 1
|
||||
[age] => 30
|
||||
)
|
||||
Prepare with emulate prepare and no bindParam options:
|
||||
Prepare with emulate prepare and SQLSRV_ENCODING_UTF8:
|
||||
Array
|
||||
(
|
||||
[name] => 가각
|
||||
[status] => 1
|
||||
[age] => 30
|
||||
)
|
||||
Prepare with emulate prepare and and SQLSRV_ENCODING_SYSTEM:
|
||||
Binding UTF8 data with SQLSRV_ENCODING_SYSTEM is tested successfully.
|
||||
Prepare with emulate prepare and encoding SQLSRV_ENCODING_BINARY:
|
||||
No results for this query
|
|
@ -17,15 +17,6 @@ try {
|
|||
$tableName = "pdo_test_table";
|
||||
createTable($conn1, $tableName, array(new ColumnMeta("int", "ID", "NOT NULL PRIMARY KEY"), "Policy" => "varchar(2)", "Label" => "varchar(10)", "Budget" => "money"));
|
||||
|
||||
try {
|
||||
$res = $conn1->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
|
||||
if ($res) {
|
||||
echo "setAttribute should have failed.\n\n";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$query = "SELECT * FROM [$tableName]";
|
||||
$stmt = $conn1->query($query);
|
||||
|
@ -186,7 +177,6 @@ try {
|
|||
|
||||
?>
|
||||
--EXPECTF--
|
||||
SQLSTATE[IMSSP]: The given attribute is only supported on the PDOStatement object.
|
||||
SQLSTATE[IMSSP]: An invalid attribute was designated on the PDOStatement object.
|
||||
Start inserting data...
|
||||
....Done....
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
GitHub issue 1310 - bind null field as varchar(max) if not binary
|
||||
--DESCRIPTION--
|
||||
The test shows null fields are no longer bound as char(1) if not binary such that it solves both issues 1310 and 1102.
|
||||
Note that this test does not connect with AE enabled because SQLDescribeParam() does not work with these queries.
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
|
@ -10,7 +11,7 @@ PHPT_EXEC=true
|
|||
<?php
|
||||
require_once('MsCommon.inc');
|
||||
|
||||
$conn = AE\connect();
|
||||
$conn = connect();
|
||||
|
||||
// Issue 1310
|
||||
$query = "SELECT CAST(ISNULL(?, -1) AS INT) AS K";
|
||||
|
|
Loading…
Reference in a new issue