Firebird/IBPP 静默插入失败
Firebird/IBPP Insert Fails Silently
我有一个 C++ 应用程序,它通过 IBPP 库使用嵌入式 Firebird 数据库(例如,使用 Windows 上的 fbembed.dll 进行嵌入式使用)。我目前使用的是 Firebird 的 2.5.3 版和 IBPP 的 2.5.3.1 版。我遇到的问题是,当我尝试插入由于列大小问题或违反约束(例如,插入应该失败)而无法插入的数据时,我没有收到任何错误或插入失败的指示。
作为一个(简化但具有代表性的)例子,我有一个 table 像:
create table USER_TABLE (
ID BIGINT not null,
USER_ID VARCHAR(10) not null,
DISPLAY_NAME VARCHAR(50) not null,
-- etc...
primary key (ID),
unique (USER_ID)
);
ID 列由插入前触发器中的生成器填充。现在我尝试使用以下代码从我的应用程序中插入:
const string SQL_STMT = "insert into USER_TABLE "
"(USER_ID, DISPLAY_NAME, etc) "
"values (?, ?, ?);";
IBPP::Statement stmt = IBPP::StatementFactory(m_db, m_tr, SQL_STMT);
stmt->Set(1, userDataObject.getUserId());//returns const string&,
stmt->Set(2, userDataObject.getDisplayName());//returns const string&
//etc, etc...
stmt->Execute();
如果我的 userDataObject.getUserId()
值是一个对于像 1234567890xxx
这样的列来说太长的字符串,我将不会像我期望的那样得到异常。我可以(显然成功地)在执行插入后使用 select ID from user_table where ...
或通过上面 SQL_STMT
变量末尾的 ... returning ID
在同一事务中获取 ID。但是一旦提交事务并且代码的其他部分尝试获取 table 中的值,它就不存在了。我从来没有得到 error/exception.
知道为什么我在这些情况下没有收到错误消息吗?
几周后,我和一位同事在调查一个略有不同的问题时偶然发现了这个问题的原因。我们 work/testing 使用 64 位 Microsoft Visual Studio C++ 工具链。 Firebird 通过 ISC_STATUS
类型的数组来库 return 状态和错误信息。库将其定义为 intptr_t
的类型定义,它是指针大小的,因此在 64 位系统上是 64 位。
IBPP 库动态加载 Firebird 客户端库。当它定义 ISC_STATUS
的版本时,它会将其定义为 long
。在 Linux/GCC 上,我认为这可行(例如,在 32 位架构上为 32 位,在 64 位架构上为 64 位),但在 Microsoft 的 Visual C++ 中 long
始终为 32 位。结果是,当我们在 64 位配置中编译 运行 时,它会将 64 位整数数组解释为 32 位整数数组。结果是,由于放入数组的所有值都是适合 32 位的正整数,因此状态数组中的每个其他值似乎都是 0。IBPP 通过检查 status 数组中的非零值来检测错误因此,数组中的第二个点在 64 位处理中始终为零。
解决方案是更新 IBPP 的 ISC_STATUS
定义以正确匹配 Firebird 客户端库正在使用的内容(这在 'ibase.h' 头文件中)。
我有一个 C++ 应用程序,它通过 IBPP 库使用嵌入式 Firebird 数据库(例如,使用 Windows 上的 fbembed.dll 进行嵌入式使用)。我目前使用的是 Firebird 的 2.5.3 版和 IBPP 的 2.5.3.1 版。我遇到的问题是,当我尝试插入由于列大小问题或违反约束(例如,插入应该失败)而无法插入的数据时,我没有收到任何错误或插入失败的指示。
作为一个(简化但具有代表性的)例子,我有一个 table 像:
create table USER_TABLE (
ID BIGINT not null,
USER_ID VARCHAR(10) not null,
DISPLAY_NAME VARCHAR(50) not null,
-- etc...
primary key (ID),
unique (USER_ID)
);
ID 列由插入前触发器中的生成器填充。现在我尝试使用以下代码从我的应用程序中插入:
const string SQL_STMT = "insert into USER_TABLE "
"(USER_ID, DISPLAY_NAME, etc) "
"values (?, ?, ?);";
IBPP::Statement stmt = IBPP::StatementFactory(m_db, m_tr, SQL_STMT);
stmt->Set(1, userDataObject.getUserId());//returns const string&,
stmt->Set(2, userDataObject.getDisplayName());//returns const string&
//etc, etc...
stmt->Execute();
如果我的 userDataObject.getUserId()
值是一个对于像 1234567890xxx
这样的列来说太长的字符串,我将不会像我期望的那样得到异常。我可以(显然成功地)在执行插入后使用 select ID from user_table where ...
或通过上面 SQL_STMT
变量末尾的 ... returning ID
在同一事务中获取 ID。但是一旦提交事务并且代码的其他部分尝试获取 table 中的值,它就不存在了。我从来没有得到 error/exception.
知道为什么我在这些情况下没有收到错误消息吗?
几周后,我和一位同事在调查一个略有不同的问题时偶然发现了这个问题的原因。我们 work/testing 使用 64 位 Microsoft Visual Studio C++ 工具链。 Firebird 通过 ISC_STATUS
类型的数组来库 return 状态和错误信息。库将其定义为 intptr_t
的类型定义,它是指针大小的,因此在 64 位系统上是 64 位。
IBPP 库动态加载 Firebird 客户端库。当它定义 ISC_STATUS
的版本时,它会将其定义为 long
。在 Linux/GCC 上,我认为这可行(例如,在 32 位架构上为 32 位,在 64 位架构上为 64 位),但在 Microsoft 的 Visual C++ 中 long
始终为 32 位。结果是,当我们在 64 位配置中编译 运行 时,它会将 64 位整数数组解释为 32 位整数数组。结果是,由于放入数组的所有值都是适合 32 位的正整数,因此状态数组中的每个其他值似乎都是 0。IBPP 通过检查 status 数组中的非零值来检测错误因此,数组中的第二个点在 64 位处理中始终为零。
解决方案是更新 IBPP 的 ISC_STATUS
定义以正确匹配 Firebird 客户端库正在使用的内容(这在 'ibase.h' 头文件中)。