C語言訪問INFORMIX資料庫 — SQLDA使用
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
1 前言概述
動態SQL語句在編譯時可能不知道有多少列資訊。在ESQL語句中,這些不確定的資料是通過SQLDA完成的。理解SQLDA的結構是理解動態SQL的關鍵。SQLDA的結構可參考《
2 用法分析
2.1 DESCRIBE ... INTO
在介面實現中的db_ifx_mquery()函式中有這樣一段程式碼:
ifx_sqlda_t *sqlda = NULL; ... EXEC SQL DESCRIBE QUERY_SQLSTMT_ID INTO sqlda;
程式碼段1
程式碼分析:
定義變數sqlda為一個空指標,但執行DESCRIBE ... INTO後,就能對sqlda進行其他操作處理了。我想這時每個人看到這裡心中都有一個疑問:DESCRIBE ... INTO到底對空指標sqlda做了什麼處理?
1) 分配空間
1. 定義變數sqlda為空指標,但DESCRIBE...INTO之後就能夠進行操作,很顯然,DESCRIBE...INTO為變數sqlda分配了記憶體空間
2. 分配的空間包括:sqlda和sqlda->sqlvar。且sqlda->sqlvar指向大小為sqld*sizeof(struct sqlvar_struct)記憶體塊。(sqlvar:指向struct sqlvar_struct結構體,即指向描述第一列資訊的sqlvar結構體)
3. 但未給sqlda->sqlvar中其他指標分配記憶體
2) 獲取語句資訊,並存放在ifx_sqlda_t結構中
獲取的語句資訊包括:
sqld:使用的sqlvar結構的個數,即:輸出列的個數
sqlvar:指向struct sqlvar_struct結構體,即:指向描述第一列資訊的sqlvar結構體
desc_name:sqlda名稱
desc_occ:sqlda結構的大小
sqltype:代表引數或列的資料型別。它是一個整數資料型別程式碼。
sqllen:代表傳送資料的長度
sqlname:代表列名或變數名
有了以上的語句資訊,就可以為後續資料行的空間分配提供依據。
舉例分析:
假設需執行查詢2列的SQL語句,在執行DESCRIBE...INTO後,變數sqlda的記憶體結構圖為:
圖1 變數sqlda的記憶體結構圖
2.2 SQLDA初始化
/* 依據sqlda的資訊,初始化其他資料 */int db_ifx_init_sqlda(db_ifx_cntx_t *context, ifx_sqlda_t *sqlda){ int ret = 0, idx = 0, msg_len = 0, row_size = 0, alloc_num = 0; struct sqlvar_struct *sqlvar = NULL; context->result = sqlda; /* Step 1. 獲取一行資料的長度 */ sqlvar = sqlda->sqlvar; for(idx=0; idx<sqlda->sqld; idx++, sqlvar++) { /* 非C下一行資料的長度 */ msg_len += sqlvar->sqllen; /* 為col->sqllen 重新賦值,該值是在C下的大小。 如:在資料庫中的字串,在C中應該多一個位元組空間來存放NULL的結束符 */ sqlvar->sqllen = rtypmsize(sqlvar->sqltype, sqlvar->sqllen); /* C下一行資料的長度 */ row_size += sqlvar->sqllen; } /* Step2. 設定FetArrSize的值 */ if(FetArrSize < 0) { if(FetBufSize <= 0) { FetBufSize = 4096; /* 預設值為4096 */ } FetArrSize = FetBufSize/msglen; } alloc_num = (FetArrSize <= 0)? 1:FetArrSize; /* Step3. 初始化列:列的取值分配空間等 */ sqlvar = sqlda->sqlvar; for(idx=0; idx<sqlda->sqld; idx++, sqlvar++) { ret = db_ifx_set_sqltype(sqlvar); if(ret < 0) { return ret; } ret = db_ifx_init_sqldata(context, sqlvar, alloc_num); if(ret < 0) { return ret; } } return msg_len;}
程式碼段2
程式碼說明:
1. 全域性變數FetBufSize、FetArrSize說明
FetBufSize:是INFORMIX中的全域性變數。此值決定了取資料庫資料時的快取大小。預設值為4096。
FetArrSize:是INFORMIX中的全域性變數。此值決定了一次FETCH可以從資料庫取多少行資料。預設值為0。
2. 變數alloc_num的值決定了後續初始化過程中空間申請大小的依據.[注:似乎INFORMIX資料庫FETCH一次只能取一行資料]
/* 依據資料庫中的資料型別,設定C下資料型別 */int db_ifx_set_sqltype(struct sqlvar_struct *sqlvar){ switch(sqlvar->sqltype) { case SQLBOOL: { sqlvar->sqltype = CBOOLTYPE; break; } case SQLSMINT: { sqlvar->sqltype = CSHORTTYPE; break; } case SQLINT: { sqlvar->sqltype = CINTTYPE; break; } case SQLINT8: case SQLSERIAL: case SQLSERIAL8: { sqlvar->sqltype = CINT8TYPE; break; } case SQLBIGSERIAL: case SQLINFXBIGINT: { sqlvar->sqltype = CBIGINTTYPE; break; } case SQLDECIMAL: { sqlvar->sqltype = CDECIMALTYPE; break; } case SQLSMFLOAT: { sqlvar->sqltype = CFLOATTYPE; break; } case SQLFLOAT: { sqlvar->sqltype = CDOUBLETYPE; break; } case SQLCHAR: { sqlvar->sqltype = CCHARTYPE; break; } case SQLNCHAR: { sqlvar->sqltype = CFIXCHARTYPE; break; } case SQLVCHAR: case SQLNVCHAR: { sqlvar->sqltype = CVCHARTYPE; break; } case SQLLVARCHAR: { sqlvar->sqltype = CLVCHARTYPE; break; } case SQLMONEY: { sqlvar->sqltype = CMONEYTYPE; break; } case SQLINTERVAL: { sqlvar->sqltype = CINVTYPE; break; } case SQLDATE: { sqlvar->sqltype = CDATETYPE; break; } case SQLDTIME: { sqlvar->sqltype = CDTIMETYPE; break; } case SQLROW: { sqlvar->sqltype = CROWTYPE; break; } case SQLSET: case SQLLIST: case SQLMULTISET: case SQLCOLLECTION: { sqlvar->sqltype = CCOLLTYPE; break; } case SQLTEXT: case SQLBYTES: { sqlvar->sqltype = CLOCATORTYPE; break; } default: /* Other data type */ { return -1; } } return 0;}
程式碼段3
程式碼說明:
1. 資料型別的轉換對應關係,請參[http://blog.csdn.net/royalapex/article/details/8205654]
/* 初始化sqldata資料空間 */int db_ifx_init_sqldata(db_ifx_cntx_t *context, struct sqlvar_struct *sqlvar, int alloc_num){ char errmsg[DB_ERR_MSG_MAX_LEN] = {0}; int ret = 0, alloc_size = 0; /* 1. 為指示符變數申請空間 */ sqlvar->sqlind = (short *)calloc(alloc_num, sizeof(short)); if(NULL == sqlvar->sqlind) { return -1; } /* 2. 為存放非TEXT 和BLOB的資料型別的sqldata申請空間. 注意: 申請的地址是(char *),在輸出資料時,要按照相應的資料型別做轉換 */ if(CLOCATORTYPE != sqlvar->sqltype) { alloc_size = alloc_num*sqlvar->sqllen; if(sqlvar->sqllen > context->convert_size) { context->convert_size = sqlvar->sqllen; } sqlvar->sqldata = (char*)calloc(1, alloc_size); if(NULL == sqlvar->sqldata) { return -1; } return 0; } /* 3. 為TEXT和BLOB的資料型別的sqldata申請空間 */ return db_ifx_alloc_loc(context, sqlvar, alloc_num);}
程式碼段4
/* 申請loc_t型別的資料空間 */int db_ifx_alloc_loc(db_ifx_cntx_t *context, struct sqlvar_struct *sqlvar, int alloc_num){ char errmsg[DB_ERR_MSG_MAX_LEN] = {0}; int idx = 0, alloc_size = 0; loc_t *loc = NULL; alloc_size = alloc_num*sqlvar->sqllen; if(sqlvar->sqllen > context->convert_size) { context->convert_size = sqlvar->sqllen; } /* 1. 為存放TEXT或BYTE列資料申請空間 */ loc = (loc_t*)calloc(1, alloc_size); if(NULL == loc) { return -1; } sqlvar->sqldata = (char *)loc; /* 2. 初試化loc_t結構 */ byfill(loc, alloc_size, 0); for(idx=0; idx<alloc_num; idx++) { loc->loc_loctype = LOCMEMORY; loc->loc_bufsize = DB_IFX_BLOB_SIZE; loc->loc_buffer = (char *)calloc(1, DB_IFX_BLOB_SIZE); if(NULL == loc->loc_buffer) { return -1; } loc->loc_oflags = 0; loc++; } return 0;}
程式碼段5
程式碼分析:
1. 由申請的空間大小的值,可以發現當取多行資料時,同一列的結果值在記憶體空間是並排存放的。其記憶體結構圖:
圖2 結果集儲存結構圖
圖2說明:
1. 圖上每個格子代表一列資料
2. 行列關係:(注:似乎INFORMIX資料庫FETCH一次只能取一行資料,也就不存在行列關係的計算了)
A:處在第一個sqlvar_struct中的結果集sqldata中第一列代表第一行第一列,第二列代表第二行第一列,第三列代表第三行第一列,....,以此類推;
B:處在第二個sqlvar_struct中的結果集sqldata中第一列代表第一行第二列,第二列代表第二行第二列,第三列代表第三行第二列, ...,以此類推;
C:依照AB的規律可推出其他的行列關係
2.3. 獲取資料
每次執行FETCH後,取到的結果集就會存放在sqlda之中,應用程式如何通過行號-列號 或 行號-列名獲取對應列的取值呢?可通過如下方式:(行號:指結果集開始位置的相對行號,而不是資料庫中的行號)(注:似乎INFORMIX資料庫FETCH一次只能取一行資料,也就不存在行列關係的計算了)
/* 根據 行號+列號 從結果集中取資料 */char *db_ifx_get_data_by_idx(int rowno, int col, void *cntx){ ifx_sqlda_t *current = NULL; struct sqlvar_struct *sqlvar = NULL; db_ifx_cntx_t *context = (db_ifx_cntx_t *)cntx; if(NULL == context->result || NULL == context->result->sqlvar || col <= 0 || col > context->result->sqld || row <= 0 || row > context->rows) { return NULL; } /* 1. 定位列 */ sqlvar = context->result->sqlvar+col-1; /* 2. 返回結果 */ return db_ifx_get_value(sqlvar, context, rowno);}
程式碼段6
/* 通過 行號+列明 從結果集中取資料 */char *db_ifx_get_data_by_name(int rowno, const char *name, db_ifx_cntx_t *context){ int idx = 0; struct sqlvar_struct *sqlvar = NULL; if(NULL == context->result || NULL == context->result->sqlvar || rowno <= 0 || rowno > context->rows) { return NULL; } /* 1. 定位列 */ sqlvar = context->result->sqlvar; for(idx=0; idx<context->result->sqld; idx++) { if(0 == strcasecmp(name, sqlvar->sqlname)) { break; } sqlvar++; } if(idx == context->result->sqld) { return NULL; } /* 3. 返回結果 */ return db_ifx_get_value(sqlvar, context, rowno);}
程式碼段7
/* 轉換並以字串形式返回結果集資料 */char *db_ifx_get_value(struct sqlvar_struct *sqlvar, db_ifx_cntx_t *cntx, int rowno){ int ret = 0; loc_t *loc = NULL; char *data = sqlvar->sqldata + (rowno-1)*sqlvar->sqllen, *convert = cntx->convert, *pconvert = NULL; memset(convert, 0, cntx->convert_size); switch (sqlvar->sqltype) { case CBOOLTYPE: { snprintf(convert, cntx->convert_size, "%d", *((unsigned char*)data)); return convert; } case CSHORTTYPE: { snprintf(convert, cntx->convert_size, "%d", *((short*)data)); return convert; } case CINTTYPE: { snprintf(convert, cntx->convert_size, "%d", *((int*)data)); return convert; } case CLONGTYPE: { snprintf(convert, cntx->convert_size, "%l", *((long*)data)); return convert; } case CINT8TYPE: { snprintf(convert, cntx->convert_size, "%ll", *((long long*)data)); return convert; } case CBIGINTTYPE: { snprintf(convert, cntx->convert_size, "%ll", *((long long*)data)); return convert; } case CDECIMALTYPE: { pconvert = convert; dectoasc((dec_t*)data, convert, cntx->convert_size, -1); /* Note: dectoasc() Left align and fill blank, so must delete blank */ while('\0' != *pconvert) { if(isblank(*pconvert)) { *pconvert = '\0'; break; } pconvert++; } return convert; } case CFLOATTYPE: { snprintf(convert, cntx->convert_size, "%f", (double)(*(float*)data)); return convert; } case CDOUBLETYPE: { snprintf(convert, cntx->convert_size, "%f", *((double*)data)); return convert; } case CMONEYTYPE: { snprintf(convert, cntx->convert_size, "%d", *(int*)data); return convert; } case CINVTYPE: { intoasc((intrvl_t*)data, convert); return convert; } case CDATETYPE: { rfmtdate(*(int*)data, "YYYYMMDD", convert); return convert; } case CDTIMETYPE: { dttoasc((dtime_t*)data, convert); return convert; } case CROWTYPE: case CCOLLTYPE: { return data; } case CCHARTYPE: case CFIXCHARTYPE: case CVCHARTYPE: case CLVCHARTYPE: { return data; } case CLOCATORTYPE: { loc = (loc_t *)sqlvar->sqldata; return loc->loc_buffer; } default: { return NULL; } } return NULL;}
程式碼段8