fb-cpp New Requisites for ODBC Driver Integration
@asfernandes Please comment when you have a chance. With your approval, I will submit the appropriate PR.
REQ-1: SQL Dialect Support in StatementOptions
Priority: High
Status: Missing in both vcpkg 0.0.2 and on main
Problem
Statement constructor hardcodes SQL_DIALECT_CURRENT (dialect 3):
// Statement.cpp line 53-54
statementHandle.reset(attachment.getHandle()->prepare(&statusWrapper, transaction.getHandle().get(),
static_cast<unsigned>(sql.length()), sql.data(), SQL_DIALECT_CURRENT, flags));
The ODBC driver supports legacy Firebird databases running in dialect 1 (InterBase compatibility mode). In dialect 1, NUMERIC/DECIMAL types map to DOUBLE PRECISION instead of INT64, and quoted identifiers behave differently. Without dialect control, the driver must fall back to the raw IAttachment::prepare() API for these databases.
Proposed API
class StatementOptions final
{
public:
unsigned getDialect() const { return dialect; }
StatementOptions& setDialect(unsigned value)
{
dialect = value;
return *this;
}
private:
unsigned dialect = SQL_DIALECT_CURRENT; // default preserves current behavior
};
Then in the Statement constructor:
statementHandle.reset(attachment.getHandle()->prepare(&statusWrapper, transaction.getHandle().get(),
static_cast<unsigned>(sql.length()), sql.data(), options.getDialect(), flags));
Impact on ODBC driver
With this, we can eliminate the raw-API fallback branch in IscStatement::prepareStatement() (Phase 14.4.1), making fbcpp::Statement the sole prepare path for all databases.
REQ-2: Output Message Buffer Accessor (getOutputMessage())
Priority: High
Status: Missing in both vcpkg 0.0.2 and on main
Problem
Statement exposes getInputMessage() for raw write access to the input parameter buffer, but there is no corresponding getOutputMessage() for the output result buffer. The output message (outMessage) is private.
The ODBC driver needs raw access to outMessage for two reasons:
-
Prefetch optimization: The driver's 64-row prefetch mechanism (IscResultSet::nextFetch()) fetches batches of rows into a local prefetch buffer, then serves them one-at-a-time via memcpy. After each fetchNext(), the driver needs to copy the raw result row out of the statement's buffer into the prefetch buffer. Without getOutputMessage(), there's no way to access the fetched data in raw form.
-
Direct Sqlda buffer integration: OdbcConvert reads result data directly from fixed offsets in a raw Firebird message buffer. If we can point OdbcConvert's descriptor pointers at offsets within outMessage (instead of maintaining a separate Sqlda.buffer), we eliminate one entire buffer and one memcpy per row.
Proposed API
Symmetrical with the existing getInputMessage():
/// @brief Provides direct access to the raw output message buffer.
std::vector<std::byte>& getOutputMessage() noexcept
{
return outMessage;
}
Impact on ODBC driver
Enables eliminating the separate Sqlda.buffer for output data and implementing the N-row prefetch using fbcpp's buffer as source.
REQ-3: fetchNext() Overload with External Buffer
Priority: Medium (alternative to REQ-2 for the prefetch use case)
Status: Missing in both vcpkg 0.0.2 and on main
Problem
All fetch methods (fetchNext(), fetchPrior(), fetchFirst(), etc.) write exclusively to the internal outMessage buffer. The ODBC driver's N-row prefetch needs to fetch consecutive rows into different offsets of a contiguous buffer (64 rows × rowSize bytes).
Currently this requires: fetchNext() → memcpy(prefetchBuffer + i*rowSize, outMessage, rowSize) per row. A fetchNext overload that writes directly to a user-supplied buffer would eliminate 64 memcpy operations per batch.
Proposed API
/// @brief Fetches the next row into the provided buffer instead of the internal outMessage.
/// @param buffer Pointer to a buffer of at least getOutputMetadata()->getMessageLength() bytes.
/// @return true if a row was fetched, false if no more data.
bool fetchNext(void* buffer);
Implementation:
bool Statement::fetchNext(void* buffer)
{
assert(isValid());
return resultSetHandle &&
resultSetHandle->fetchNext(&statusWrapper, buffer) == fb::IStatus::RESULT_OK;
}
Impact on ODBC driver
Enables zero-copy N-row prefetch: each fetchNext(prefetchBuffer + i*rowSize) writes the row directly to its final location in the prefetch buffer.
Note
If REQ-2 (getOutputMessage()) is accepted, REQ-3 becomes a nice-to-have optimization rather than a requirement — the driver can memcpy from getOutputMessage().data() after each fetchNext().
REQ-4: (Informational) Batch and CursorType — Already on main
Priority: N/A (resolved — pending vcpkg release)
Status: Present on main, missing in vcpkg 0.0.2
The following features needed by the ODBC driver already exist on main fb-cpp branch:
fbcpp::Batch class (Batch.h/Batch.cpp) — for ODBC array parameter execution (SQL_ATTR_PARAMSET_SIZE > 1)
CursorType::SCROLLABLE in StatementOptions — for ODBC scrollable cursors (SQL_CURSOR_STATIC)
StatementOptions::setCursorName() — for ODBC SQLSetCursorName()
Statement::getInputMessage() — raw input buffer access
These are blocked only by the vcpkg package version (0.0.2). Once a new version is published, the ODBC driver can immediately use them.
See #41.
Summary
| Req |
Description |
Exists in local? |
Exists in vcpkg? |
Change needed in fb-cpp? |
| REQ-1 |
Dialect in StatementOptions |
❌ No |
❌ No |
Yes — new option |
| REQ-2 |
getOutputMessage() accessor |
❌ No |
❌ No |
Yes — trivial addition |
| REQ-3 |
fetchNext(void*) overload |
❌ No |
❌ No |
Yes — performance optimization |
| REQ-4 |
Batch, CursorType, cursor name, getInputMessage |
✅ Yes |
❌ No |
No — just needs vcpkg release |
fb-cpp New Requisites for ODBC Driver Integration
@asfernandes Please comment when you have a chance. With your approval, I will submit the appropriate PR.
REQ-1: SQL Dialect Support in
StatementOptionsPriority: High
Status: Missing in both vcpkg 0.0.2 and on
mainProblem
Statementconstructor hardcodesSQL_DIALECT_CURRENT(dialect 3):The ODBC driver supports legacy Firebird databases running in dialect 1 (InterBase compatibility mode). In dialect 1,
NUMERIC/DECIMALtypes map toDOUBLE PRECISIONinstead ofINT64, and quoted identifiers behave differently. Without dialect control, the driver must fall back to the rawIAttachment::prepare()API for these databases.Proposed API
Then in the
Statementconstructor:statementHandle.reset(attachment.getHandle()->prepare(&statusWrapper, transaction.getHandle().get(), static_cast<unsigned>(sql.length()), sql.data(), options.getDialect(), flags));Impact on ODBC driver
With this, we can eliminate the raw-API fallback branch in
IscStatement::prepareStatement()(Phase 14.4.1), making fbcpp::Statement the sole prepare path for all databases.REQ-2: Output Message Buffer Accessor (
getOutputMessage())Priority: High
Status: Missing in both vcpkg 0.0.2 and on
mainProblem
StatementexposesgetInputMessage()for raw write access to the input parameter buffer, but there is no correspondinggetOutputMessage()for the output result buffer. The output message (outMessage) isprivate.The ODBC driver needs raw access to
outMessagefor two reasons:Prefetch optimization: The driver's 64-row prefetch mechanism (
IscResultSet::nextFetch()) fetches batches of rows into a local prefetch buffer, then serves them one-at-a-time viamemcpy. After eachfetchNext(), the driver needs to copy the raw result row out of the statement's buffer into the prefetch buffer. WithoutgetOutputMessage(), there's no way to access the fetched data in raw form.Direct Sqlda buffer integration:
OdbcConvertreads result data directly from fixed offsets in a raw Firebird message buffer. If we can pointOdbcConvert's descriptor pointers at offsets withinoutMessage(instead of maintaining a separateSqlda.buffer), we eliminate one entire buffer and onememcpyper row.Proposed API
Symmetrical with the existing
getInputMessage():Impact on ODBC driver
Enables eliminating the separate
Sqlda.bufferfor output data and implementing the N-row prefetch using fbcpp's buffer as source.REQ-3:
fetchNext()Overload with External BufferPriority: Medium (alternative to REQ-2 for the prefetch use case)
Status: Missing in both vcpkg 0.0.2 and on
mainProblem
All fetch methods (
fetchNext(),fetchPrior(),fetchFirst(), etc.) write exclusively to the internaloutMessagebuffer. The ODBC driver's N-row prefetch needs to fetch consecutive rows into different offsets of a contiguous buffer (64 rows × rowSize bytes).Currently this requires:
fetchNext()→memcpy(prefetchBuffer + i*rowSize, outMessage, rowSize)per row. AfetchNextoverload that writes directly to a user-supplied buffer would eliminate 64 memcpy operations per batch.Proposed API
Implementation:
Impact on ODBC driver
Enables zero-copy N-row prefetch: each
fetchNext(prefetchBuffer + i*rowSize)writes the row directly to its final location in the prefetch buffer.Note
If REQ-2 (
getOutputMessage()) is accepted, REQ-3 becomes a nice-to-have optimization rather than a requirement — the driver canmemcpyfromgetOutputMessage().data()after eachfetchNext().REQ-4: (Informational)
BatchandCursorType— Already onmainPriority: N/A (resolved — pending vcpkg release)
Status: Present on
main, missing in vcpkg 0.0.2The following features needed by the ODBC driver already exist on
mainfb-cpp branch:fbcpp::Batchclass (Batch.h/Batch.cpp) — for ODBC array parameter execution (SQL_ATTR_PARAMSET_SIZE > 1)CursorType::SCROLLABLEinStatementOptions— for ODBC scrollable cursors (SQL_CURSOR_STATIC)StatementOptions::setCursorName()— for ODBCSQLSetCursorName()Statement::getInputMessage()— raw input buffer accessThese are blocked only by the vcpkg package version (0.0.2). Once a new version is published, the ODBC driver can immediately use them.
See #41.
Summary
getOutputMessage()accessorfetchNext(void*)overload