/************************************************ * * * Filename : dbCon.c * * * * This is function demonstrates the series of * * steps it involves in opening a connection * * to the Sybase database. * * * * NOTE: Not all variables used are declared; * * Such as context, connection etc. as this * * is just a demo snippet. * * * ***********************************************/ void dbConnect () { CS_INT rc; CS_INT *outlen; CS_INT buf_len; CS_INT msglimit; CS_INT netdriver; /*-----------------------------------------* * Allocate a connection to the server * *-----------------------------------------*/ rc = ct_con_alloc (context, &connection); if (rc != CS_SUCCEED) { strncpy (msgstr, "CT_CONALLOC failed", \ msg_size); no_errors_sw = FALSE ; error_out (rc); } /*-----------------------------------------* * Alter properties of the * * connection for user-id * *-----------------------------------------*/ buf_len = user_size; rc = ct_con_props (connection, (long)CS_SET,\ (long)CS_USERNAME, username, buf_len,\ outlen); if (rc != CS_SUCCEED) { strncpy (msgstr, "CT_CON_PROPS for \ user-id failed",msg_size); no_errors_sw = FALSE ; error_out (rc); } /*-----------------------------------------* * Alter properties of the * * connection for password * *-----------------------------------------*/ buf_len = pwd_size; rc = ct_con_props (connection, (long)CS_SET,\ (long)CS_PASSWORD, pwd, buf_len, outlen); if (rc != CS_SUCCEED) { strncpy (msgstr, "CT_CON_PROPS for \ password failed", msg_size); no_errors_sw = FALSE ; error_out (rc); } /*-----------------------------------------* * Alter properties of the * * connection for transaction * *-----------------------------------------*/ buf_len = tran_size; rc = ct_con_props (connection, (long)CS_SET,\ (long)CS_TRANSACTION_NAME, tran,\ buf_len, outlen); if (rc != CS_SUCCEED) { strncpy (msgstr, "CT_CON_PROPS for \ transaction failed", msg_size); no_errors_sw = FALSE ; error_out (rc); } /*-----------------------------------------* * Alter properties of the connection * * for network driver * *-----------------------------------------*/ /*-----------------------------------------* * default value for non-recognized * * driver name * *-----------------------------------------*/ netdriver = 9999; /*-----------------------------------------* * if no netdriver entered, * * default is LU62 * *-----------------------------------------*/ if (strncmp(driver," ",9) == 0 ?? \ strncmp(driver,"LU62",4) == 0) netdriver = CS_LU62; else if (strncmp(driver,"INTERLINK",8) == 0) netdriver = CS_INTERLINK; else if (strncmp(driver,"IBMTCPIP",8) == 0) netdriver = CS_TCPIP; else if (strncmp(driver,"CPIC",4) == 0) netdriver = CS_NCPIC; rc = ct_con_props (connection, (long)CS_SET,\ (long)CS_NET_DRIVER, (long)netdriver,\ CS_UNUSED, outlen); if (rc != CS_SUCCEED) { strncpy (msgstr, "CT_CON_PROPS for \ network driver failed",msg_size); no_errors_sw = FALSE ; error_out (rc); } /*-----------------------------------------* * Setup retrieval of All Messages * *-----------------------------------------*/ rc = ct_diag (connection, CS_INIT, \ CS_UNUSED, CS_UNUSED, CS_NULL); if (rc != CS_SUCCEED) { strncpy (msgstr, "CT_DIAG CS_INIT \ failed", msg_size); no_errors_sw = FALSE ; error_out (rc); } /*-----------------------------------------* * Set the upper limit of number * * of messages * *-----------------------------------------*/ msglimit = 5 ; rc = ct_diag (connection, CS_MSGLIMIT, \ CS_ALLMSG_TYPE, CS_UNUSED, &msglimit); if (rc != CS_SUCCEED) { strncpy (msgstr, "CT_DIAG CS_MSGLIMIT \ failed", msg_size); no_errors_sw = FALSE ; error_out (rc); } /*-----------------------------------------* * Open connection to the server * * or CICS region * *-----------------------------------------*/ rc = ct_connect (connection, servname, \ server_size); if (rc != CS_SUCCEED) { strncpy (msgstr, "CT_CONNECT failed",\ msg_size); no_errors_sw = FALSE ; error_out (rc); } }이 코드는 API들을 호출하고 에러-검사가 많다는 것을 알려준다. 얼마 더 간단한 Pro*C가 이것을 만드는지! Pro*C 소스 파일들은 .PC 또는 .pc 확장자를 가진다는 것에 주의하라. Pro*C 프리-컴파일러는 실질적인 .c또는 .cpp 파일들을 생성한다.
/*----------------------------------------------* * This appears in the top of the file in the * * global area. Not inside any function. * *----------------------------------------------*/ EXEC SQL INCLUDE SQLCA; EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL WHENEVER SQLWARNING CONTINUE; EXEC SQL WHENEVER NOT FOUND CONTINUE; /*----------------------------------------------* * This is a simple C or C++ function that * * would make use of Pro*C code to connect * * to the database. * *----------------------------------------------*/ int database_connect() { EXEC SQL BEGIN DECLARE SECTION; char *usr; char *pswd; char *srvr; char *dbase; char *cnct; EXEC SQL END DECLARE SECTION; EXEC SQL CONNECT :usr identified by \ :pswd AT :cnct using :srvr; err_check (&sqlca, __FUNCTION__, \ __LINE__); EXEC SQL AT :cnct USE :dbase; err_check (&sqlca, __FUNCTION__, \ __LINE__); } /*---------------------------------------------* * Here is the basic error handling * * function snippet. * *---------------------------------------------*/ int error_check (SQLCA * sqlca_ptr, char *func_name, int line_num) { SQLCA *p_sqlca; if (p_sqlca -> sqlcode < 0) { printf ("Error at function: %s at \ line %d\n", func_name, line_num); printf ("\tError Code: %d\n", \ p_sqlca -> sqlerrm.sqlerrml); printf ("\tError Description: %s\n", \ p_sqlca -> sqlerrm.sqlerrmc); return p_sqlca -> sqlcode; } else if (p_sqlca -> sqlcode == 0) { printf ("%s operation successful\n", \ func_name); return p_sqlca -> sqlcode; } return 0; }코드의 라인들의 수와 코드의 명확성이 API 콜들을 직접적으로 사용하는 것과 무엇이 다른가!
COMMIT tran를 Pro*C 코드로 바꾸면 다음과 같다.
EXEC SQL COMMIT tran;"호스트 변수"를 사용하여 호스트 언어와 SQL 연결하기
EXEC SQL BEGIN DECLARE SECTION; // 여기에 모든 호스트 값들을 선언한다. EXEC SQL END DECLARE SECTION;호스트 변수는 이름이 콜론(:)으로 시작하도록 한다.
/*-----------------------------------------* * For simplicity I have just allocated * * memory statically. They can even be * * pointers and allocations can be managed * * later. * *-----------------------------------------*/ EXEC SQL BEGIN DECLARE SECTION; char username[128]; char password[128]; char db_name[128]; char connection_name[128]; EXEC SQL END DECLARE SECTION; strcpy (username, "casper"); strcpy (password, "knock knock"); strcpy (db_name, "cust_db"); strcpy (connection_name, "Sai"); EXEC SQL CONNECT :username IDENTIFIED BY :password AT :connection_name USING :db_name;당신의 프로그램에 여러 개의 데이터베이스 연결을 가지길 원한다고 가정해 보자. 당신의 코드는 다음과 같이 될 것이다.
char custdb[256] = "Cust_Db"; char invdb[256] = "Inventory_Db"; EXEC SQL CONNECT :username IDENTIFIED BY :password AT :custdb USING :db_string1; EXEC SQL CONNECT :username IDENTIFIED BY :password AT :invdb USING :db_string2;그런 후 당신은 SQL 문장들을 다음과 같이 실행한다:
EXEC SQL AT "Cust_Db" /* or */ EXEC SQL AT :custdb SELECT ...그러나, 이 값들에는 다음과 같은 몇몇 경고가 있다.
struct part_cust_rec { char cust_name[256]; float discount_rate; }; // a set of 50 key customers struct part_cust_rec key_custs[50]; EXEC SQL AT "Cust_Db" SELECT Customer_Name, Customer_Discount FROM Customer INTO :key_custs.custname, \ :key_custs.discount_rate WHERE Customer_Discount > 25지시자 변수
지시자 변수 값 | 설명 |
0 | 연산이 성공적이었다. |
-1 | insert나 update 시, NULL이 반환되었다. |
-2 | "long" 타입에서 문자형 호스트 값으로 출력 시 끝이 잘렸으나, 원본 컬럼의 길이를 결정할 수 없다. |
>0 | SELECT 또는 FETCH의 결과를 문자형 호스트 변수로 변환시 끝이 잘렸다. 이런 경우, 호스트 변수가 멀티바이트 문자형 변수이면, 지시자 값은 문자형들의 원본 컬럼 길이이다. 만약, 호스트 변수가 멀티바이트 문자형 값이 아니면, 지시자의 길이는 바이트의 원본 컬럼 길이이다. |
EXEC SQL BEGIN DECLARE SECTION; int cust_id; char cust_name[256]; char cust_address[512]; short indicator_addr; EXEC SQL END DECLARE SECTION; EXEC SQL AT "Cust_Db" SELECT Customer_Name, Customer_Address FROM Customer INTO :cust_name, :cust_address:indicator_addr /*-------------------------------------------* * Alternate Declaration of the INTO clause * * * * INTO :cust_name,:cust_address * * INDICATOR :indicator_addr * * WHERE Customer_Id = :cust_id; * *-------------------------------------------*/ if (indicator_addr == -1) { /* Address is NULL */ cout << "Customer"s Contact \ Address is not registered..."; ... }통신 지역과 에러 처리
EXEC SQL INCLUDE SQLCA.H; // In Oracle그리고,
EXEC SQL INCLUDE SQLCA; // In Sybase이 선언은 가장 최근에 실행된 SQL을 위한 SQL 상태 코드, 에러 메시지, 경고 메시지 등을 가진 sqlca라 불리는 구조체를 포함한다. 당신은 sqlca 구조체의 직접적인 출력을 통해 상세한 정보를 읽은 수 있다.
struct sqlca { /*-------------------------------------* * sqlcaid : Holds the hardcoded * * string "SQLCA" * *-------------------------------------*/ char sqlcaid[8]; /*-------------------------------------* * sqlabc : Holds the length of the * * structure * *-------------------------------------*/ long sqlabc; /*-------------------------------------* * sqlcode : Holds the status code * * of the most recently executed * * SQL statement. * *-------------------------------------*/ long sqlcode; /*-------------------------------------* * sqlerrm is a structure to hold * * error description. * * * * sqlerrmc : contains error * * description of the status code * * in sqlerrmc (upto 70 chars). * *-------------------------------------*/ struct { unsigned short sqlerrml; char sqlerrmc[70]; } sqlerrm; /*-------------------------------------* * sqlerrp : Un-used * * * * The array element sqlerrd[2] : * * Holds number of Rows * * processed by the most recent * * SQL statement * * * * The array element sqlerrd[4] : * * Holds offset of the most recent * * parse error in SQL. * * * * The array elements * * sqlerrd[0,1,3,5] : Are Un-used * *-------------------------------------*/ char sqlerrp[8]; long sqlerrd[6]; /*-------------------------------------* * sqlwarn : Holds warning information * * sqlext : Un-used. * *-------------------------------------*/ char sqlwarn[8]; char sqlext[8]; };예외 처리
EXEC SQL WHENEVERCONDITION 은 다음과 같은 것을 할 수 있다:
EXEC SQL BEGIN TRANSACTION cust_transaction_insert; EXEC SQL AT "Cust_Db" INSERT INTO Customer VALUES (:cust_id, :cust_name, :cust_address, :cust_discount); if (ptr_sqlca -> sqlcode < 0) { printf ("Your Transaction Will be \ rolled back.\n"); printf ("Error Code: %d\n", \ ptr_sqlca->sqlerrm.sqlerrml); printf ("Error Description: %s\n", \ ptr_sqlca->sqlerrm->sqlerrmc); EXEC SQL ROLLBACK TRANSACTION cust_transaction_insert; } /*-----------------------------------------* * This marks a Save point in your * * transaction. At any time ahead in your * * program if there is a roll back, you * * have an option of rolling back * * completely or to this point. * *-----------------------------------------*/ EXEC SQL SAVEPOINT start_update; /* ... Some Program Logic goes here ... */ EXEC SQL AT "Cust_Db" UPDATE Customer SET Customer_Discount = :cust_discount WHERE Customer_Id = :cust_id; if (ptr_sqlca -> sqlcode < 0) { printf ("Unable to update discount rates for the customer. %d\n", cust_id); printf ("Error Code: %d\n", ptr_sqlca->sqlerrm.sqlerrml); printf ("Error Description: %s\n", ptr_sqlca->sqlerrm->sqlerrmc); /*-------------------------------------* * Your transaction will be rolled back* * to the most recent save point * *-------------------------------------*/ EXEC SQL ROLLBACK TRANSACTION start_update; /*-------------------------------------* * If you had just said * * EXEC SQL ROLLBACK TRANSACTION * * The entire transaction would have * * been rolled back. Not just * * upto the save point. * *-------------------------------------*/ } EXEC SQL COMMIT cust_transaction_insert;커서
EXEC SQL BEGIN DECLARE SECTION; char customer_name[256]; long cust_id; char customer_address[512]; char customer_email[128]; float customer_discount; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE cust_cursor CURSOR FOR SELECT cust_id, cust_name, cust_address, cust_email, cust_discount FROM customer WHERE cust_name = :customer_name; EXEC SQL OPEN cust_cursor; EXEC SQL WHENEVER NOT FOUND DO break; while (1) { EXEC SQL FETCH cust_cursor INTO :customer_id, :customer_name, :customer_address, :customer_email, :customer_discout; /*-------------------------------------* * Logic to process retrieved * * values go here; * *-------------------------------------*/ } EXEC SQL CLOSE cust_cursor;커서의 SELECT의 부분으로 INTO 절을 사용할 수 없다는 것을 주의하라. 대신, 선택된 컬럼들을 호스트 값들로 가져오기 위해 FETCH를 사용한다. 또한 하나의 소스 파일에서 커서를 선언하고 다른 소스 파일에서 열거나 사용할 수 없다.
#include나는 Sun 솔라리스 머신 상에서 Sybase 10.0.1 프리-컴파일러를 사용하여 이 프로그램을 작성하고 컴파일하였다.#include #include EXEC SQL INCLUDE SQLCA; EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL WHENEVER SQLWARNING CONTINUE; EXEC SQL WHENEVER NOT FOUND CONTINUE; int connect (char *connection_name, char *db_server, char *db_instance, char *uname, char *passwd); int error_check (char *func_name, int line_num); void finish_proc (); int main (int argc, char *argv[]) { char dataserver[128]; char database[128]; char username[128]; char password[128]; char connection[128]; int status = 0; int choice = 0; SQLCA *p_sqlca; EXEC SQL BEGIN DECLARE SECTION; CS_INT emp_id; CS_CHAR emp_name[50]; CS_CHAR addr[50]; CS_CHAR phno[50]; CS_CHAR mgr_name[50]; short ind_mgr; float salary; CS_CHAR join_date[50]; EXEC SQL END DECLARE SECTION; printf ("Enter the database \ server name: "); scanf ("%s", dataserver); printf ("Enter the database \ instance name: "); scanf ("%s", database); printf ("Enter the username: "); scanf ("%s", username); printf ("Enter the password: "); scanf ("%s", password); printf ("Enter a name for your \ connection: "); scanf ("%s", connection); status = connect (connection, dataserver, database, username, password); if (status < 0) { printf ("Unable to proceed. Exiting...\n"); exit (1); } atexit(finish_proc); while (1) { printf ("\n\n"); printf ("\tEnter 1 for SELECT, 2 for\ INSERT and 0 to QUIT: "); scanf ("%d", &choice); switch (choice) { case 1: EXEC SQL DECLARE emp_cursor CURSOR \ FOR SELECT employee_id, \ employee_name, address, phno,\ manager_name, salary, \ joining_date FROM Emp; EXEC SQL OPEN emp_cursor; printf("employee_id\temployee_name\t\ address\tphno\\tmanager_name\t\ salary\t\joining_date\n"); printf("-----------\t\-------------\t -------\t\----\t------------\t------\ \t------------\n"); while (1) { EXEC SQL FETCH emp_cursor INTO :emp_id, :emp_name,:addr, :phno, :mgr_name:ind_mgr,:salary, :join_date; printf ("%d\t%s\t%s\t%s\t%s \ \t%f\t%s\n", emp_id, \ emp_name, addr, phno, \ ((ind_mgr == -1) ? \ "NULL" : mgr_name), salary, join_date); } break; case 2: printf ("Enter the employee id"); scanf ("%d", &emp_id); printf ("Enter the employee name:"); scanf ("%s", emp_name); printf ("Enter the employee \ address: "); scanf ("%s", addr); printf ("Enter the employee \ phno: "); scanf ("%s", phno); printf ("Enter the employee\ manager name: "); scanf ("%s", mgr_name); printf ("Enter the employee \ salary: "); scanf ("%f", &salary); printf ("Enter the employee \ joining date (MM/DD/YYYY):"); scanf ("%s", join_date); EXEC SQL BEGIN TRANSACTION emp_insert; EXEC SQL INSERT INTO Emp VALUES (:emp_id, :emp_name, :addr, :phno, :mgr_name, :salary, :join_date); p_sqlca = &sqlca; if (p_sqlca -> sqlcode < 0) { printf ("Error Code: %d\n",\ p_sqlca -> \ sqlerrm.sqlerrml); printf ("Error Description: %s\n",p_sqlca -> \ sqlerrm.sqlerrmc); printf ("Your transaction will be rolled back."); EXEC SQL ROLLBACK TRANSACTION emp_insert; } EXEC SQL COMMIT TRANSACTION emp_insert; break; case 0: default: EXEC SQL DISCONNECT DEFAULT; exit(0); } } } int connect (char *c_name, char *db_svr, char *db_inst, char *uname, char *passwd) { int status = 0; EXEC SQL BEGIN DECLARE SECTION; char *connection_name; char *db_server; char *username; char *password; char *db_instance; EXEC SQL END DECLARE SECTION; connection_name = c_name; db_server = db_svr; db_instance = db_inst; username = uname; password = passwd; EXEC SQL CONNECT :username identified by :password AT :connection_name using :db_server; status = error_check("connect", __LINE__); if (status < 0) return status; EXEC SQL AT :connection_name USE :db_instance; status = error_check("connect", __LINE__); if (status < 0) return status; return status; } int error_check (char *func_name, int line_num) { SQLCA *p_sqlca; p_sqlca = &sqlca; if (p_sqlca -> sqlcode < 0) { printf ("Error at function: %s \ at line %d\n", func_name, line_num); printf ("\tError Code: %d\n", p_sqlca -> sqlerrm.sqlerrml); printf ("\tError Description: %s\n", p_sqlca -> sqlerrm.sqlerrmc); return -1; } else if (p_sqlca -> sqlcode == 0) { printf ("%s operation successful\n", func_name); return 0; } return 0; }
# # Note: You will need to compile sybesql.c # and link the sybesql.o with your object file. # You can find sybesql.c under # $SYBASE_HOME/include # $ cc -g -c -I/opt/sybase_10.0.1/sybembsql/include -I/opt/sybase_10.0.1/include sybesql.c $ /opt/sybase_10.0.1/sybembsql/bin/cpre -a -r \ -m -C ANSI_C db_delegate.PC $ cc -g -c \ -I/opt/sybase_10.0.1/sybembsql/include \ -I/opt/sybase_10.0.1/include db_delegate.c $ cc -g -mt sybesql.o db_delegate.o \ /opt/sybase_10.0.1/lib/libct.a \ /opt/sybase_10.0.1/lib/libcs.a \ /opt/sybase_10.0.1/lib/libcomn.a \ /opt/sybase_10.0.1/lib/libtcl.a \ /opt/sybase_10.0.1/lib/libintl.a \ /opt/sybase_10.0.1/lib/libtli.a \ -L/opt/sybase_10.0.1/lib \ -L/opt/sybase_10.0.1/sybembsql/lib \ -lsocket -lnsl -ldl -lm -o db_delegateSai Kiran Gummaraj는 소프트웨어 산업에 소프트웨어 엔지니어로 약 8년 가까이 종사하고 있다.
이전 글 : 리눅스에서 메모리가 부족할 때
최신 콘텐츠