4、释放资源。 5.4.1 SQLDA 可以通过SQLDA为嵌入SQL语句提供输入数据和从嵌入SQ语句中输出数据。理解SQLDA的结构是理解动态SQL的关键。 我们知道,动态SQL语句在编译时可能不知道有多少列信息。在嵌入SQL语句中,这些不确定的数据是通过SQLDA完成的。SQLDA的结构非常灵活,在该结构的固定部分,指明了多少列等信息(如下图中的sqld=2,表示为两列信息),在该结构的后面,有一个可变长的结构(SQLVAR结构),说明每列的信息。
SQLDA结构 Sqld=2 sqlvar Desc_name Desc_occ Desc_next
Sqltype=500 Sqllen sqldata ….. Sqltype=501 Sqllen Sqldata …..
图6-6 SQLDA结构示例 具体SQLDA的结构在sqlda.h中定义,是: struct sqlvar_struct { short sqltype;/* variable type*/ short sqllen;/* length in bytes*/ char *sqldata;/* pointer to data*/ short *sqlind;/* pointer to indicator*/ char *sqlname;/* variable name*/ char *sqlformat;/* reserved for future use */ short sqlitype;/* ind variable type*/ short sqlilen;/* ind length in bytes*/ char *sqlidata;/* ind data pointer*/ };
struct sqlda { short sqld; struct sqlvar_struct *sqlvar; char desc_name[19];/* descriptor name */ short desc_occ;/* size of sqlda structure */ struct sqlda *desc_next;/* pointer to next sqlda struct */ };
#endif /* _SQLDA */ 从上面这个定义看出,SQLDA是一种由三个不同部分组成的可变长数据结构。位于SQLDA开端的sqldaid用于标志该SQLDA描述了多少列的信息;而后是一个或多个sqlvar结构 ,用于标志列数据。当用SQLDA把参数送到执行语句时,每一个参数都是一个sqlvar结构;当用SQLDA返回输出列信息时,每一列都是一个sqlvar结构。第三部分是SQLDA结构的描述信息部分。具体每个元素的含义为: lSqld。目前使用的sqlvar结构的个数。即输出列的个数。 lSqlvar。指向sqlvar_struct结构。 即指向描述第一列信息的sqlvar结构。 lDesc_name。Sqlda的名称。 lDesc_occ。Sqlda结构的大小。 lDesc_next。指向下一个SQLDA结构。 lSqltype。代表参数或列的数据类型。它是一个整数数据类型代码。具体每个整数的含义见第二节。 l Sqllen。代表传送数据的长度。如:2,即代表二字节整数。如果是字符串,则该数据为字符串中的字符数量。 lSqldata。指向数据的地址。注意,仅仅是一个地址。 lSqlind。代表是否为NULL。如果该列不允许为NULL,则该字段不赋值;如果该列允许为NULL,则:该字段若为0,表示数据值不为NULL,若为-1,表示数据值为NULL。 lSqlname。代表列名或变量名。它是一个结构。包含length和data。Length是名字的长度;data是名字。 lSqlformat。保留为以后使用。 lSqlitype。指定用户定义的指示符变量的数据类型。 lSqlilen。指定用户定义的指示符变量的长度。 lSqlidata。指向用户定义的指示符变量所存放的数据。
下面这个ADHOC程序非常经典,演示了SQLDA的作用。模拟一个不确定的查询,然后通过SQLDA来获得数据,并打印出来。 EXEC SQL include locator.h; EXEC SQL include sqltypes.h; #define BLOBSIZE 32276; main() { int i = 0; int row_count; /**** Step 1: 声明一个SQLDA结构,来存放查询的数据 ********/ struct sqlda *da_ptr; /*连接到数据库服务器*/ EXEC SQL connect to 'stores7'; if ( SQLCODE < 0 ) { printf("CONNECT failed: %d\n", SQLCODE) exit(0); } /* 创建一个临时表,模拟一个不确定列和表的环境*/ EXEC SQL create table blob_tab (int_col integer, blob_col byte); /* load_db函数是往blob_tab表插入数据,读者不用关心它的代码*/ load_db(); /* PREPARE查询语句 */ EXEC SQL prepare selct_id 'select * from tab1'; /* Step 2: 使用describe函数完成两个功能:一是为sqlda分配空间, 二是获取语句信息,并存放在SQLDA结构中。*/ EXEC SQL describe selct_id into da_ptr; /* Step 3: 初试化sqlda结构,如:为列分配空间,改变数据类型等。*/ row_size = init_sqlda(da_ptr, 0); /* 为PREPARE的SELECT语句声明和打开游标*/ EXEC SQL declare curs for selct_id; EXEC SQL open curs; while (1) { /* Step 4: 执行fetch操作,将一行数据存放在sqlda结构中*/ EXEC SQL fetch curs using descriptor da_ptr; /* 是否到达最后一行?,若是,则退出。 */ if ( SQLCODE == SQLNOTFOUND ) break; /* Step 5: 从SQLDA中打印数据,使用sqlca.sqlerrd[2]来获得查询的行数*/ printf("\n===============\n"); printf("FETCH %d\n", i++); printf("==============="); print_sqlda(da_ptr, ((FetArrSize == 0) ? 1 : sqlca.sqlerrd[2])); /* Step 6: 循环执行FETCH,直到处理完所有的行(SQLCODE为SQLNOTFOUND)*/ } /* Step 7: 释放申请的内存空间,如游标、SQLDA、创建的临时表等*/ EXEC SQL free selct_id; EXEC SQL close curs; EXEC SQL free curs; free_sqlda(da_ptr); cleanup_db(); } /************************************************************************ * 函数: init_sqlda() * 作用: 为SQLDA申请空间 * 返回值: 0 正确,否则有错误 ************************************************************************/ int init_sqlda(in_da, print) struct sqlda *in_da; int print; { int i, j, row_size=0, msglen=0, num_to_alloc; struct sqlvar_struct *col_ptr; loc_t *temp_loc; char *type; if (print) printf("columns: %d. ", in_da->sqld); /* Step 1: 获得一行数据的长度 */ for (i = 0, col_ptr = in_da->sqlvar; i < in_da->sqld; i++, col_ptr++) /* msglen变量存放查询数据的所有列的长度和。*/ msglen += col_ptr->sqllen; /* get database sizes */ /* 为col_ptr->sqllen 重新赋值,该值是在C下的大小。如:在数据库中的字符串,在C中应该多一个字节空间来存放NULL的结束符。*/ col_ptr->sqllen = rtypmsize(col_ptr->sqltype, col_ptr->sqllen); /*row_size变量存放了在C程序中的所有列的长度和。这个值是应用程序为存放一行数据所需要申请的内存空间*/ row_size += col_ptr->sqllen; } if (print) printf("Total row size = %d\n", row_size); /* Step 2: 设置FetArrSize值*/ if (FetArrSize == -1) /* if FetArrSize not yet initialized */ { if (FetBufSize == 0) /* if FetBufSize not set */ FetBufSize = 4096; /* default FetBufSize */ FetArrSize = FetBufSize/msglen; } num_to_alloc = (FetArrSize == 0)? 1: FetArrSize; /* 设置sqlvar_struct结构中的数据类型为相应的C的数据类型*/ for (i = 0, col_ptr = in_da->sqlvar; i < in_da->sqld; i++, col_ptr++) { switch(col_ptr->sqltype) { case SQLCHAR: type = "char "; col_ptr->sqltype = CCHARTYPE; break; case SQLINT: type = "int "; col_ptr->sqltype = CINTTYPE; break; case SQLBYTES: case SQLTEXT: if (col_ptr->sqltype == SQLBYTES) type = "blob "; else type = "text "; col_ptr->sqltype = CLOCATORTYPE; /* Step 3 :只有数据类型为TEXT 和BLOB时,才执行。为存放TEXT 或BYTE列数据申请空间*/ temp_loc = (loc_t *)malloc(col_ptr->sqllen * num_to_alloc); if (!temp_loc) { fprintf(stderr, "blob sqldata malloc failed\n"); return(-1); } col_ptr->sqldata = (char *)temp_loc; /* Step 4:只有数据类型为TEXT 和BLOB时,才执行。初试化loc_t结构*/ byfill(temp_loc, col_ptr->sqllen*num_to_alloc ,0); for (j = 0; j< num_to_alloc; j++, temp_loc++) { temp_loc->loc_loctype = LOCMEMORY; temp_loc->loc_bufsize = BLOBSIZE; temp_loc->loc_buffer = (char *)malloc(BLOBSIZE); if (!temp_loc->loc_buffer) { fprintf(stderr, "loc_buffer malloc failed\n"); return(-1); } temp_loc->loc_oflags = 0; /* clear flag */ } /* end for */ break; default: /* 其他数据类型*/ fprintf(stderr, "not yet handled(%d)!\n", col_ptr->sqltype); return(-1); } /* switch */ /* Step 5: 为指示符变量申请空间*/ col_ptr->sqlind = (short *) malloc(sizeof(short) * num_to_alloc); if (!col_ptr->sqlind) { printf("indicator malloc failed\n"); return -1; /* Step 6 :为存放非TEXT 和BLOB的数据类型的sqldata申请空间.注意的是,申请的地址是(char *),在输出数据时,要按照相应的数据类型做转换。*/ if (col_ptr->sqltype != CLOCATORTYPE) { col_ptr->sqldata = (char *) malloc(col_ptr->sqllen * num_to_alloc); if (!col_ptr->sqldata) { printf("sqldata malloc failed\n"); return -1; } if (print) printf("column %3d, type = %s(%3d), len=%d\n", i+1, type, col_ptr->sqltype, col_ptr->sqllen); } /* end for */ return msglen; } /************************************************************************ * 函数: print_sqlda * 作用: 打印存放在SQLDA结构中的数据。 ************************************************************************/ void print_sqlda(sqlda, count) struct sqlda *sqlda; int count; { void *data; int i, j; loc_t *temp_loc; struct sqlvar_struct *col_ptr; char *type; char buffer[512]; int ind; char i1, i2; /* 打印列数 (sqld) 和行数*/ printf("\nsqld: %d, fetch-array elements: %d.\n", sqlda->sqld, count); /* 外循环:针对每一行数据循环处理 */ for (j = 0; j < count; j ++) { if (count > 1) { printf("record[%4d]:\n", j); printf("col | type | id | len | ind | rin | data "); printf("| value\n"); printf("--------------------------------------------"); printf("------------------\n"); } /* 内循环: 针对每一列数据处理*/ for (i = 0, col_ptr = sqlda->sqlvar; i < sqlda->sqld; i++, col_ptr++) { data = col_ptr->sqldata + (j*col_ptr->sqllen); switch (col_ptr->sqltype) { case CFIXCHARTYPE: case CCHARTYPE: type = "char"; if (col_ptr->sqllen > 40) sprintf(buffer, " %39.39s<..", data); else sprintf(buffer, "%*.*s", col_ptr->sqllen, col_ptr->sqllen, data); break; case CINTTYPE: type = "int"; sprintf(buffer, " %d", *(int *) data); break; case CLOCATORTYPE: type = "byte"; temp_loc = (loc_t *)(col_ptr->sqldata + (j * sizeof(loc_t))); sprintf(buffer, " buf ptr: %p, buf sz: %d, blob sz: %d", temp_loc->loc_buffer, temp_loc->loc_bufsize, temp_loc->loc_size); break; default: type = "??????"; sprintf(buffer, " type not implemented: ", "can't print %d", col_ptr->sqltype); break; } /* end switch */ i1 = (col_ptr->sqlind==NULL) ? 'X' : (((col_ptr->sqlind)[j] != 0) ? 'T' : 'F'); i2 = (risnull(col_ptr->sqltype, data)) ? 'T' : 'F'; printf("%3d | %-6.6s | %3d | %3d | %c | %c | ", i, type, col_ptr->sqltype, col_ptr->sqllen, i1, i2); printf("%8p |%s\n", data, buffer); } /* end for (i=0...) */ } /* end for (j=0...) */ } /************************************************************************ * 函数: free_sqlda * 作用: 释放以下对象申请的内存空间 * o loc_buffer memory (used by TEXT & BYTE) * o sqldata memory * o sqlda structure ************************************************************************/ void free_sqlda(sqlda) struct sqlda *sqlda; { int i,j, num_to_dealloc; struct sqlvar_struc *col_ptr; loc_t *temp_loc; for (i = 0, col_ptr = sqlda->sqlvar; i < sqlda->sqld; i++, col_ptr++) { if ( col_ptr->sqltype = CLOCATORTYPE ) { /* Free memory for blob buffer of each element in fetch array */ num_to_dealloc = (FetArrSize == 0)? 1: FetArrSize; temp_loc = (loc_t *) col_ptr->sqldata; for (j = 0; j< num_to_dealloc; j++, temp_loc++) free(temp_loc->loc_buffer); } /* Free memory for sqldata (contains fetch array) */ free(col_ptr->sqldata); } /* Free memory for sqlda structure */ free(sqlda); }
| | |