前幾天一個朋友要我寫點關于數據庫編程方面的東西,可一直由于工作比較忙,到現在已經一個多星期了,正好煙草的項目由于最終方案的原因而停止了,新的ATM的P端的程序昨天基本已經順利調整完了。相信今天上午是個清閑的上午,就寫點關于動態SQL方面的東西吧。
嵌入SQL語言都是靜態SQL語言,即在編譯時已經確定了引用的表和列。主變量不改變表和列信息。我們使用主變量改變查詢參數,但是不能用主變量代替表名或列名。否則,系統報錯。動態SQL語句就是來解決這個問題。
動態SQL語句的目的是,不是在編譯時確定SQL的表和列,而是讓程序在運行時提供,并將SQL語句文本傳給DBMS執行。靜態SQL語句在編譯時已經生成執行計劃。而動態SQL語句,只有在執行時才產生執行計劃。動態SQL語句首先執行PREPARE語句要求DBMS分析、確認和優化語句,并為其生成執行計劃。DBMS還設置SQLCODE以表明語句中發現的錯誤。當程序執行完“PREPARE”語句后,就可以用EXECUTE語句執行執行計劃,并設置SQLCODE,以表明完成狀態。
使用動態SQL,共分成四種方法:
方法 支持的SQL語句 實現方法
1 該語句內不包含宿主變量,該語句不是查詢語句 execute immediate
2 該語句內包含輸入宿主變量 ,該語句不是查詢語句 prepare和execute
3 包含已知數目的輸入宿主變量或列的查詢 prepare和fetch
4 包含未知數目的輸入宿主變量或列的查詢 prepare和fetch,用描述符
按照功能和處理上的劃分,動態SQL應該分成兩類來解釋:動態修改和動態查詢。方法1和方法2完成動態修改。方法3和方法4完成了動態查詢。
一、動態修改
方法1和方法2完成動態修改。對于方法1,表示要執行一個完整的T-SQL語句,該語句沒有宿主變量,不是一個查詢語句。因為沒有宿主變量來帶入不同的參數,所以不能通過方法1來重復執行修改語句。具體語法為:
exec sql [at connection_name] execute immediate
{: host_variable | string};
其中,host_variable和string是存放完整T-SQL語句。
例:提示用戶輸入被更新書的條件,然后組合成為一個完整的SQL語句,并執行更新。
exec sql begin declare section;
CS_CHAR sqlstring[200];
exec sql end declare section;
char cond[150];
exec sql whenever sqlerror call err_p();
exec sql whenever sqlwarning call warn_p();
strcpy(sqlstring,
"update titles set price=price*1.10 where ");
printf("Enter search condition:");
scanf("%s", cond);
strcat(sqlstring, cond);
exec sql execute immediate :sqlstring;
exec sql commit work;
對于方法2,可以執行一個包含輸入宿主變量的動態修改語句。該方法要使用PREPARE語句和EXECUTE語句。PREPARE語句是動態SQL語句獨有的語句。其語法為:
PREPARE 語句名 FROM 宿主變量|字符串
該語句接收含有SQL語句串的宿主變量,并把該語句送到DBMS。DBMS編譯語句并生成執行計劃。在語句串中包含一個“?”表明參數,當執行語句時,DBMS需要參數來替代這些“?”。PREPRARE執行的結果是,DBMS用語句名標志準備后的語句。SQL SERVER編譯后的語句以臨時存儲過程的形式存放在緩沖區中。語句名類似于游標名,是一個SQL標識符。在執行SQL語句時,EXECUTE語句后面是這個語句名。請看下面這個例子:
EXEC SQL BEGIN DECLARE SECTION;
char prep[] = "INSERT INTO mf_table VALUES(?,?,?)";
char name[30];
char car[30];
double num;
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE prep_stat FROM :prep;
while (SQLCODE == 0)
{
strcpy(name, "Elaine");
strcpy(car, "Lamborghini");
num = 4.9;
EXEC SQL EXECUTE prep_stat USING :name, :car, :num;
}
在這個例子中,prep_stat是語句名,prep宿主變量的值是一個INSERT語句,包含了三個參數(3個“?”)。PREPARE的作用是,DBMS編譯這個語句并生成執行計劃,并把語句名標志這個準備后的語句。值得注意的是,PREPARE中的語句名的作用范圍為整個程序,所以不允許在同一個程序中使用相同的語句名在多個PREPARE語句中。
EXECUTE語句是動態SQL獨有的語句。它的語法如下:
EXECUTE 語句名 USING 宿主變量 | DESCRIPTOR 描述符名
請看上面這個例子中的“EXEC SQL EXECUTE prep_stat USING :name, :car, :num;”語句,它的作用是,請求DBMS執行PREPARE語句準備好的語句。當要執行的動態語句中包含一個或多個參數標志時,在EXECUTE語句必須為每一個參數提供值,如::name、:car和:num。這樣的話,EXECUTE語句用宿主變量值逐一代替準備語句中的參數標志(“?”),從而,為動態執行語句提供了輸入值。
使用主變量提供值,USING子句中的主變量數必須同動態語句中的參數標志數一致,而且每一個主變量的數據類型必須同相應參數所需的數據類型相一致。各主變量也可以有一個伴隨主變量的指示符變量。當處理EXECUTE語句時,如果指示符變量包含一個負值,就把NULL值賦予相應的參數標志。除了使用主變量為參數提供值,也可以通過SQLDA提供值(關于這個,后邊會講到)。
二、動態游標
使用動態游標可以完成方法3。
游標分為靜態游標和動態游標兩類。對于靜態游標,在定義游標時就已經確定了完整的SELECT語句。在SELECT語句中可以包含主變量來接收輸入值。當執行游標的OPEN語句時,主變量的值被放入SELECT語句。在OPEN語句中,不用指定主變量,因為在DECLARE CURSOR語句中已經放置了主變量。請看下面靜態游標的例子:
EXEC SQL BEGIN DECLARE SECTION;
char szLastName[] = "White";
char szFirstName[30];
EXEC SQL END DECLARE SECTION;
EXEC SQL
DECLARE author_cursor CURSOR FOR
SELECT au_fname FROM authors WHERE au_lname = :szLastName;
EXEC SQL OPEN author_cursor;
EXEC SQL FETCH author_cursor INTO :szFirstName;
動態游標和靜態游標不同。以下是動態游標使用的句法:
1)、聲明游標:
對于動態游標,在DECLARE CURSOR語句中不包含SELECT語句。而是,定義了在PREPARE中的語句名,PREPARE語句規定與查詢相關的語句名稱。具體語法為:
exec sql [at connection_name] declare cursor_name
cursor for statement_name;
如:EXEC SQL DECLARE author_cursor CURSOR FOR select_statement;
值得注意的是,聲明動態游標是一個可執行語句,應該在PREPARE語句后執行。
2)、打開游標
完整語法為:OPEN 游標名 [USING 主變量名 | DESCRIPTOR 描述名]
在動態游標中,OPEN語句的作用是使DBMS定位相關的游標在第一行查詢結果前。當OPEN語句成功執行完畢后,游標處于打開狀態,并為FETCH語句做準備。OPEN語句執行一條由PREPARE語句預編譯的語句。如果動態查詢正文中包含有一個或多個參數標志時,OPEN語句必須為這些參數提供參數值。USING子句的作用就是規定參數值。可以使用主變量提供參數值,也可以通過描述名(即SQLDA)提供參數值。如:EXEC SQL OPEN author_cursor USING :szLastName;。
3)、取一行值
FETCH語法為:FETCH 游標名 INTO USING DESCRIPTOR 描述符名。
動態FETCH語句的作用是,把游標移到下一行,并把這一行的各列值送到SQLDA中。注意的是,靜態FETCH語句的作用是用主變量表接收查詢到的列值。在方法3中,使用的是靜態FETCH語句獲得值。動態FETCH語句只在方法4中使用。
4)、關閉游標
如:EXEC SQL CLOSE c1;
關閉游標的同時,會釋放由游標添加的鎖和放棄未處理的數據。在關閉游標前,該游標必須已經聲明和打開。另外,程序終止時,系統會自動關閉所有打開的游標。
總之,在動態游標的DECLARE CURSOR語句中不包含SELECT語句。而是,定義了在PREPARE中的語句名,用PREPARE語句規定與查詢相關的語句名稱。當PREPARE語句中的語句包含了參數,那么在OPEN語句中必須指定提供參數值的主變量或SQLDA。動態DECLARE CURSOR語句是一個可執行語句。該子句必須在OPEN、FETCH、CLOSE語句之前使用。請看下面這個例子,描述了完成方法3的五個步驟:PREPARE、DECLARE、OPEN、FETCH和CLOSE。
……
EXEC SQL BEGIN DECLARE SECTION;
char szCommand[] = "SELECT au_fname FROM authors WHERE au_lname = ?";
char szLastName[] = "White";
char szFirstName[30];
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE select_statement FROM :szCommand;
EXEC SQL DECLARE author_cursor CURSOR FOR select_statement;
EXEC SQL OPEN author_cursor USING :szLastName;
EXEC SQL FETCH author_cursor INTO :szFirstName;
EXEC SQL CLOSE author_cursor;
………
下面是一個實現方法3的實際例子。提示用戶輸入排序的條件,并把符合條件的書信息顯示出來。
……
exec sql begin declare section;
CS_CHAR sqlstring[200];
CS_FLOAT bookprice,condprice;
CS_CHAR booktitle[200];
exec sql end declare section;
char orderby[150];
exec sql whenever sqlerror call err_p();
exec sql whenever sqlwarning call warn_p();
strcpy(sqlstring,
"select title,price from titles\
where price>? order by ");
printf("Enter the order by clause:");
scanf("%s", orderby);
strcat(sqlstring, orderby);
exec sql prepare select_state from :sqlstring;
exec sql declare select_cur cursor for
select_state;
condprice = 10; /* 可以提示用戶輸入這個值*/
exec sql open select_cur using :condprice;
exec sql whenever not found goto end;
for (;;)
{
exec sql fetch select_cur
into :booktitle,:bookprice;
printf("%20s %bookprice=%6.2f\n",
booktitle, bookprice);
}
end:
exec sql close select_cur;
exec sql commit work;
………
三、SQLDA
要實現方法4,則需要使用SQLDA(也可以使用SQL Descriptors,請讀者參閱幫助信息)。可以通過SQLDA為嵌入SQL語句提供不確定的輸入數據和從嵌入SQ語句中輸出不確定數據。理解SQLDA的結構是理解動態SQL的關鍵。
我們知道,動態SQL語句在編譯時可能不知道有多少列信息。在嵌入SQL語句中,這些不確定的數據是通過SQLDA完成的。SQLDA的結構非常靈活,在該結構的固定部分,指明了多少列等信息(如下圖中的sqld=2,表示為兩列信息),在該結構的后面,有一個可變長的結構(sd_column結構),說明每列的信息。
SQLDA結構
Sd_Sqld=2
Sd_column
……
Sd_datafmt
Sd_Sqllen
Sd_sqldata
…..
Sd_datafmt
Sd_Sqllen
Sd_Sqldata
…..
具體SQLDA的結構在sqlda.h中定義,是:
typedef struct _sqlda
{
CS_SMALLINT sd_sqln;
CS_SMALLINT sd_sqld;
struct _sd_column
{
CS_DATAFMT sd_datafmt;
CS_VOID *sd_sqldata;
CS_SMALLINT sd_sqlind;
CS_INT sd_sqllen;
CS_VOID*sd_sqlmore;
} sd_column[1];
} syb_sqlda;
typedef syb_sqlda SQLDA;
從上面這個定義看出,SQLDA是一種由兩個不同部分組成的可變長數據結構。從位于SQLDA開端的sd_sqln到sd_sqld為固定部分,用于標志該SQLDA,并規定這一特定的SQLDA的長度。而后是一個或多個sd_column結構 ,用于標志列數據或參數。當用SQLDA把參數送到執行語句時,每一個參數都是一個sd_column結構;當用SQLDA返回輸出列信息時,每一列都是一個sd_column 結構。具體每個元素的含義為:
lSd_Sqln。分配的sd_column結構的個數。等價于可以允許的最大輸入參數的個數或輸出列的個數。
lSd_Sqld。目前使用的sd_column結構的個數。
lSd_column[].sd_datafmt。標志同列相關的CS_DATAFMT結構。
lSd_column[].sd_Sqldata。指向數據的地址。注意,僅僅是一個地址。
lSd_column[].sd_sqllen。sd_sqldata指向的數據的長度。
lSd_column[].sd_Sqlind。代表是否為NULL。如果該列不允許為NULL,則該字段不賦值;如果該列允許為NULL,則:該字段若為0,表示數據值不為NULL,若為-1,表示數據值為NULL。
lSd_column[].sd_sqlmore。保留為將來使用。
下面我們來看一個具體的例子。這個例子是通過output_descriptor查詢數據庫中的數據,是通過input_descriptor傳遞參數。這個例子的作用是,模擬一個動態查詢,并顯示查詢結果。動態查詢的執行過程如下:
1)、如同構造動態UPDATE語句或DELETE語句的方法一樣,程序在緩沖器中構造一個有效的SELECT語句。
2)、程序用PREPARE語句把動態查詢語句送到DBMS,DBMS準備、確認和優化語句,并生成一個應用計劃。
3)、動態DECLARE CURSOR語句說明查詢游標,動態DECLARE CURSOR語句規定與動態SELECT語句有關的語句名稱。如:例子中的statement。
4)、程序用DESCRIBE語句請求DBMS提供SQLDA中描述信息,即告訴程序有多少列查詢結果、各列名稱、數據類型和長度。DESCRIBE語句只用于動態查詢。具體見下一節。
5)、為SQLDA申請存放一列查詢結果的存儲塊(即:sqldata指向的數據區),也為SQLDA的列的指示符變量申請空間。程序把數據區地址和指示符變量地址送入SQLDA,以告訴DBMS向何處回送查詢結果。
6)、動態格式的OPEN語句。即打開存放查詢到的數據集(動態SELECT語句產生的數據)的第一行。
7)、動態格式的FETCH語句把游標當前行的結果送到SQLDA。(動態FETCH語句和靜態FETCH語句的不同是:靜態FETCH語句規定了用主變量接收數據;而動態FETCH語句是用SQLDA接收數據。)并把游標指向下一行結果集。
8)、CLOSE語句關閉游標。
具體程序如下:
exec sql include sqlca;
exec sql include sqlda;
...
/*input_ descriptor是通過SQLDA傳遞參數,output_descriptor是通過SQLDA返回列數據*/
SQLDA *input_descriptor, *output_descriptor;
CS_SMALLINT small;
CS_CHAR character[20];
/*申請空間*/
input_descriptor = (SQLDA *)malloc(SYB_SQLDA_SIZE(3));
/*設置參數的最大個數*/
input_descriptor->sqlda_sqln = 3;
/*申請空間*/
output_descriptor = (SQLDA *)malloc(SYB_SQLDA_SIZE(3));
/*設置列數的最大值*/
output_descriptor->sqlda_sqln = 3;
*p_retcode = CS_SUCCEED;
/*連接數據庫服務器*/
exec sql connect "sa" identified by password;
/* 創建一張example表,并插入一些例子數據,用于演示SQLDA的使用*/
exec sql drop table example;
exec sql create table example (fruit char(30), number int);
exec sql insert example values ('tangerine', 1);
exec sql insert example values ('pomegranate', 2);
exec sql insert example values ('banana', 3);
/* 準備和描述查詢語句*/
exec sql prepare statement from
"select fruit from example where number = ?";
/*describe語句的作用是,將查詢所需要的參數信息存放在input_descriptor中*/
exec sql describe input statement using descriptor input_descriptor;
/*設置SQLDA中指向參數數據的地址信息(sqldata)和數據長度(sqlda_sqllen)*/
input_descriptor->sqlda_column[0].sqlda_datafmt.datatype =CS_SMALLINT_TYPE;
input_descriptor->sqlda_column[0].sqlda_sqldata = &small;
input_descriptor->sqlda_column[0].sqlda_sqllen = sizeof(small);
small = 2;
/*將查詢語句的列信息存放在output_descriptor中*/
exec sql describe output statement using descriptor output_descriptor;
if (output_descriptor->sqlda_sqld != 1 ||
output_descriptor->sqlda_column[0].sqlda_datafmt.datatype !=
CS_CHAR_TYPE)
FAIL;
else
printf("first describe output \n");
/*設置存放列數據的地址信息*/
output_descriptor->sqlda_column[0].sqlda_sqldata = character;
output_descriptor->sqlda_column[0].sqlda_datafmt.maxlength = 20;
/*通過input_descriptor將輸入參數帶入查詢語句,并將結果通過output_descriptor帶出*/
exec sql execute statement into descriptor output_descriptor \
using descriptor input_descriptor;
/*打印結果---單行結果*/
printf("expected pomegranate, got %s\n",character);
/*釋放申請的內存空間*/
exec sql deallocate prepare statement;
/* 多行結果示例。對多行查詢語句做準備和描述操作*/
exec sql prepare statement from \
"select number from example where fruit = ?";
/*為多行結果聲明游標*/
exec sql declare c cursor for statement;
exec sql describe input statement using descriptor input_descriptor;
/*設置查詢的參數地址信息*/
input_descriptor->sqlda_column->sqlda_sqldata = character;
input_descriptor->sqlda_column->sqlda_datafmt.maxlength =CS_NULLTERM;
/*設置參數值為banana,也可以提示用戶輸入這些信息*/
strcpy(character, "banana");
input_descriptor->sqlda_column->sqlda_sqllen = CS_NULLTERM;
/*打開游標*/
exec sql open c using descriptor input_descriptor;
/*設置輸出列的信息*/
exec sql describe output statement using descriptor output_descriptor;
/*設置存放數據的地址信息*/
output_descriptor->sqlda_column->sqlda_sqldata = character;
output_descriptor->sqlda_column->sqlda_datafmt.datatype =CS_CHAR_TYPE;
output_descriptor->sqlda_column->sqlda_datafmt.maxlength = 20;
output_descriptor->sqlda_column->sqlda_sqllen = 20;
output_descriptor->sqlda_column->sqlda_datafmt.format =
(CS_FMT_NULLTERM | CS_FMT_PADBLANK);
exec sql fetch c into descriptor output_descriptor;
/*打印列的數據*/
printf("expected pomegranate, got %s\n", character);
exec sql commit work;
……….
上面這個例子是典型的動態查詢程序。該程序中演示了PREPARE語句和DESCRIBE語句的處理方式,以及為程序中檢索到的數據分配空間。要注意程序中如何設置sqlda_column結構中的的各個變量。這個程序也演示了OPEN、FETCH和CLOSE語句在動態查詢中的應用。值得注意的是,FETCH語句只使用了SQLDA,不使用主變量。由于程序中預先申請了sqlda_column結構中的SQLDATA空間,所以DBMS知道將查詢到的數據保存在何處。該程序還考慮了查詢數據為NULL的處理。
值得注意的是,SQDA結構不是SQL標準。每個數據庫廠商的實現方式有可能不同。
四、DESCRIBE語句
該語句只有動態SQL才有。該語句是在PREPARE語句之后,在OPEN語句之前使用。該語句的作用是,設置SQLDA中的描述信息,如:列名、數據類型和長度等。DESCRIBE語句的語法為:
DESCRIBE 語句名 INTO 描述符名
如:exec sql describe output statement using descriptor output_descriptor;。
在執行DESCRIBE前,用戶必須給出SQLDA中的SQLN的值(表示最多有多少列),該值也說明了SQLDA中最多有多少個sqlda_column結構。然后,執行DESCRIBE語句,該語句填充每一個sqlda_column結構。每個sqlda_column結構中的相應列為:
lSd_datafmt結構:列名等信息。
lSd_sqllen列:給出列的長度。
注意,sd_sqldata列不填充。由程序在FETCH語句之前,給出數據緩沖器地址和指示符地址。
<!--[if !supportLineBreakNewLine]-->
下邊的話,給出兩個例子吧,方便對上邊講到的東西做個對比:
1)、TELECOM程序
該程序是模擬電信費用查詢。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined ( DB2 )
#define SQLNOTFOUND 100
#include <sql.h>
#elif defined ( ORA7 )
#define SQLNOTFOUND 1403
#endif
#if defined (SYBASE)
#define SQLNOTFOUND 100
#endif
EXEC SQL INCLUDE sqlca;
EXEC SQL BEGIN DECLARE SECTION;
char user[30];
char passwd[30];
char Usr_name[61];
char Dev_no[9];
long Call_flg;
char Called_arno[11];
char Called_no[15];
char Call_dat[21];
double Call_dur;
double Call_rate;
double Call_fee;
double Add_fee;
char as_dev_no[9];
EXEC SQL END DECLARE SECTION;
void main(){
char statusbuf[1024], s[30];
/*連接到SQL SERVER服務器*/
printf("\nplease enter your userid ");
gets(user);
printf("\npassword ");
gets(passwd);
exec sql connect :user identified by :passwd;
exec sql use pubs2;
/*輸入想要查詢的電話號碼*/
printf("\nPlease enter the telephone number:");
gets(as_dev_no );
/*聲明游標*/
EXEC SQL DECLARE c1 CURSOR FOR
SELECT bas_infot.Usr_name, auto10a_list.Dev_no, auto10a_list.Call_flg, auto10a_list.Called_arno, auto10a_list.Called_no
, auto10a_list.Call_dat, auto10a_list.Call_dur, auto10a_list.Call_rate, auto10a_list.Call_fee,
FROM auto10a_list, bas_infot
WHERE ( auto10a_list.Dev_no = bas_infot.Dev_no ) AND auto10a_list.Dev_no = :as_dev_no;
/*打開游標,指向查詢相關電話信息的結果集*/
EXEC SQL OPEN c1; /* :rk.2:erk. */
do{
/*取出一行數據到各個變量*/
EXEC SQL FETCH c1 INTO
:Usr_name, :Dev_no, :Call_flg, :Called_arno, :Called_no, :Call_dat, :Call_dur, :Call_rate, :Call_fee, :Add_fee;
if( (sqlca.sqlcode == SQLNOTFOUND) || (sqlca.sqlcode <0) )
break;
/*顯示數據*/
printf("%s,%s,%d,%s,%s,%s,%7.0f,%8.3f,%7.2f,%6.2f\n"
, Usr_name, Dev_no, Call_flg, Called_arno, Called_no, Call_dat, Call_dur, Call_rate, Call_fee, Add_fee );
}while(1);
EXEC SQL CLOSE c1;
EXEC SQL DEALLOCATE CURSOR c1;
Exec sql disconnect all;
return (0);
}
2)、 ADHOC程序
該程序的功能是:用戶輸入任意SQL語句,并執行和打印結果。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Defines for BINDING */
/*初試化SQLDA*/
int init_da (SQLDA **DAPointer, int DAsqln);
/*為存放列數據的sd_column結構申請空間*/
int alloc_host_vars (SQLDA *sqldaPointer);
/*釋放SQLDA所申請的空間*/
void free_da (SQLDA *sqldaPointer);
/*獲取列名信息*/
char * readColName (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer);
/*獲取列數據*/
char * readCol (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer);
#ifdef __cplusplus
}
#endif
/*定義最大列數*/
#define MAX_COLUMNS255
#define MAX_CHAR_FOR_DOUBLE20
#define MAX_CHAR_FOR_LONG15
#define MAX_CHAR_FOR_DATETIME30
#define MAX_CHAR_FOR_DEFAULT100
EXEC SQL INCLUDE SQLCA ;
EXEC SQL INCLUDE SQLDA ;
#define SQLSTATE sqlca.sqlstate
#define SQLCODE sqlca.sqlcode
/*處理SQL語句*/
int process_statement( char * ) ;
int main() {
int rc ;
char st[1024];
char tmpstr[1024];
/*獲得SQL語句*/
printf("Please enter the any sql statement:");
gets( st);
/* 處理該語句 */
rc = process_statement( st ) ;
/*打印處理結果*/
printf( "%d", rc);
printf("the sqlcode is %d",SQLCODE);
}
/******************************************************************************
* FUNCTION : process_statement
* 處理sql 語句
*****************************************************************************/
int process_statement ( char * sqlInput ) {
int counter = 0 ;
SQLDA * sqldaPointer ;
short sqlda_d ; /* Total columns */
short idx;
char buffer[4096];
char varname[1024];
char colnamelist[4096];
EXEC SQL BEGIN DECLARE SECTION ;
char st[1024] ;
EXEC SQL END DECLARE SECTION ;
strcpy( st, sqlInput ) ;
/* 為SQLDA結構申請空間" */
if (init_da( &sqldaPointer, MAX_COLUMNS ) == -1)
{
return -1;
}
/*準備SQL語句*/
EXEC SQL PREPARE statement1 from :st ;
if (SQLCODE < 0)
{
free_da(sqldaPointer);
return SQLCODE;
}
/*獲取查詢列的信息到SQLDA結構*/
EXEC SQL DESCRIBE statement1 USING DESCRIPTOR sqldaPointer ;
/* 如果SQLCODE為0,則表示為SELECT語句 */
if ( SQLCODE != 0 ) {
free_da(sqldaPointer);
return SQLCODE;
} /* end if */
sqlda_d = sqldaPointer->sd_sqld ;
if ( sqlda_d > 0 ) {
/* 為存放列數據的sd_column結構申請空間 */
if (alloc_host_vars( sqldaPointer ) == -1)
{free_da(sqldaPointer);
return -1;
}
/*聲明游標*/
EXEC SQL DECLARE pcurs CURSOR FOR statement1 ;
/打開游標*/
EXEC SQL OPEN pcurs ;
if (SQLCODE < 0)
return SQLCODE;
/*取一行數據到SQLDA結構*/
EXEC SQL FETCH pcurs INTO DESCRIPTOR sqldaPointer;
if (SQLCODE < 0)
{
EXEC SQL CLOSE pcurs ;
return SQLCODE;
}
/*顯示列標題 */
colnamelist[0] = 0;
for ( idx=0; idx< sqlda_d; idx++)
{ strcat(colnamelist, readColName(sqldaPointer, idx, buffer));
if (idx < sqlda_d -1)
strcat(colnamelist, ",");
}
/* 顯示行數據*/
while ( SQLCODE == 0 ) {
counter++ ;
for ( idx=0; idx< sqlda_d; idx++)
printf("%s",readCol(sqldaPointer, idx, buffer));
EXEC SQL FETCH pcurs INTO DESCRIPTOR sqldaPointer ;
} /* endwhile */
/*關閉游標*/
EXEC SQL CLOSE pcurs ;
EXEC SQL DEALLOCATE CURSOR pcurs;
/* 釋放為SQLDA申請的空間 */
free_da( sqldaPointer ) ;
} else { /* 不是SELECT語句*/
EXEC SQL EXECUTE statement1 ;
free_da( sqldaPointer ) ;
if (SQLCODE < 0)
return SQLCODE;
} /* end if */
return( 0 ) ;
} /* end of program : ADHOC.CP */
/******************************************************************************* PROCEDURE : init_da
*為SQLDA分配空間。使用SQLDASIZE 獲得SQLDA的大小。如果返回-1,則表示分配
*空間不成功。
******************************************************************************/
int init_da (SQLDA **DAPointer, int DAsqln) {
int idx;
*DAPointer = (SQLDA *)malloc(SYB_SQLDA_SIZE(DAsqln));
if (*DAPointer == NULL)
return (-1);
memset (*DAPointer, '\0', SYB_SQLDA_SIZE(DAsqln));
(*DAPointer)->sd_sqln = DAsqln;
(*DAPointer)->sd_sqld = 0;
return 0;
}
/******************************************************************************* FUNCTION : alloc_host_vars
*為存放列數據的sd_column結構申請空間。如果返回-1,則表示不能獲得足夠內存。
******************************************************************************/
int alloc_host_vars (SQLDA *sqldaPointer) {
short idx;
for (idx = 0; idx < sqldaPointer->sd_sqld; idx++) {
switch (sqldaPointer->sd_column[idx].sd_datafmt.datatype ) {
case CS_CHAR_TYPE:
case CS_VARCHAR_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (sqldaPointer->sd_column[idx].sd_sqllen + 1 );
sqldaPointer->sd_column[idx].sd_sqllen ++;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
case CS_TINYINT_TYPE:
case CS_SMALLINT_TYPE:
case CS_INT_TYPE:
case CS_VOID_TYPE:
case CS_USHORT_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_LONG);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_LONG;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
case CS_REAL_TYPE:
case CS_FLOAT_TYPE:
case CS_BIT_TYPE:
case CS_MONEY_TYPE:
case CS_MONEY4_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_DOUBLE);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_DOUBLE;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
case CS_DATETIME_TYPE:
case CS_DATETIME4_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_DATETIME);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_DATETIME;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
case CS_NUMERIC_TYPE:
case CS_DECIMAL_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (sqldaPointer->sd_column[idx].sd_datafmt.precision + 3 );
sqldaPointer->sd_column[idx].sd_sqllen = sqldaPointer->sd_column[idx].sd_datafmt.precision + 3;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
default:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_DEFAULT);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_DEFAULT;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
} /* endswitch */
if (sqldaPointer->sd_column[idx].sd_sqldata == NULL) {
return (-1);
}
} /* endfor */
return 0;
}
/******************************************************************************* FUNCTION : free_da
* 釋放SQLDA申請的空間。
******************************************************************************/
void free_da (SQLDA *sqldaPointer) {
short idx;
for (idx = 0; idx < sqldaPointer->sd_sqld; idx++) {
free (sqldaPointer->sd_column[idx].sd_sqldata);
} /* endfor */
free (sqldaPointer);
}
/******************************************************************************* PROCEDURE : readColName
* 返回列名
******************************************************************************/
char * readColName (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer) {
strcpy(buffer, sqldaPointer->sd_column[sd_columnIndex].sd_datafmt.name);
return buffer;
}
/******************************************************************************* PROCEDURE : readCol
* 返回列數據。
******************************************************************************/
char * readCol (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer){
short numBytes;
short idx, ind ; /* Array idx variables */
/* Variables for decoding packed decimal data */
char tmpstr[1024];
short collen;
char *dataptr;
/* 檢查是否為NULL */
if ( sqldaPointer->sd_column[sd_columnIndex].sd_sqlind )
{ buffer[0] = 0;
return buffer;
}
/*返回列數據到buffer變量*/
strcpy( buffer, (char *) sqldaPointer->sd_column[ sd_columnIndex ].sd_sqldata);
return buffer;
}
/* COMMENT OUT OFF */
<!--[endif]-->
<!--[if !supportLineBreakNewLine]--><!--[endif]-->
http://rickya.bokee.com/viewdiary.14009093.html