diff --git a/pdo_fbird/VERSION.txt b/pdo_fbird/VERSION.txt new file mode 100644 index 00000000..347caf39 --- /dev/null +++ b/pdo_fbird/VERSION.txt @@ -0,0 +1 @@ +13.0.2 diff --git a/pdo_fbird/config.m4 b/pdo_fbird/config.m4 index b330bbd3..5a2ed260 100644 --- a/pdo_fbird/config.m4 +++ b/pdo_fbird/config.m4 @@ -1,5 +1,6 @@ -dnl pdo_fbird — PDO driver for Firebird (fbird: DSN prefix) +dnl pdo_fbird - PDO driver for Firebird (fbird: DSN prefix) dnl Separate shared extension depending on the firebird extension. +dnl Self-contained: shared headers from parent are bundled in this package. PHP_ARG_WITH([pdo-fbird], [for Firebird PDO support (fbird: DSN)], @@ -16,11 +17,9 @@ if test "$PHP_PDO_FBIRD" != "no"; then AC_DEFINE(HAVE_PDO_FBIRD,1,[Whether pdo_fbird is available]) - dnl Version: read from VERSION.txt (same as main extension) + dnl Version: read from VERSION.txt (bundled in this package) AC_MSG_CHECKING([for PDO fbird version]) - if test -f "$srcdir/../VERSION.txt"; then - PHP_PDO_FBIRD_VERSION=`cat "$srcdir/../VERSION.txt" | tr -d ' \n\r\t'` - elif test -f "$srcdir/VERSION.txt"; then + if test -f "$srcdir/VERSION.txt"; then PHP_PDO_FBIRD_VERSION=`cat "$srcdir/VERSION.txt" | tr -d ' \n\r\t'` else PHP_PDO_FBIRD_VERSION="0.0.0-unknown" @@ -73,9 +72,6 @@ if test "$PHP_PDO_FBIRD" != "no"; then PHP_ADD_INCLUDE($FIREBIRD_INCDIR) PHP_ADD_LIBRARY_WITH_PATH(fbclient, $FIREBIRD_LIBDIR, PDO_FBIRD_SHARED_LIBADD) - dnl Also include the parent extension headers - PHP_ADD_INCLUDE([$srcdir/..]) - PHP_NEW_EXTENSION(pdo_fbird, pdo_fbird.c \ pdo_fbird_driver.c \ diff --git a/pdo_fbird/fbird_classes.h b/pdo_fbird/fbird_classes.h new file mode 100644 index 00000000..712537f4 --- /dev/null +++ b/pdo_fbird/fbird_classes.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: PHP-3.01 + * SPDX-FileCopyrightText: The PHP Group and contributors (see CREDITS) */ + +#ifndef FBIRD_CLASSES_H +#define FBIRD_CLASSES_H + +#include "php.h" +#include "php_fbird_includes.h" + +extern zend_class_entry *fbird_connection_ce; +extern zend_class_entry *fbird_transaction_ce; +extern zend_class_entry *fbird_statement_ce; +extern zend_class_entry *fbird_resultset_ce; +extern zend_class_entry *fbird_blob_ce; +extern zend_class_entry *fbird_service_ce; +extern zend_class_entry *fbird_event_ce; +extern zend_class_entry *fbird_batch_ce; + +#if FB_API_VER >= 40 +extern zend_class_entry *fbird_decfloat_ce; +void fbird_register_decfloat_class(void); +void fbird_setup_decfloat_object(zval *return_value, unsigned char precision, + const void *raw_bytes, unsigned char byte_len); +#else +static zend_class_entry *fbird_decfloat_ce = NULL; +#endif + +void fbird_register_classes(void); +void fbird_setup_connection_object(zval *return_value, zend_resource *res); +void fbird_setup_service_object(zval *return_value, zend_resource *res); +void fbird_setup_event_object(zval *rv, fbird_event *ev); +#if FB_API_VER >= 40 +void fbird_setup_batch_object(zval *rv, fbird_batch *batch); +#endif + +/** + * Extract the zend_resource* from a Firebird\Connection object. + * Returns NULL if obj is not a Firebird\Connection or has no resource. + */ +zend_resource *fbird_connection_get_resource(zend_object *obj); + +/** + * Extract the zend_resource* from a Firebird\Service object. + * Returns NULL if obj is not a Firebird\Service or has no resource. + */ +zend_resource *fbird_service_get_resource(zend_object *obj); + +/** + * Extract the zend_resource* from a Firebird\Transaction object. + * Returns NULL if obj is not a Firebird\Transaction or has no resource. + */ +zend_resource *fbird_transaction_get_resource(zend_object *obj); + +/** + * Wrap a le_trans zend_resource* in a Firebird\Transaction object. + * Stores the resource as a weak reference (EG(regular_list) owns it). + */ +void fbird_setup_transaction_object(zval *return_value, zend_resource *res); + +/** + * Extract the zend_resource* from a Firebird\ResultSet object. + * Returns NULL if obj is not a Firebird\ResultSet or has no resource. + */ +zend_resource *fbird_resultset_get_resource(zend_object *obj); + +/** + * Extract the zend_resource* from a Firebird\Statement object. + * Returns NULL if obj is not a Firebird\Statement or has no resource. + */ +zend_resource *fbird_statement_get_resource(zend_object *obj); + +/** + * Wrap a le_query zend_resource* in a Firebird\ResultSet object. + * Stores the resource as a weak reference (EG(regular_list) owns it). + */ +void fbird_setup_resultset_object(zval *return_value, zend_resource *res); + +/** + * Wrap a le_query zend_resource* in a Firebird\Statement object. + * Same weak-reference pattern as fbird_setup_resultset_object(). + */ +void fbird_setup_statement_object(zval *return_value, zend_resource *res); + +/** + * Extract the zend_resource* from a Firebird\Blob object (M3 Phase G bridge). + * Returns NULL if no resource is set (OOP-native path). + */ +zend_resource *fbird_blob_get_resource(zend_object *obj); + +/** + * Wrap a le_blob zend_resource* in a Firebird\Blob object. + * Stores the resource as a weak reference (EG(regular_list) owns it). + */ +void fbird_setup_blob_object(zval *return_value, zend_resource *res); + +/** + * Extract the fbird_event* from a Firebird\Event object. + * Returns NULL if obj is not a Firebird\Event. + */ +fbird_event *fbird_event_get_ptr(zend_object *obj); + +#if FB_API_VER >= 40 +/** + * Extract the fbird_batch* from a Firebird\Batch object. + * Returns NULL if obj is not a Firebird\Batch. + */ +fbird_batch *fbird_batch_get_ptr(zend_object *obj); +#endif + +#endif /* FBIRD_CLASSES_H */ diff --git a/pdo_fbird/fbird_service_types.h b/pdo_fbird/fbird_service_types.h new file mode 100644 index 00000000..87902b7d --- /dev/null +++ b/pdo_fbird/fbird_service_types.h @@ -0,0 +1,23 @@ +/* fbird_service_types.h - Unified service struct for procedural + OOP paths. + * Included by php_fbird_includes.h (procedural) and fbird_class_internal.h (OOP). + * Eliminates the dual-struct cast pattern (OC-11). */ +#ifndef FBIRD_SERVICE_TYPES_H +#define FBIRD_SERVICE_TYPES_H + +#include "php.h" +#include "zend.h" + +typedef struct fbird_service_s { + char *hostname; + char *username; + zend_resource *res; /* resource handle (procedural path) or reference (OOP wrap) */ + void *fbsvc; /* OO API ServiceWrapper* (fbsvc_attach) */ + zend_object std; /* must be last for zend_object_alloc (OOP path only) */ +} fbird_service; + +static inline fbird_service *fbird_service_from_obj(zend_object *obj) { + return (fbird_service *)((char *)obj - XtOffsetOf(fbird_service, std)); +} +#define Z_FBIRD_SERVICE_P(zv) fbird_service_from_obj(Z_OBJ_P(zv)) + +#endif /* FBIRD_SERVICE_TYPES_H */ diff --git a/pdo_fbird/firebird_utils.h b/pdo_fbird/firebird_utils.h new file mode 100755 index 00000000..6e4f25a5 --- /dev/null +++ b/pdo_fbird/firebird_utils.h @@ -0,0 +1,1667 @@ +/* SPDX-License-Identifier: PHP-3.01 + * SPDX-FileCopyrightText: The PHP Group and contributors (see CREDITS) */ + +#ifndef FIREBIRD_UTILS_H +#define FIREBIRD_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Firebird 3.0+ OO API required; FB_API_VER >= 30 enforced at configure time via config.m4. */ + +#include +#include "php_fbird_includes.h" + +unsigned fbu_get_client_version(void *master_ptr); +ISC_TIME fbu_encode_time(void *master_ptr, unsigned hours, unsigned minutes, + unsigned seconds, unsigned fractions); +ISC_DATE fbu_encode_date(void *master_ptr, unsigned year, unsigned month, unsigned day); + +/** + * Extract SQLCODE from a Firebird status vector. + * Wraps isc_sqlcode() to isolate the legacy isc_* call in firebird_utils.cpp. + * + * @param status_vector ISC_STATUS array (local status[256]) + * @return SQLCODE value (negative for errors, 0 for success) + */ +long fbu_sqlcode(const ISC_STATUS *status_vector); + +/* Type Encoding/Decoding Functions (OO API via IUtil interface) */ + +/** + * Encode timestamp from components using OO API. + * + * @param master_ptr IMaster interface pointer + * @param year Year (1-9999) + * @param month Month (1-12) + * @param day Day (1-31) + * @param hours Hours (0-23) + * @param minutes Minutes (0-59) + * @param seconds Seconds (0-59) + * @param fractions Fractions of second (0-9999, tenths of milliseconds) + * @return Encoded ISC_TIMESTAMP, or {0,0} on error + */ +ISC_TIMESTAMP fbu_encode_timestamp(void *master_ptr, unsigned year, unsigned month, unsigned day, + unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions); + +/** + * Decode ISC_TIME to time components using OO API. + * Replaces legacy isc_decode_sql_time(). + * + * @param master_ptr IMaster interface pointer + * @param time ISC_TIME value to decode + * @param hours Output: hours (0-23) + * @param minutes Output: minutes (0-59) + * @param seconds Output: seconds (0-59) + * @param fractions Output: fractions of second (0-9999) + */ +void fbu_decode_time(void *master_ptr, ISC_TIME time, + unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions); + +/** + * Decode ISC_DATE to date components using OO API. + * Replaces legacy isc_decode_sql_date(). + * + * @param master_ptr IMaster interface pointer + * @param date ISC_DATE value to decode + * @param year Output: year (1-9999) + * @param month Output: month (1-12) + * @param day Output: day (1-31) + */ +void fbu_decode_date(void *master_ptr, ISC_DATE date, + unsigned* year, unsigned* month, unsigned* day); + +/** + * Decode ISC_TIMESTAMP to date and time components using OO API. + * Replaces legacy isc_decode_timestamp(). + * + * @param master_ptr IMaster interface pointer + * @param timestamp ISC_TIMESTAMP value to decode + * @param year Output: year (1-9999) + * @param month Output: month (1-12) + * @param day Output: day (1-31) + * @param hours Output: hours (0-23) + * @param minutes Output: minutes (0-59) + * @param seconds Output: seconds (0-59) + * @param fractions Output: fractions of second (0-9999) + */ +void fbu_decode_timestamp(void *master_ptr, const ISC_TIMESTAMP* timestamp, + unsigned* year, unsigned* month, unsigned* day, + unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions); + +/* Firebird OO API Connection Functions */ + +/** + * Create a database connection using the Firebird OO API. + * + * @param master_ptr Pointer to IMaster interface (from FBG(master_instance)) + * @param database Database path (null-terminated) + * @param db_len Length of database path + * @param user Username (null-terminated, may be NULL) + * @param user_len Length of username + * @param password Password (null-terminated, may be NULL) + * @param password_len Length of password + * @param charset Character set (null-terminated, may be NULL) + * @param charset_len Length of charset + * @param role SQL role (null-terminated, may be NULL) + * @param role_len Length of role + * @param num_buffers Number of page buffers (0 for default) + * @param dialect SQL dialect (1, 2, or 3) + * @param force_write Force write flag (-1 = not set, 0 = async, 1 = sync) + * @param status_vector Output status vector for errors + * @return Pointer to connection object, or NULL on failure + */ +void* fbc_connect( + void* master_ptr, + const char* database, size_t db_len, + const char* user, size_t user_len, + const char* password, size_t password_len, + const char* charset, size_t charset_len, + const char* role, size_t role_len, + int num_buffers, + int dialect, + int force_write, + ISC_STATUS* status_vector +); + +/** + * Extended connect with FB 5.0+ parallel workers support. + * Same as fbc_connect() but adds parallel_workers parameter. + * parallel_workers = 0 means server default. + */ +void* fbc_connect_ex( + void* master_ptr, + const char* database, size_t db_len, + const char* user, size_t user_len, + const char* password, size_t password_len, + const char* charset, size_t charset_len, + const char* role, size_t role_len, + int num_buffers, + int dialect, + int force_write, + int parallel_workers, + ISC_STATUS* status_vector +); + +/** + * Detach a connection created with fbc_connect(). + * + * @param connection Pointer returned by fbc_connect() + * @param status_vector Output status vector for errors + * @return 0 on success, non-zero on failure + */ +int fbc_disconnect(void* connection, ISC_STATUS* status_vector); + +/** + * Drop a database (destructive operation). + * + * @param connection Pointer returned by fbc_connect() + * @param status_vector Output status vector for errors + * @return 0 on success, non-zero on failure + */ +int fbc_drop_database(void* connection, ISC_STATUS* status_vector); + +/** + * Create a new database using CREATE DATABASE SQL statement. + * Uses OO API IUtil::executeCreateDatabase() internally. + * + * @param master_ptr Pointer to IMaster interface + * @param create_sql Complete CREATE DATABASE SQL statement + * @param dialect SQL dialect (typically 3) + * @param status_vector Output status vector for errors + * @return Pointer to fb::Connection object for the new database, or NULL on failure + */ +void* fbc_create_database( + void* master_ptr, + const char* create_sql, + unsigned dialect, + ISC_STATUS* status_vector +); + +/** + * Check if a connection is valid. + * + * @param connection Pointer returned by fbc_connect() + * @return 1 if connected, 0 if not + */ +int fbc_is_connected(void* connection); + +/** + * Ping the server to verify the connection is alive. + * Executes a lightweight query (SELECT 1 FROM RDB$DATABASE) to confirm + * the server is reachable and the connection is valid. + * + * @param master_ptr IMaster interface pointer + * @param connection Pointer returned by fbc_connect() + * @param status_vector Output status vector for errors + * @return 1 if alive, 0 if dead or error + */ +int fbc_ping(void* master_ptr, void* connection, ISC_STATUS* status_vector); + +/** + * Get the IAttachment pointer from a connection. + * + * @param connection Pointer returned by fbc_connect() + * @return Raw IAttachment pointer, or NULL + */ +void* fbc_get_attachment(void* connection); + +/** + * Get the server version from a connection. + * + * @param connection Pointer returned by fbc_connect() + * @return Version code (FB30=30, FB40=40, FB50=50), or 0 if invalid + */ +unsigned fbc_get_server_version(void* connection); + +#if FB_API_VER >= 40 +/** + * Set statement execution timeout (FB 4.0+). + * @param connection Pointer returned by fbc_connect() + * @param ms Timeout in milliseconds (0 = no timeout) + * @param status_vector Output status vector + * @return 0 on success, -1 on failure + */ +int fbc_set_statement_timeout(void* connection, unsigned int ms, ISC_STATUS* status_vector); + +/** + * Get statement execution timeout (FB 4.0+). + * @param connection Pointer returned by fbc_connect() + * @return Timeout in milliseconds (0 = no timeout) + */ +unsigned int fbc_get_statement_timeout(void* connection); + +/** + * Set connection idle timeout (FB 4.0+). + * @param connection Pointer returned by fbc_connect() + * @param sec Timeout in seconds (0 = no timeout) + * @param status_vector Output status vector + * @return 0 on success, -1 on failure + */ +int fbc_set_idle_timeout(void* connection, unsigned int sec, ISC_STATUS* status_vector); + +/** + * Get connection idle timeout (FB 4.0+). + * @param connection Pointer returned by fbc_connect() + * @return Timeout in seconds (0 = no timeout) + */ +unsigned int fbc_get_idle_timeout(void* connection); +#endif + +/** + * Retrieve database/connection information via the OO API. + * Wraps Firebird::IAttachment::getInfo(). + * + * @param master_ptr IMaster interface pointer + * @param attachment_ptr IAttachment pointer (from fbc_get_attachment()) + * @param items_length Info items length + * @param items Info items to request (isc_info_* constants) + * @param buffer_length Output buffer length + * @param buffer Output buffer + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbc_get_info( + void* master_ptr, + void* attachment_ptr, + unsigned items_length, + const unsigned char* items, + unsigned buffer_length, + unsigned char* buffer, + ISC_STATUS* status_vector +); + +/* Firebird OO API Transaction Functions */ + +/** + * Start a transaction using the OO API. + * + * @param master_ptr Pointer to IMaster interface + * @param attachment_ptr Pointer to IAttachment interface (from fbc_get_attachment()) + * @param tpb_len Length of TPB buffer + * @param tpb Transaction parameter buffer (may be NULL for defaults) + * @param status_vector Output status vector for errors + * @return Pointer to transaction object, or NULL on failure + */ +void* fbt_start( + void* master_ptr, + void* attachment_ptr, + unsigned tpb_len, + const unsigned char* tpb, + ISC_STATUS* status_vector +); + +/** + * Commit a transaction created with fbt_start(). + * + * @param transaction Pointer returned by fbt_start() + * @param status_vector Output status vector for errors + * @return 0 on success, non-zero on failure + */ +int fbt_commit(void* transaction, ISC_STATUS* status_vector); + +/** + * Rollback a transaction created with fbt_start(). + * + * @param transaction Pointer returned by fbt_start() + * @param status_vector Output status vector for errors + * @return 0 on success, non-zero on failure + */ +int fbt_rollback(void* transaction, ISC_STATUS* status_vector); + +/** + * Commit with retaining (keeps transaction context). + * + * @param transaction Pointer returned by fbt_start() + * @param status_vector Output status vector for errors + * @return 0 on success, non-zero on failure + */ +int fbt_commit_retaining(void* transaction, ISC_STATUS* status_vector); + +/** + * Rollback with retaining (keeps transaction context). + * + * @param transaction Pointer returned by fbt_start() + * @param status_vector Output status vector for errors + * @return 0 on success, non-zero on failure + */ +int fbt_rollback_retaining(void* transaction, ISC_STATUS* status_vector); + +/** + * Check if a transaction is active. + * + * @param transaction Pointer returned by fbt_start() + * @return 1 if active, 0 if not + */ +int fbt_is_active(void* transaction); + +/** + * Get the ITransaction pointer from a transaction wrapper. + * + * @param transaction Pointer returned by fbt_start() + * @return Raw ITransaction pointer, or NULL + */ +void* fbt_get_handle(void* transaction); + +/** + * Free a transaction wrapper without commit/rollback. + * Use only when the transaction was already ended via other means. + * + * @param transaction Pointer returned by fbt_start() + */ +void fbt_free(void* transaction); + +/** + * Retrieve transaction information via the OO API. + * Wraps Firebird::ITransaction::getInfo(). + * + * @param master_ptr IMaster interface pointer + * @param transaction_ptr ITransaction pointer (from fbt_get_handle()) + * @param items_length Info items length + * @param items Info items to request + * @param buffer_length Output buffer length + * @param buffer Output buffer + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbt_get_info( + void* master_ptr, + void* transaction_ptr, + unsigned items_length, + const unsigned char* items, + unsigned buffer_length, + unsigned char* buffer, + ISC_STATUS* status_vector +); + +/* Firebird OO API Statement Functions */ + +/** + * Prepare a statement using OO API. + * + * @param master_ptr IMaster interface pointer (from FBG(master_instance)) + * @param attachment_ptr IAttachment pointer (from fbc_get_attachment()) + * @param transaction_ptr ITransaction pointer (from fbt_get_handle()) + * @param sql SQL statement text + * @param sql_length Length of SQL text (0 = null-terminated) + * @param dialect SQL dialect (1, 2, or 3) + * @param status_vector Output ISC_STATUS array + * @return Opaque StatementWrapper pointer or NULL on error + */ +void* fbs_prepare( + void* master_ptr, + void* attachment_ptr, + void* transaction_ptr, + const char* sql, + unsigned sql_length, + unsigned dialect, + ISC_STATUS* status_vector +); + +/** + * Execute a non-SELECT statement. + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer (from fbs_prepare()) + * @param transaction_ptr ITransaction pointer + * @param in_msg Input message buffer + * @param in_metadata IMessageMetadata for input + * @param out_msg Output message buffer + * @param out_metadata IMessageMetadata for output + * @param status_vector Output ISC_STATUS array + * @return 1 on success, 0 on error + */ +int fbs_execute( + void* master_ptr, + void* statement_ptr, + void* transaction_ptr, + void* in_msg, + void* in_metadata, + void* out_msg, + void* out_metadata, + ISC_STATUS* status_vector +); + +/** + * Open cursor for SELECT statement. + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer + * @param transaction_ptr ITransaction pointer + * @param in_msg Input message buffer + * @param in_metadata IMessageMetadata for input + * @param cursor_flags Cursor flags + * @param status_vector Output ISC_STATUS array + * @return 1 on success, 0 on error + */ +int fbs_open_cursor( + void* master_ptr, + void* statement_ptr, + void* transaction_ptr, + void* in_msg, + void* in_metadata, + unsigned cursor_flags, + ISC_STATUS* status_vector +); + +/** + * Fetch next row from cursor. + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer + * @param out_msg Output message buffer + * @param status_vector Output ISC_STATUS array + * @return 1 = row fetched, 0 = end of data, -1 = error + */ +int fbs_fetch( + void* master_ptr, + void* statement_ptr, + void* out_msg, + ISC_STATUS* status_vector +); + +/** + * Fetch previous row from scrollable cursor. + * @return 1 = row fetched, 0 = end of data, -1 = error + */ +int fbs_fetch_prior(void* master_ptr, void* statement_ptr, void* out_msg, ISC_STATUS* status_vector); + +/** + * Fetch first row from scrollable cursor. + * @return 1 = row fetched, 0 = end of data, -1 = error + */ +int fbs_fetch_first(void* master_ptr, void* statement_ptr, void* out_msg, ISC_STATUS* status_vector); + +/** + * Fetch last row from scrollable cursor. + * @return 1 = row fetched, 0 = end of data, -1 = error + */ +int fbs_fetch_last(void* master_ptr, void* statement_ptr, void* out_msg, ISC_STATUS* status_vector); + +/** + * Fetch row at absolute position from scrollable cursor. + * @return 1 = row fetched, 0 = end of data, -1 = error + */ +int fbs_fetch_absolute(void* master_ptr, void* statement_ptr, int position, void* out_msg, ISC_STATUS* status_vector); + +/** + * Fetch row at relative offset from scrollable cursor. + * @return 1 = row fetched, 0 = end of data, -1 = error + */ +int fbs_fetch_relative(void* master_ptr, void* statement_ptr, int offset, void* out_msg, ISC_STATUS* status_vector); + +/** + * Close cursor. + * + * @param statement_ptr StatementWrapper pointer + * @param status_vector Output ISC_STATUS array + * @return 1 on success, 0 on error + */ +int fbs_close_cursor(void* statement_ptr, ISC_STATUS* status_vector); + +/** + * Free/unprepare statement. + * + * @param statement_ptr StatementWrapper pointer + * @param status_vector Output ISC_STATUS array + * @return 1 on success, 0 on error + */ +int fbs_free(void* statement_ptr, ISC_STATUS* status_vector); + +/** + * Get statement type. + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer + * @param status_vector Output ISC_STATUS array + * @return Statement type constant + */ +unsigned fbs_get_type(void* master_ptr, void* statement_ptr, ISC_STATUS* status_vector); + +/** + * Get affected rows count. + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer + * @param status_vector Output ISC_STATUS array + * @return Affected rows count + */ +ISC_UINT64 fbs_get_affected_records(void* master_ptr, void* statement_ptr, ISC_STATUS* status_vector); + +/** + * Get input metadata. + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer + * @param status_vector Output ISC_STATUS array + * @return IMessageMetadata pointer or NULL + */ +void* fbs_get_input_metadata(void* master_ptr, void* statement_ptr, ISC_STATUS* status_vector); + +/** + * Get output metadata. + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer + * @param status_vector Output ISC_STATUS array + * @return IMessageMetadata pointer or NULL + */ +void* fbs_get_output_metadata(void* master_ptr, void* statement_ptr, ISC_STATUS* status_vector); + +/** + * Get input parameter count from statement. + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer + * @param status_vector Output ISC_STATUS array + * @return Number of input parameters (0 if none or error) + */ +unsigned fbs_get_input_count(void* master_ptr, void* statement_ptr, ISC_STATUS* status_vector); + +/** + * Get output field count from statement. + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer + * @param status_vector Output ISC_STATUS array + * @return Number of output fields (0 if none or error) + */ +unsigned fbs_get_output_count(void* master_ptr, void* statement_ptr, ISC_STATUS* status_vector); + +/** + * Get raw IStatement pointer. + * + * @param statement_ptr StatementWrapper pointer + * @return Raw IStatement pointer or NULL + */ +void* fbs_get_statement(void* statement_ptr); + +#if FB_API_VER >= 40 +/** + * Set per-statement execution timeout (FB 4.0+). + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer + * @param ms Timeout in milliseconds (0 = no timeout) + * @param status_vector Output ISC_STATUS array + * @return 0 on success, -1 on failure + */ +int fbs_set_timeout(void* master_ptr, void* statement_ptr, unsigned int ms, ISC_STATUS* status_vector); + +/** + * Get per-statement execution timeout (FB 4.0+). + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer + * @param status_vector Output ISC_STATUS array + * @return Timeout in milliseconds (0 = no timeout or error) + */ +unsigned int fbs_get_timeout(void* master_ptr, void* statement_ptr, ISC_STATUS* status_vector); +#endif + +/** + * Check if statement is prepared. + * + * @param statement_ptr StatementWrapper pointer + * @return 1 if prepared, 0 otherwise + */ +int fbs_is_prepared(void* statement_ptr); + +/** + * Check if cursor is open. + * + * @param statement_ptr StatementWrapper pointer + * @return 1 if cursor open, 0 otherwise + */ +int fbs_is_cursor_open(void* statement_ptr); + +/** + * Set cursor name for positioned updates (WHERE CURRENT OF). + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer + * @param cursor_name Cursor name string + * @param status_vector Output ISC_STATUS array + * @return 1 on success, 0 on error + */ +int fbs_set_cursor_name(void* master_ptr, void* statement_ptr, const char* cursor_name, ISC_STATUS* status_vector); + +/** + * Execute a statement that returns a single INT64 value (e.g., GEN_ID()). + * This is a convenience function that opens a cursor, fetches one row, + * extracts the first INT64 column, and closes the cursor. + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr StatementWrapper pointer (from fbs_prepare()) + * @param transaction_ptr ITransaction pointer + * @param status_vector Output ISC_STATUS array + * @return The INT64 value from the first column, or 0 on error + */ +ISC_INT64 fbs_execute_singleton_int64( + void* master_ptr, + void* statement_ptr, + void* transaction_ptr, + ISC_STATUS* status_vector +); + +/* Firebird OO API Blob Functions */ + +/** + * Create a new blob for writing. + * + * @param master_ptr IMaster interface pointer + * @param attachment_ptr IAttachment pointer (from fbc_get_attachment()) + * @param transaction_ptr ITransaction pointer (from fbt_get_handle()) + * @param blob_id Output: blob ID after creation + * @param bpb_length BPB (Blob Parameter Block) length + * @param bpb BPB data + * @param status_vector Output status vector + * @return Opaque blob wrapper pointer, or NULL on error + */ +void* fbb_create(void* master_ptr, + void* attachment_ptr, + void* transaction_ptr, + ISC_QUAD* blob_id, + unsigned bpb_length, + const unsigned char* bpb, + ISC_STATUS* status_vector); + +/** + * Open an existing blob for reading. + * + * @param master_ptr IMaster interface pointer + * @param attachment_ptr IAttachment pointer + * @param transaction_ptr ITransaction pointer + * @param blob_id Blob ID to open + * @param bpb_length BPB length + * @param bpb BPB data + * @param status_vector Output status vector + * @return Opaque blob wrapper pointer, or NULL on error + */ +void* fbb_open(void* master_ptr, + void* attachment_ptr, + void* transaction_ptr, + const ISC_QUAD* blob_id, + unsigned bpb_length, + const unsigned char* bpb, + ISC_STATUS* status_vector); + +/** + * Write a segment to the blob. + * + * @param master_ptr IMaster interface pointer + * @param blob_wrapper Blob wrapper pointer + * @param length Segment length + * @param buffer Data to write + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbb_put_segment(void* master_ptr, + void* blob_wrapper, + unsigned length, + const void* buffer, + ISC_STATUS* status_vector); + +/** + * Read a segment from the blob. + * + * @param master_ptr IMaster interface pointer + * @param blob_wrapper Blob wrapper pointer + * @param buffer_length Buffer size + * @param buffer Output buffer + * @param actual_length Output: actual bytes read + * @param status_vector Output status vector + * @return 0 on success with more data, 1 on EOF, 2 on segment, -1 on error + */ +int fbb_get_segment(void* master_ptr, + void* blob_wrapper, + unsigned buffer_length, + void* buffer, + unsigned* actual_length, + ISC_STATUS* status_vector); + +/** + * Close the blob (commit writes). + * + * @param master_ptr IMaster interface pointer + * @param blob_wrapper Blob wrapper pointer + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbb_close(void* master_ptr, void* blob_wrapper, ISC_STATUS* status_vector); + +/** + * Seek within an open stream BLOB. + * + * @param master_ptr IMaster interface (from fb_get_master_interface) + * @param blob_wrapper BlobWrapper pointer from fbb_open or fbb_create + * @param whence Seek mode: 0=SEEK_SET, 1=SEEK_CUR, 2=SEEK_END + * @param offset Byte offset relative to whence + * @param result_position Output: New absolute position after seek + * @param status_vector ISC status vector for error reporting + * + * @return 1 on success, 0 on failure + */ +int fbb_seek(void* master_ptr, + void* blob_wrapper, + int whence, + int offset, + int* result_position, + ISC_STATUS* status_vector); + +/** + * Cancel the blob (discard writes). + * + * @param master_ptr IMaster interface pointer + * @param blob_wrapper Blob wrapper pointer + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbb_cancel(void* master_ptr, void* blob_wrapper, ISC_STATUS* status_vector); + +/** + * Get blob info. + * + * @param master_ptr IMaster interface pointer + * @param blob_wrapper Blob wrapper pointer + * @param items_length Info items length + * @param items Info items to request + * @param buffer_length Output buffer length + * @param buffer Output buffer + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbb_get_info(void* master_ptr, + void* blob_wrapper, + unsigned items_length, + const unsigned char* items, + unsigned buffer_length, + unsigned char* buffer, + ISC_STATUS* status_vector); + +/** + * Get blob ID from wrapper. + * + * @param blob_wrapper Blob wrapper pointer + * @param blob_id Output: blob ID + */ +void fbb_get_blob_id(void* blob_wrapper, ISC_QUAD* blob_id); + +/** + * Check if blob is open. + * + * @param blob_wrapper Blob wrapper pointer + * @return 1 if open, 0 if closed or invalid + */ +int fbb_is_open(void* blob_wrapper); + +/** + * Get the raw IBlob handle from wrapper. + * + * @param blob_wrapper Blob wrapper pointer + * @return Raw IBlob pointer, or NULL if invalid + */ +void* fbb_get_handle(void* blob_wrapper); + +/** + * Free blob wrapper (without closing - blob must be closed first). + * + * @param blob_wrapper Blob wrapper pointer + */ +void fbb_free(void* blob_wrapper); + +/* Firebird OO API Event Functions */ + +/** + * Cancel queued events. + * + * @param master_ptr IMaster interface pointer + * @param events_wrapper Events wrapper pointer (from fbe_queue()) + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbe_cancel(void* master_ptr, void* events_wrapper, ISC_STATUS* status_vector); + +/** + * Check if an event has fired (non-blocking). + * + * @param events_wrapper Events wrapper pointer + * @return 1 if event fired, 0 otherwise + */ +int fbe_has_event_fired(void* events_wrapper); + +/** + * Reset the event fired flag. + * + * @param events_wrapper Events wrapper pointer + */ +void fbe_reset_event_fired(void* events_wrapper); + +/** + * Check if events are queued. + * + * @param events_wrapper Events wrapper pointer + * @return 1 if queued, 0 otherwise + */ +int fbe_is_queued(void* events_wrapper); + +/** + * Free events wrapper. + * + * @param events_wrapper Events wrapper pointer + */ +void fbe_free(void* events_wrapper); + +/** + * Free an event buffer allocated by fbe_event_block() / isc_event_block(). + * + * @param buf Buffer to free (may be NULL) + */ +void fbe_event_free(unsigned char* buf); + +/** + * Build event parameter block (EPB) for up to 15 events. + * Replacement for isc_event_block(). + * + * @param event_buf Output: allocated event buffer + * @param result_buf Output: allocated result buffer + * @param count Number of event names + * @return Length of the event buffer + */ +unsigned short fbe_event_block(unsigned char** event_buf, unsigned char** result_buf, + unsigned short count, ...); + +/** + * Wait synchronously for events using OO API attachment pointer. + * Uses fb_get_database_handle() to obtain a legacy handle from IAttachment*. + * + * @param status_vector Output status vector + * @param attachment_ptr IAttachment pointer (from fbc_get_attachment()) + * @param buffer_length Length of event buffer + * @param event_buffer Event buffer (from fbe_event_block()) + * @param result_buffer Result buffer (from fbe_event_block()) + * @return 0 on success, non-zero on error + */ +ISC_STATUS fbe_wait_for_event_oo(ISC_STATUS* status_vector, void* attachment_ptr, + unsigned short buffer_length, + unsigned char* event_buffer, + unsigned char* result_buffer); + +/** + * Decode event counts from result buffer. + * Replacement for isc_event_counts(). + * + * @param result_counts Output: array of event counts + * @param buffer_length Length of event buffer + * @param event_buffer Event buffer (from fbe_event_block()) + * @param result_buffer Result buffer (filled by fbe_wait_for_event()) + */ +void fbe_event_counts(ISC_ULONG* result_counts, unsigned short buffer_length, + unsigned char* event_buffer, unsigned char* result_buffer); + +/* Firebird OO API Service Functions */ + +/** + * Attach to the service manager using OO API. + * + * @param master_ptr IMaster interface pointer + * @param service_name Service name (e.g., "localhost:service_mgr") + * @param spb_length Service parameter buffer length + * @param spb Service parameter buffer + * @param status_vector Output status vector + * @return Opaque service wrapper pointer, or NULL on error + */ +void* fbsvc_attach(void* master_ptr, + const char* service_name, + unsigned spb_length, + const unsigned char* spb, + ISC_STATUS* status_vector); + +/** + * Detach from the service manager. + * + * @param master_ptr IMaster interface pointer (unused, for API consistency) + * @param service_wrapper Service wrapper pointer (from fbsvc_attach()) + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbsvc_detach(void* master_ptr, void* service_wrapper, ISC_STATUS* status_vector); + +/** + * Start a service task. + * + * @param master_ptr IMaster interface pointer (unused, for API consistency) + * @param service_wrapper Service wrapper pointer + * @param spb_length Service parameter buffer length + * @param spb Service parameter buffer + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbsvc_start(void* master_ptr, + void* service_wrapper, + unsigned spb_length, + const unsigned char* spb, + ISC_STATUS* status_vector); + +/** + * Query service status/results. + * + * @param master_ptr IMaster interface pointer (unused, for API consistency) + * @param service_wrapper Service wrapper pointer + * @param send_length Send buffer length + * @param send_items Send buffer + * @param recv_length Receive items length + * @param recv_items Receive items + * @param buffer_length Output buffer length + * @param buffer Output buffer + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbsvc_query(void* master_ptr, + void* service_wrapper, + unsigned send_length, + const unsigned char* send_items, + unsigned recv_length, + const unsigned char* recv_items, + unsigned buffer_length, + unsigned char* buffer, + ISC_STATUS* status_vector); + +/** + * Check if attached to service manager. + * + * @param service_wrapper Service wrapper pointer + * @return 1 if attached, 0 otherwise + */ +int fbsvc_is_attached(void* service_wrapper); + +/** + * Free service wrapper (without detach - service must be detached first). + * + * @param service_wrapper Service wrapper pointer + */ +void fbsvc_free(void* service_wrapper); + +/* Firebird OO API Array Functions */ + +/** + * Get an array slice from the database using OO API. + * + * @param master_ptr IMaster interface pointer + * @param attachment_ptr IAttachment pointer (from fbc_get_attachment()) + * @param transaction_ptr ITransaction pointer (from fbt_get_handle()) + * @param array_id The ISC_QUAD array identifier + * @param desc Array descriptor (ISC_ARRAY_DESC) + * @param buffer Output buffer to receive array data + * @param buffer_length Buffer length (updated with actual bytes read) + * @param status_vector Output status vector + * @return 0 on success, non-zero on failure + */ +int fba_get_slice(void* master_ptr, + void* attachment_ptr, + void* transaction_ptr, + ISC_QUAD* array_id, + const ISC_ARRAY_DESC* desc, + void* buffer, + ISC_LONG* buffer_length, + ISC_STATUS* status_vector); + +/** + * Lookup array descriptor/bounds using OO API. + * + * This replaces isc_array_lookup_bounds() by querying system tables + * via IStatement/IResultSet. + * + * @param master_ptr IMaster interface pointer + * @param attachment_ptr IAttachment pointer (from fbc_get_attachment()) + * @param transaction_ptr ITransaction pointer (from fbt_get_handle()) + * @param relation_name Table name (case-insensitive) + * @param field_name Column name (case-insensitive) + * @param desc Output descriptor (ISC_ARRAY_DESC) + * @param status_vector Output status vector + * @return 0 on success, non-zero on failure + */ +int fba_lookup_bounds(void* master_ptr, + void* attachment_ptr, + void* transaction_ptr, + const char* relation_name, + const char* field_name, + ISC_ARRAY_DESC* desc, + ISC_STATUS* status_vector); + +/** + * Put an array slice to the database using OO API. + * + * @param master_ptr IMaster interface pointer + * @param attachment_ptr IAttachment pointer (from fbc_get_attachment()) + * @param transaction_ptr ITransaction pointer (from fbt_get_handle()) + * @param array_id The ISC_QUAD array identifier (output for new arrays) + * @param desc Array descriptor (ISC_ARRAY_DESC) + * @param buffer Input buffer containing array data + * @param buffer_length Buffer length + * @param status_vector Output status vector + * @return 0 on success, non-zero on failure + */ +int fba_put_slice(void* master_ptr, + void* attachment_ptr, + void* transaction_ptr, + ISC_QUAD* array_id, + const ISC_ARRAY_DESC* desc, + const void* buffer, + ISC_LONG buffer_length, + ISC_STATUS* status_vector); + +/* FB 4.0+ Extended Features (Timezone support, enhanced metadata) */ +#if FB_API_VER >= 40 +void fbu_decode_time_tz(void *master_ptr, const ISC_TIME_TZ* time_tz, unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions, + unsigned time_zone_buffer_length, char* time_zone_buffer); +void fbu_decode_timestamp_tz(void *master_ptr, const ISC_TIMESTAMP_TZ* timestamp_tz, + unsigned* year, unsigned* month, unsigned* day, + unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions, + unsigned time_zone_buffer_length, char* time_zone_buffer); + +/* Encode time with timezone - pass timezone as string like "+02:00" or "Europe/Berlin" */ +int fbu_encode_time_tz(void *master_ptr, ISC_TIME_TZ* time_tz, + unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions, + const char* time_zone); + +/* Encode timestamp with timezone - pass timezone as string like "+02:00" or "Europe/Berlin" */ +int fbu_encode_timestamp_tz(void *master_ptr, ISC_TIMESTAMP_TZ* timestamp_tz, + unsigned year, unsigned month, unsigned day, + unsigned hours, unsigned minutes, unsigned seconds, unsigned fractions, + const char* time_zone); + +/** + * Convert FB_I128 (INT128) to string representation. + * @param master_ptr IMaster pointer + * @param value Pointer to FB_I128 value + * @param scale Scale factor (negative for decimal places) + * @param buffer Output buffer + * @param buffer_length Buffer size + * @return 0 on success, -1 on error + */ +int fbu_int128_to_string(void *master_ptr, const void *value, int scale, + char *buffer, unsigned buffer_length); + +/** + * Convert FB_DEC16 (DECFLOAT(16)) to string representation. + * @param master_ptr IMaster pointer + * @param value Pointer to FB_DEC16 value + * @param buffer Output buffer + * @param buffer_length Buffer size + * @return 0 on success, -1 on error + */ +int fbu_decfloat16_to_string(void *master_ptr, const void *value, + char *buffer, unsigned buffer_length); + +/** + * Convert FB_DEC34 (DECFLOAT(34)) to string representation. + * @param master_ptr IMaster pointer + * @param value Pointer to FB_DEC34 value + * @param buffer Output buffer + * @param buffer_length Buffer size + * @return 0 on success, -1 on error + */ +int fbu_decfloat34_to_string(void *master_ptr, const void *value, + char *buffer, unsigned buffer_length); + +/** + * Convert string to FB_DEC16 (DECFLOAT(16)). + * @param master_ptr IMaster pointer + * @param str String representation (e.g., "3.141592653589793") + * @param value Output: FB_DEC16 value + * @return 0 on success, -1 on error + */ +int fbu_string_to_decfloat16(void *master_ptr, const char *str, void *value); + +/** + * Convert string to FB_DEC34 (DECFLOAT(34)). + * @param master_ptr IMaster pointer + * @param str String representation + * @param value Output: FB_DEC34 value + * @return 0 on success, -1 on error + */ +int fbu_string_to_decfloat34(void *master_ptr, const char *str, void *value); + +/** + * Convert string to FB_I128 (INT128). + * @param master_ptr IMaster pointer + * @param str String representation + * @param scale Scale factor (negative for decimal places) + * @param value Output: FB_I128 value + * @return 0 on success, -1 on error + */ +int fbu_string_to_int128(void *master_ptr, const char *str, int scale, void *value); +#endif // FB_API_VER >= 40 + +int fbu_insert_field_info(void *master_ptr, ISC_STATUS* status, int is_outvar, int num, + zval *into_array, void *statement_ptr); +int fbu_insert_aliases(void *master_ptr, ISC_STATUS* status, fbird_query *fb_query, + void *statement_ptr); + +/** + * Metadata C interop functions for OO API message buffer operations. + * These functions provide access to IMessageMetadata interface for + * allocating message buffers and extracting field values during fetch. + */ + +/** + * Get message buffer size from metadata. + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer (from fbs_get_output_metadata) + * @return Buffer size in bytes, or 0 on error + */ +unsigned fbm_get_message_length(void* master_ptr, void* metadata_ptr); + +/** + * Get field count from metadata. + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer + * @return Number of fields + */ +unsigned fbm_get_count(void* master_ptr, void* metadata_ptr); + +/** + * Get field data offset in message buffer. + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer + * @param index Field index (0-based) + * @return Byte offset of field data in buffer + */ +unsigned fbm_get_offset(void* master_ptr, void* metadata_ptr, unsigned index); + +/** + * Get null indicator offset in message buffer. + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer + * @param index Field index (0-based) + * @return Byte offset of null indicator in buffer + */ +unsigned fbm_get_null_offset(void* master_ptr, void* metadata_ptr, unsigned index); + +/** + * Get field SQL type. + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer + * @param index Field index (0-based) + * @return SQL type code (SQL_TEXT, SQL_VARYING, SQL_LONG, etc.) + */ +unsigned fbm_get_type(void* master_ptr, void* metadata_ptr, unsigned index); + +/** + * Get field subtype (for blobs, char sets). + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer + * @param index Field index (0-based) + * @return Subtype value + */ +unsigned fbm_get_subtype(void* master_ptr, void* metadata_ptr, unsigned index); + +/** + * Get field data length. + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer + * @param index Field index (0-based) + * @return Data length in bytes + */ +unsigned fbm_get_length(void* master_ptr, void* metadata_ptr, unsigned index); + +/** + * Get field scale (for numeric types). + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer + * @param index Field index (0-based) + * @return Scale value (negative for decimal places) + */ +int fbm_get_scale(void* master_ptr, void* metadata_ptr, unsigned index); + +/** + * Get field charset ID. + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer + * @param index Field index (0-based) + * @return Charset ID + */ +unsigned fbm_get_charset(void* master_ptr, void* metadata_ptr, unsigned index); + +/** + * Get field name. + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer + * @param index Field index (0-based) + * @return Field name (internal pointer - do not free) + */ +const char* fbm_get_field(void* master_ptr, void* metadata_ptr, unsigned index); + +/** + * Get field alias. + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer + * @param index Field index (0-based) + * @return Field alias (internal pointer - do not free) + */ +const char* fbm_get_alias(void* master_ptr, void* metadata_ptr, unsigned index); + +/** + * Get field relation (table) name. + * @param master_ptr IMaster pointer + * @param metadata_ptr IMessageMetadata pointer + * @param index Field index (0-based) + * @return Relation name (internal pointer - do not free) + */ +const char* fbm_get_relation(void* master_ptr, void* metadata_ptr, unsigned index); + +/** + * Release metadata reference. + * @param metadata_ptr IMessageMetadata pointer + */ +void fbm_release(void* metadata_ptr); + +/* IXpbBuilder-based Parameter Block Construction */ + +/** + * Build TPB (Transaction Parameter Buffer) using OO API IXpbBuilder. + * Replaces manual byte array construction with cleaner builder pattern. + * + * Supported flags (PHP_FBIRD_* constants from php_fbird_includes.h): + * - Access mode: PHP_FBIRD_READ, PHP_FBIRD_WRITE + * - Isolation: PHP_FBIRD_CONSISTENCY, PHP_FBIRD_CONCURRENCY, PHP_FBIRD_COMMITTED + * - Record versioning: PHP_FBIRD_REC_VERSION, PHP_FBIRD_REC_NO_VERSION + * - Lock resolution: PHP_FBIRD_WAIT, PHP_FBIRD_NOWAIT, PHP_FBIRD_LOCK_TIMEOUT + * - FB 4.0+ READ CONSISTENCY: PHP_FBIRD_READ_CONSISTENCY + * + * @param master_ptr IMaster interface pointer + * @param trans_flags PHP transaction flags bitmask (PHP_FBIRD_* constants) + * @param lock_timeout Lock timeout in seconds (only used when flags include PHP_FBIRD_LOCK_TIMEOUT) + * @param buffer_length Output: length of TPB buffer + * @param status_vector Output status vector for errors + * @return Allocated TPB buffer (caller must free with fbxpb_free_tpb), or NULL on error + */ +unsigned char* fbxpb_build_tpb( + void* master_ptr, + zend_long trans_flags, + zend_long lock_timeout, + unsigned* buffer_length, + ISC_STATUS* status_vector +); + +/** + * Free TPB buffer allocated by fbxpb_build_tpb(). + * + * @param buffer Buffer to free (NULL-safe) + */ +void fbxpb_free_tpb(unsigned char* buffer); + +/* Limbo Transaction Functions (Two-Phase Commit Recovery) + * + * These functions support recovery of transactions that were prepared but + * not committed in a two-phase commit scenario (limbo transactions). + */ + +/** + * Get list of limbo transaction IDs from a database. + * + * Limbo transactions are transactions that were prepared (first phase of 2PC) + * but never committed or rolled back. This function retrieves their IDs using + * isc_info_limbo database info request. + * + * @param master_ptr IMaster interface pointer + * @param attachment_ptr IAttachment pointer (from fbc_get_attachment()) + * @param trans_ids Output array to receive transaction IDs + * @param max_ids Maximum number of IDs to retrieve (size of trans_ids array) + * @param status_vector Output status vector + * @return Number of limbo transaction IDs found (0 if none), -1 on error + */ +int fbt_get_limbo_transactions( + void* master_ptr, + void* attachment_ptr, + ISC_INT64* trans_ids, + unsigned max_ids, + ISC_STATUS* status_vector +); + +/** + * Reconnect to a limbo transaction for recovery. + * + * This function reconnects to a prepared (limbo) transaction using its ID, + * allowing the caller to commit or rollback the transaction. + * + * @param master_ptr IMaster interface pointer + * @param attachment_ptr IAttachment pointer + * @param trans_id The limbo transaction ID (from fbt_get_limbo_transactions) + * @param status_vector Output status vector + * @return Transaction wrapper pointer that can be committed/rolled back, or NULL on error + */ +void* fbt_reconnect( + void* master_ptr, + void* attachment_ptr, + ISC_INT64 trans_id, + ISC_STATUS* status_vector +); + +/* IBatch API Functions (Firebird 4.0+ Bulk Operations) + * + * The IBatch interface provides high-performance bulk INSERT operations. + * Using batch operations can provide 10-12x speedup for large data loads. + * These functions are only available when compiled with FB_API_VER >= 40. + */ + +#if FB_API_VER >= 40 + +/** + * Create a batch operation from a prepared statement. + * + * @param master_ptr IMaster interface pointer + * @param statement_ptr IStatement pointer (from fbs_get_statement()) + * @param buffer_size Buffer size hint (0 for default, typically 16MB) + * @param status_vector Output status vector + * @return Opaque batch wrapper pointer, or NULL on error + */ +void* fbbatch_create( + void* master_ptr, + void* statement_ptr, + unsigned buffer_size, + ISC_STATUS* status_vector +); + +/** + * Add parameter data to the batch. + * + * The input buffer should contain the parameter values in the format + * expected by the prepared statement's input metadata. + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer (from fbbatch_create()) + * @param count Number of messages (rows) to add + * @param in_buffer Input message buffer containing parameter data + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbbatch_add( + void* master_ptr, + void* batch_wrapper, + unsigned count, + const void* in_buffer, + ISC_STATUS* status_vector +); + +/** + * Execute the batch operation. + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer + * @param transaction_ptr ITransaction pointer for the batch execution + * @param total_processed Output: total number of messages processed + * @param error_count Output: number of messages that failed + * @param status_vector Output status vector + * @return 1 on success (even with partial errors), 0 on complete failure + */ +int fbbatch_execute( + void* master_ptr, + void* batch_wrapper, + void* transaction_ptr, + unsigned* total_processed, + unsigned* error_count, + ISC_STATUS* status_vector +); + +/** + * Cancel the batch without executing. + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbbatch_cancel( + void* master_ptr, + void* batch_wrapper, + ISC_STATUS* status_vector +); + +/** + * Close and free the batch. + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbbatch_close( + void* master_ptr, + void* batch_wrapper, + ISC_STATUS* status_vector +); + +/** + * Get the input metadata for the batch. + * + * This can be used to determine the message buffer format for fbbatch_add(). + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer + * @param status_vector Output status vector + * @return IMessageMetadata pointer (caller should release), or NULL on error + */ +void* fbbatch_get_metadata( + void* master_ptr, + void* batch_wrapper, + ISC_STATUS* status_vector +); + +/** + * Get BLOB alignment requirement for the batch. + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer + * @param status_vector Output status vector + * @return Alignment in bytes, or 0 on error + */ +unsigned fbbatch_get_blob_alignment( + void* master_ptr, + void* batch_wrapper, + ISC_STATUS* status_vector +); + +/* IBatch BLOB Handling Functions + * + * These functions provide advanced BLOB handling within batch operations, + * allowing inline BLOB creation without pre-creating BLOBs separately. + */ + +/** + * Add inline BLOB data to the batch. + * + * Creates a new BLOB within the batch context and returns a BLOB ID + * that can be used when adding rows via fbbatch_add(). + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer (from fbbatch_create()) + * @param length Length of BLOB data in bytes + * @param data BLOB data buffer + * @param blob_id_out Output: BLOB ID for use in batch parameters + * @param bpb_length BPB (Blob Parameter Block) length (0 for default) + * @param bpb BPB data (NULL for default) + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbbatch_add_blob( + void* master_ptr, + void* batch_wrapper, + unsigned length, + const void* data, + ISC_QUAD* blob_id_out, + unsigned bpb_length, + const unsigned char* bpb, + ISC_STATUS* status_vector +); + +/** + * Append data to the current BLOB being constructed. + * + * Used for streaming large BLOBs in chunks. Must be called after + * fbbatch_add_blob() or a previous fbbatch_append_blob_data() call. + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer + * @param length Length of data chunk + * @param data Data chunk buffer + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbbatch_append_blob_data( + void* master_ptr, + void* batch_wrapper, + unsigned length, + const void* data, + ISC_STATUS* status_vector +); + +/** + * Add BLOB data using stream mode. + * + * Alternative to addBlob() that uses different internal handling. + * Data is streamed directly without intermediate buffering. + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer + * @param length Length of BLOB data + * @param data BLOB data buffer + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbbatch_add_blob_stream( + void* master_ptr, + void* batch_wrapper, + unsigned length, + const void* data, + ISC_STATUS* status_vector +); + +/** + * Register an existing BLOB for use in batch operations. + * + * Takes a BLOB ID that was created outside the batch context and + * registers it for use within the batch. + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer + * @param existing_blob Existing BLOB ID to register + * @param batch_blob_id Output: BLOB ID for use in batch parameters + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbbatch_register_blob( + void* master_ptr, + void* batch_wrapper, + const ISC_QUAD* existing_blob, + ISC_QUAD* batch_blob_id, + ISC_STATUS* status_vector +); + +/** + * Set default BPB (Blob Parameter Block) for batch BLOB operations. + * + * Sets the default parameters used for all subsequent BLOB operations + * in this batch that don't specify their own BPB. + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer + * @param bpb_length BPB length + * @param bpb BPB data + * @param status_vector Output status vector + * @return 1 on success, 0 on error + */ +int fbbatch_set_default_bpb( + void* master_ptr, + void* batch_wrapper, + unsigned bpb_length, + const unsigned char* bpb, + ISC_STATUS* status_vector +); + +/* IBatch Detailed Error Reporting + * + * These structures and functions provide detailed per-row error information + * from batch execution, including SQLSTATE codes and error messages. + */ + +/** + * Per-row error entry from batch execution. + * + * Contains detailed information about a specific row that failed + * during batch execution. + */ +typedef struct { + unsigned position; /**< Row position (0-based index in batch) */ + int state; /**< Completion state (see IBatchCompletionState constants) */ + char sqlstate[6]; /**< SQLSTATE code (5 chars + null terminator) */ + char* message; /**< Error message (allocated, caller must free) */ +} fbbatch_error_entry; + +/** + * Batch completion result with detailed error information. + * + * Contains summary counts and an array of detailed error entries + * for all failed rows. + */ +typedef struct { + unsigned total_count; /**< Total rows processed */ + unsigned success_count; /**< Number of successful rows */ + unsigned error_count; /**< Number of failed rows */ + fbbatch_error_entry* errors; /**< Array of error_count entries (caller must free) */ +} fbbatch_completion_result; + +/** IBatchCompletionState constants */ +#define FBBATCH_EXECUTE_FAILED (-1) /**< Row execution failed */ +#define FBBATCH_SUCCESS_NO_INFO 0 /**< Success, no additional info */ +#define FBBATCH_NO_MORE_ERRORS (-2) /**< No more errors to report */ + +/** + * Execute batch with detailed completion state. + * + * Similar to fbbatch_execute() but returns detailed per-row error + * information via the fbbatch_completion_result structure. + * + * @param master_ptr IMaster interface pointer + * @param batch_wrapper Batch wrapper pointer + * @param transaction_ptr ITransaction pointer + * @param result Output: Completion result with error details (caller must free via fbbatch_free_result()) + * @param status_vector Output status vector + * @return 1 on success (even with partial errors), 0 on complete failure + */ +int fbbatch_execute_detailed( + void* master_ptr, + void* batch_wrapper, + void* transaction_ptr, + fbbatch_completion_result* result, + ISC_STATUS* status_vector +); + +/** + * Free error entries array from fbbatch_execute_detailed(). + * + * Frees all allocated memory in the error entries array, including + * individual error messages. + * + * @param errors Error entries array to free + * @param count Number of entries in the array + */ +void fbbatch_free_errors(fbbatch_error_entry* errors, unsigned count); + +/** + * Free completion result from fbbatch_execute_detailed(). + * + * Convenience function that frees the errors array and resets the result. + * + * @param result Completion result to free + */ +void fbbatch_free_result(fbbatch_completion_result* result); + +#endif /* FB_API_VER >= 40 */ + + +#ifdef __cplusplus +} +#endif + +#endif /* FIREBIRD_UTILS_H */ diff --git a/pdo_fbird/pdo_fbird_driver.c b/pdo_fbird/pdo_fbird_driver.c index 26161b5d..2b08282b 100644 --- a/pdo_fbird/pdo_fbird_driver.c +++ b/pdo_fbird/pdo_fbird_driver.c @@ -16,9 +16,9 @@ #include "php_pdo_fbird.h" #include "php_pdo_fbird_int.h" #include -#include "../firebird_utils.h" -#include "../php_firebird.h" -#include "../php_fbird_includes.h" +#include "firebird_utils.h" +#include "php_firebird.h" +#include "php_fbird_includes.h" extern const struct pdo_stmt_methods pdo_fbird_stmt_methods; diff --git a/pdo_fbird/pdo_fbird_error.c b/pdo_fbird/pdo_fbird_error.c index 495fd604..8f9979b4 100644 --- a/pdo_fbird/pdo_fbird_error.c +++ b/pdo_fbird/pdo_fbird_error.c @@ -12,7 +12,7 @@ #include "ext/pdo/php_pdo_driver.h" #include "php_pdo_fbird_int.h" #include -#include "../firebird_utils.h" +#include "firebird_utils.h" /* Map common Firebird GDS error codes to SQLSTATE strings */ static const struct { diff --git a/pdo_fbird/pdo_fbird_stmt.c b/pdo_fbird/pdo_fbird_stmt.c index 2fabfa54..b566804b 100644 --- a/pdo_fbird/pdo_fbird_stmt.c +++ b/pdo_fbird/pdo_fbird_stmt.c @@ -19,10 +19,10 @@ #include #include #include "zend_smart_str.h" -#include "../firebird_utils.h" -#include "../php_firebird.h" -#include "../php_fbird_includes.h" -#include "../fbird_classes.h" +#include "firebird_utils.h" +#include "php_firebird.h" +#include "php_fbird_includes.h" +#include "fbird_classes.h" /* {{{ php_firebird_preprocess — replace :name with ? and build name→position map */ zend_string *php_firebird_preprocess(const char *sql, size_t sql_len, diff --git a/pdo_fbird/php_fbird_includes.h b/pdo_fbird/php_fbird_includes.h new file mode 100755 index 00000000..1a1b3d31 --- /dev/null +++ b/pdo_fbird/php_fbird_includes.h @@ -0,0 +1,622 @@ +/* SPDX-License-Identifier: PHP-3.01 + * SPDX-FileCopyrightText: The PHP Group and contributors (see CREDITS) */ + +#ifndef PHP_FBIRD_INCLUDES_H +#define PHP_FBIRD_INCLUDES_H + +#include +#ifndef PHP_WIN32 +#include +#include +#endif + +/* Firebird 3.0+ required; FB_API_VER undefined means a missing or incompatible client library. */ +#ifndef FB_API_VER +#error "FB_API_VER is not defined. Firebird 3.0+ client libraries are required." +#endif + +/* Compatibility for older Firebird headers (pre-4.0) */ +#ifndef isc_tpb_read_consistency +#define isc_tpb_read_consistency 70 +#endif + +#ifndef isc_dpb_set_bind +#define isc_dpb_set_bind 88 +#endif + +#ifndef SQLDA_CURRENT_VERSION +#define SQLDA_CURRENT_VERSION SQLDA_VERSION1 +#endif + +/* Metadata identifier length (bytes). FB 4.0+ supports 63 chars (UTF8 = 4 bytes/char) */ +#ifndef METADATALENGTH +# if FB_API_VER >= 40 +# define METADATALENGTH 252 /* 63 characters * 4 bytes (UTF8) */ +# else +# define METADATALENGTH 31 /* Legacy 31 byte limit */ +# endif +#endif + +/* CHECK_LINK macro — shared by firebird.c and fbird_connection.c */ +#define CHECK_LINK(link) { if ((link)==NULL) { \ + php_error_docref(NULL, E_WARNING, "A link to the server could not be established"); \ + RETURN_FALSE; } } + +#define RESET_ERRMSG do { FBG(errmsg)[0] = '\0'; FBG(sql_code) = 0; memset(FBG(last_status), 0, sizeof(FBG(last_status))); } while (0) + +/* Error check helper: Firebird status vectors indicate error when [0]==1 && [1]!=0 */ +#define FB_STATUS_ERROR(s) ((s)[0] == 1 && (s)[1] != 0) + +#ifdef FBIRD_DEBUG +#define FBDEBUG(a) php_printf("::: %s (%s:%d)\n", a, __FILE__, __LINE__); +#endif + +#ifndef FBDEBUG +#define FBDEBUG(a) +#endif + +extern int le_link, le_plink, le_trans, le_query, le_blob, le_event; +#if FB_API_VER >= 40 +extern int le_batch; +#endif + +#define LE_LINK "Firebird link" +#define LE_PLINK "Firebird persistent link" +#define LE_TRANS "Firebird transaction" +#define LE_EVENT "Firebird event" +#define LE_BLOB "Firebird blob" +#define LE_QUERY "Firebird query" +#define LE_SVC "Firebird service manager handle" +#define LE_BATCH "Firebird batch" + +#define FBIRD_MSGSIZE 512 +#define MAX_ERRMSG (FBIRD_MSGSIZE*2) + +#define FB_DEF_DATE_FMT "%Y-%m-%d" +#define FB_DEF_TIME_FMT "%H:%M:%S" + +/* this value should never be > USHRT_MAX */ +#define FBIRD_BLOB_SEG 4096 + +ZEND_BEGIN_MODULE_GLOBALS(fbird) + ISC_STATUS last_status[256]; /* last error status vector for fbird_sqlstate() */ + zend_resource *default_link; + zend_long num_links, num_persistent; + char errmsg[MAX_ERRMSG]; + zend_long sql_code; + zend_long default_trans_params; + zend_long default_lock_timeout; /* only used together with trans_param FBIRD_LOCK_TIMEOUT */ + zend_long blob_segment_size; /* configurable BLOB segment size (default: 4096) */ + void *get_master_interface; + void *master_instance; + int client_version; + int client_major_version; + int client_minor_version; + pid_t init_pid; /* PID at initialization for fork-safety detection */ + int exception_mode; /* Exception mode: 0=SILENT (default), 1=THROW */ + bool in_mshutdown; /* Flag: true during MSHUTDOWN to prevent EG() access */ +ZEND_END_MODULE_GLOBALS(fbird) + +ZEND_EXTERN_MODULE_GLOBALS(fbird) + +typedef struct { + struct tr_list *tr_list; + unsigned short dialect; + struct event *event_head; + /* OO API connection wrapper (fb::Connection* from fbc_connect()) */ + void *fbc_connection; + /* Hash key for connection cache lookup (16-byte MD5). + * Used by fbird_close to remove stale cache entries from EG(regular_list). + * Fixes: Issue #35 - Heap Use-After-Free in fbird_pconnect */ + char hash_key[16]; + /* PID at connection creation for fork-safety validation. + * Fixes: Issue #36 - UAF with pcntl_fork/PHPStan parallel mode */ + pid_t created_pid; + /* Whether this is a persistent connection (le_plink). + * Used by autocommit logic to skip default-tx commit for pconnect + * (Issue #294 — cleanup_db() may drop DB before MSHUTDOWN). */ + bool is_persistent; + /* Per-connection error context (#439). Stored at error time so + * fbird_errmsg($link) returns connection-specific errors. */ + char errmsg[1024]; + ISC_STATUS_ARRAY last_status; +} fbird_db_link; + +typedef struct { + unsigned short link_cnt; + unsigned long affected_rows; + /* OO API transaction wrapper (fb::Transaction* from fbt_start()) */ + void *fbt_transaction; + fbird_db_link *db_link[1]; /* last member */ +} fbird_transaction; + +typedef struct tr_list { + fbird_transaction *trans; + struct tr_list *next; +} fbird_tr_list; + +typedef struct { + unsigned short type; + ISC_QUAD bl_qd; + /* OO API blob wrapper (fb::BlobWrapper* from fbb_create()/fbb_open()) */ + void *fbb_blob; +} fbird_blob; + +typedef struct event { + fbird_db_link *link; + zend_resource* link_res; + ISC_LONG event_id; + unsigned short event_count; + char **events; + unsigned char *event_buffer; /* Buffer for event registration (isc_que_events) */ + unsigned char *result_buffer; /* Buffer for event results (counts) */ + zval callback; + void *thread_ctx; + struct event *event_next; + enum event_state { NEW, ACTIVE, PENDING_REREGISTER, DEAD } state; + int needs_reregistration; + unsigned short buffer_size; + int callback_count; + int max_callbacks; + char *last_fired_event; /* Phase H: name of last event that fired (for Event::getName()) */ + /* Phase 7: OO API event wrapper (fb::EventsWrapper* from fbe_queue()) + * When non-NULL, events are queued via the modern OO API. + * Note: The current polling model continues to use isc_wait_for_event() + * for synchronous operation; this field is for future async support. */ + void *fbe_events; +} fbird_event; + +#include "fbird_service_types.h" + +/* sql variables union + * used for convert and binding input variables + */ +typedef struct { + union { +#ifdef SQL_BOOLEAN + FB_BOOLEAN bval; +#endif + short sval; + float fval; + double dval; /* Added for SQL_DOUBLE binding */ + ISC_LONG lval; + ISC_INT64 i64val; /* Added for SQL_INT64 binding */ + ISC_QUAD qval; + ISC_TIMESTAMP tsval; + ISC_DATE dtval; + ISC_TIME tmval; +#if FB_API_VER >= 40 + ISC_TIMESTAMP_TZ tstzval; + ISC_TIME_TZ tmtzval; + FB_DEC16 dec16; + FB_DEC34 dec34; + FB_I128 i128; +#endif + } val; + short nullind; +} BIND_BUF; + +typedef struct { + ISC_ARRAY_DESC ar_desc; + ISC_LONG ar_size; /* size of entire array in bytes */ + unsigned short el_type, el_size; +} fbird_array; + +typedef struct _fb_query { + fbird_db_link *link; + fbird_transaction *trans; + zend_resource *trans_res; + zend_resource *res; + XSQLDA *in_sqlda, *out_sqlda; + fbird_array *in_array, *out_array; + unsigned short type, has_more_rows, is_open; + unsigned short in_array_cnt, out_array_cnt; + unsigned short dialect; + char *query; + ISC_UCHAR statement_type; + BIND_BUF *bind_buf; + ISC_SHORT *in_nullind, *out_nullind; + ISC_USHORT in_fields_count, out_fields_count; + HashTable *ht_aliases, *ht_ind; // Precomputed for fbird_fetch_*() + int was_result_once; + /* Whether this query instance owns the statement handle and must DSQL_drop it + * in the destructor. Result resources created for SELECT reuse the parent's + * statement handle and must NOT drop it to avoid invalidating the prepared + * statement (fixes: tests/006.phpt, tests/bug45373.phpt, etc.). */ + bool owns_stmt_handle; + /* Parent/children linkage to allow invalidating dependent results when the + * prepared statement is freed (ensures TypeError on use-after-free, as + * expected by tests/use_after_free-002.phpt). */ + struct _fb_query *parent; + struct _fb_query *child_head; + struct _fb_query *child_next; + /* OO API statement wrapper (fb::Statement* from fbs_prepare()) */ + void *fbs_statement; + void *fbs_resultset; /* OO API IResultSet* for cursor operations */ + /* OO API message buffer for fetch operations (Phase 12+) + * These replace XSQLDA-based data transfer when using OO API. */ + void *out_metadata; /* IMessageMetadata* from fbs_get_output_metadata() */ + void *out_msg_buffer; /* Message buffer for fetch (allocated based on metadata) */ + unsigned out_msg_length; /* Message buffer size */ + void *in_metadata; /* IMessageMetadata* for input parameters */ + void *in_msg_buffer; /* Message buffer for input parameters */ + unsigned in_msg_length; /* Input message buffer size */ +} fbird_query; + +#if FB_API_VER >= 40 +/** + * Batch operation wrapper for Firebird 4.0+ IBatch interface. + * Provides high-performance bulk INSERT operations. + */ +typedef struct { + void *fbbatch_wrapper; /* OO API batch wrapper (from fbbatch_create()) */ + fbird_transaction *trans; /* Associated transaction */ + fbird_query *query; /* Parent prepared statement */ + zend_resource *query_res; /* Strong reference to query resource (Issue #185). + * Prevents premature destruction of the IStatement* + * when the PHP query variable goes out of scope + * before fbird_batch_execute() is called. */ + void *in_metadata; /* IMessageMetadata for input parameters */ + void *in_msg_buffer; /* Message buffer for row data */ + unsigned in_msg_length; /* Message buffer size */ +} fbird_batch; +#endif /* FB_API_VER >= 40 */ + +enum php_fbird_option { + PHP_FBIRD_DEFAULT = 0, + PHP_FBIRD_CREATE = 0, + /* connection flags */ + PHP_FBIRD_CONNECT_FORCE_NEW = 2, /* Force new connection, bypass connection reuse (matches PGSQL_CONNECT_FORCE_NEW) */ + /* fetch flags */ + PHP_FBIRD_FETCH_BLOBS = 1, + PHP_FBIRD_FETCH_ARRAYS = 2, + PHP_FBIRD_UNIXTIME = 4, + PHP_FBIRD_FETCH_DATE_OBJ = 8, /* Return DATE/TIME/TIMESTAMP as DateTimeImmutable */ + /* transaction access mode */ + PHP_FBIRD_WRITE = 1, + PHP_FBIRD_READ = 2, + /* transaction isolation level */ + PHP_FBIRD_CONCURRENCY = 4, + PHP_FBIRD_COMMITTED = 8, + PHP_FBIRD_REC_NO_VERSION = 32, + PHP_FBIRD_REC_VERSION = 64, + PHP_FBIRD_CONSISTENCY = 16, + /* transaction lock resolution */ + PHP_FBIRD_WAIT = 128, + PHP_FBIRD_NOWAIT = 256, + PHP_FBIRD_LOCK_TIMEOUT = 512, + + /* Table reservation lock types */ + PHP_FBIRD_LOCK_SHARED = 1024, + PHP_FBIRD_LOCK_PROTECTED = 2048, + PHP_FBIRD_LOCK_EXCLUSIVE = 4096, // Not used explicitly in legacy, but good for completeness + + /* Table reservation access types */ + PHP_FBIRD_LOCK_READ = 8192, + PHP_FBIRD_LOCK_WRITE = 16384, + + /* Firebird 4.0+ features */ + PHP_FBIRD_READ_CONSISTENCY = 32768, + + /* Event timeout return value */ + PHP_FBIRD_EVENT_TIMEOUT = -2 +}; + +#define FBG(v) ZEND_MODULE_GLOBALS_ACCESSOR(fbird, v) + +#if defined(ZTS) && defined(COMPILE_DL_FIREBIRD) +#ifdef __cplusplus +extern "C" { +#endif +ZEND_TSRMLS_CACHE_EXTERN() +#ifdef __cplusplus +} +#endif +#endif + +/* jane: fixed #516 - BLOB_ID_LEN was 13 (truncated 32-bit gds_quad_low to 16-bit), + * now 17 (8 hex + colon + 8 hex) matching _php_fbird_quad_to_string(). + * gds_quad_low is ISC_ULONG (32-bit unsigned) per firebird/impl/types_pub.h:194. + * BLOB_ID_MASK changed from %x:%hx (16-bit) to %x:%x (full 32-bit). + * Old 13-char IDs still parse correctly via _php_fbird_string_to_quad (uses %x:%x). + * BLOB_ID_LEN_LEGACY is for backwards-compat length checks in binding paths. + */ +#define BLOB_ID_LEN 17 +#define BLOB_ID_LEN_LEGACY 13 +#define BLOB_ID_MASK "%x:%x" + +#define BLOB_INPUT 1 +#define BLOB_OUTPUT 2 + +#ifdef PHP_WIN32 +// Since we target PHP 8.1+ only, always use modern format +#define LL_MASK "ll" +#define LL_LIT(lit) lit ## I64 +typedef void (__stdcall *info_func_t)(char*); +#else +#define LL_MASK "ll" +#define LL_LIT(lit) lit ## ll +typedef void (*info_func_t)(char*); +#endif + +extern zend_class_entry *firebird_exception_ce; +extern const zend_function_entry firebird_exception_methods[]; + +void _php_fbird_error(ISC_STATUS *status); +void _php_fbird_module_error(const char *, ...) + PHP_ATTRIBUTE_FORMAT(printf,1,2); + +/* determine if a resource is a link or transaction handle */ +#define PHP_FBIRD_LINK_TRANS(zv, lh, th) \ + do { \ + if (!zv) { \ + lh = (fbird_db_link *)zend_fetch_resource2( \ + FBG(default_link), "Firebird link", le_link, le_plink); \ + } else { \ + _php_fbird_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, zv, &lh, &th); \ + } \ + if (SUCCESS != _php_fbird_def_trans(lh, &th)) { RETURN_FALSE; } \ + } while (0) + +int _php_fbird_def_trans(fbird_db_link *fb_link, fbird_transaction **trans); +void _php_fbird_get_link_trans(INTERNAL_FUNCTION_PARAMETERS, zval *link_id, + fbird_db_link **fb_link, fbird_transaction **trans); + +/* provided by fbird_query.c */ +void php_fbird_query_minit(INIT_FUNC_ARGS); + +/* provided by fbird_blobs.c */ +void php_fbird_blobs_minit(INIT_FUNC_ARGS); +int _php_fbird_string_to_quad(char const *id, ISC_QUAD *qd); +zend_string *_php_fbird_quad_to_string(ISC_QUAD const qd); +int _php_fbird_blob_get(zval *return_value, fbird_blob *fb_blob, zend_ulong max_len); +int _php_fbird_blob_add(zval *string_arg, fbird_blob *fb_blob); + +/* provided by fbird_events.c */ +void php_fbird_events_minit(INIT_FUNC_ARGS); +void _php_fbird_free_event(fbird_event *event); + +/* provided by fbird_service.c */ +void php_fbird_service_minit(INIT_FUNC_ARGS); + +#ifdef __cplusplus +extern "C" { +#endif + +void _php_fbird_insert_alias(HashTable *ht, const char *alias); + +#ifdef __cplusplus +} +#endif + +#ifndef max +#define max(a,b) ((a)>(b)?(a):(b)) +#endif + + +typedef void* (ISC_EXPORT *fb_get_master_interface_t)(void); + +/* Resource Type Validation Macros (TypeError Support) + * + * These macros provide consistent error handling when wrong resource types + * are passed to fbird_* functions. In exception mode (FBIRD_EXCEPTION_MODE_THROW), + * they throw TypeError. Otherwise, they emit E_WARNING for backward compatibility. + */ + +/* Helper function prototype - implementation in firebird.c */ +const char *_fbird_res_type_name(int type); + +/* Validate connection resource (le_link or le_plink). + * M3: Also accepts Firebird\Connection objects (weak-ref to same resource). */ +#define FBIRD_VALIDATE_LINK_EX(zv, argnum, var) do { \ + /* M3 object path: Firebird\Connection accepted alongside resources */ \ + if (Z_TYPE_P(zv) == IS_OBJECT && \ + instanceof_function(Z_OBJCE_P(zv), fbird_connection_ce)) { \ + zend_resource *_cres = fbird_connection_get_resource(Z_OBJ_P(zv)); \ + if (!_cres) { RETURN_FALSE; } \ + var = (fbird_db_link *)_cres->ptr; \ + if (!var) { RETURN_FALSE; } \ + break; \ + } \ + int _res_type = Z_RES_TYPE_P(zv); \ + if (_res_type != le_link && _res_type != le_plink) { \ + if (FBG(exception_mode) == FBIRD_EXCEPTION_MODE_THROW) { \ + zend_argument_type_error(argnum, \ + "must be a Firebird connection resource, %s resource given", \ + _fbird_res_type_name(_res_type)); \ + RETURN_THROWS(); \ + } else { \ + php_error_docref(NULL, E_WARNING, \ + "Argument #%d must be a Firebird connection resource, %s resource given", \ + argnum, _fbird_res_type_name(_res_type)); \ + RETURN_FALSE; \ + } \ + } \ + var = (fbird_db_link *)zend_fetch_resource2_ex(zv, LE_LINK, le_link, le_plink); \ + if (!var) { RETURN_FALSE; } \ +} while(0) + +/* Validate transaction resource (le_trans). + * M3: Also accepts Firebird\Transaction objects (weak-ref to same resource). */ +#define FBIRD_VALIDATE_TRANS_EX(zv, argnum, var) do { \ + /* M3 object path: Firebird\Transaction accepted alongside resources */ \ + if (Z_TYPE_P(zv) == IS_OBJECT && \ + instanceof_function(Z_OBJCE_P(zv), fbird_transaction_ce)) { \ + zend_resource *_tres = fbird_transaction_get_resource(Z_OBJ_P(zv)); \ + if (!_tres) { RETURN_FALSE; } \ + var = (fbird_transaction *)_tres->ptr; \ + if (!var) { RETURN_FALSE; } \ + break; \ + } \ + int _res_type = Z_RES_TYPE_P(zv); \ + if (_res_type != le_trans) { \ + if (FBG(exception_mode) == FBIRD_EXCEPTION_MODE_THROW) { \ + zend_argument_type_error(argnum, \ + "must be a Firebird transaction resource, %s resource given", \ + _fbird_res_type_name(_res_type)); \ + RETURN_THROWS(); \ + } else { \ + php_error_docref(NULL, E_WARNING, \ + "Argument #%d must be a Firebird transaction resource, %s resource given", \ + argnum, _fbird_res_type_name(_res_type)); \ + RETURN_FALSE; \ + } \ + } \ + var = (fbird_transaction *)zend_fetch_resource_ex(zv, LE_TRANS, le_trans); \ + if (!var) { RETURN_FALSE; } \ +} while(0) + +/* Validate query/result resource (le_query). + * M3 Phase G: Also accepts Firebird\ResultSet objects (weak-ref to same resource). */ +/* Issue #297: Exported for OOP Statement::execute() to call directly */ +int _php_fbird_exec(INTERNAL_FUNCTION_PARAMETERS, fbird_query *fb_query, zval *args, int bind_n); + +#define FBIRD_VALIDATE_QUERY_EX(zv, argnum, var) do { \ + /* M3 object path: Firebird\ResultSet accepted alongside resources */ \ + if (Z_TYPE_P(zv) == IS_OBJECT && \ + instanceof_function(Z_OBJCE_P(zv), fbird_resultset_ce)) { \ + zend_resource *_qres = fbird_resultset_get_resource(Z_OBJ_P(zv)); \ + if (!_qres) { RETURN_FALSE; } \ + var = (fbird_query *)_qres->ptr; \ + if (!var) { \ + if (FBG(exception_mode) == FBIRD_EXCEPTION_MODE_THROW) { \ + zend_argument_type_error(argnum, \ + "must be a valid (non-freed) Firebird query/result resource or Firebird\\ResultSet"); \ + RETURN_THROWS(); \ + } \ + php_error_docref(NULL, E_WARNING, \ + "Argument #%d must be a valid (non-freed) Firebird query/result resource or Firebird\\ResultSet", \ + argnum); \ + RETURN_FALSE; \ + } \ + break; \ + } \ + /* Issue #297: Firebird\Statement (from fbird_prepare/ex) accepted */ \ + if (Z_TYPE_P(zv) == IS_OBJECT && \ + instanceof_function(Z_OBJCE_P(zv), fbird_statement_ce)) { \ + zend_resource *_qres = fbird_statement_get_resource(Z_OBJ_P(zv)); \ + if (!_qres) { RETURN_FALSE; } \ + var = (fbird_query *)_qres->ptr; \ + if (!var) { \ + if (FBG(exception_mode) == FBIRD_EXCEPTION_MODE_THROW) { \ + zend_argument_type_error(argnum, \ + "must be a valid (non-freed) Firebird query resource or Firebird\\Statement"); \ + RETURN_THROWS(); \ + } \ + php_error_docref(NULL, E_WARNING, \ + "Argument #%d must be a valid (non-freed) Firebird query resource or Firebird\\Statement", \ + argnum); \ + RETURN_FALSE; \ + } \ + break; \ + } \ + /* Must be IS_RESOURCE - anything else (object, array, etc.) is a type error */ \ + if (Z_TYPE_P(zv) != IS_RESOURCE) { \ + if (FBG(exception_mode) == FBIRD_EXCEPTION_MODE_THROW) { \ + zend_argument_type_error(argnum, \ + "must be a Firebird query/result resource, %s given", \ + zend_get_type_by_const(Z_TYPE_P(zv))); \ + RETURN_THROWS(); \ + } else { \ + php_error_docref(NULL, E_WARNING, \ + "Argument #%d must be a Firebird query/result resource, %s given", \ + argnum, zend_get_type_by_const(Z_TYPE_P(zv))); \ + RETURN_FALSE; \ + } \ + } \ + int _res_type = Z_RES_TYPE_P(zv); \ + if (_res_type != le_query) { \ + if (FBG(exception_mode) == FBIRD_EXCEPTION_MODE_THROW) { \ + zend_argument_type_error(argnum, \ + "must be a Firebird query/result resource, %s resource given", \ + _fbird_res_type_name(_res_type)); \ + RETURN_THROWS(); \ + } else { \ + php_error_docref(NULL, E_WARNING, \ + "Argument #%d must be a Firebird query/result resource, %s resource given", \ + argnum, _fbird_res_type_name(_res_type)); \ + RETURN_FALSE; \ + } \ + } \ + var = (fbird_query *)zend_fetch_resource_ex(zv, LE_QUERY, le_query); \ + if (!var) { RETURN_FALSE; } \ +} while(0) + +/* Validate blob resource (le_blob). + * M3 Phase G: Also accepts Firebird\Blob objects (weak-ref to same resource). */ +#define FBIRD_VALIDATE_BLOB_EX(zv, argnum, var) do { \ + /* M3 object path: Firebird\Blob accepted alongside resources */ \ + if (Z_TYPE_P(zv) == IS_OBJECT && \ + instanceof_function(Z_OBJCE_P(zv), fbird_blob_ce)) { \ + zend_resource *_bres = fbird_blob_get_resource(Z_OBJ_P(zv)); \ + if (!_bres) { RETURN_FALSE; } \ + var = (fbird_blob *)_bres->ptr; \ + if (!var) { \ + if (FBG(exception_mode) == FBIRD_EXCEPTION_MODE_THROW) { \ + zend_argument_type_error(argnum, \ + "must be a valid (non-freed) Firebird blob resource or Firebird\\Blob"); \ + RETURN_THROWS(); \ + } \ + php_error_docref(NULL, E_WARNING, \ + "Argument #%d must be a valid (non-freed) Firebird blob resource or Firebird\\Blob", \ + argnum); \ + RETURN_FALSE; \ + } \ + break; \ + } \ + /* Must be IS_RESOURCE - anything else is a type error */ \ + if (Z_TYPE_P(zv) != IS_RESOURCE) { \ + if (FBG(exception_mode) == FBIRD_EXCEPTION_MODE_THROW) { \ + zend_argument_type_error(argnum, \ + "must be a Firebird blob resource, %s given", \ + zend_get_type_by_const(Z_TYPE_P(zv))); \ + RETURN_THROWS(); \ + } else { \ + php_error_docref(NULL, E_WARNING, \ + "Argument #%d must be a Firebird blob resource, %s given", \ + argnum, zend_get_type_by_const(Z_TYPE_P(zv))); \ + RETURN_FALSE; \ + } \ + } \ + int _res_type_b = Z_RES_TYPE_P(zv); \ + if (_res_type_b != le_blob) { \ + if (FBG(exception_mode) == FBIRD_EXCEPTION_MODE_THROW) { \ + zend_argument_type_error(argnum, \ + "must be a Firebird blob resource, %s resource given", \ + _fbird_res_type_name(_res_type_b)); \ + RETURN_THROWS(); \ + } else { \ + php_error_docref(NULL, E_WARNING, \ + "Argument #%d must be a Firebird blob resource, %s resource given", \ + argnum, _fbird_res_type_name(_res_type_b)); \ + RETURN_FALSE; \ + } \ + } \ + var = (fbird_blob *)zend_fetch_resource_ex(zv, LE_BLOB, le_blob); \ + if (!var) { RETURN_FALSE; } \ +} while(0) + +/* Validate event handle (le_event resource OR Firebird\Event object). + * M3 Phase H: Firebird\Event objects own fbird_event* directly (no resource indirection). + * Callers must #include "fbird_classes.h" before using this macro. */ +#define FBIRD_VALIDATE_EVENT_EX(zv, argnum, var) do { \ + if (Z_TYPE_P(zv) == IS_OBJECT && \ + instanceof_function(Z_OBJCE_P(zv), fbird_event_ce)) { \ + (var) = fbird_event_get_ptr(Z_OBJ_P(zv)); \ + if (!(var) || (var)->state == DEAD) { \ + zend_argument_type_error((argnum), \ + "must be a valid (non-freed) Firebird\\Event"); \ + RETURN_THROWS(); \ + } \ + break; \ + } \ + if (Z_TYPE_P(zv) != IS_RESOURCE) { \ + zend_argument_type_error((argnum), \ + "must be of type Firebird\\Event or resource"); \ + RETURN_THROWS(); \ + } \ + (var) = (fbird_event *)zend_fetch_resource_ex((zv), LE_EVENT, le_event); \ + if (!(var)) { RETURN_FALSE; } \ +} while (0) + +#endif /* PHP_FBIRD_INCLUDES_H */ diff --git a/pdo_fbird/php_firebird.h b/pdo_fbird/php_firebird.h new file mode 100755 index 00000000..1da74f22 --- /dev/null +++ b/pdo_fbird/php_firebird.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: PHP-3.01 + * SPDX-FileCopyrightText: The PHP Group and contributors (see CREDITS) */ + +#ifndef PHP_FIREBIRD_H +#define PHP_FIREBIRD_H + +extern zend_module_entry firebird_module_entry; +#define phpext_firebird_ptr &firebird_module_entry + +#include "ibase.h" + +#ifndef FB_API_VER + static_assert(false, "FATAL: FB_API_VER is not defined. Assumed very old, unsupported client library"); +#endif + +/* Version string: defined by configure script via AC_DEFINE_UNQUOTED. + * Fallback for manual builds or missing configure detection. */ +#ifndef PHP_FIREBIRD_VERSION_STRING +# define PHP_FIREBIRD_VERSION_STRING "0.0.0-unknown" +#endif + +/* Legacy compatibility macro (use PHP_FIREBIRD_VERSION_STRING in new code) */ +#define PHP_FIREBIRD_VER_STR PHP_FIREBIRD_VERSION_STRING + +/* Numeric version for the FBIRD_VER constant (two-digit style like FB_API_VER). + * This is a simplified value; for full version info use PHP_FIREBIRD_VERSION_STRING. */ +#ifndef PHP_FIREBIRD_VER +# define PHP_FIREBIRD_VER 100 /* 10.x series */ +#endif + +PHP_MINIT_FUNCTION(fbird); +PHP_RINIT_FUNCTION(fbird); +PHP_MSHUTDOWN_FUNCTION(fbird); +PHP_RSHUTDOWN_FUNCTION(fbird); +PHP_MINFO_FUNCTION(fbird); + +PHP_FUNCTION(fbird_connect); +PHP_FUNCTION(fbird_pconnect); +PHP_FUNCTION(fbird_close); +PHP_FUNCTION(fbird_ping); +PHP_FUNCTION(fbird_server_version); +PHP_FUNCTION(fbird_drop_db); +PHP_FUNCTION(fbird_create_database); +PHP_FUNCTION(fbird_prepare_ex); +PHP_FUNCTION(fbird_query); +PHP_FUNCTION(fbird_multi_query); +PHP_FUNCTION(fbird_fetch_row); +PHP_FUNCTION(fbird_fetch_assoc); +PHP_FUNCTION(fbird_fetch_array); +PHP_FUNCTION(fbird_fetch_object); +PHP_FUNCTION(fbird_data_seek); +PHP_FUNCTION(fbird_fetch_all); +PHP_FUNCTION(fbird_fetch_column); +PHP_FUNCTION(fbird_stmt_reset); +PHP_FUNCTION(fbird_stmt_attr_get); +PHP_FUNCTION(fbird_stmt_attr_set); +PHP_FUNCTION(fbird_free_result); +PHP_FUNCTION(fbird_name_result); +PHP_FUNCTION(fbird_prepare); +PHP_FUNCTION(fbird_execute); +PHP_FUNCTION(fbird_free_query); + +PHP_FUNCTION(fbird_execute_statement); +PHP_FUNCTION(fbird_execute_query); +PHP_FUNCTION(fbird_execute_auto); +PHP_FUNCTION(fbird_query_params_tx); + +PHP_FUNCTION(fbird_gen_id); +PHP_FUNCTION(fbird_last_insert_id); +PHP_FUNCTION(fbird_num_fields); +PHP_FUNCTION(fbird_num_params); +PHP_FUNCTION(fbird_affected_rows); +PHP_FUNCTION(fbird_field_info); +PHP_FUNCTION(fbird_param_info); +PHP_FUNCTION(fbird_result_metadata); +PHP_FUNCTION(fbird_list_tables); +PHP_FUNCTION(fbird_list_fields); +PHP_FUNCTION(fbird_meta_data); + +PHP_FUNCTION(fbird_trans); +PHP_FUNCTION(fbird_trans_start); +PHP_FUNCTION(fbird_commit); +PHP_FUNCTION(fbird_rollback); +PHP_FUNCTION(fbird_commit_ret); +PHP_FUNCTION(fbird_rollback_ret); + +PHP_FUNCTION(fbird_savepoint); +PHP_FUNCTION(fbird_rollback_savepoint); +PHP_FUNCTION(fbird_release_savepoint); + +PHP_FUNCTION(fbird_trans_info); +PHP_FUNCTION(fbird_connection_info); + +PHP_FUNCTION(fbird_blob_create); +PHP_FUNCTION(fbird_blob_add); +PHP_FUNCTION(fbird_blob_cancel); +PHP_FUNCTION(fbird_blob_open); +PHP_FUNCTION(fbird_blob_get); +PHP_FUNCTION(fbird_blob_export); +PHP_FUNCTION(fbird_blob_close); +PHP_FUNCTION(fbird_blob_echo); +PHP_FUNCTION(fbird_blob_info); +PHP_FUNCTION(fbird_blob_import); +PHP_FUNCTION(fbird_blob_create_stream); +PHP_FUNCTION(fbird_blob_open_stream); +PHP_FUNCTION(fbird_blob_create_seekable); +PHP_FUNCTION(fbird_blob_open_seekable); +PHP_FUNCTION(fbird_blob_seek); + +PHP_FUNCTION(fbird_add_user); +PHP_FUNCTION(fbird_modify_user); +PHP_FUNCTION(fbird_delete_user); + +PHP_FUNCTION(fbird_service_attach); +PHP_FUNCTION(fbird_service_detach); +PHP_FUNCTION(fbird_backup); +PHP_FUNCTION(fbird_restore); +PHP_FUNCTION(fbird_maintain_db); +PHP_FUNCTION(fbird_db_info); +PHP_FUNCTION(fbird_server_info); + +PHP_FUNCTION(fbird_errmsg); +PHP_FUNCTION(fbird_errcode); +PHP_FUNCTION(fbird_sqlstate); + +PHP_FUNCTION(fbird_escape_string); +PHP_FUNCTION(fbird_escape_literal); +PHP_FUNCTION(fbird_escape_identifier); + +/* Exception Mode API (PDO-style error handling) */ +PHP_FUNCTION(fbird_set_exception_mode); +PHP_FUNCTION(fbird_get_exception_mode); + +/* Exception mode constants */ +#define FBIRD_EXCEPTION_MODE_SILENT 0 +#define FBIRD_EXCEPTION_MODE_THROW 1 + +PHP_FUNCTION(fbird_wait_event); +PHP_FUNCTION(fbird_set_event_handler); +PHP_FUNCTION(fbird_poll_event); +PHP_FUNCTION(fbird_free_event_handler); + +PHP_FUNCTION(fbird_get_client_version); +PHP_FUNCTION(fbird_get_client_major_version); +PHP_FUNCTION(fbird_get_client_minor_version); + +/* Limbo Transaction Functions (Two-Phase Commit Recovery) */ +PHP_FUNCTION(fbird_get_limbo_transactions); +PHP_FUNCTION(fbird_reconnect_transaction); + +/* IBatch API Functions (Firebird 4.0+ Bulk Operations) */ +#if FB_API_VER >= 40 +PHP_FUNCTION(fbird_batch_create); +PHP_FUNCTION(fbird_batch_add); +PHP_FUNCTION(fbird_batch_execute); +PHP_FUNCTION(fbird_batch_cancel); +PHP_FUNCTION(fbird_batch_add_blob); +PHP_FUNCTION(fbird_batch_register_blob); +PHP_FUNCTION(fbird_batch_get_blob_alignment); +PHP_FUNCTION(fbird_batch_append_blob_data); +PHP_FUNCTION(fbird_batch_add_blob_stream); +PHP_FUNCTION(fbird_batch_set_default_bpb); + +/* Statement/Session Timeout Functions (Firebird 4.0+) */ +PHP_FUNCTION(fbird_set_statement_timeout); +PHP_FUNCTION(fbird_get_statement_timeout); +PHP_FUNCTION(fbird_set_idle_timeout); +PHP_FUNCTION(fbird_get_idle_timeout); + +/* Per-Statement Timeout Functions (Firebird 4.0+) */ +PHP_FUNCTION(fbird_stmt_set_timeout); +PHP_FUNCTION(fbird_stmt_get_timeout); +#endif /* FB_API_VER >= 40 */ + +#else + +#define phpext_firebird_ptr NULL + +#endif /* PHP_FIREBIRD_H */ +/* M2 Procedural Parity: charset, bind, blob, debug, service (#366-#380, #476-#478) */ +PHP_FUNCTION(fbird_set_charset); +PHP_FUNCTION(fbird_character_set_name); +PHP_FUNCTION(fbird_bind_param); +PHP_FUNCTION(fbird_bind_result); +PHP_FUNCTION(fbird_debug); +PHP_FUNCTION(fbird_blob_truncate); +PHP_FUNCTION(fbird_blob_erase); +PHP_FUNCTION(fbird_blob_flush); +PHP_FUNCTION(fbird_send_long_data); +PHP_FUNCTION(fbird_nbak); +PHP_FUNCTION(fbird_trace_start); +PHP_FUNCTION(fbird_trace_stop); +#if FB_API_VER >= 40 +PHP_FUNCTION(fbird_set_session_timezone); +PHP_FUNCTION(fbird_get_session_timezone); +#endif +PHP_FUNCTION(fbird_dump_debug_info); +void _php_fbird_error_for_link(ISC_STATUS *status, void *link); +PHP_FUNCTION(fbird_error_list); +PHP_FUNCTION(fbird_error_field); diff --git a/pdo_fbird/php_pdo_fbird_int.h b/pdo_fbird/php_pdo_fbird_int.h index 5d08b195..66d609c8 100644 --- a/pdo_fbird/php_pdo_fbird_int.h +++ b/pdo_fbird/php_pdo_fbird_int.h @@ -8,7 +8,7 @@ #include "php.h" #include "ext/pdo/php_pdo_driver.h" #include -#include "../firebird_utils.h" +#include "firebird_utils.h" /* Per-connection driver data stored in pdo_dbh_t->driver_data */ typedef struct {