Skip to content

New Requisites for ODBC Driver Integration #42

@fdcastel

Description

@fdcastel

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:

  1. 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.

  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions