更新时间:2026-10-21 GMT+08:00
分享

SQLDA

SQLDA(SQL Descriptor Area,SQL描述符区域)是一个C语言结构体,用于动态处理SQL查询的结果集或输入参数。它允许程序在运行时灵活处理编译期未知数量和类型的列或参数。

在嵌入式SQL程序中使用SQLDA,需要导入对应的头文件:

EXEC SQL INCLUDE sqlda.h;
通过SQLDA提取游标数据的语句如下:
sqlda_t         *mysqlda;  
EXEC SQL FETCH 3 FROM mycursor INTO DESCRIPTOR mysqlda;
注意此处SQL关键词被省略了,命名SQL描述符区域章节中关于INTO和USING关键词的用例在一定条件下也适用。在一个DESCRIBE语句中,如果使用了INTO关键词,则DESCRIPTOR关键词可以省略。
EXEC SQL DESCRIBE prepared_statement INTO mysqlda;

SQLDA相关结构体定义

SQLDA 的核心数据结构包括sqlda_t、sqlvar_t和struct sqlname。

  • sqlda_t结构

    sqlda_t结构体用于存储查询结果集的整体信息。其定义如下:

    struct sqlda_struct {
        char sqldaid[8];                /* SQLDA标识符,固定为"SQLDA   " */
        long sqldabc;                   /* 已分配空间的总大小(字节数) */
        short sqln;                     /* 已分配的sqlvar数组容量或输入参数数量 */
        short sqld;                     /* 结果集中实际的列数 */
        struct sqlda_struct *desc_next; /* 指向下一行数据的SQLDA结构体指针(链表结构) */
        struct sqlvar_struct sqlvar[1]; /* 存储每列元数据与实际数据的变长数组 */
    };
    typedef struct sqlda_struct sqlda_t;

    字段解释:

    • sqldaid:SQLDA的标识符,通常是固定字符串“SQLDA ”。
    • sqldabc:已分配的空间大小,单位为字节。
    • sqln:输入参数的数量,或者在处理查询结果时,表示结果集中的列数。
    • sqld:结果集中的列数。
    • desc_next:用于链式存储多个结果集。
    • sqlvar:存储每个列的数据和元信息。
  • sqlvar_t结构

    sqlvar_t结构体保存单个列的值及其元数据(如类型和长度)。其定义如下:

    struct sqlvar_struct {
        short sqltype;          /* 列的数据类型标识符(如ECPGt_char、ECPGt_int等) */
        short sqllen;           /* 列数据的二进制字节长度 */
        char *sqldata;          /* 指向实际存储列数据的内存缓冲区指针 */
        short *sqlind;          /* 指向空值指示符(Indicator)的指针(0表示非空,-1表示NULL) */
        struct sqlname sqlname; /* 列名称结构体 */
    };
    typedef struct sqlvar_struct sqlvar_t;

    字段解释:

    • sqltype:列的数据类型(如整数、字符等)。
    • sqllen:列的数据长度。
    • sqldata:数据存储缓冲区的内存地址指针。
    • sqlind:指示该列是否为空,0表示非空,-1表示为空。
    • sqlname:列的名称。
  • struct sqlname结构

    sqlname结构体保存列的名称。其定义如下:

    #define NAMEDATALEN 64
    struct sqlname {
        short length;           /* 列名字符串的实际长度 */
        char data[NAMEDATALEN]; /* 存放列名字符串的字符数组 */
    };

    字段解释:

    • length:列名的长度。
    • data:列名字符串。

SQLDA检索结果集

通过SQLDA检索查询结果集的一般步骤:

  1. 声明一个sqlda_t结构来接收结果集。
  2. 执行FETCH、EXECUTE或DESCRIBE命令来处理一个已声明SQLDA的查询。
  3. 通过查看sqlda_t结构的成员sqln来获取当前结果集中的实际列数。
  4. 从sqlda_t结构体的成员sqlvar[0]、sqlvar[1]...中得到每一列的值。
  5. 沿着sqlda_t结构的成员desc_next指针到达下一行(sqlda_t)。
  6. 根据需要重复上述步骤。
示例如下:
/* 声明一个sqlda_t结构来接收结果集。*/
sqlda_t *sqlda1;

/* 指定一个命令中的SQLDA。如下是一个FETCH命令的例子。*/
EXEC SQL FETCH NEXT FROM cur1 INTO DESCRIPTOR sqlda1;

/* 运行一个循环顺着链表来检索行。*/
sqlda_t *cur_sqlda;
for (cur_sqlda = sqlda1;
     cur_sqlda != NULL;
     cur_sqlda = cur_sqlda->desc_next)
{
    ...
}

/* 在循环内部,运行另一个循环来检索行中每一列的数据(sqlvar_t结构)。*/
for (i = 0; i < cur_sqlda->sqld; i++)
{
    sqlvar_t v = cur_sqlda->sqlvar[i];
    char *sqldata = v.sqldata;
    short sqllen  = v.sqllen;
    ...
}

/* 要得到一列的值,应检查sqlvar_t结构的成员sqltype的值。然后,根据列类型切换到一种合适的方法从sqlvar域中复制数据到一个主变量。*/
char var_buf[1024];
switch (v.sqltype)
{
    case ECPGt_char:
        memset(&var_buf, 0, sizeof(var_buf));
        memcpy(&var_buf, sqldata, (sizeof(var_buf) <= sqllen ? sizeof(var_buf) - 1 : sqllen));
        break;

    case ECPGt_int: 
        memcpy(&intval, sqldata, sqllen);
        snprintf(var_buf, sizeof(var_buf), "%d", intval);
        break;
    ...
}

使用SQLDA传递查询参数

使用SQLDA传递输入参数给一个预备查询的一般步骤如下:

  1. 创建一个预备查询(PREPARE语句)。
  2. 声明一个sqlda_t结构体作为SQLDA。
  3. 为SQLDA分配内存区域。
  4. 在分配好的内存中设置(复制)输入值。
  5. 打开一个在SQLDA上声明的游标。
  6. 打开游标或执行语句时通过USING DESCRIPTOR传入SQLDA。
  7. 释放内存。
示例如下:
/* 首先,创建一个预备语句。 */
    EXEC SQL BEGIN DECLARE SECTION;
    char query[1024] = "SELECT d.oid, * FROM pg_database d, pg_stat_database s WHERE d.oid = s.datid AND (d.datname = ? OR d.oid = ?)";
    EXEC SQL END DECLARE SECTION;
    EXEC SQL PREPARE stmt1 FROM :query;

    /* 为一个SQLDA分配内存,并且在sqlda_t结构的sqln成员变量中设置输入参数的数量。
     * 当预备查询要求两个或多个输入参数时,应用必须分配额外的内存空间,空间的大小为 (参数数目 - 1) * sizeof(sqlvar_t)。
     * 该示例展示了为两个输入参数分配内存空间。
     */
    sqlda_t *sqlda2;
    sqlda2 = (sqlda_t *) malloc(sizeof(sqlda_t) + sizeof(sqlvar_t));
    memset(sqlda2, 0, sizeof(sqlda_t) + sizeof(sqlvar_t));
    sqlda2->sqln = 2; /* 输入变量的数目 */

    /* 内存分配之后,把参数值存储到sqlvar[]数组(当SQLDA在接收结果集时,这也是用来检索列值的数组)。
     在这个例子中,输入参数是"testdb"(字符串类型)和1(整数类型)。*/
    sqlda2->sqlvar[0].sqltype = ECPGt_char;
    sqlda2->sqlvar[0].sqldata = "testdb";
    sqlda2->sqlvar[0].sqllen  = 8;
    int intval = 1;
    sqlda2->sqlvar[1].sqltype = ECPGt_int;
    sqlda2->sqlvar[1].sqldata = (char *) &intval;
    sqlda2->sqlvar[1].sqllen  = sizeof(intval);

    /* 通过打开一个游标并且说明之前已经建立好的SQLDA,输入参数被传递给预备语句。*/
    EXEC SQL OPEN cur1 USING DESCRIPTOR sqlda2;

    /* 最后,用完输入SQLDA后必须显式地释放已分配的内存空间,这与用于接收查询结果的SQLDA不同。*/
    free(sqlda2);

相关文档