一、前言概述
之前寫過3篇關於C語言訪問INFORMIX數據的博文:第一篇講述ESQL基本語法;第二篇講述接口實現;第三篇講述SQLDA結構。此篇根據以上3篇的內容,進一步分析執行動態查找語句中關於ifx_sqlda_t的使用。
二、用法分析
2.1 DESCRIBE ... INTO
在接口實現中的db_ifx_mquery()函數中有這樣一段代碼:
代碼段一:
[html]
ifx_sqlda_t *sqlda = NULL;
...
EXEC SQL DESCRIBE QUERY_SQLSTMT_ID INTO sqlda;
代碼分析:
定義變量sqlda為一個空指針,但執行DESCRIBE ... INTO後,就能對sqlda進行其他操作處理了。我想這時每個人看到這裡心中都有一個疑問:DESCRIBE ... INTO到底對空指針sqlda做了什麼處理?
①、分配空間
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中其他指針分配內存
②、獲取語句信息,並存放在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初始化
代碼段二:
[cpp]
/* 依據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(-1 == FetArrSize)
{
if(FetBufSize < msg_len)
{
FetBufSize = msg_len;
}
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;
}
代碼說明:
1. 全局變量FetBufSize、FetArrSize說明
FetBufSize:是INFORMIX中的全局變量。此值決定了取數據庫數據時的緩存大小
FetArrSize:是INFORMIX中的全局變量。此值決定了一次FETCH可以從數據庫取多少行數據
2. 變量alloc_num的值決定了一次FETCH可以從數據庫取多少行數據,其將成為後續初始化過程中空間申請大小的依據
代碼段三:
[cpp]
/* 依據數據庫中的數據類型,設置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;
}
代碼說明:
1. 數據類型的轉換對應關系,請參考:http://blog.csdn.net/royalapex/article/details/8205654
代碼段四:
[cpp]
/* 初始化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);
}
代碼段五:
[cpp]
/* 申請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;
}
代碼說明:
1. 由申請的空間大小的值,可以發現當取多行數據時,同一列的結果值在內存空間是並排存放的。其內存結構圖:
圖2 結果集存儲結構圖
圖2說明:
1. 圖上每個格子代表一列數據
2. 行列關系:
A:處在第一個sqlvar_struct中的結果集sqldata中第一列代表第一行第一列,第二列代表第二行第一列,第三列代表第三行第一列,....,以此類推;
B:處在第二個sqlvar_struct中的結果集sqldata中第一列代表第一行第二列,第二列代表第二行第二列,第三列代表第三行第二列, ...,以此類推;
C:依照AB的規律可推出其他的行列關系
3. 獲取數據
在執行FETCH後,結果集就會存放在sqlda之中,應用程序如何通過行號-列號 或 行號-列名獲取對應列的取值呢?可通過如下方式:
代碼段六:
[cpp]
/* 根據行號+列號從結果集中取數據 */
char *db_ifx_get_data_by_idx(int row, 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);
}
代碼段七:
[cpp] view plaincopy
/* 通過行號+列明從結果集中取數據 */
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);
}
代碼段八:
[cpp] view plaincopy
/* 轉換並以字符串形式返回結果集數據 */
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;
}