In any application software, accessing a database system is almost inevitable. Some programming languages, such as Java and C++ with custom libraries, do a very good job of providing good programmer interfaces to access the database. What if you want to access a database using C or COBOL or FORTRAN? This is usually the case in firms that use legacy applications--commonly large banks. These firms phase out systems at a much slower pace than do companies in other industries, due to the systems being extremely critical: they perform electronic funds tracking. It's difficult to introduce new technologies while rolling out the older technology just to satisfy the thirst of technology evangelists. Such systems usually rely on old ways of accessing databases, including low-level database libraries such as OCI in case of Oracle, Sybase Open Client Connect Libraries, and Pro*C. It takes plenty of planning to move to newer generation systems; it takes years of parallel and phased development in order to phase out older implementations.
Database access mechanisms such as Pro*C will stay relevant for some time to come. Pro*C is a very programmer- and reviewer-friendly way of writing code to access a database. The pre-compilation stage turns Pro*C code into low-level structures and function calls. This paradigm provides quite a few advantages. First, it shields the programmer from the underlying API calls used to access the database. Second, the actual SQL statements and the host programming language code really mix very well to produce very readable and clear code. It's not perfect, but I appreciate it.
Embedded SQL is a superset of Sybase's T-SQL or Oracle's PL/SQL that lets you place SQL statements in application programs written in languages such as C and COBOL. Pro*C allows the C programmer to write database access code fast and with less of a learning curve. For people who are familiar with both C and SQL, this is a cakewalk. Its worth noting that there are differences between implementations of Pro*C across different database vendors due to the differences between database architectures, datatypes, etc. Each new release of a database may announce certain enhancements or changes to its Embedded SQL pre-compiler. It is best to track changes on this front by referring to the vendor database websites.
This article discusses the ways and means of writing Pro*C code to access and modify a database system. My test databases are mainly Sybase and Oracle, as these are in wide use in the same environments as Pro*C. Finally, it's worth noting that some people refer to Pro*C as Embedded SQL.
Ready? What does it take to connect to a Sybase database using conventional C API calls (such as using Sybase OpenClient Connect Libraries)?
Note: This code, dbCon.c, is the sample Sybase programming code available from Sybase.
/************************************************
* *
* 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);
}
}
This code invokes quite a few APIs and performs plenty of error checking. How much simpler Pro*C makes this! Note that Pro*C source files have the extensions .PC or .pc. The Pro*C pre-compiler will produce the actual .c or .cpp files.
|
The following example Pro*C program shows only the declarations and statements required for opening a connection to the database successfully. I'll explain it later. Just see how much more concise it is:
/*----------------------------------------------*
* 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;
}
What a difference in the number of lines of code and the clarity of the code, as compared to using API calls directly!
The Pro*C pre-compiler often comes with database management software such as Sybase or Oracle. The Pro*C pre-compiler replaces each of the Pro*C calls with appropriate C/C++ calls. The C/C++ compiler then compiles and links the generated C/C++ program like any other regular program. The linker obviously needs to know the locations of the Sybase- or Oracle-specific libraries. Figure 1 shows the related phases.

Figure 1. Flow of compiling a Pro*C program
Every Pro*C statement follows a general syntax pattern: they begin with the keywords EXEC SQL and terminate with a semi-colon (;). The code that follows the keywords EXEC SQL mostly correspond one-to-one with the actual SQL code of the database, of course with the extra ability to pass host language (C/C++) variables to the database and retrieve return values. For example, the SQL statement:
COMMIT tran
translates into Pro*C code as:
EXEC SQL COMMIT tran;
Host variables transfer data between the host language, such as C/C++, and the SQL database calls. To declare the host variables:
EXEC SQL BEGIN DECLARE SECTION;
// Here declare all the host variables.
EXEC SQL END DECLARE SECTION;
Refer to a host variable by prefixing its name with a colon (:). For example:
/*-----------------------------------------*
* 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;
Suppose you want to have multiple database connections in your program. Your code will look something like this:
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;
Then execute your SQL statements, like this:
EXEC SQL AT "Cust_Db"
/* or */
EXEC SQL AT :custdb
SELECT ...
There are some caveats to these variables, however.
EXEC SQL (BEGIN/END) DECLARE section, but you cannot use pointers or pointer expressions as host variables.The host variable datatype must be compatible with the database column type. For example, the C datatypes char and char[] matches the VARCHAR Oracle type. short/int/long/float/etc. match the NUMBER/NUMBER(P,S) type in the database.
You can also declare host variable structures to represent a row in your database table. An array of host structures would then represent a set of rows in the table. Pro*C allows you to fetch or manipulate the array of host structs at once rather than fetching/manipulating data for individual host variables. This improves performance and gives a more logical way to represent the database data. For example:
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
Every host variable can have an indicator variable associated with it. Indicator variables are two-byte integer values that indicate the value of the corresponding host variable. Declare them as part of the host variable declaration in the DECLARE section.
Table one shows the indicator values and their meanings.
| Indicator Variable Value | Description |
|---|---|
| 0 | The operation was successful. |
| -1 | A NULL was returned, inserted, or updated. |
| -2 | Output to a character host variable from a "long" type was truncated, but the original column length cannot be determined. |
| >0 | The result of a SELECT or FETCH into a character host variable was truncated. In this case, if the host variable is a multibyte character variable, the indicator value is the original column length in characters. If the host variable is not a multibyte character variable, then the indicator length is the original column length in bytes. |
Indicator values are straightforward to use:
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...";
...
}
Apart from using the host variables to pass data back and forth between database and host language, SQL Communication Area (or SQLCA) can propagate status and runtime information between the database and the host. SQLCA sends runtime information of the database to the host language to take appropriate actions upon certain database events.
Use SQLCA with:
EXEC SQL INCLUDE SQLCA.H; // In Oracle
and
EXEC SQL INCLUDE SQLCA; // In Sybase
This declaration includes a structure called sqlca, which has a SQL status code, an error message, a warning message, etc., for the most recently executed SQL. You can read details directly out of the sqlca structure.
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];
};
|
Handle errors by examining the error codes and reasons from the sqlca structure, or use the WHENEVER clause:
EXEC SQL WHENEVER <CONDITION> <ACTION>
CONDITION can be:
SQLERROR, indicating that an error occurred while executing the previous SQL command.SQLWARNING, indicating that a warning occurred a result of executing the previous SQL command.NOT FOUND, indicating that the database found no data as a result of the previous SQL command.ACTION can be:
STOP, which will exit() the program, rolling back all uncommitted transactions.CONTINUE, which will try to continue executing the program despite the error.DO <function>, where function, an error-handling function, which will call that function.GOTO <label>, which will transfer control to the labelled code.
Database transactions allow you to treat a set of SQL statements as a single unit. Changes made as part of these SQL statements either get committed to the database permanently or rolled back (undone), always maintaining data integrity. Pro*C allows you to define, begin, and end transactions just like in SQL or procedural SQL. Note that, unless you wrap your SQL statements in transactions, each SQL statement occurs in an individual transaction. This code snippet illustrates the use of transaction statements in Pro*C:
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;
When a query returns multiple rows, you can process each row of data by using either "Cursors" or "Host Arrays." The Cursor refers to the current row in the set of rows returned by the query. This allows your program to process the data one row at a time.
Operating on a cursor involves using the statements:
DECLARE CURSOR to associate the name of a cursor to a query.OPEN to execute the query and to identify the set of rows returned by the query as an "Active Set" of the cursor.FETCH to operate on individual rows by fetching the next row from the set.CLOSE to disable the cursor and close the active set.An example will help:
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;
Note that you cannot use the INTO clause as part of the Cursor's SELECT. Instead, use a FETCH to select columns into host variables.
You also cannot declare a cursor in one source file and try to open/use it in another source file.
Here's a simple example to demonstrate the SELECT and INSERT operations:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
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;
}
I wrote and compiled this program using the Sybase 10.0.1 pre-compiler tools on a Sun Solaris machine:
#
# 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_delegate
Sai Kiran Gummaraj is a software engineer with close to eight years of experience in the software industry.
Return to O'Reilly Databases
Copyright © 2009 O'Reilly Media, Inc.