调用ODBC中SQLBindParameter引发的段错误

在使用ODBC进行数据库操作时,遇到调用SQLBindParameter进行参数绑定时出现段错误的问题。原因是传递了局部变量的地址作为参数长度,正确的做法是使用全局变量或在SQLExecute后释放。当StrLen_or_IndPtr指向SQL_DATA_AT_EXEC时,数据将通过SQLPutData发送。在执行SQL语句前,应确保绑定的存储位置有效且在同一个过程作用域内或全局声明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前提背景:

遇到了一个特别奇怪的问题,调用SQLBindParameter进行参数绑定是,最后一个参数交互的是绑定数据的长度,整数类型指针,随后我就传入了个局部遍历取地址传入,随后执行时程序说不定什么时候就莫名的段错误,最后查找文档和对这段代码进行调试,最后确定要传入的是全局变量,或当调用SQLExecute执行完SQL语句再释放,因为在调用 SQLExecute() 或 SQLExecDirect() 时,并且 StrLen_or_IndPtr (最后一个指针)指向 SQL_DATA_AT_EXEC 值时, 将使用 SQLPutData() 来发送数据。此参数称为执行数据参数。 以下红色标记,是对上述出现问题的准确回复。

参考文件:

DB2 通用数据库 iSeries 版 SQL 调用层接口(ODBC)

SQLBindParameter() 用来使 SQL 语句中的参数标记与应用程序变量相关联(绑定)。当调用SQLExecute()SQLExecDirect() 时,将把数据从应用程序传送至 DBMS。在传送数据时,可能会发生数据转换。

还必须使用此函数来将应用程序存储器绑定到存储过程 CALL 语句的参数,该参数可以是输入参数、输出参数或输入输出参数。此函数本质上是 SQLSetParam() 的扩展。

语法

SQLRETURN SQLBindParameter(SQLHSTMT          StatementHandle,
                           SQLSMALLINT      ParameterNumber,
                           SQLSMALLINT       InputOutputType,
                           SQLSMALLINT       ValueType,
                           SQLSMALLINT       ParameterType,
                           SQLINTEGER        ColumnSize,
                           SQLSMALLINT       DecimalDigits,
                           SQLPOINTER        ParameterValuePtr,
                           SQLINTEGER        BufferLength,
                           SQLINTEGER       *StrLen_or_IndPtr);

函数自变量

表 22. SQLBindParameter 自变量

数据类型自变量使用描述
SQLHSTMTStatementHandle输入语句句柄
SQLSMALLINTParameterNumber输入参数标记编号,此编号从 1 开始从左到右按顺序排序。
SQLSMALLINTInputOutputType输入参数的类型。IPD 的 SQL_DESC_PARAMETER_TYPE 字段的值也设置为此自变量。支持的类型包括:
  • SQL_PARAM_INPUT:参数标记与不是存储过程 CALL 的 SQL 语句相关联;或者,它标记调用(CALL)的存储过程的输入参数。

    执行语句时,将把参数的实际数据值发送至服务器:ParameterValuePtr 缓冲区必须包含有效的输入数据值;StrLen_or_IndPtr 缓冲区必须包含 SQL_NTS、SQL_NULL_DATA 或者(如果应当通过SQLParamData()SQLPutData() 发送值的话)SQL_DATA_AT_EXEC 的相应长度值。

  • SQL_PARAM_INPUT_OUTPUT:参数标记与调用(CALL)的存储过程的输入/输出参数相关联。

    执行语句时,将把参数的实际数据值发送至服务器:ParameterValuePtr 缓冲区必须包含有效的输入数据值;StrLen_or_IndPtr 缓冲区必须包含 SQL_NTS、SQL_NULL_DATA 或者(如果应当通过SQLParamData()SQLPutData() 发送值的话)SQL_DATA_AT_EXEC 的相应长度值。

  • SQL_PARAM_OUTPUT:参数标记与调用(CALL)的存储过程的输出参数或该存储过程的返回值相关联。

    在执行语句之后,将把输出参数的数据返回到 ParameterValuePtrStrLen_or_IndPtr 指定的应用程序缓冲区,除非它们都是空指针,如果是那样,将把输出数据废弃。如果输出参数不带返回值,则将StrLen_or_IndPtr 设置为 SQL_NULL_DATA。

SQLSMALLINTValueType输入参数的 C 数据类型。支持下列类型:
  • SQL_CHAR
  • SQL_VARCHAR
  • SQL_NUMERIC
  • SQL_DECIMAL
  • SQL_INTEGER
  • SQL_SMALLINT
  • SQL_BIGINT
  • SQL_FLOAT
  • SQL_REAL
  • SQL_DOUBLE
  • SQL_GRAPHIC
  • SQL_VARGRAPHIC
  • SQL_DATETIME
  • SQL_TYPE_DATE
  • SQL_TYPE_TIME
  • SQL_TYPE_TIMESTAMP
  • SQL_BLOB
  • SQL_CLOB
  • SQL_DBCLOB
  • SQL_BLOB_LOCATOR
  • SQL_CLOB_LOCATOR
  • SQL_DBCLOB_LOCATOR

指定 SQL_C_DEFAULT 将导致将数据由其缺省 C 数据类型转换为 ParameterType 指示的类型。

SQLSMALLINTParameterType输入参数的 SQL 数据类型。
SQLINTEGERColumnSize输入对应的参数标记的精度。如果ParameterType 指示:
  • 二进制或单字节字符串(例如 SQL_CHAR),则此自变量是此参数标记的最大长度(字节)。
  • 双字节字符串(例如 SQL_GRAPHIC),则此自变量是此参数的最大长度(以双字节字符数计)。
  • SQL_DECIMAL 或 SQL_NUMERIC,则此自变量是最大小数精度。
  • 否则,忽略此自变量。
SQLSMALLINTDecimalDigits输入相应参数的标度(如果ParameterType 是 SQL_DECIMAL 或 SQL_NUMERIC 的话)。如果 ParameterType 是 SQL_TYPE_TIMESTAMP,则这是时间戳记的字符表示法中小数点右边的位数(例如,yyyy-mm-dd hh:mm:ss.fff 的标度是 3)。

除了用于此处提到的 ParameterType 值之外,忽略 DecimalDigits

SQLPOINTERParameterValuePtr输入(延迟)和/或输出(延迟)
  • 对于输入(InputOutputType 设置为 SQL_PARAM_INPUT 或 SQL_PARAM_INPUT_OUTPUT):

    在执行时,如果 StrLen_or_IndPtr 不包含 SQL_NULL_DATA 或 SQL_DATA_AT_EXEC,则 ParameterValuePtr 指向包含参数的实际数据的缓冲区。

    如果 StrLen_or_IndPtr 包含 SQL_DATA_AT_EXEC,则 ParameterValuePtr 是应用程序定义的与此参数相关联的 32 位值。这个 32 位值通过后续的SQLParamData() 调用返回给应用程序。

    如果调用 SQLParamOptions() 来对参数指定多个值, 则 ParameterValuePtr 是指向 BufferLength 值的输入缓冲区数组的指针。

  • 对于输出(InputOutputType 设置为 SQL_PARAM_OUTPUT 或 SQL_PARAM_INPUT_OUTPUT):

    ParameterValuePtr 指向将用来存储存储过程的输出参数的缓冲区。

    如果 InputOutputType 设置为 SQL_PARAM_OUTPUT,并且 ParameterValuePtrStrLen_or_IndPtr 都是空指针,则废弃存储过程的输出参数值或返回值。

SQLINTEGERBufferLength输入不使用。
SQLINTEGER *StrLen_or_IndPtr输入(延迟)和/或输出(延迟)

如果这是输入或输入/输出参数:

这是指向一个位置的指针,(在执行语句时,)该位置包含存储在 ParameterValuePtr 处的参数标记值的长度。

要对参数标记指定空值,此存储器位置必须包含 SQL_NULL_DATA。

如果 ValueType 是 SQL_C_CHAR,则此存储位置必须包含存储在 ParameterValuePtr 处的数据的精确长度,或者,如果ParameterValuePtr 处的内容是以空终止的, 则必须包含 SQL_NTS。

如果 ValueType 指示 LOB 数据,则此存储位置必须包含存储在 ParameterValuePtr 处的数据的长度。

如果 ValueType 指示字符数据(显式指定,或使用 SQL_C_DEFAULT 隐式指定),并且此指针设置为空,则假定应用程序总是在ParameterValuePtr 中提供以空终止的字符串。这还暗示此参数标记永远不会具有空值。

在调用 SQLExecute()SQLExecDirect() 时,并且 StrLen_or_IndPtr 指向 SQL_DATA_AT_EXEC 值时,将使用SQLPutData() 来发送数据。此参数称为执行数据参数。

用法

参数标记由 SQL 语句中的“?”字符表示,它用来指示语句中的一个位置,执行语句时,将在该位置替代应用程序提供的值。这个值是从应用程序变量获取的。

在执行 SQL 语句之前,应用程序必须将变量绑定到 SQL 语句中的每个参数标记。对于此函数,ParameterValuePtrStrLen_or_IndPtr 是延迟自变量;在执行语句时,存储位置必须有效并且包含输入数据值。这意味着应该将 SQLExecDirect()SQLExecute() 调用与 SQLBindParameter() 调用置于同一个过程作用域中,或者,必须动态地分配或静态地或全局地声明这些存储位置。

参数标记是通过编号(ParameterNumber)引用的,并且是从 1 开始从左到右按顺序编号的。

此函数绑定的所有参数将一直有效,这将持续到使用 SQL_DROP 或 SQL_RESET_PARAMS 选项调用 SQLFreeStmt() 为止,或持续到对同一个参数编号ParameterNumber 再次调用 SQLBindParameter() 为止。

在执行 SQL 语句且处理结果之后,应用程序可能希望重新使用该语句句柄来执行另一个 SQL 语句。如果参数标记规格不同(参数数目、长度或类型),则应使用 SQL_RESET_PARAMS 来调用SQLFreeStmt() 以重设或清除参数绑定。

ValueType 给出的 C 缓冲区数据类型必须与 ParameterType 指示的 SQL 数据类型相兼容,否则会出错。

应用程序可以在 ParameterValuePtr 缓冲区中传送参数值,也可以通过调用一次或多次 SQLPutData() 来进行传送。在后一种情况下,这些参数是执行数据参数。应用程序通过将 SQL_DATA_AT_EXEC 值放入StrLen_or_IndPtr 缓冲区来将执行数据参数的情况通知 DB2 UDB CLI。它将 ParameterValuePtr 输入自变量设置成一个 32 位值,后续的SQLParamData() 调用将返回这个值,并且这个值可用来标识参数位置。

由于在执行语句之前不会对 ParameterValuePtrStrLen_or_IndPtr 引用的变量中的数据进行验证,所以在调用SQLExecute()SQLExecDirect() 之前不会检测或报告数据内容或格式错误。

SQLBindParameter() 在实质上是通过提供用于指定参数是输入、输入和输出或输出的方法来扩展 SQLSetParam() 函数的功能。要正确地处理存储过程的参数,此信息是必需的。

InputOutputType 自变量指定参数的类型。不调用过程的 SQL 语句中的所有参数都是输入参数。存储过程调用中的参数可以是输入、输入/输出或输出参数。虽然 DB2 存储过程自变量约定通常暗示所有过程自变量都是输入/输出自变量,但应用程序员仍可以选择在SQLBindParameter() 上更确切地指定输入或输出性质以遵循更严格的编码样式。并且,请注意,这些类型应该与使用 SQL CREATE PROCEDURE 语句注册存储过程时指定的参数类型一致。

  • 如果应用程序不能确定过程调用中的参数的类型,则将 InputOutputType 设置为 SQL_PARAM_INPUT;如果数据源对该参数返回一个值,则 DB2 UDB CLI 将其废弃。
  • 如果应用程序将一个参数标记为 SQL_PARAM_INPUT_OUTPUT 或 SQL_PARAM_OUTPUT,并且数据源没有返回值,DB2 UDB CLI 就会将StrLen_or_IndPtr 缓冲区设置为 SQL_NULL_DATA。
  • 如果应用程序将参数标记为 SQL_PARAM_OUTPUT,则在处理 CALL 语句之后将参数的数据返回给应用程序。如果 ParameterValuePtrStrLen_or_IndPtr 自变量都是空指针,则 DB2 UDB CLI 废弃输出值。如果数据源没有对某个输出参数返回值,DB2 UDB CLI 就会将StrLen_or_IndPtr 缓冲区设置为 SQL_NULL_DATA。
  • 对于此函数,ParameterValuePtrStrLen_or_IndPtr 都是延迟自变量。对于 InputOutputType 设置为 SQL_PARAM_INPUT 或 SQL_PARAM_INPUT_OUTPUT 的情况,在执行语句时,存储位置必须有效并且包含输入数据值。这意味着应该将SQLExecDirect()SQLExecute() 调用与 SQLBindParameter() 调用置于同一个过程作用域中,或者,必须动态地分配或静态地/全局地声明这些存储位置。

    同样,如果 InputOutputType 设置为 SQL_PARAM_OUTPUT 或 SQL_PARAM_INPUT_OUTPUT,则在执行 CALL 语句之前,ParameterValuePtr 和 StrLen_or_IndPtr 缓冲区位置必须保持有效。

应用程序可以在 ParameterValuePtr 缓冲区中传送参数值,也可以通过调用一次或多次 SQLPutData() 来进行传送。在后一种情况下,这些参数是执行数据参数。应用程序通过将 SQL_DATA_AT_EXEC 值放入 StrLen_or_IndPtr 缓冲区来将执行数据参数的情况通知 DB2 UDB CLI。它将ParameterValuePtr 输入自变量设置成一个 32 位值,后续的 SQLParamData() 调用将返回这个值,并且这个值可用来标识参数位置。

当使用 SQLBindParameter() 来将应用程序变量绑定到存储过程的输出参数时,如果在内存中将 ParameterValuePtr 缓冲区相邻地放在StrLen_or_IndPtr 缓冲区之后,则 DB2 UDB CLI 能提供一定程度的性能增强。例如:

    struct {  SQLINTEGER  StrLen_or_IndPtr;
              SQLCHAR     ParameterValuePtr[MAX_BUFFER];
           } column;

返回码

  • SQL_SUCCESS
  • SQL_SUCCESS_WITH_INFO
  • SQL_ERROR
  • SQL_INVALID_HANDLE 
文章来源: http://publib.boulder.ibm.com/iseries/v5r2/ic2989/index.htm?info/cli/rzadpmst50.htm

若是进行ODBC或DB2进行数据库开发的话,建议参考此文档,对开发工作经会大有裨益:

http://publib.boulder.ibm.com/iseries/v5r2/ic2989/index.htm?info/cli/rzadpmst50.htm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值