8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-02 10:40:38 +01:00

Fixed #7298: Info result parsing

This commit is contained in:
AlexPeshkoff 2022-09-28 19:57:19 +03:00
parent a46096d447
commit f75ff97b66
10 changed files with 126 additions and 174 deletions

View File

@ -288,22 +288,22 @@ void TDR_list_limbo(FB_API_HANDLE handle, const TEXT* name, const SINT64 switche
TraNumber id;
tdr* trans;
UCHAR* ptr = buffer;
bool flag = true;
while (flag)
for (Firebird::ClumpletReader p(Firebird::ClumpletReader::InfoResponse, buffer, sizeof(buffer));
!p.isEof(); p.moveNext())
{
const USHORT item = *ptr++;
const USHORT length = (USHORT) gds__vax_integer(ptr, 2);
ptr += 2;
UCHAR item = p.getClumpTag();
if (item == isc_info_end)
break;
const USHORT length = (USHORT) p.getClumpLength();
switch (item)
{
case isc_info_limbo:
id = isc_portable_integer(ptr, length);
id = p.getBigInt();
if (switches & (sw_commit | sw_rollback | sw_two_phase | sw_prompt))
{
TDR_reconnect_multiple(handle, id, name, switches);
ptr += length;
break;
}
if (!tdgbl->uSvc->isService())
@ -326,7 +326,6 @@ void TDR_list_limbo(FB_API_HANDLE handle, const TEXT* name, const SINT64 switche
tdgbl->uSvc->putSInt64(isc_spb_single_tra_id_64, id);
else
tdgbl->uSvc->putSLong(isc_spb_single_tra_id, (SLONG) id);
ptr += length;
break;
case isc_info_truncated:
@ -336,10 +335,6 @@ void TDR_list_limbo(FB_API_HANDLE handle, const TEXT* name, const SINT64 switche
// msg 72: More limbo transactions than fit. Try again
// And how it's going to retry with a bigger buffer if the buffer is fixed size?
}
// fall through
case isc_info_end:
flag = false;
break;
default:

View File

@ -11636,8 +11636,6 @@ IBatch* WriteRelationMeta::createBatch(BurpGlobals* tdgbl, IAttachment* att)
ClumpletReader rdr(ClumpletReader::InfoResponse, infoBuf, sizeof infoBuf);
for (rdr.rewind(); !rdr.isEof(); rdr.moveNext())
{
if (rdr.getClumpTag() == isc_info_end)
break;
switch (rdr.getClumpTag())
{
case IBatch::INF_BUFFER_BYTES_SIZE:

View File

@ -687,6 +687,20 @@ void ClumpletReader::moveNext()
if (isEof())
return; // no need to raise useless exceptions
FB_SIZE_T cs = getClumpletSize(true, true, true);
switch (kind)
{
case InfoResponse:
switch (getClumpTag())
{
case isc_info_end:
case isc_info_truncated:
// terminating clumplet
cur_offset = getBufferLength();
return;
}
}
adjustSpbState();
cur_offset += cs;
}

View File

@ -6160,22 +6160,22 @@ void ISQL_get_version(bool call_by_create_db)
return;
}
const UCHAR* p = buffer;
while (*p != isc_info_end && *p != isc_info_truncated && p < buffer + sizeof(buffer))
for (Firebird::ClumpletReader p(Firebird::ClumpletReader::InfoResponse, buffer, sizeof(buffer)); !p.isEof(); p.moveNext())
{
const UCHAR item = (UCHAR) *p++;
const USHORT length = gds__vax_integer(p, sizeof(USHORT));
p += sizeof(USHORT);
UCHAR item = p.getClumpTag();
if (item == isc_info_end)
break;
switch (item)
{
case isc_info_ods_version:
isqlGlob.major_ods = gds__vax_integer(p, length);
isqlGlob.major_ods = p.getInt();
break;
case isc_info_ods_minor_version:
isqlGlob.minor_ods = gds__vax_integer(p, length);
isqlGlob.minor_ods = p.getInt();
break;
case isc_info_db_sql_dialect:
global_dialect_spoken = gds__vax_integer(p, length);
global_dialect_spoken = p.getInt();
if (isqlGlob.major_ods < ODS_VERSION10)
{
if (isqlGlob.SQL_dialect > SQL_DIALECT_V5 && setValues.Warnings)
@ -6224,7 +6224,7 @@ void ISQL_get_version(bool call_by_create_db)
case isc_info_error:
// Error indicates an option was not understood by the
// remote server.
if (*p == isc_info_firebird_version)
if (p.getBytes()[0] == isc_info_firebird_version)
{
// must be an old or non Firebird server
break;
@ -6270,8 +6270,8 @@ void ISQL_get_version(bool call_by_create_db)
// to put it all. It's a FULL or NOTHING answer. It grows with redirection.
// The command SHOW version that calls isc_version() will return more info.
isqlGlob.printf("Server version:%s", NEWLINE);
const UCHAR* q = p; // We don't want to spoil p with a wrong calculation.
const UCHAR* limit = q + length;
const UCHAR* q = p.getBytes(); // We don't want to spoil p with a wrong calculation.
const UCHAR* limit = q + p.getClumpLength();
for (int times = *q++; times && q < limit; --times)
{
int l = *q++;
@ -6285,7 +6285,7 @@ void ISQL_get_version(bool call_by_create_db)
break;
case frb_info_att_charset:
isqlGlob.att_charset = gds__vax_integer(p, length);
isqlGlob.att_charset = p.getInt();
break;
default:
@ -6293,7 +6293,6 @@ void ISQL_get_version(bool call_by_create_db)
item, NEWLINE);
break;
}
p += length;
}
if (isqlGlob.major_ods < ODS_VERSION8)

View File

@ -46,6 +46,7 @@
#include "../jrd/intl.h"
#include "../common/intlobj_new.h"
#include "../common/classes/AlignedBuffer.h"
#include "../common/classes/ClumpletReader.h"
#include "../isql/isql_proto.h"
#include "../isql/show_proto.h"
#include "../isql/iutils_proto.h"
@ -408,12 +409,12 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
*info_buf = '\0';
SCHAR* info = info_buf;
for (const UCHAR* d = buffer; *d != isc_info_end;)
for (Firebird::ClumpletReader p(Firebird::ClumpletReader::InfoResponse, buffer, sizeof(buffer)); !p.isEof(); p.moveNext())
{
UCHAR item = p.getClumpTag();
SINT64 value_out = 0;
const UCHAR item = *d++;
const int length = ISQL_vax_integer(d, 2);
d += 2;
/*
* This is not the best solution but it fixes the lack of <LF> characters
* in Windows ISQL. This will need to remain until we modify the messages
@ -426,12 +427,12 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
break;
case isc_info_page_size:
value_out = ISQL_vax_integer(d, length);
value_out = p.getInt();
sprintf(info, "PAGE_SIZE %" SQUADFORMAT"%s", value_out, separator);
break;
case isc_info_db_size_in_pages:
value_out = ISQL_vax_integer(d, length);
value_out = p.getInt();
if (translate)
{
IUTILS_msg_get(NUMBER_PAGES, msg, SafeArg() << value_out);
@ -442,7 +443,7 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
break;
case fb_info_pages_used:
value_out = ISQL_vax_integer(d, length);
value_out = p.getInt();
if (translate)
{
IUTILS_msg_get(NUMBER_USED_PAGES, msg, SafeArg() << value_out);
@ -453,7 +454,7 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
break;
case fb_info_pages_free:
value_out = ISQL_vax_integer(d, length);
value_out = p.getInt();
if (translate)
{
IUTILS_msg_get(NUMBER_FREE_PAGES, msg, SafeArg() << value_out);
@ -464,8 +465,7 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
break;
case fb_info_crypt_state:
value_out = ISQL_vax_integer(d, length);
value_out = p.getInt();
if (translate)
{
Firebird::string s;
@ -495,7 +495,7 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
break;
case isc_info_sweep_interval:
value_out = ISQL_vax_integer(d, length);
value_out = p.getInt();
if (translate)
{
IUTILS_msg_get(SWEEP_INTERV, msg, SafeArg() << value_out);
@ -506,32 +506,32 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
break;
case isc_info_forced_writes:
value_out = ISQL_vax_integer (d, length);
value_out = p.getInt();
sprintf (info, "Forced Writes are %s%s", (value_out == 1 ? "ON" : "OFF"), separator);
break;
case isc_info_oldest_transaction :
value_out = ISQL_vax_integer (d, length);
value_out = p.getInt();
sprintf(info, "Transaction - oldest = %" SQUADFORMAT"%s", value_out, separator);
break;
case isc_info_oldest_active :
value_out = ISQL_vax_integer (d, length);
value_out = p.getInt();
sprintf(info, "Transaction - oldest active = %" SQUADFORMAT"%s", value_out, separator);
break;
case isc_info_oldest_snapshot :
value_out = ISQL_vax_integer (d, length);
value_out = p.getInt();
sprintf(info, "Transaction - oldest snapshot = %" SQUADFORMAT"%s", value_out, separator);
break;
case isc_info_next_transaction :
value_out = ISQL_vax_integer (d, length);
value_out = p.getInt();
sprintf (info, "Transaction - Next = %" SQUADFORMAT"%s", value_out, separator);
break;
case isc_info_base_level:
value_out = ISQL_vax_integer(d, length);
value_out = p.getInt();
if (translate)
{
IUTILS_msg_get(BASE_LEVEL, msg, SafeArg() << value_out);
@ -542,7 +542,7 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
break;
case isc_info_limbo:
value_out = ISQL_vax_integer(d, length);
value_out = p.getInt();
if (translate)
{
IUTILS_msg_get(LIMBO, msg, SafeArg() << value_out);
@ -553,21 +553,21 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
break;
case isc_info_ods_version:
isqlGlob.major_ods = ISQL_vax_integer(d, length);
isqlGlob.major_ods = p.getInt();
break;
case isc_info_ods_minor_version:
value_out = ISQL_vax_integer(d, length);
value_out = p.getInt();
sprintf(info, "ODS = %" SLONGFORMAT".%" SQUADFORMAT"%s",
(SLONG) isqlGlob.major_ods, value_out, separator);
break;
case fb_info_wire_crypt:
if (d && length)
sprintf (info, "Wire crypt plugin: %.*s%s", length, d, separator);
if (p.getClumpLength())
sprintf (info, "Wire crypt plugin: %.*s%s", p.getClumpLength(), p.getBytes(), separator);
break;
case fb_info_protocol_version:
value_out = ISQL_vax_integer(d, length);
value_out = p.getInt();
if (value_out)
sprintf(info, "Protocol version = %" SQUADFORMAT"%s", value_out, separator);
else
@ -575,14 +575,14 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
break;
case isc_info_creation_date:
if (length == sizeof(ISC_TIMESTAMP) && !crdatePrinted)
if (p.getClumpLength() == sizeof(ISC_TIMESTAMP) && !crdatePrinted)
{
ISC_TIMESTAMP ts;
const UCHAR* p = d;
ts.timestamp_date = ISQL_vax_integer(p, sizeof(ISC_DATE));
p += sizeof(ISC_DATE);
ts.timestamp_time = ISQL_vax_integer(p, sizeof(ISC_TIME));
const UCHAR* t = p.getBytes();
ts.timestamp_date = ISQL_vax_integer(t, sizeof(ISC_DATE));
t += sizeof(ISC_DATE);
ts.timestamp_time = ISQL_vax_integer(t, sizeof(ISC_TIME));
struct tm time;
isc_decode_timestamp(&ts, &time);
@ -594,16 +594,16 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
break;
case fb_info_creation_timestamp_tz:
if (length == sizeof(ISC_TIMESTAMP_TZ))
if (p.getClumpLength() == sizeof(ISC_TIMESTAMP))
{
ISC_TIMESTAMP_TZ tsz;
const UCHAR* p = d;
tsz.utc_timestamp.timestamp_date = ISQL_vax_integer(p, sizeof(ISC_DATE));
p += sizeof(ISC_DATE);
tsz.utc_timestamp.timestamp_time = ISQL_vax_integer(p, sizeof(ISC_TIME));
p += sizeof(ISC_TIME);
tsz.time_zone = ISQL_vax_integer(p, sizeof(ULONG));
const UCHAR* t = p.getBytes();
tsz.utc_timestamp.timestamp_date = ISQL_vax_integer(t, sizeof(ISC_DATE));
t += sizeof(ISC_DATE);
tsz.utc_timestamp.timestamp_time = ISQL_vax_integer(t, sizeof(ISC_TIME));
t += sizeof(ISC_TIME);
tsz.time_zone = ISQL_vax_integer(t, sizeof(ULONG));
unsigned year, month, day, hours, minutes, seconds, fractions;
char timeZone[Firebird::TimeZoneUtil::MAX_SIZE];
@ -627,8 +627,8 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
{
// Will print with garbage for now.
//It's sprintf(info, "DB/Host = %.*s", length, d);
const UCHAR* s = d;
const UCHAR* end = s + length;
const UCHAR* s = p.getBytes();
const UCHAR* end = s + p.getClumpLength();
++s; // Skip useless indicator.
int len = *s++;
printf("DB = %.*s\n", len, s);
@ -646,8 +646,6 @@ bool SHOW_dbb_parameters(Firebird::IAttachment* db_handle,
case isc_info_truncated:
return info > info_buf; // If we got some items, we are (partially) successful.
}
d += length;
info += strlen(info);
}
@ -6406,17 +6404,10 @@ static processing_state show_users()
processing_state rc = OBJECT_NOT_FOUND;
for (const UCHAR* data = buffer; *data != isc_info_end;)
for (Firebird::ClumpletReader p(Firebird::ClumpletReader::InfoResponse, buffer, sizeof(buffer)); !p.isEof(); p.moveNext())
{
const UCHAR item = *data++;
const int length = gds__vax_integer(data, 2);
data += 2;
switch (item)
switch (p.getClumpTag())
{
case isc_info_end:
break;
case isc_info_user_names:
{
if (rc == OBJECT_NOT_FOUND)
@ -6427,9 +6418,9 @@ static processing_state show_users()
rc = SKIP; // We found at least one user.
}
int len = *data;
fb_assert(len == length - 1);
const UCHAR* uname = data + 1;
unsigned len = p.getBytes()[0];
fb_assert(len == p.getClumpLength() - 1);
const UCHAR* uname = p.getBytes() + 1;
// Let's mark all attachments with our same user with a # prefix.
bool same(len == my_user->vary_length && !memcmp(my_user->vary_string, uname, len));
isqlGlob.printf("%c %.*s", same ? '#' : ' ', len, uname);
@ -6444,10 +6435,6 @@ static processing_state show_users()
isqlGlob.printf("%s\n", msg);
return rc; // If we got some items, we are (partially) successful.
}
data += length;
if (data >= buffer + sizeof(buffer))
break;
}
return rc;

View File

@ -124,21 +124,15 @@ bool checkCreateDatabaseGrant(const MetaString& userName, const MetaString& trus
check("IAttachment::getInfo", &st);
int dialect = SQL_DIALECT_V5; // reasonable default
const UCHAR* p = buffer;
while (*p != isc_info_end && *p != isc_info_truncated && p < buffer + sizeof(buffer))
{
const UCHAR item = (UCHAR) *p++;
const USHORT length = gds__vax_integer(p, sizeof(USHORT));
p += sizeof(USHORT);
switch (item)
for (ClumpletReader p(ClumpletReader::InfoResponse, buffer, sizeof(buffer)); !p.isEof(); p.moveNext())
{
switch (p.getClumpTag())
{
case isc_info_db_sql_dialect:
dialect = gds__vax_integer(p, length);
dialect = p.getInt();
break;
}
p += length;
}
UserId::makeRoleName(role, dialect);

View File

@ -158,37 +158,33 @@ void IscConnection::attach(thread_db* tdbb)
memset(m_features, false, sizeof(m_features));
m_sqlDialect = 1;
const unsigned char* p = buff, *end = buff + sizeof(buff);
while (p < end)
for (ClumpletReader p(ClumpletReader::InfoResponse, buff, sizeof(buff)); !p.isEof(); p.moveNext())
{
const UCHAR item = *p++;
const USHORT len = m_iscProvider.isc_vax_integer(p, sizeof(USHORT));
p += sizeof(USHORT);
switch (item)
const UCHAR* b = p.getBytes();
switch (p.getClumpTag())
{
case isc_info_db_sql_dialect:
m_sqlDialect = m_iscProvider.isc_vax_integer(p, len);
m_sqlDialect = p.getInt();
break;
case fb_info_features:
for (int i = 0; i < len; i++)
for (unsigned i = 0; i < p.getClumpLength(); i++)
{
if (p[i] == 0)
if (b[i] == 0)
ERR_post(Arg::Gds(isc_random) << Arg::Str("Bad provider feature value"));
if (p[i] < fb_feature_max)
setFeature(static_cast<info_features>(p[i]));
if (b[i] < fb_feature_max)
setFeature(static_cast<info_features>(b[i]));
// else this provider supports unknown feature, ignore it.
}
break;
case isc_info_error:
{
const ULONG err = m_iscProvider.isc_vax_integer(p + 1, len - 1);
const ULONG err = m_iscProvider.isc_vax_integer(p.getBytes() + 1, p.getClumpLength() - 1);
if (err == isc_infunk)
{
if (*p == fb_info_features)
if (p.getBytes()[0] == fb_info_features)
{
// Used provider follow Firebird error reporting conventions but is not aware of
// this info item. Assume Firebird 3 or earlier.
@ -203,14 +199,8 @@ void IscConnection::attach(thread_db* tdbb)
case isc_info_truncated:
ERR_post(Arg::Gds(isc_random) << Arg::Str("Result truncation in isc_database_info"));
case isc_info_end:
p = end;
break;
}
p += len;
}
}
void IscConnection::doDetach(thread_db* tdbb)

View File

@ -2754,11 +2754,11 @@ static USHORT check_statement_type( Rsr* statement)
if (!(local_status.getState() & Firebird::IStatus::STATE_ERRORS))
{
for (const UCHAR* info = buffer; (*info != isc_info_end) && !done;)
for (ClumpletReader p(ClumpletReader::InfoResponse, buffer, sizeof(buffer)); !p.isEof(); p.moveNext())
{
const USHORT l = (USHORT) gds__vax_integer(info + 1, 2);
const USHORT type = (USHORT) gds__vax_integer(info + 3, l);
switch (*info)
const USHORT type = (USHORT) p.getInt();
switch (p.getClumpTag())
{
case isc_info_sql_stmt_type:
switch (type)
@ -2773,17 +2773,12 @@ static USHORT check_statement_type( Rsr* statement)
break;
}
break;
case isc_info_sql_batch_fetch:
if (type == 0)
ret |= STMT_NO_BATCH;
break;
case isc_info_error:
case isc_info_truncated:
done = true;
break;
}
info += 3 + l;
}
}

View File

@ -32,6 +32,7 @@
#include "../yvalve/MasterImplementation.h"
#include "../common/classes/rwlock.h"
#include "../common/classes/ClumpletReader.h"
#include "firebird/impl/inf_pub.h"
#include "../common/isc_proto.h"
#include "../jrd/acl.h"
@ -107,36 +108,26 @@ bool DTransaction::buildPrepareInfo(CheckStatusWrapper* status, TdrBuffer& tdr,
if (status->getState() & IStatus::STATE_ERRORS)
return false;
UCHAR* const end = bigBuffer.end();
while (buf < end)
for (ClumpletReader p(ClumpletReader::InfoResponse, buf, bigBuffer.getCount()); !p.isEof(); p.moveNext())
{
UCHAR item = buf[0];
++buf;
const USHORT length = (USHORT) gds__vax_integer(buf, 2);
const USHORT length = (USHORT) p.getClumpLength();
// Prevent information out of sync.
UCHAR lengthByte = length > MAX_UCHAR ? MAX_UCHAR : length;
buf += 2;
switch(item)
switch(p.getClumpTag())
{
case isc_info_tra_id:
tdr.add(TDR_TRANSACTION_ID);
tdr.add(lengthByte);
tdr.add(buf, lengthByte);
tdr.add(p.getBytes(), lengthByte);
break;
case fb_info_tra_dbpath:
tdr.add(TDR_DATABASE_PATH);
tdr.add(lengthByte);
tdr.add(buf, lengthByte);
tdr.add(p.getBytes(), lengthByte);
break;
case isc_info_end:
return true;
}
buf += length;
}
return true;

View File

@ -458,29 +458,23 @@ void UtilInterface::getFbVersion(CheckStatusWrapper* status, IAttachment* att,
if (status->getState() & Firebird::IStatus::STATE_ERRORS)
return;
const UCHAR* p = buf;
redo = false;
ClumpletReader p(ClumpletReader::InfoResponse, buf, buf_len);
while (!redo && *p != isc_info_end && p < buf + buf_len)
for (redo = false; !(redo || p.isEof()); p.moveNext())
{
const UCHAR item = *p++;
const USHORT len = static_cast<USHORT>(gds__vax_integer(p, 2));
p += 2;
switch (item)
switch (p.getClumpTag())
{
case isc_info_firebird_version:
versions = (TEXT*) p;
versions = (TEXT*) p.getBytes();
break;
case isc_info_implementation:
implementations = (TEXT*) p;
implementations = (TEXT*) p.getBytes();
break;
case fb_info_implementation:
dbis = p;
if (dbis[0] * 6 + 1 > len)
dbis = p.getBytes();
if (dbis[0] * 6u + 1u > p.getClumpLength())
{
// fb_info_implementation value appears incorrect
dbis = NULL;
@ -493,13 +487,13 @@ void UtilInterface::getFbVersion(CheckStatusWrapper* status, IAttachment* att,
case isc_info_truncated:
redo = true;
// fall down...
case isc_info_end:
break;
default:
(Arg::Gds(isc_random) << "Invalid info item").raise();
}
p += len;
}
// Our buffer wasn't large enough to hold all the information,
@ -1441,37 +1435,35 @@ int API_ROUTINE gds__blob_size(FB_API_HANDLE* b, SLONG* size, SLONG* seg_count,
*
**************************************/
ISC_STATUS_ARRAY status_vector;
SCHAR buffer[64];
UCHAR buffer[64];
if (isc_blob_info(status_vector, b, sizeof(blob_items), blob_items, sizeof(buffer), buffer))
if (isc_blob_info(status_vector, b, sizeof(blob_items), blob_items, sizeof(buffer), (SCHAR*)buffer))
{
isc_print_status(status_vector);
return FALSE;
}
const UCHAR* p = reinterpret_cast<UCHAR*>(buffer);
UCHAR item;
while ((item = *p++) != isc_info_end)
for (ClumpletReader p(ClumpletReader::InfoResponse, buffer, sizeof(buffer)); !p.isEof(); p.moveNext())
{
const USHORT l = gds__vax_integer(p, 2);
p += 2;
const SLONG n = gds__vax_integer(p, l);
p += l;
UCHAR item = p.getClumpTag();
if (item == isc_info_end)
break;
switch (item)
{
case isc_info_blob_max_segment:
if (max_seg)
*max_seg = n;
*max_seg = p.getInt();
break;
case isc_info_blob_num_segments:
if (seg_count)
*seg_count = n;
*seg_count = p.getInt();
break;
case isc_info_blob_total_length:
if (size)
*size = n;
*size = p.getInt();
break;
default:
@ -2976,16 +2968,10 @@ static void get_ods_version(CheckStatusWrapper* status, IAttachment* att,
if (status->getState() & Firebird::IStatus::STATE_ERRORS)
return;
const UCHAR* p = buffer;
UCHAR item;
while ((item = *p++) != isc_info_end)
for (ClumpletReader p(ClumpletReader::InfoResponse, buffer, sizeof(buffer)); !p.isEof(); p.moveNext())
{
const USHORT l = static_cast<USHORT>(gds__vax_integer(p, 2));
p += 2;
const USHORT n = static_cast<USHORT>(gds__vax_integer(p, l));
p += l;
switch (item)
const USHORT n = static_cast<USHORT>(p.getInt());
switch (p.getClumpTag())
{
case isc_info_ods_version:
*ods_version = n;
@ -2995,6 +2981,9 @@ static void get_ods_version(CheckStatusWrapper* status, IAttachment* att,
*ods_minor_version = n;
break;
case isc_info_end:
break;
default:
(Arg::Gds(isc_random) << "Invalid info item").raise();
}