Saved php types with metadata when fetching (#1049)
This commit is contained in:
parent
e30752fc6c
commit
b77bfa82f4
|
@ -781,8 +781,12 @@ int pdo_sqlsrv_stmt_get_col_data( _Inout_ pdo_stmt_t *stmt, _In_ int colno,
|
|||
"Invalid column number in pdo_sqlsrv_stmt_get_col_data" );
|
||||
|
||||
// set the encoding if the user specified one via bindColumn, otherwise use the statement's encoding
|
||||
sqlsrv_php_type = driver_stmt->sql_type_to_php_type( static_cast<SQLUINTEGER>( driver_stmt->current_meta_data[colno]->field_type ),
|
||||
static_cast<SQLUINTEGER>( driver_stmt->current_meta_data[colno]->field_size ), true );
|
||||
// save the php type for next use
|
||||
sqlsrv_php_type = driver_stmt->sql_type_to_php_type(
|
||||
static_cast<SQLINTEGER>(driver_stmt->current_meta_data[colno]->field_type),
|
||||
static_cast<SQLUINTEGER>(driver_stmt->current_meta_data[colno]->field_size),
|
||||
true);
|
||||
driver_stmt->current_meta_data[colno]->sqlsrv_php_type = sqlsrv_php_type;
|
||||
|
||||
// if a column is bound to a type different than the column type, figure out a way to convert it to the
|
||||
// type they want
|
||||
|
@ -825,6 +829,9 @@ int pdo_sqlsrv_stmt_get_col_data( _Inout_ pdo_stmt_t *stmt, _In_ int colno,
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// save the php type for the bound column
|
||||
driver_stmt->current_meta_data[colno]->sqlsrv_php_type = sqlsrv_php_type;
|
||||
}
|
||||
|
||||
SQLSRV_PHPTYPE sqlsrv_phptype_out = SQLSRV_PHPTYPE_INVALID;
|
||||
|
|
|
@ -1576,15 +1576,23 @@ struct field_meta_data {
|
|||
SQLSMALLINT field_scale;
|
||||
SQLSMALLINT field_is_nullable;
|
||||
bool field_is_money_type;
|
||||
sqlsrv_phptype sqlsrv_php_type;
|
||||
|
||||
field_meta_data() : field_name_len(0), field_type(0), field_size(0), field_precision(0),
|
||||
field_scale (0), field_is_nullable(0), field_is_money_type(false)
|
||||
{
|
||||
reset_php_type();
|
||||
}
|
||||
|
||||
~field_meta_data()
|
||||
{
|
||||
}
|
||||
|
||||
void reset_php_type()
|
||||
{
|
||||
sqlsrv_php_type.typeinfo.type = SQLSRV_PHPTYPE_INVALID;
|
||||
sqlsrv_php_type.typeinfo.encoding = SQLSRV_ENCODING_INVALID;
|
||||
}
|
||||
};
|
||||
|
||||
// *** statement constants ***
|
||||
|
|
|
@ -244,6 +244,12 @@ void sqlsrv_stmt::new_result_set( TSRMLS_D )
|
|||
// delete sensivity data
|
||||
clean_up_sensitivity_metadata();
|
||||
|
||||
// reset sqlsrv php type in meta data
|
||||
size_t num_fields = this->current_meta_data.size();
|
||||
for (size_t f = 0; f < num_fields; f++) {
|
||||
this->current_meta_data[f]->reset_php_type();
|
||||
}
|
||||
|
||||
// create a new result set
|
||||
if( cursor_type == SQLSRV_CURSOR_BUFFERED ) {
|
||||
sqlsrv_malloc_auto_ptr<sqlsrv_buffered_result_set> result;
|
||||
|
@ -1121,9 +1127,6 @@ void core_sqlsrv_get_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
|
|||
|
||||
sqlsrv_phptype sqlsrv_php_type = sqlsrv_php_type_in;
|
||||
|
||||
SQLLEN sql_field_type = 0;
|
||||
SQLLEN sql_field_len = 0;
|
||||
|
||||
// Make sure that the statement was executed and not just prepared.
|
||||
CHECK_CUSTOM_ERROR( !stmt->executed, stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) {
|
||||
throw core::CoreException();
|
||||
|
@ -1132,37 +1135,47 @@ void core_sqlsrv_get_field( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT field_i
|
|||
// if the field is to be cached, and this field is being retrieved out of order, cache prior fields so they
|
||||
// may also be retrieved.
|
||||
if( cache_field && (field_index - stmt->last_field_index ) >= 2 ) {
|
||||
sqlsrv_phptype invalid;
|
||||
invalid.typeinfo.type = SQLSRV_PHPTYPE_INVALID;
|
||||
for( int i = stmt->last_field_index + 1; i < field_index; ++i ) {
|
||||
SQLSRV_ASSERT( reinterpret_cast<field_cache*>( zend_hash_index_find_ptr( Z_ARRVAL( stmt->field_cache ), i )) == NULL, "Field already cached." );
|
||||
core_sqlsrv_get_field( stmt, i, invalid, prefer_string, field_value, field_len, cache_field, sqlsrv_php_type_out TSRMLS_CC );
|
||||
// delete the value returned since we only want it cached, not the actual value
|
||||
if( field_value ) {
|
||||
efree( field_value );
|
||||
field_value = NULL;
|
||||
*field_len = 0;
|
||||
}
|
||||
}
|
||||
sqlsrv_phptype invalid;
|
||||
invalid.typeinfo.type = SQLSRV_PHPTYPE_INVALID;
|
||||
for( int i = stmt->last_field_index + 1; i < field_index; ++i ) {
|
||||
SQLSRV_ASSERT( reinterpret_cast<field_cache*>( zend_hash_index_find_ptr( Z_ARRVAL( stmt->field_cache ), i )) == NULL, "Field already cached." );
|
||||
core_sqlsrv_get_field( stmt, i, invalid, prefer_string, field_value, field_len, cache_field, sqlsrv_php_type_out TSRMLS_CC );
|
||||
// delete the value returned since we only want it cached, not the actual value
|
||||
if( field_value ) {
|
||||
efree( field_value );
|
||||
field_value = NULL;
|
||||
*field_len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the php type was not specified set the php type to be the default type.
|
||||
if (sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_INVALID) {
|
||||
SQLSRV_ASSERT(stmt->current_meta_data.size() > field_index, "core_sqlsrv_get_field - meta data vector not in sync" );
|
||||
sql_field_type = stmt->current_meta_data[field_index]->field_type;
|
||||
if (stmt->current_meta_data[field_index]->field_precision > 0) {
|
||||
sql_field_len = stmt->current_meta_data[field_index]->field_precision;
|
||||
|
||||
// Get the corresponding php type from the sql type and then save the result for later
|
||||
if (stmt->current_meta_data[field_index]->sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_INVALID) {
|
||||
SQLLEN sql_field_type = 0;
|
||||
SQLLEN sql_field_len = 0;
|
||||
|
||||
sql_field_type = stmt->current_meta_data[field_index]->field_type;
|
||||
if (stmt->current_meta_data[field_index]->field_precision > 0) {
|
||||
sql_field_len = stmt->current_meta_data[field_index]->field_precision;
|
||||
}
|
||||
else {
|
||||
sql_field_len = stmt->current_meta_data[field_index]->field_size;
|
||||
}
|
||||
sqlsrv_php_type = stmt->sql_type_to_php_type(static_cast<SQLINTEGER>(sql_field_type), static_cast<SQLUINTEGER>(sql_field_len), prefer_string);
|
||||
stmt->current_meta_data[field_index]->sqlsrv_php_type = sqlsrv_php_type;
|
||||
}
|
||||
else {
|
||||
sql_field_len = stmt->current_meta_data[field_index]->field_size;
|
||||
// use the previously saved php type
|
||||
sqlsrv_php_type = stmt->current_meta_data[field_index]->sqlsrv_php_type;
|
||||
}
|
||||
|
||||
// Get the corresponding php type from the sql type.
|
||||
sqlsrv_php_type = stmt->sql_type_to_php_type(static_cast<SQLINTEGER>(sql_field_type), static_cast<SQLUINTEGER>(sql_field_len), prefer_string);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that we have an acceptable type to convert.
|
||||
CHECK_CUSTOM_ERROR( !is_valid_sqlsrv_phptype( sqlsrv_php_type ), stmt, SQLSRV_ERROR_INVALID_TYPE ) {
|
||||
CHECK_CUSTOM_ERROR(!is_valid_sqlsrv_phptype(sqlsrv_php_type), stmt, SQLSRV_ERROR_INVALID_TYPE) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
|
||||
|
|
81
test/functional/pdo_sqlsrv/pdo_569_query_varcharmax_ae.phpt
Normal file
81
test/functional/pdo_sqlsrv/pdo_569_query_varcharmax_ae.phpt
Normal file
|
@ -0,0 +1,81 @@
|
|||
--TEST--
|
||||
GitHub issue #569 - direct query on varchar max fields results in function sequence error (Always Encrypted)
|
||||
--DESCRIPTION--
|
||||
This is similar to pdo_569_query_varcharmax.phpt but is not limited to testing the Always Encrypted feature in Windows only.
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif_mid-refactor.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsCommon_mid-refactor.inc");
|
||||
|
||||
try {
|
||||
$conn = connect();
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$tableName = 'pdoTestTable_569_ae';
|
||||
createTable($conn, $tableName, array(new ColumnMeta("int", "id", "IDENTITY"), "c1" => "nvarchar(max)"));
|
||||
|
||||
$input = array();
|
||||
|
||||
$input[0] = 'some very large string';
|
||||
$input[1] = '1234567890.1234';
|
||||
$input[2] = 'über über';
|
||||
|
||||
$numRows = 3;
|
||||
$tsql = "INSERT INTO $tableName (c1) VALUES (?)";
|
||||
|
||||
$stmt = $conn->prepare($tsql);
|
||||
for ($i = 0; $i < $numRows; $i++) {
|
||||
$stmt->bindParam(1, $input[$i]);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
$tsql = "SELECT id, c1 FROM $tableName ORDER BY id";
|
||||
$stmt = $conn->prepare($tsql);
|
||||
$stmt->execute();
|
||||
|
||||
// Fetch one row each time with different pdo type and/or encoding
|
||||
$result = $stmt->fetch(PDO::FETCH_NUM);
|
||||
if ($result[1] !== $input[0]) {
|
||||
echo "Expected $input[0] but got: ";
|
||||
var_dump($result[0]);
|
||||
}
|
||||
|
||||
$stmt->bindColumn(2, $value, PDO::PARAM_LOB, 0, PDO::SQLSRV_ENCODING_SYSTEM);
|
||||
$result = $stmt->fetch(PDO::FETCH_BOUND);
|
||||
if (!$result || $value !== $input[1]) {
|
||||
echo "Expected $input[1] but got: ";
|
||||
var_dump($result);
|
||||
}
|
||||
|
||||
$stmt->bindColumn(2, $value, PDO::PARAM_STR);
|
||||
$result = $stmt->fetch(PDO::FETCH_BOUND);
|
||||
if (!$result || $value !== $input[2]) {
|
||||
echo "Expected $input[2] but got: ";
|
||||
var_dump($value);
|
||||
}
|
||||
|
||||
// Fetch again but all at once
|
||||
$stmt->execute();
|
||||
$rows = $stmt->fetchall(PDO::FETCH_ASSOC);
|
||||
for ($i = 0; $i < $numRows; $i++) {
|
||||
$i = $rows[$i]['id'] - 1;
|
||||
if ($rows[$i]['c1'] !== $input[$i]) {
|
||||
echo "Expected $input[$i] but got: ";
|
||||
var_dump($rows[$i]['c1']);
|
||||
}
|
||||
}
|
||||
|
||||
unset($stmt);
|
||||
unset($conn);
|
||||
} catch (PDOException $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
96
test/functional/sqlsrv/srv_569_query_varcharmax_ae.phpt
Normal file
96
test/functional/sqlsrv/srv_569_query_varcharmax_ae.phpt
Normal file
|
@ -0,0 +1,96 @@
|
|||
--TEST--
|
||||
GitHub issue #569 - sqlsrv_query on varchar max fields results in function sequence error
|
||||
--DESCRIPTION--
|
||||
This is similar to srv_569_query_varcharmax.phpt but is not limited to testing the Always Encrypted feature in Windows only.
|
||||
--ENV--
|
||||
PHPT_EXEC=true
|
||||
--SKIPIF--
|
||||
<?php require('skipif_versions_old.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once('MsCommon.inc');
|
||||
|
||||
$conn = AE\connect(array('CharacterSet'=>'UTF-8'));
|
||||
|
||||
$tableName = 'srvTestTable_569_ae';
|
||||
|
||||
$columns = array(new AE\ColumnMeta('int', 'id', 'identity'),
|
||||
new AE\ColumnMeta('nvarchar(max)', 'c1'));
|
||||
AE\createTable($conn, $tableName, $columns);
|
||||
|
||||
$input = array();
|
||||
|
||||
$input[0] = 'some very large string';
|
||||
$input[1] = '1234567890.1234';
|
||||
$input[2] = 'über über';
|
||||
|
||||
$numRows = 3;
|
||||
$isql = "INSERT INTO $tableName (c1) VALUES (?)";
|
||||
for ($i = 0; $i < $numRows; $i++) {
|
||||
$stmt = sqlsrv_prepare($conn, $isql, array($input[$i]));
|
||||
$result = sqlsrv_execute($stmt);
|
||||
if (!$result) {
|
||||
fatalError("Failed to insert row $i into $tableName");
|
||||
}
|
||||
}
|
||||
|
||||
// Select all from test table
|
||||
$tsql = "SELECT id, c1 FROM $tableName ORDER BY id";
|
||||
$stmt = sqlsrv_prepare($conn, $tsql);
|
||||
if (!$stmt) {
|
||||
fatalError("Failed to read from $tableName");
|
||||
}
|
||||
$result = sqlsrv_execute($stmt);
|
||||
if (!$result) {
|
||||
fatalError("Failed to select data from $tableName");
|
||||
}
|
||||
|
||||
// Fetch each row as an array
|
||||
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
|
||||
$i = $row['id'] - 1;
|
||||
if ($row['c1'] !== $input[$i]) {
|
||||
echo "Expected $input[$i] but got: ";
|
||||
var_dump($fieldVal);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch again, one field each time
|
||||
sqlsrv_execute($stmt);
|
||||
|
||||
$i = 0;
|
||||
while ($i < $numRows) {
|
||||
sqlsrv_fetch($stmt);
|
||||
|
||||
switch ($i) {
|
||||
case 0:
|
||||
$fieldVal = sqlsrv_get_field($stmt, 1, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
|
||||
break;
|
||||
case 1:
|
||||
$stream = sqlsrv_get_field($stmt, 1);
|
||||
while (!feof( $stream)) {
|
||||
$fieldVal = fread($stream, 50);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$fieldVal = sqlsrv_get_field($stmt, 1, SQLSRV_PHPTYPE_STRING('utf-8'));
|
||||
break;
|
||||
}
|
||||
|
||||
if ($fieldVal !== $input[$i]) {
|
||||
echo 'Expected $input[$i] but got: ';
|
||||
var_dump($fieldVal);
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
dropTable($conn, $tableName);
|
||||
|
||||
echo "Done\n";
|
||||
|
||||
sqlsrv_free_stmt($stmt);
|
||||
sqlsrv_close($conn);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
Loading…
Reference in a new issue