From 950de97fca7784bf3d3e3ceee5590dc545ab6db3 Mon Sep 17 00:00:00 2001 From: Simonov Denis Date: Tue, 9 Jul 2024 16:58:02 +0300 Subject: [PATCH 1/6] PDO_Firebird: Supported Firebird 4.0 datatypes --- ext/pdo_firebird/firebird_driver.c | 55 +++++++++++++++++++ ext/pdo_firebird/tests/fb4_datatypes.phpt | 52 ++++++++++++++++++ .../tests/fb4_datatypes_params.phpt | 44 +++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 ext/pdo_firebird/tests/fb4_datatypes.phpt create mode 100644 ext/pdo_firebird/tests/fb4_datatypes_params.phpt diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 97d0539f1edbc..f0e6ad7a8829a 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -460,6 +460,51 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa return 1; } +#if FB_API_VER >= 40 +/* set coercing a data type */ +static void set_coercing_data_type(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. In any case, at least the first three data types */ + /* can only be mapped to strings. The last two too, but for them it is potentially possible to set */ + /* the display format, as is done for TIMESTAMP. This function allows you to ensure minimal performance */ + /* of queries if they contain columns and 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; + } + } +} +/* }}} */ +#endif + /* map driver specific error message to PDO error */ void php_firebird_set_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *state, const size_t state_len, const char *msg, const size_t msg_len) /* {{{ */ @@ -605,6 +650,11 @@ static bool firebird_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, /* {{{ */ break; } +#if FB_API_VER >= 40 + /* set coercing a data type */ + set_coercing_data_type(&S->out_sqlda); +#endif + /* allocate the input descriptors */ if (isc_dsql_describe_bind(H->isc_status, &s, PDO_FB_SQLDA_VERSION, &num_sqlda)) { break; @@ -618,6 +668,11 @@ 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_data_type(S->in_sqlda); +#endif } stmt->driver_data = S; diff --git a/ext/pdo_firebird/tests/fb4_datatypes.phpt b/ext/pdo_firebird/tests/fb4_datatypes.phpt new file mode 100644 index 0000000000000..aa15288dd2dda --- /dev/null +++ b/ext/pdo_firebird/tests/fb4_datatypes.phpt @@ -0,0 +1,52 @@ +--TEST-- +PDO_Firebird: Supported Firebird 4.0 datatypes +--EXTENSIONS-- +pdo_firebird +--SKIPIF-- + +--XLEAK-- +A bug in firebird causes a memory leak when calling `isc_attach_database()`. +See https://github.com/FirebirdSQL/firebird/issues/7849 +--FILE-- +prepare($sql); +$stmt->execute(); +$data = $stmt->fetch(\PDO::FETCH_ASSOC); +$stmt->closeCursor(); +$str = json_encode($data, JSON_PRETTY_PRINT); +echo $str; +echo "\ndone\n"; +?> +--EXPECT-- +{ + "I64": 15, + "I128": "15", + "N": "123.97", + "N2": "123.97", + "TS": "2024-05-04 12:59:34", + "TS_TZ": "2024-05-04 12:59:34.2390 Europe\/Moscow", + "T": "12:59:34", + "T_TZ": "12:59:34.2390 Europe\/Moscow", + "DF16": "1.128", + "DF34": "1.128" +} +done diff --git a/ext/pdo_firebird/tests/fb4_datatypes_params.phpt b/ext/pdo_firebird/tests/fb4_datatypes_params.phpt new file mode 100644 index 0000000000000..af41099e00762 --- /dev/null +++ b/ext/pdo_firebird/tests/fb4_datatypes_params.phpt @@ -0,0 +1,44 @@ +--TEST-- +PDO_Firebird: Supported Firebird 4.0 datatypes (parameters) +--EXTENSIONS-- +pdo_firebird +--SKIPIF-- + +--XLEAK-- +A bug in firebird causes a memory leak when calling `isc_attach_database()`. +See https://github.com/FirebirdSQL/firebird/issues/7849 +--FILE-- +prepare($sql); +$stmt->execute([12, 12.34, '2024-05-04 12:59:34.239 Europe/Moscow', '12:59 Europe/Moscow', 12.34, 12.34]); +$data = $stmt->fetch(\PDO::FETCH_ASSOC); +$stmt->closeCursor(); +$str = json_encode($data, JSON_PRETTY_PRINT); +echo $str; +echo "\ndone\n"; +?> +--EXPECT-- +{ + "I128": "12", + "N2": "12.34", + "TS_TZ": "2024-05-04 12:59:34.2390 Europe\/Moscow", + "T_TZ": "12:59:00.0000 Europe\/Moscow", + "DF16": "12.34", + "DF34": "12.34" +} +done From 546af7860756d5dcdf6464452267e5e5e12a2265 Mon Sep 17 00:00:00 2001 From: Simonov Denis Date: Thu, 11 Jul 2024 09:25:33 +0300 Subject: [PATCH 2/6] tab indent --- ext/pdo_firebird/firebird_driver.c | 26 +++++++++---------- ext/pdo_firebird/tests/fb4_datatypes.phpt | 24 ++++++++--------- .../tests/fb4_datatypes_params.phpt | 16 ++++++------ 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index f0e6ad7a8829a..3e09e4dabf204 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -464,7 +464,7 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa /* set coercing a data type */ static void set_coercing_data_type(XSQLDA* sqlda) { - /* Data types introduced in Firebird 4.0 are difficult to process using the Firebird Legacy API. */ + /* 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. In any case, at least the first three data types */ /* can only be mapped to strings. The last two too, but for them it is potentially possible to set */ @@ -475,30 +475,30 @@ static void set_coercing_data_type(XSQLDA* sqlda) short nullable; XSQLVAR* var; for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { - dtype = (var->sqltype & ~1); /* drop flag bit */ + dtype = (var->sqltype & ~1); /* drop flag bit */ nullable = (var->sqltype & 1); switch(dtype) { case SQL_INT128: - var->sqltype = SQL_VARYING + nullable; + var->sqltype = SQL_VARYING + nullable; var->sqllen = 46; var->sqlscale = 0; - break; + break; case SQL_DEC16: var->sqltype = SQL_VARYING + nullable; var->sqllen = 24; - break; + break; case SQL_DEC34: var->sqltype = SQL_VARYING + nullable; var->sqllen = 43; - break; + break; case SQL_TIMESTAMP_TZ: - var->sqltype = SQL_VARYING + nullable; + var->sqltype = SQL_VARYING + nullable; var->sqllen = 58; - break; + break; case SQL_TIME_TZ: - var->sqltype = SQL_VARYING + nullable; + var->sqltype = SQL_VARYING + nullable; var->sqllen = 46; - break; + break; } } } @@ -651,7 +651,7 @@ static bool firebird_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, /* {{{ */ } #if FB_API_VER >= 40 - /* set coercing a data type */ + /* set coercing a data type */ set_coercing_data_type(&S->out_sqlda); #endif @@ -671,8 +671,8 @@ static bool firebird_handle_preparer(pdo_dbh_t *dbh, zend_string *sql, /* {{{ */ #if FB_API_VER >= 40 /* set coercing a data type */ - set_coercing_data_type(S->in_sqlda); -#endif + set_coercing_data_type(S->in_sqlda); +#endif } stmt->driver_data = S; diff --git a/ext/pdo_firebird/tests/fb4_datatypes.phpt b/ext/pdo_firebird/tests/fb4_datatypes.phpt index aa15288dd2dda..bf0d77c813720 100644 --- a/ext/pdo_firebird/tests/fb4_datatypes.phpt +++ b/ext/pdo_firebird/tests/fb4_datatypes.phpt @@ -12,18 +12,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(); diff --git a/ext/pdo_firebird/tests/fb4_datatypes_params.phpt b/ext/pdo_firebird/tests/fb4_datatypes_params.phpt index af41099e00762..ffdd6034cd246 100644 --- a/ext/pdo_firebird/tests/fb4_datatypes_params.phpt +++ b/ext/pdo_firebird/tests/fb4_datatypes_params.phpt @@ -12,14 +12,14 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 require 'testdb.inc'; $sql = <<<'SQL' - SELECT - CAST(? AS INT128) AS i128, - 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(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(); From af79543b0dc91a34b6c710b7d12288a4a6868553 Mon Sep 17 00:00:00 2001 From: Simonov Denis Date: Thu, 11 Jul 2024 09:37:18 +0300 Subject: [PATCH 3/6] add EMPTY_SWITCH_DEFAULT_CASE() --- ext/pdo_firebird/firebird_driver.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 3e09e4dabf204..f440d787d6548 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -483,26 +483,31 @@ static void set_coercing_data_type(XSQLDA* sqlda) 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; + + EMPTY_SWITCH_DEFAULT_CASE() } } } -/* }}} */ #endif /* map driver specific error message to PDO error */ From 0e056cbf6f85b8ec19063413921bc64927708048 Mon Sep 17 00:00:00 2001 From: Simonov Denis Date: Wed, 17 Jul 2024 22:15:27 +0300 Subject: [PATCH 4/6] skip if firebird api version < 40 --- ext/pdo_firebird/firebird_driver.c | 4 ++-- ext/pdo_firebird/tests/fb4_datatypes.phpt | 6 +++++- ext/pdo_firebird/tests/fb4_datatypes_params.phpt | 8 +++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 125b0e889b756..d17201e915296 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -503,8 +503,8 @@ static void set_coercing_data_type(XSQLDA* sqlda) var->sqltype = SQL_VARYING + nullable; var->sqllen = 46; break; - - EMPTY_SWITCH_DEFAULT_CASE() + default: + break; } } } diff --git a/ext/pdo_firebird/tests/fb4_datatypes.phpt b/ext/pdo_firebird/tests/fb4_datatypes.phpt index bf0d77c813720..f3c79d29687db 100644 --- a/ext/pdo_firebird/tests/fb4_datatypes.phpt +++ b/ext/pdo_firebird/tests/fb4_datatypes.phpt @@ -12,7 +12,7 @@ See https://github.com/FirebirdSQL/firebird/issues/7849 require 'testdb.inc'; $sql = <<<'SQL' - SELECT + SELECT CAST(15 AS BIGINT) AS i64, CAST(15 AS INT128) AS i128, 123.97 AS N, @@ -28,6 +28,10 @@ SQL; $dbh = getDbConnection(); +if ($dbh->getAttribute(Pdo\Firebird::ATTR_API_VERSION) < 40) { + die('skip: Firebird API version must be greater than or equal to 40'); +} + $stmt = $dbh->prepare($sql); $stmt->execute(); $data = $stmt->fetch(\PDO::FETCH_ASSOC); diff --git a/ext/pdo_firebird/tests/fb4_datatypes_params.phpt b/ext/pdo_firebird/tests/fb4_datatypes_params.phpt index ffdd6034cd246..dd31408c47d7b 100644 --- a/ext/pdo_firebird/tests/fb4_datatypes_params.phpt +++ b/ext/pdo_firebird/tests/fb4_datatypes_params.phpt @@ -14,6 +14,7 @@ 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, @@ -24,8 +25,12 @@ SQL; $dbh = getDbConnection(); +if ($dbh->getAttribute(Pdo\Firebird::ATTR_API_VERSION) < 40) { + die('skip: Firebird API version must be greater than or equal to 40'); +} + $stmt = $dbh->prepare($sql); -$stmt->execute([12, 12.34, '2024-05-04 12:59:34.239 Europe/Moscow', '12:59 Europe/Moscow', 12.34, 12.34]); +$stmt->execute([12, 12.34, 12.34, '2024-05-04 12:59:34.239 Europe/Moscow', '12:59 Europe/Moscow', 12.34, 12.34]); $data = $stmt->fetch(\PDO::FETCH_ASSOC); $stmt->closeCursor(); $str = json_encode($data, JSON_PRETTY_PRINT); @@ -35,6 +40,7 @@ echo "\ndone\n"; --EXPECT-- { "I128": "12", + "N1": "12.34", "N2": "12.34", "TS_TZ": "2024-05-04 12:59:34.2390 Europe\/Moscow", "T_TZ": "12:59:00.0000 Europe\/Moscow", From 4b71218fbd2f74685e55b664d9bce11a4a310102 Mon Sep 17 00:00:00 2001 From: Simonov Denis Date: Thu, 18 Jul 2024 08:43:30 +0300 Subject: [PATCH 5/6] usage Pdo\Firebird::getApiVersion() in tests --- ext/pdo_firebird/tests/fb4_datatypes.phpt | 10 +++++----- ext/pdo_firebird/tests/fb4_datatypes_params.phpt | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/pdo_firebird/tests/fb4_datatypes.phpt b/ext/pdo_firebird/tests/fb4_datatypes.phpt index f3c79d29687db..aaca8d29af7c9 100644 --- a/ext/pdo_firebird/tests/fb4_datatypes.phpt +++ b/ext/pdo_firebird/tests/fb4_datatypes.phpt @@ -3,7 +3,11 @@ PDO_Firebird: Supported Firebird 4.0 datatypes --EXTENSIONS-- pdo_firebird --SKIPIF-- - + --XLEAK-- A bug in firebird causes a memory leak when calling `isc_attach_database()`. See https://github.com/FirebirdSQL/firebird/issues/7849 @@ -28,10 +32,6 @@ SQL; $dbh = getDbConnection(); -if ($dbh->getAttribute(Pdo\Firebird::ATTR_API_VERSION) < 40) { - die('skip: Firebird API version must be greater than or equal to 40'); -} - $stmt = $dbh->prepare($sql); $stmt->execute(); $data = $stmt->fetch(\PDO::FETCH_ASSOC); diff --git a/ext/pdo_firebird/tests/fb4_datatypes_params.phpt b/ext/pdo_firebird/tests/fb4_datatypes_params.phpt index dd31408c47d7b..da2806cda29f3 100644 --- a/ext/pdo_firebird/tests/fb4_datatypes_params.phpt +++ b/ext/pdo_firebird/tests/fb4_datatypes_params.phpt @@ -3,7 +3,11 @@ PDO_Firebird: Supported Firebird 4.0 datatypes (parameters) --EXTENSIONS-- pdo_firebird --SKIPIF-- - + --XLEAK-- A bug in firebird causes a memory leak when calling `isc_attach_database()`. See https://github.com/FirebirdSQL/firebird/issues/7849 @@ -25,10 +29,6 @@ SQL; $dbh = getDbConnection(); -if ($dbh->getAttribute(Pdo\Firebird::ATTR_API_VERSION) < 40) { - die('skip: Firebird API version must be greater than or equal to 40'); -} - $stmt = $dbh->prepare($sql); $stmt->execute([12, 12.34, 12.34, '2024-05-04 12:59:34.239 Europe/Moscow', '12:59 Europe/Moscow', 12.34, 12.34]); $data = $stmt->fetch(\PDO::FETCH_ASSOC); From af31c84d5f72c7136d9a18fe257cf65fe8a4a925 Mon Sep 17 00:00:00 2001 From: Simonov Denis Date: Mon, 22 Jul 2024 19:18:30 +0300 Subject: [PATCH 6/6] Tests fixed. There is currently no way to set the output format for time zone types, so the %s substitution is used in the expected output. --- ext/pdo_firebird/tests/fb4_datatypes.phpt | 4 ++-- ext/pdo_firebird/tests/fb4_datatypes_params.phpt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/pdo_firebird/tests/fb4_datatypes.phpt b/ext/pdo_firebird/tests/fb4_datatypes.phpt index aaca8d29af7c9..3ae452d5eb4e4 100644 --- a/ext/pdo_firebird/tests/fb4_datatypes.phpt +++ b/ext/pdo_firebird/tests/fb4_datatypes.phpt @@ -40,14 +40,14 @@ $str = json_encode($data, JSON_PRETTY_PRINT); echo $str; echo "\ndone\n"; ?> ---EXPECT-- +--EXPECTF-- { "I64": 15, "I128": "15", "N": "123.97", "N2": "123.97", "TS": "2024-05-04 12:59:34", - "TS_TZ": "2024-05-04 12:59:34.2390 Europe\/Moscow", + "TS_TZ": "%s 12:59:34.2390 Europe\/Moscow", "T": "12:59:34", "T_TZ": "12:59:34.2390 Europe\/Moscow", "DF16": "1.128", diff --git a/ext/pdo_firebird/tests/fb4_datatypes_params.phpt b/ext/pdo_firebird/tests/fb4_datatypes_params.phpt index da2806cda29f3..f1b4827b9e724 100644 --- a/ext/pdo_firebird/tests/fb4_datatypes_params.phpt +++ b/ext/pdo_firebird/tests/fb4_datatypes_params.phpt @@ -37,12 +37,12 @@ $str = json_encode($data, JSON_PRETTY_PRINT); echo $str; echo "\ndone\n"; ?> ---EXPECT-- +--EXPECTF-- { "I128": "12", "N1": "12.34", "N2": "12.34", - "TS_TZ": "2024-05-04 12:59:34.2390 Europe\/Moscow", + "TS_TZ": "%s 12:59:34.2390 Europe\/Moscow", "T_TZ": "12:59:00.0000 Europe\/Moscow", "DF16": "12.34", "DF34": "12.34"