Skip to content

Commit d026ec7

Browse files
committed
Consolidate Issue #161 fix behind two single-purpose helpers
The previous fix inlined the SQL_VARYING prefix write (`*(unsigned short*)buf = len;` + data at `buf + 2`) into 18 `conv<Numeric|Date|Time>ToString[W]` helpers and repeated the SQL_C_WCHAR → byte-variant reroute at 8 switch sites in `getAdressFunction`, with a peek-only `HeadSqlVar::isSqlVarying()` getter to let the ODBC layer inspect the prepared sqltype. That shape is hard to maintain (any future `convDecfloatToString` has to remember to copy the same branch) and leaks Firebird buffer-layout knowledge into the ODBC conversion layer. This commit collapses the repetition behind two named primitives: * `HeadSqlVar::writeStringData(src, len)` — new inline method on the IscDbc interface, beside `setSqlLen`/`setSqlData`. It writes into the sqlvar's pre-allocated buffer using the correct layout for the prepared type (`[uint16 len][data]` for SQL_VARYING, bare bytes + effective sqllen for SQL_TEXT) and does not mutate the VARYING metadata. Replaces the `isSqlVarying()` getter — behavior, not a peek. * `writeConvertedString(pointer, indicatorTo, to, src, len)` — new file-scope helper in OdbcConvert.cpp. Single write-back path for every numeric/date/time → string converter: Firebird-side targets go through `writeStringData`, app-owned targets get a bare memcpy + indicator update. Each `conv*ToString` helper now produces a local ASCII buffer and calls this helper instead of touching `*(unsigned short*)pointer` or `setSqlLen` directly. * `chooseNumericToStringConv(to, byteFn, wideFn)` — tiny inline that replaces the 8 identical `if (to->isIndicatorSqlDa) return byte; else return wide;` blocks in `getAdressFunction`. The rationale comment lives once, at the helper. Also rewrites the `Sqlda::getPrecision` comment to explain that the `orgVarSqlProperties` read for INPUT parameters is a symmetry with the existing `getColumnDisplaySize` pattern (INPUT precision is immutable from prepare time), not an Issue-161-specific workaround. Code behavior unchanged. Net: 18 + 8 scattered sites replaced by 2 helpers and 1 method; ODBC layer no longer writes uint16 length prefixes or reads `sqltype == SQL_VARYING`. Verified against Firebird 5.0.3 on Windows x64, across all three charset configs (UTF8+UTF8, ISO8859_1+ISO8859_1, ISO8859_1+UTF8): 125 passing tests / 0 failing, including `Issue161_SLongToVarcharViaStoredProcedure`, `Issue161_SLongToVarcharViaDml`, `NumericAsCharParam` and the rest of `ParamConversionsTest` / `WCharTest`.
1 parent 3d3b7a2 commit d026ec7

4 files changed

Lines changed: 150 additions & 220 deletions

File tree

IscDbc/Connection.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,11 +524,11 @@ class HeadSqlVar
524524
//virtual void setSqlSubType ( short subtype ) = 0;
525525
virtual void setSqlLen ( short len ) = 0;
526526
virtual short getSqlMultiple () = 0;
527-
virtual bool isSqlVarying () = 0;
528527

529528
virtual char * getSqlData() = 0;
530529
virtual short * getSqlInd() = 0;
531530
virtual void setSqlData( char *data ) = 0;
531+
virtual void writeStringData( const char *src, int len ) = 0;
532532
//virtual void setSqlInd( short *ind ) = 0;
533533

534534
virtual bool isReplaceForParamArray () = 0;

IscDbc/IscHeadSqlVar.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,27 @@ class IscHeadSqlVar : public HeadSqlVar
102102
inline short getSqlMultiple () { return sqlMultiple; }
103103
inline char * getSqlData() { return sqlvar->sqldata; }
104104
inline short * getSqlInd() { return sqlvar->sqlind; }
105-
inline bool isSqlVarying() { return sqlvar->sqltype == SQL_VARYING; }
105+
106+
// Issue #161: write `len` bytes from `src` into the sqlvar's pre-allocated
107+
// buffer, honoring Firebird's in-buffer layout for the prepared type:
108+
// SQL_VARYING : [uint16 len][data...] — leaves sqltype/sqllen untouched
109+
// so checkAndRebuild() sees no override and Firebird reads
110+
// the buffer using the original VARCHAR(N) metadata.
111+
// SQL_TEXT : bare bytes; reports the effective length via sqllen.
112+
inline void writeStringData(const char *src, int len)
113+
{
114+
char *buf = sqlvar->sqldata;
115+
if (sqlvar->sqltype == SQL_VARYING)
116+
{
117+
*(unsigned short*)buf = (unsigned short)len;
118+
memcpy(buf + sizeof(short), src, len);
119+
}
120+
else
121+
{
122+
memcpy(buf, src, len);
123+
sqlvar->sqllen = (short)len;
124+
}
125+
}
106126

107127
// not used
108128
//void setSqlInd( short *ind ) { sqlvar->sqlind = ind; }

IscDbc/Sqlda.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -760,15 +760,13 @@ const char* Sqlda::getColumnName(int index)
760760
int Sqlda::getPrecision(int index)
761761
{
762762
CAttrSqlVar *curVar = Var(index);
763-
// Issue #161: for INPUT parameters the conversion code mutates sqlvar
764-
// (via setTypeText/setSqlLen). Reading precision from the current
765-
// sqlvar makes `record->length` shrink after the first row was bound,
766-
// which then causes subsequent numeric→VARCHAR conversions to write
767-
// only the first character of multi-digit values (silent data loss).
768-
// The precision of a prepared parameter is immutable, so read the
769-
// length/type snapshot captured at prepare time for INPUT params.
770-
// SQL_ARRAY still needs the mutable CAttrSqlVar (for the `array`
771-
// pointer), so fall through to Var(index) in that case.
763+
// INPUT parameter precision is immutable from prepare time, even if the
764+
// conversion layer later mutates sqllen on the CAttrSqlVar (e.g. the
765+
// SQL_TEXT branch of writeStringData reports the effective length that
766+
// way). Read from the orgSqlProperties snapshot so re-binding never
767+
// sees a shrunken precision — this mirrors getColumnDisplaySize above.
768+
// OUTPUT always uses the current sqlvar; SQL_ARRAY needs the mutable
769+
// CAttrSqlVar for its `array` pointer.
772770
const SqlProperties *var =
773771
(SqldaDir == SQLDA_INPUT && curVar->sqltype != SQL_ARRAY)
774772
? orgVarSqlProperties(index)

0 commit comments

Comments
 (0)