Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fetch_bignumeric attribute #1500

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions source/pdo_sqlsrv/pdo_dbh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ enum PDO_STMT_OPTIONS {
PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE,
PDO_STMT_OPTION_EMULATE_PREPARES,
PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE,
PDO_STMT_OPTION_FETCHES_BIGNUMERIC_TYPE,
PDO_STMT_OPTION_FETCHES_DATETIME_TYPE,
PDO_STMT_OPTION_FORMAT_DECIMALS,
PDO_STMT_OPTION_DECIMAL_PLACES,
Expand All @@ -104,6 +105,7 @@ const stmt_option PDO_STMT_OPTS[] = {
{ NULL, 0, PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, std::unique_ptr<stmt_option_buffered_query_limit>( new stmt_option_buffered_query_limit ) },
{ NULL, 0, PDO_STMT_OPTION_EMULATE_PREPARES, std::unique_ptr<stmt_option_emulate_prepares>( new stmt_option_emulate_prepares ) },
{ NULL, 0, PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, std::unique_ptr<stmt_option_fetch_numeric>( new stmt_option_fetch_numeric ) },
{ NULL, 0, PDO_STMT_OPTION_FETCHES_BIGNUMERIC_TYPE, std::unique_ptr<stmt_option_fetch_bignumeric>( new stmt_option_fetch_bignumeric ) },
{ NULL, 0, PDO_STMT_OPTION_FETCHES_DATETIME_TYPE, std::unique_ptr<stmt_option_fetch_datetime>( new stmt_option_fetch_datetime ) },
{ NULL, 0, PDO_STMT_OPTION_FORMAT_DECIMALS, std::unique_ptr<stmt_option_format_decimals>( new stmt_option_format_decimals ) },
{ NULL, 0, PDO_STMT_OPTION_DECIMAL_PLACES, std::unique_ptr<stmt_option_decimal_places>( new stmt_option_decimal_places ) },
Expand Down Expand Up @@ -592,6 +594,7 @@ pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( _In_ SQLHANDLE h, _In_ error_callback e, _In_ vo
query_timeout( QUERY_TIMEOUT_INVALID ),
client_buffer_max_size( PDO_SQLSRV_G( client_buffer_max_size )),
fetch_numeric( false ),
fetch_bignumeric( false ),
fetch_datetime( false ),
format_decimals( false ),
decimal_places( NO_CHANGE_DECIMAL_PLACES ),
Expand Down Expand Up @@ -1273,6 +1276,10 @@ bool pdo_sqlsrv_dbh_set_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout
driver_dbh->fetch_numeric = zend_is_true(val);
break;

case SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE:
driver_dbh->fetch_bignumeric = zend_is_true(val);
break;

case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
driver_dbh->fetch_datetime = zend_is_true(val);
break;
Expand Down Expand Up @@ -1504,6 +1511,12 @@ int pdo_sqlsrv_dbh_get_attr(_Inout_ pdo_dbh_t *dbh, _In_ zend_long attr, _Inout_
break;
}

case SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE:
{
ZVAL_BOOL( return_value, driver_dbh->fetch_bignumeric );
break;
}

case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
{
ZVAL_BOOL( return_value, driver_dbh->fetch_datetime );
Expand Down Expand Up @@ -1959,6 +1972,10 @@ void add_stmt_option_key(_Inout_ sqlsrv_context& ctx, _In_ size_t key, _Inout_ H
option_key = PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE;
break;

case SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE:
option_key = PDO_STMT_OPTION_FETCHES_BIGNUMERIC_TYPE;
break;

case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
option_key = PDO_STMT_OPTION_FETCHES_DATETIME_TYPE;
break;
Expand Down
1 change: 1 addition & 0 deletions source/pdo_sqlsrv/pdo_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ namespace {
{ "SQLSRV_ATTR_CURSOR_SCROLL_TYPE" , SQLSRV_ATTR_CURSOR_SCROLL_TYPE },
{ "SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE", SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE },
{ "SQLSRV_ATTR_FETCHES_NUMERIC_TYPE", SQLSRV_ATTR_FETCHES_NUMERIC_TYPE },
{ "SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE", SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE },
{ "SQLSRV_ATTR_FETCHES_DATETIME_TYPE", SQLSRV_ATTR_FETCHES_DATETIME_TYPE },
{ "SQLSRV_ATTR_FORMAT_DECIMALS" , SQLSRV_ATTR_FORMAT_DECIMALS },
{ "SQLSRV_ATTR_DECIMAL_PLACES" , SQLSRV_ATTR_DECIMAL_PLACES },
Expand Down
33 changes: 31 additions & 2 deletions source/pdo_sqlsrv/pdo_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,12 @@ void stmt_option_fetch_numeric:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_opt
pdo_stmt->fetch_numeric = zend_is_true(value_z);
}

void stmt_option_fetch_bignumeric:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt );
pdo_stmt->fetch_bignumeric = zend_is_true(value_z);
}

void stmt_option_fetch_datetime:: operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z )
{
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt );
Expand Down Expand Up @@ -930,6 +936,10 @@ int pdo_sqlsrv_stmt_set_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
driver_stmt->fetch_numeric = zend_is_true(val);
break;

case SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE:
driver_stmt->fetch_bignumeric = zend_is_true(val);
break;

case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
driver_stmt->fetch_datetime = zend_is_true(val);
break;
Expand Down Expand Up @@ -1027,6 +1037,12 @@ int pdo_sqlsrv_stmt_get_attr( _Inout_ pdo_stmt_t *stmt, _In_ zend_long attr, _In
break;
}

case SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE:
{
ZVAL_BOOL( return_value, driver_stmt->fetch_bignumeric );
break;
}

case SQLSRV_ATTR_FETCHES_DATETIME_TYPE:
{
ZVAL_BOOL( return_value, driver_stmt->fetch_datetime );
Expand Down Expand Up @@ -1539,10 +1555,23 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( _In_ SQLINTEGER sql_type,
}
break;
case SQL_BIGINT:
if ( this->fetch_bignumeric ) {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_INT;
}
else {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
}
break;
case SQL_DECIMAL:
case SQL_NUMERIC:
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
if ( this->fetch_bignumeric ) {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_FLOAT;
}
else {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = SQLSRV_ENCODING_CHAR;
}
break;
case SQL_CHAR:
case SQL_GUID:
Expand Down
9 changes: 9 additions & 0 deletions source/pdo_sqlsrv/php_pdo_sqlsrv_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ enum PDO_SQLSRV_ATTR {
SQLSRV_ATTR_CURSOR_SCROLL_TYPE,
SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE,
SQLSRV_ATTR_FETCHES_NUMERIC_TYPE,
SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE,
SQLSRV_ATTR_FETCHES_DATETIME_TYPE,
SQLSRV_ATTR_FORMAT_DECIMALS,
SQLSRV_ATTR_DECIMAL_PLACES,
Expand Down Expand Up @@ -187,6 +188,7 @@ struct pdo_sqlsrv_dbh : public sqlsrv_conn {
long query_timeout;
zend_long client_buffer_max_size;
bool fetch_numeric;
bool fetch_bignumeric;
bool fetch_datetime;
bool format_decimals;
short decimal_places;
Expand Down Expand Up @@ -225,6 +227,10 @@ struct stmt_option_fetch_numeric : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};

struct stmt_option_fetch_bignumeric : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};

struct stmt_option_fetch_datetime : public stmt_option_functor {
virtual void operator()( _Inout_ sqlsrv_stmt* stmt, stmt_option const* /*opt*/, _In_ zval* value_z );
};
Expand All @@ -241,11 +247,13 @@ struct pdo_sqlsrv_stmt : public sqlsrv_stmt {
placeholders(NULL),
bound_column_param_types( NULL ),
fetch_numeric( false ),
fetch_bignumeric( false ),
fetch_datetime( false )
{
pdo_sqlsrv_dbh* db = static_cast<pdo_sqlsrv_dbh*>( c );
direct_query = db->direct_query;
fetch_numeric = db->fetch_numeric;
fetch_bignumeric = db->fetch_bignumeric;
fetch_datetime = db->fetch_datetime;
format_decimals = db->format_decimals;
decimal_places = db->decimal_places;
Expand All @@ -265,6 +273,7 @@ struct pdo_sqlsrv_stmt : public sqlsrv_stmt {

pdo_param_type* bound_column_param_types;
bool fetch_numeric;
bool fetch_bignumeric;
bool fetch_datetime;
};

Expand Down
72 changes: 72 additions & 0 deletions test/functional/pdo_sqlsrv/pdo_fetch_bignumeric_as_int.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
--TEST--
Test attribute PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE for bigint columns
--DESCRIPTION--
Test attribute PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE for bigint columns.
The input value is a bigint bigger than the max int value in mssql.
Note that the existing attribute ATTR_STRINGIFY_FETCHES should have no effect on data retrieval.
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");

try {
$tableName = "pdo_test_table";

// Connect
$conn = connect();

// Run test
Test();

// Set PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE = false (default)
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, FALSE);
Test();

// Set PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE = true
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, TRUE);
Test();

// Close connection
unset($stmt);
unset($conn);

print "Done";
} catch (PDOException $e) {
var_dump($e->errorInfo);
}

// Generic test starts here
function Test()
{
global $conn, $tableName;

// Drop table if exists
createTable($conn, $tableName, array("c1" => "bigint"));

// Insert data using bind values
$sql = "INSERT INTO $tableName VALUES (32147483647)";
$stmt = $conn->prepare($sql);
$stmt->execute();

// Get data
$sql = "SELECT * FROM $tableName";
$stmt = $conn->query($sql);
$row = $stmt->fetchAll(PDO::FETCH_NUM);

// Print out
for ($i=0; $i<$stmt->rowCount(); $i++) {
var_dump($row[$i][0]);
}

// clean up
dropTable( $conn, $tableName );
unset( $stmt );
}
?>

--EXPECT--
string(11) "32147483647"
string(11) "32147483647"
int(32147483647)
Done
139 changes: 139 additions & 0 deletions test/functional/pdo_sqlsrv/pdo_fetch_bignumeric_nulls.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
--TEST--
Test attribute PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE for bigint, decimal and numeric types with null values
--DESCRIPTION--
Test attribute PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE for different bigint, decimal and numeric
types with null values. Whether retrieved as strings, ints or floats should return NULLs.
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");

function checkNull($row, $columns, $fetchStyle)
{
$size = count($columns);
for ($i = 0; $i < $size; $i++) {
$col = $columns[$i];
if ($fetchStyle == PDO::FETCH_ASSOC) {
$dtObj = $row[$col];
} else {
// assume PDO::FETCH_BOTH
$dtObj = $row[$i];
}
if (!is_null($dtObj)) {
echo "Expected NULL for column $col but got: ";
var_dump($dtObj);
}
}
}

function runTest($conn, $query, $columns, $useBuffer = false)
{
// fetch the date time values as strings or date time objects
// prepare with or without buffered cursor
$options = array();
if ($useBuffer) {
$options = array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL,
PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED);
}

// fetch_bignumeric off
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, false);
$stmt = $conn->prepare($query, $options);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_NUM);
checkNull($row, $columns, PDO::FETCH_NUM);

// fetch_bignumeric on
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, true);
$stmt = $conn->prepare($query, $options);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
checkNull($row, $columns, PDO::FETCH_ASSOC);

// conn attribute fetch_bignumeric on, but statement attribute fetch_bignumeric off --
// expected strings to be returned because statement attribute overrides the
// connection attribute
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, true);
$stmt = $conn->prepare($query, $options);
$stmt->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, false);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_NUM);
checkNull($row, $columns, PDO::FETCH_NUM);

// conn attribute fetch_bignumeric unchanged, but statement attribute fetch_bignumeric on --
// expected datetime objects to be returned (this time no need to prepare the statement)
$stmt->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, true);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
checkNull($row, $columns, PDO::FETCH_ASSOC);

// likewise, conn attribute fetch_bignumeric off, but statement attribute
// fetch_bignumeric on -- expected int/float objects to be returned
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, false);
$stmt = $conn->prepare($query, $options);
$stmt->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, true);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_BOTH);
checkNull($row, $columns, PDO::FETCH_BOTH);

// conn attribute fetch_bignumeric unchanged, but statement attribute fetch_bignumeric off --
// expected strings to be returned (again no need to prepare the statement)
$stmt->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, false);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_NUM);
checkNull($row, $columns, PDO::FETCH_NUM);

// last test: set statement attribute fetch_datetime on with no change to
// prepared statement -- expected int/float objects to be returned
$stmt->setAttribute(PDO::SQLSRV_ATTR_FETCHES_BIGNUMERIC_TYPE, true);
$i = 0;
do {
$stmt->execute();
$dtObj = $stmt->fetchColumn($i);
if (!is_null($dtObj)) {
echo "Expected NULL for column " . ($i + 1) . " but got: ";
var_dump($dtObj);
}
} while (++$i < count($columns));
}

try {
$conn = connect();

// create a test table
$tableName = "TestNullDateTime";
$columns = array('c1', 'c2', 'c3', 'c4', 'c5', 'c6');
$colMeta = array(new ColumnMeta('bigint', $columns[0]),
new ColumnMeta('bigint', $columns[1]),
new ColumnMeta('decimal(5,2)', $columns[2]),
new ColumnMeta('numeric(5,2)', $columns[3]),
new ColumnMeta('decimal(38,4)', $columns[4]),
new ColumnMeta('numeric(38,4)', $columns[5]));
createTable($conn, $tableName, $colMeta);

$value = null;
$query = "INSERT INTO $tableName VALUES(?, ?, ?, ?, ?, ?)";
$stmt = $conn->prepare($query);
for ($i = 0; $i < count($columns); $i++) {
$stmt->bindParam($i+1, $value, PDO::PARAM_NULL);
}
$stmt->execute();

$query = "SELECT * FROM $tableName";

runTest($conn, $query, $columns);
runTest($conn, $query, $columns, true);

dropTable($conn, $tableName);

echo "Done\n";

unset($stmt);
unset($conn);
} catch (PDOException $e) {
var_dump($e->errorInfo);
}
?>
--EXPECT--
Done
Loading