Skip to content

Commit

Permalink
PDO_Firebird: Supported Firebird 4.0 datatypes (php#14897)
Browse files Browse the repository at this point in the history
Five new data types are now available: INT128, DEC16, DEC34, TIMESTAMP_TZ, TIME_TZ.
These are available starting with Firebird 4.0.

closes php#14897
  • Loading branch information
sim1984 authored and SakiTakamachi committed Jul 23, 2024
1 parent d4ef8b9 commit 00e4588
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 0 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ PHP NEWS
. Added Pdo\Firebird::ATTR_API_VERSION. (SakiTakamachi)
. Added getApiVersion() and removed from getAttribute().
(SakiTakamachi)
. Supported Firebird 4.0 datatypes. (sim1984)

- PGSQL:
. pg_convert/pg_insert/pg_update/pg_delete ; regexes are now cached.
Expand Down
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,8 @@ PHP 8.4 UPGRADE NOTES
. The content that is built changes depending on the value of FB_API_VER in
ibase.h, so added static method Pdo\Firebird::getApiVersion() to obtain that
value. This value can also be referenced from phpinfo.
. Five new data types are now available: INT128, DEC16, DEC34, TIMESTAMP_TZ, TIME_TZ.
These are available starting with Firebird 4.0.

- PDO_MYSQL:
. getAttribute, enabled to get the value of ATTR_FETCH_TABLE_NAMES.
Expand Down
60 changes: 60 additions & 0 deletions ext/pdo_firebird/firebird_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,56 @@ 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;
default:
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) /* {{{ */
Expand Down Expand Up @@ -605,6 +655,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;
Expand All @@ -618,6 +673,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;
Expand Down
56 changes: 56 additions & 0 deletions ext/pdo_firebird/tests/fb4_datatypes.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
--TEST--
PDO_Firebird: Supported Firebird 4.0 datatypes
--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');
}
?>
--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
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();

$stmt = $dbh->prepare($sql);
$stmt->execute();
$data = $stmt->fetch(\PDO::FETCH_ASSOC);
$stmt->closeCursor();
$str = json_encode($data, JSON_PRETTY_PRINT);
echo $str;
echo "\ndone\n";
?>
--EXPECTF--
{
"I64": 15,
"I128": "15",
"N": "123.97",
"N2": "123.97",
"TS": "2024-05-04 12:59:34",
"TS_TZ": "%s 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
50 changes: 50 additions & 0 deletions ext/pdo_firebird/tests/fb4_datatypes_params.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
PDO_Firebird: Supported Firebird 4.0 datatypes (parameters)
--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');
}
?>
--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
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();

$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);
$stmt->closeCursor();
$str = json_encode($data, JSON_PRETTY_PRINT);
echo $str;
echo "\ndone\n";
?>
--EXPECTF--
{
"I128": "12",
"N1": "12.34",
"N2": "12.34",
"TS_TZ": "%s 12:59:34.2390 Europe\/Moscow",
"T_TZ": "12:59:00.0000 Europe\/Moscow",
"DF16": "12.34",
"DF34": "12.34"
}
done

0 comments on commit 00e4588

Please sign in to comment.