Skip to content

PDO_Firebird: Add connection level SESSION_TIMEZONE attribute #15480

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

Closed
wants to merge 8 commits into from
Closed
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
82 changes: 20 additions & 62 deletions ext/pdo_firebird/firebird_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -463,53 +463,6 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa

#if FB_API_VER >= 40
/* set coercing a data type */
static void set_coercing_input_data_types(XSQLDA* sqlda)
{
/* Data types introduced in Firebird 4.0 are difficult to process using the Firebird Legacy API. */
/* These data types include DECFLOAT(16), DECFLOAT(34), INT128 (NUMERIC/DECIMAL(38, x)), */
/* TIMESTAMP WITH TIME ZONE, and TIME WITH TIME ZONE. */
/* This function allows you to ensure minimal performance */
/* of queries if they contain parameters of the above types. */
unsigned int i;
short dtype;
short nullable;
XSQLVAR* var;
for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) {
dtype = (var->sqltype & ~1); /* drop flag bit */
nullable = (var->sqltype & 1);
switch(dtype) {
case SQL_INT128:
var->sqltype = SQL_VARYING + nullable;
var->sqllen = 46;
var->sqlscale = 0;
break;

case SQL_DEC16:
var->sqltype = SQL_VARYING + nullable;
var->sqllen = 24;
break;

case SQL_DEC34:
var->sqltype = SQL_VARYING + nullable;
var->sqllen = 43;
break;

case SQL_TIMESTAMP_TZ:
var->sqltype = SQL_VARYING + nullable;
var->sqllen = 58;
break;

case SQL_TIME_TZ:
var->sqltype = SQL_VARYING + nullable;
var->sqllen = 46;
break;

default:
break;
}
}
}

static void set_coercing_output_data_types(XSQLDA* sqlda)
{
/* Data types introduced in Firebird 4.0 are difficult to process using the Firebird Legacy API. */
Expand Down Expand Up @@ -602,14 +555,12 @@ void php_firebird_set_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *state,
einfo->errmsg_length = read_len;
einfo->errmsg = pestrndup(buf, read_len, dbh->is_persistent);

#if FB_API_VER >= 25
char sqlstate[sizeof(pdo_error_type)];
fb_sqlstate(sqlstate, H->isc_status);
if (sqlstate != NULL && strlen(sqlstate) < sizeof(pdo_error_type)) {
strcpy(*error_code, sqlstate);
goto end;
}
#endif
} else if (msg && msg_len) {
einfo->errmsg_length = msg_len;
einfo->errmsg = pestrndup(msg, einfo->errmsg_length, dbh->is_persistent);
Expand Down Expand Up @@ -730,11 +681,6 @@ static bool firebird_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, /* {{{ */
if (isc_dsql_describe_bind(H->isc_status, &s, PDO_FB_SQLDA_VERSION, S->in_sqlda)) {
break;
}

#if FB_API_VER >= 40
/* set coercing a data type */
set_coercing_input_data_types(S->in_sqlda);
#endif
}

stmt->driver_data = S;
Expand Down Expand Up @@ -1333,7 +1279,6 @@ static int pdo_firebird_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val)
}
/* }}} */

#if FB_API_VER >= 30
/* called by PDO to check liveness */
static zend_result pdo_firebird_check_liveness(pdo_dbh_t *dbh) /* {{{ */
{
Expand All @@ -1343,7 +1288,6 @@ static zend_result pdo_firebird_check_liveness(pdo_dbh_t *dbh) /* {{{ */
return fb_ping(H->isc_status, &H->db) ? FAILURE : SUCCESS;
}
/* }}} */
#endif

/* called by PDO to retrieve driver-specific information about an error that has occurred */
static void pdo_firebird_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval *info) /* {{{ */
Expand Down Expand Up @@ -1383,11 +1327,7 @@ static const struct pdo_dbh_methods firebird_methods = { /* {{{ */
NULL, /* last_id not supported */
pdo_firebird_fetch_error_func,
pdo_firebird_get_attribute,
#if FB_API_VER >= 30
pdo_firebird_check_liveness,
#else
NULL,
#endif
NULL, /* get driver methods */
NULL, /* request shutdown */
pdo_firebird_in_manually_transaction,
Expand Down Expand Up @@ -1437,9 +1377,21 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
}

do {
#if FB_API_VER >= 40
zend_string *session_timezone = pdo_attr_strval(driver_options, PDO_FB_SESSION_TIMEZONE, NULL);
#endif

static char const dpb_flags[] = {
isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name };
char const *dpb_values[] = { dbh->username, dbh->password, vars[1].optval, vars[2].optval };
isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name
#if FB_API_VER >= 40
, isc_dpb_session_time_zone
#endif
};
char const *dpb_values[] = { dbh->username, dbh->password, vars[1].optval, vars[2].optval
#if FB_API_VER >= 40
, session_timezone ? ZSTR_VAL(session_timezone) : NULL
#endif
};
char dpb_buffer[256] = { isc_dpb_version1 }, *dpb;

dpb = dpb_buffer + 1;
Expand All @@ -1454,6 +1406,12 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
}
}

#if FB_API_VER >= 40
if (session_timezone) {
zend_string_release_ex(session_timezone, 0);
}
#endif

H->sql_dialect = PDO_FB_DIALECT;
if (vars[3].optval) {
H->sql_dialect = atoi(vars[3].optval);
Expand Down
16 changes: 9 additions & 7 deletions ext/pdo_firebird/firebird_statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,11 +349,11 @@ static int pdo_firebird_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno,
#endif
param_type = PDO_PARAM_INT;
break;
#ifdef SQL_BOOLEAN

case SQL_BOOLEAN:
param_type = PDO_PARAM_BOOL;
break;
#endif

default:
param_type = PDO_PARAM_STR;
break;
Expand Down Expand Up @@ -542,11 +542,9 @@ static int pdo_firebird_stmt_get_col(
/* TODO: Why is this not returned as the native type? */
ZVAL_STR(result, zend_strpprintf_unchecked(0, "%.16H", php_get_double_from_sqldata(var->sqldata)));
break;
#ifdef SQL_BOOLEAN
case SQL_BOOLEAN:
ZVAL_BOOL(result, *(FB_BOOLEAN*)var->sqldata);
break;
#endif
case SQL_TYPE_DATE:
isc_decode_sql_date((ISC_DATE*)var->sqldata, &t);
fmt = S->H->date_format ? S->H->date_format : PDO_FB_DEF_DATE_FMT;
Expand Down Expand Up @@ -744,7 +742,6 @@ static int pdo_firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param
}
}

#ifdef SQL_BOOLEAN
/* keep native BOOLEAN type */
if ((var->sqltype & ~1) == SQL_BOOLEAN) {
switch (Z_TYPE_P(parameter)) {
Expand Down Expand Up @@ -797,8 +794,6 @@ static int pdo_firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param
}
break;
}
#endif


/* check if a NULL should be inserted */
switch (Z_TYPE_P(parameter)) {
Expand Down Expand Up @@ -829,6 +824,13 @@ static int pdo_firebird_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param
case SQL_TIMESTAMP:
case SQL_TYPE_DATE:
case SQL_TYPE_TIME:
#if FB_API_VER >= 40
case SQL_INT128:
case SQL_DEC16:
case SQL_DEC34:
case SQL_TIMESTAMP_TZ:
case SQL_TIME_TZ:
#endif
force_null = (Z_STRLEN_P(parameter) == 0);
}
if (!force_null) {
Expand Down
3 changes: 3 additions & 0 deletions ext/pdo_firebird/pdo_firebird.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class Firebird extends \PDO

/** @cvalue PDO_FB_WRITABLE_TRANSACTION */
public const int WRITABLE_TRANSACTION = UNKNOWN;

/** @cvalue PDO_FB_SESSION_TIMEZONE */
public const int SESSION_TIMEZONE = UNKNOWN;

public static function getApiVersion(): int {}
}
8 changes: 7 additions & 1 deletion ext/pdo_firebird/pdo_firebird_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions ext/pdo_firebird/php_pdo_firebird_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ enum {

/* transaction access mode */
PDO_FB_WRITABLE_TRANSACTION,

/* session time zone */
PDO_FB_SESSION_TIMEZONE,
};

#endif /* PHP_PDO_FIREBIRD_INT_H */
33 changes: 14 additions & 19 deletions ext/pdo_firebird/tests/fb4_datatypes.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,10 @@ pdo_firebird
--SKIPIF--
<?php require('skipif.inc');
if (Pdo\Firebird::getApiVersion() < 40) {
die('skip: Firebird API version must be greater than or equal to 40');
die('skip: Firebird API version must be greater than or equal to 40');
}
require 'testdb.inc';
$dbh = getDbConnection();
$stmt = $dbh->query("SELECT RDB\$get_context('SYSTEM', 'ENGINE_VERSION') AS VERSION FROM RDB\$DATABASE");
$data = $stmt->fetch(\PDO::FETCH_ASSOC);
if (!$data || !array_key_exists('VERSION', $data) || version_compare($data['VERSION'], '4.0.0') < 0) {
die("skip Firebird Server version must be greater than or equal to 4.0.0");
}
checkMinServerVersion('4.0.0');
?>
--XLEAK--
A bug in firebird causes a memory leak when calling `isc_attach_database()`.
Expand All @@ -23,18 +18,18 @@ See https://github.com/FirebirdSQL/firebird/issues/7849
require 'testdb.inc';

$sql = <<<'SQL'
SELECT
CAST(15 AS BIGINT) AS i64,
CAST(15 AS INT128) AS i128,
123.97 AS N,
CAST(123.97 AS NUMERIC(38,2)) AS N2,
CAST('2024-05-04 12:59:34.239' AS TIMESTAMP) AS TS,
CAST('2024-05-04 12:59:34.239 Europe/Moscow' AS TIMESTAMP WITH TIME ZONE) AS TS_TZ,
CAST('12:59:34.239' AS TIME) AS T,
CAST('12:59:34.239 Europe/Moscow' AS TIME WITH TIME ZONE) AS T_TZ,
CAST(1.128 AS DECFLOAT(16)) AS df16,
CAST(1.128 AS DECFLOAT(34)) AS df34
FROM RDB$DATABASE
SELECT
CAST(15 AS BIGINT) AS i64,
CAST(15 AS INT128) AS i128,
123.97 AS N,
CAST(123.97 AS NUMERIC(38,2)) AS N2,
CAST('2024-05-04 12:59:34.239' AS TIMESTAMP) AS TS,
CAST('2024-05-04 12:59:34.239 Europe/Moscow' AS TIMESTAMP WITH TIME ZONE) AS TS_TZ,
CAST('12:59:34.239' AS TIME) AS T,
CAST('12:59:34.239 Europe/Moscow' AS TIME WITH TIME ZONE) AS T_TZ,
CAST(1.128 AS DECFLOAT(16)) AS df16,
CAST(1.128 AS DECFLOAT(34)) AS df34
FROM RDB$DATABASE
SQL;

$dbh = getDbConnection();
Expand Down
27 changes: 11 additions & 16 deletions ext/pdo_firebird/tests/fb4_datatypes_params.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,10 @@ pdo_firebird
--SKIPIF--
<?php require('skipif.inc');
if (Pdo\Firebird::getApiVersion() < 40) {
die('skip: Firebird API version must be greater than or equal to 40');
die('skip: Firebird API version must be greater than or equal to 40');
}
require 'testdb.inc';
$dbh = getDbConnection();
$stmt = $dbh->query("SELECT RDB\$get_context('SYSTEM', 'ENGINE_VERSION') AS VERSION FROM RDB\$DATABASE");
$data = $stmt->fetch(\PDO::FETCH_ASSOC);
if (!$data || !array_key_exists('VERSION', $data) || version_compare($data['VERSION'], '4.0.0') < 0) {
die("skip Firebird Server version must be greater than or equal to 4.0.0");
}
checkMinServerVersion('4.0.0');
?>
--XLEAK--
A bug in firebird causes a memory leak when calling `isc_attach_database()`.
Expand All @@ -23,15 +18,15 @@ See https://github.com/FirebirdSQL/firebird/issues/7849
require 'testdb.inc';

$sql = <<<'SQL'
SELECT
CAST(? AS INT128) AS i128,
CAST(? AS NUMERIC(18,2)) AS N1,
CAST(? AS NUMERIC(38,2)) AS N2,
CAST(? AS TIMESTAMP WITH TIME ZONE) AS TS_TZ,
CAST(? AS TIME WITH TIME ZONE) AS T_TZ,
CAST(? AS DECFLOAT(16)) AS df16,
CAST(? AS DECFLOAT(34)) AS df34
FROM RDB$DATABASE
SELECT
CAST(? AS INT128) AS i128,
CAST(? AS NUMERIC(18,2)) AS N1,
CAST(? AS NUMERIC(38,2)) AS N2,
CAST(? AS TIMESTAMP WITH TIME ZONE) AS TS_TZ,
CAST(? AS TIME WITH TIME ZONE) AS T_TZ,
CAST(? AS DECFLOAT(16)) AS df16,
CAST(? AS DECFLOAT(34)) AS df34
FROM RDB$DATABASE
SQL;

$dbh = getDbConnection();
Expand Down
50 changes: 50 additions & 0 deletions ext/pdo_firebird/tests/fb4_session_timezone.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
PDO_Firebird: Firebird 4.0 set session time zone
--EXTENSIONS--
pdo_firebird
--SKIPIF--
<?php require('skipif.inc');
if (Pdo\Firebird::getApiVersion() < 40) {
die('skip: Firebird API version must be greater than or equal to 40');
}
require 'testdb.inc';
checkMinServerVersion('4.0.0');
?>
--XLEAK--
A bug in firebird causes a memory leak when calling `isc_attach_database()`.
See https://github.com/FirebirdSQL/firebird/issues/7849
--FILE--
<?php
require 'testdb.inc';

$sql = <<<'SQL'
SELECT
rdb$get_context('SYSTEM', 'SESSION_TIMEZONE') as tz,
trim(replace(current_timestamp, localtimestamp, '')) as tz1,
trim(replace(current_time, localtime, '')) as tz2
FROM rdb$database
SQL;

$dbh = connectToDb([
Pdo\Firebird::SESSION_TIMEZONE => 'Europe/Rome',
]);

$stmt = $dbh->prepare($sql);
$stmt->execute();
$data = $stmt->fetch(\PDO::FETCH_ASSOC);
$stmt->closeCursor();

var_dump($data);
echo "\ndone\n";
?>
--EXPECT--
array(3) {
["TZ"]=>
string(11) "Europe/Rome"
["TZ1"]=>
string(11) "Europe/Rome"
["TZ2"]=>
string(11) "Europe/Rome"
}

done
Loading
Loading