Programming with SQL Relay using the C++ API
- Compiling an SQL Relay Client Program
- Establishing a Sessions
- Executing Queries
- Commits and Rollbacks
- Temporary Tables
- Catching Errors
- Substitution and Bind Variables
- Re-Binding and Re-Executing
- Accessing Fields in the Result Set
- Dealing With Large Result Sets
- Cursors
- Getting Column Information
- Stored Procedures
- Caching The Result Set
- Suspending and Resuming Sessions
Compiling an SQL Relay Client Program
When writing an SQL Relay client program using the C++ API, you need to include the sqlrclient.h file.
> > #include
1<sqlrelay sqlrclient.h="">
2>
3
4You'll also need to link against the sqlrclient and rudiments libraries. The include file is usually found in /usr/local/firstworks/include and the libraries are usually found in /usr/local/firstworks/lib.
5
6The command to compile your .C file to object code will look something like this (assuming you're using GNU C++):
7
8> g++ -I/usr/local/firstworks/include -c myprogram.C
9
10The command to compile your .o file to an executable will look something like this (assuming you're using GNU C++):
11
12> g++ -o myprogram myprogram.o -L/usr/local/firstworks/lib -lsqlrclient -lrudiments
13
14Establishing a Session
15
16
17
18To use SQL Relay, you have to identify the connection that you intend to use.
19
20>
21> #include <sqlrelay sqlrclient.h="">
22>
23> main() {
24>
25> sqlrconnection con= **new** sqlrconnection("host",9000,"","user","password",0,1);
26>
27> ... execute some queries ...
28>
29> **delete** con;
30> }
31>
32
33After calling the constructor, a session is established when the first query, ping() or identify() is run.
34
35For the duration of the session, the client stays connected to a database connection daemon. While one client is connected, no other client can connect. Care should be taken to minimize the length of a session.
36
37If you're using a transactional database, ending a session has a catch. Database connection daemons can be configured to send either a commit or rollback at the end of a session if DML queries were executed during the session with no commit or rollback. Program accordingly.
38
39Executing Queries
40
41
42
43Allocate a cursor, then call sendQuery() or sendFileQuery() to run a query. The same cursor may be used over and over.
44
45>
46> #include <sqlrelay sqlrclient.h="">
47>
48> main() {
49>
50> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
51> sqlrcursor *cur= **new** sqlrcursor(con);
52>
53> cur->sendQuery("select * from my_table");
54>
55> ... do some stuff that takes a short time ...
56>
57> cur->sendFileQuery("/usr/local/myprogram/sql","myquery.sql");
58> con->endSession();
59>
60> ... do some stuff that takes a long time ...
61>
62> cur->sendQuery("select * from my_other_table");
63> con->endSession();
64>
65> ... process the result set ...
66>
67> **delete** cur;
68> **delete** con;
69> }
70>
71
72Note the call to endSession() after the call to sendFileQuery(). Since the program does some stuff that takes a long time between that query and the next, ending the session there allows another client an opportunity to use that database connection while your client is busy. The next call to sendQuery() establishes another session. Since the program does some stuff that takes a short time between the first two queries, it's OK to leave the session open between them.
73
74Commits and Rollbacks
75
76
77
78If you need to execute a commit or rollback, you should use the commit() and rollback() methods of the sqlrconnection class rather than sending a "commit" or "rollback" query. There are two reasons for this. First, it's much more efficient to call the methods. Second, if you're writing code that can run on transactional or non-transactional databases, some non-transactional databases will throw errors if they receive a "commit" or "rollback" query, but by calling the commit() and rollback() methods you instruct the database connection daemon to call the commit and rollback API methods for that database rather than issuing them as queries. If the API's have no commit or rollback methods, the calls do nothing and the database throws no error. This is especially important when using SQL Relay with ODBC.
79
80You can also turn Autocommit on or off with the autoCommitOn() and autoCommitOff() methods of the sqlrconnection class. When Autocommit is on, the database performs a commit after each successful DML or DDL query. When Autocommit is off, the database commits when the client instructs it to, or (by default) when a client disconnects. For databases that don't support Autocommit, autoCommitOn() and autoCommitOff() have no effect.
81
82Temporary Tables
83
84
85
86Some databases support temporary tables. That is, tables which are automatically dropped or truncated when an application closes it's connection to the database or when a transaction is committed or rolled back.
87
88For databases which drop or truncate tables when a transaction is committed or rolled back, temporary tables work naturally.
89
90However, for databases which drop or truncate tables when an application closes it's connection to the database, there is an issue. Since SQL Relay maintains persistent database connections, when an application disconnects from SQL Relay, the connection between SQL Relay and the database remains, so the database does not know to drop or truncate the table. To remedy this situation, SQL Relay parses each query to see if it created a temporary table, keeps a list of temporary tables and drops (or truncates them) when the application disconnects from SQL Relay. Since each database has slightly different syntax for creating a temporary table, SQL Relay parses each query according to the rules for that database.
91
92In effect, temporary tables should work when an application connects to SQL Relay in the same manner that they would work if the application connected directly to the database.
93
94Catching Errors
95
96
97
98If your call to sendQuery() or sendFileQuery() returns a 0, the query failed. You can find out why by calling errorMessage().
99
100>
101> #include <sqlrelay sqlrclient.h="">
102> #include <iostream.h>
103>
104> main() {
105>
106> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
107> sqlrcursor *cur= **new** sqlrcursor(con);
108>
109> **if** (!cur->sendQuery("select * from my_nonexistant_table")) {
110> cout << cur->errorMessage() << endl;
111> }
112>
113> **delete** cur;
114> **delete** con;
115> }
116>
117>
118
119Substitution and Bind Variables
120
121
122
123Programs rarely execute fixed queries. More often than not, some part of the query is dynamically generated. It's convenient to store queries in files so they can be changed by a non-C++ programmer. The SQL Relay API provides methods for making substitutions and binds in those queries.
124
125For a detailed discussion of substitutions and binds, see this document .
126
127Rather than just calling sendFileQuery() you call prepareFileQuery(), substitution(), inputBind() and executeQuery().
128
129>
130> /usr/local/myprogram/sql/myquery.sql:
131>
132> select * from mytable $(whereclause)
133>
134>
135> Program code:
136>
137>
138> #include <sqlrelay sqlrclient.h="">
139>
140> main() {
141>
142> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
143> sqlrcursor *cur= **new** sqlrcursor(con);
144>
145> cur->prepareFileQuery("/usr/local/myprogram/sql","myquery.sql");
146> cur->substitution("whereclause","where col1=:value1");
147> cur->inputBind("value1","true");
148> cur->executeQuery();
149>
150> ... process the result set ...
151>
152> **delete** cur;
153> **delete** con;
154> }
155>
156
157If you're using a database with an embedded procedural language, you may want to retrieve data from a call to one of it's functions. To facilitate this, SQL Relay provides the defineOutputBind() and getOutputBind() methods.
158
159> PL/SQL Procedure:
160>
161>
162>
163> FUNCTION sp_mytable RETURN types.cursorType
164> l_cursor types.cursorType;
165> BEGIN
166> OPEN l_cursor FOR **SELECT** * FROM mytable;
167> RETURN l_cursor;
168> END;
169>
170>
171>
172> Program code:
173>
174>
175> #include <sqlrelay sqlrclient.h="">
176>
177> main() {
178>
179> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
180> sqlrcursor *cur= **new** sqlrcursor(con);
181>
182> cur->prepareQuery("begin :result:=addTwoNumbers(:num1,:num2); end;");
183> cur->inputBind("num1",10);
184> cur->inputBind("num2",20);
185> cur->defineOutputBind("result",100);
186> cur->executeQuery();
187> **int** result=atoi(cur->getOutputBind("result"));
188> con->endSession();
189>
190> ... do something with the result ...
191>
192> **delete** cur;
193> **delete** con;
194> }
195>
196
197The getOutputBind() method returns a NULL value as an empty string. If you would it to come back as a NULL instead, you can call the getNullsAsNulls() method. To revert to the default behavior, you can call getNullsAsEmptyStrings().
198
199The getOutputBind() method returns a string, if you would like to get the value as a long or double, you can use getOutputBindAsLong() or getOutputBindAsDouble().
200
201If you are using Oracle 8i or higher, you can insert data into BLOB and CLOB columns using the inputBindBlob(), inputBindClob() methods.
202
203If you are curious how many bind variables have been declared in a query, you can call countBindVariables() after preparing the query.
204
205>
206> #include <sqlrelay sqlrclient.h="">
207>
208> main() {
209>
210> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
211> sqlrcursor *cur= **new** sqlrcursor(con);
212>
213> cur->executeQuery("create table images (image blob, description clob)");
214>
215> **unsigned** **char** imagedata[40000];
216> **unsigned** **long** imagelength;
217>
218> ... read an image from a file into imagedata and the length of the
219> file into imagelength ...
220>
221> **unsigned** **char** description[40000];
222> **unsigned** **long** desclength;
223>
224> ... read a description from a file into description and the length of
225> the file into desclength ...
226>
227> cur->prepareQuery("insert into images values (:image,:desc)");
228> cur->inputBindBlob("image",imagedata,imagelength);
229> cur->inputBindClob("desc",description,desclength);
230> cur->executeQuery();
231>
232> **delete** cur;
233> **delete** con;
234> }
235>
236
237Likewise, with Oracle 8i, you can retreive BLOB or CLOB data using defineOutputBindBlob(), defineOutputBindClob(), getOutputBind() and getOutputBindLength().
238
239>
240> #include <sqlrelay sqlrclient.h="">
241>
242> main() {
243>
244> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
245> sqlrcursor *cur= **new** sqlrcursor(con);
246>
247> cur->prepareQuery("begin select image into :image from images; select description into :desc from images; end;");
248> cur->defineOutputBindBlob("image");
249> cur->defineOutputBindClob("desc");
250> cur->executeQuery();
251>
252> **char** *image=cur->getOutputBind("image");
253> **long** imagelength=cur->getOutputBindLength("image");
254>
255> **char** *desc=cur->getOutputBind("desc");
256> **char** *desclength=cur->getOutputBindLength("desc");
257>
258> con->endSession();
259>
260> ... do something with image and desc ...
261>
262> **delete** cur;
263> **delete** con;
264> }
265>
266
267Sometimes it's convenient to bind a bunch of variables that may or may not actually be in the query. For example, if you are building a web based application, it may be easy to just bind all the form variables/values from the previous page, even though some of them don't appear in the query. Databases usually generate errors in this case. Calling validateBinds() just prior to calling executeQuery() causes the API to check the query for each bind variable before actually binding it, preventing those kinds of errors. There is a performance cost associated with calling validateBinds().
268
269Re-Binding and Re-Execution
270
271
272
273Another feature of the prepare/bind/execute paradigm is the ability to prepare, bind and execute a query once, then re-bind and re-execute the query over and over without re-preparing it. If your backend database natively supports this paradigm, you can reap a substantial performance improvement.
274
275>
276> #include <sqlrelay sqlrclient.h="">
277>
278> main() {
279>
280> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
281> sqlrcursor *cur= **new** sqlrcursor(con);
282>
283> cur->prepareQuery("select * from mytable where mycolumn>:value");
284> cur->inputBind("value",1);
285> cur->executeQuery();
286>
287> ... process the result set ...
288>
289> cur->clearBinds();
290> cur->inputBind("value",5);
291> cur->executeQuery();
292>
293> ... process the result set ...
294>
295> cur->clearBinds();
296> cur->inputBind("value",10);
297> cur->executeQuery();
298>
299> ... process the result set ...
300>
301> **delete** cur;
302> **delete** con;
303> }
304>
305
306Accessing Fields in the Result Set
307
308
309
310The rowCount(), colCount() and getField() methods are useful for processing result sets.
311
312>
313> #include <sqlrelay sqlrclient.h="">
314> #include <iostream.h>
315>
316> main() {
317>
318> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
319> sqlrcursor *cur= **new** sqlrcursor(con);
320>
321> cur->sendQuery("select * from my_table");
322> con->endSession();
323>
324> **for** ( **int** row=0; row<cur->rowCount(); row++) {
325> **for** ( **int** col=0; col<cur->colCount(); col++) {
326> cout << cur->getField(row,col) << ",";
327> }
328> cout << endl;
329> }
330>
331> **delete** cur;
332> **delete** con;
333> }
334>
335
336The getField() method returns a string. If you would like to get a field as a long or double, you can use getFieldAsLong() and getFieldAsDouble().
337
338You can also use getRow() which returns a NULL-terminated array of the fields in the row.
339
340>
341> #include <sqlrelay sqlrclient.h="">
342> #include <iostream.h>
343>
344> main() {
345>
346> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
347> sqlrcursor *cur= **new** sqlrcursor(con);
348>
349> cur->sendQuery("select * from my_table");
350> con->endSession();
351>
352> **for** ( **int** row=0; row<cur->rowCount(); row++) {
353> **char** **rowarray=cur->getRow(row);
354> **for** ( **int** col=0; col<cur->colCount(); col++) {
355> cout << rowarray[col] << ",";
356> }
357> cout << endl;
358> }
359>
360> **delete** cur;
361> **delete** con;
362> }
363>
364
365The getField() and getRow() methods return NULL fields as empty strings. If you would like them to come back as NULL's instead, you can call the getNullsAsNulls() method. To revert to the default behavior, you can call getNullsAsEmptyStrings().
366
367If you want to access the result set, but don't care about the column information (column names, types or sizes) and don't mind getting fields by their numeric index instead of by name, you can call the dontGetColumnInfo() method prior to executing your query. This can result in a performance improvement, especially when many queries with small result sets are executed in rapid succession. You can call getColumnInfo() again later to turn off this feature.
368
369Dealing With Large Result Sets
370
371
372
373SQL Relay normally buffers the entire result set. This can speed things up at the cost of memory. With large enough result sets, it makes sense to buffer the result set in chunks instead of all at once.
374
375Use setResultSetBufferSize() to set the number of rows to buffer at a time. Calls to getRow() and getField() cause the chunk containing the requested field to be fetched. Rows in that chunk are accessible but rows before it are not.
376
377For example, if you setResultSetBufferSize(5) and execute a query that returns 20 rows, rows 0-4 are available at once, then rows 5-9, then 10-14, then 15-19. When rows 5-9 are available, getField(0,0) will return NULL and getField(11,0) will cause rows 10-14 to be fetched and return the requested value.
378
379When buffering the result set in chunks, don't end the session until after you're done with the result set.
380
381If you call setResultSetBufferSize() and forget what you set it to, you can always call getResultSetBufferSize().
382
383When buffering a result set in chunks, the rowCount() method returns the number of rows returned so far. The firstRowIndex() method returns the index of the first row of the currently buffered chunk.
384
385>
386> #include <sqlrelay sqlrclient.h="">
387> #include <iostream.h>
388>
389> main() {
390>
391> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
392> sqlrcursor *cur= **new** sqlrcursor(con);
393>
394> cur->setResultSetBufferSize(5);
395>
396> cur->sendQuery("select * from my_table");
397>
398> **int** done=0;
399> **int** row=0;
400> **char** *field;
401> **while** (!done) {
402> **for** ( **int** col=0; col<cur->colCount(); col++) {
403> **if** (field=cur->getField(row,col)) {
404> cout << field << ",";
405> } **else** {
406> done=1;
407> }
408> }
409> cout << endl;
410> row++;
411> }
412>
413> cur->sendQuery("select * from my_other_table");
414>
415> ... process this query's result set in chunks also ...
416>
417> cur->setResultSetBufferSize(0);
418>
419> cur->sendQuery("select * from my_third_table");
420>
421> ... process this query's result set all at once ...
422>
423> con->endSession();
424>
425> **delete** cur;
426> **delete** con;
427> }
428>
429
430Cursors
431
432
433
434Cursors make it possible to execute queries while processing the result set of another query. You can select rows from a table in one query, then iterate through it's result set, inserting rows into another table, using only 1 database connection for both operations.
435
436For example:
437
438>
439> #include <sqlrelay sqlrclient.h="">
440>
441> main() {
442>
443> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
444> sqlrcursor *cursor1= **new** sqlrcursor(con);
445> sqlrcursor *cursor2= **new** sqlrcursor(con);
446>
447> cursor1->setResultSetBufferSize(10);
448> cursor1->sendQuery("select * from my_huge_table");
449>
450> **int** index=0;
451> **while** (!cursor1->endOfResultSet()) {
452> cursor2->prepareQuery("insert into my_other_table values (:1,:2,:3)");
453> cursor2->inputBind("1",cursor1->getField(index,1));
454> cursor2->inputBind("2",cursor1->getField(index,2));
455> cursor2->inputBind("3",cursor1->getField(index,3));
456> cursor2->executeQuery();
457> }
458>
459> **delete** cursor2;
460> **delete** cursor1;
461> **delete** con;
462> }
463>
464
465Prior to SQL Relay version 0.25, you would have had to buffer the first result set or use 2 database connections instead of just 1.
466
467If you are using stored procedures with Oracle 8i or higher, a stored procedure can execute a query and return a cursor. A cursor bind variable can then retrieve that cursor. Your program can retrieve the result set from the cursor. All of this can be accomplished using defineOutputBindCursor(), getOutputBindCursor() and fetchFromOutputBindCursor().
468
469> PL/SQL Procedure:
470>
471>
472>
473> FUNCTION sp_mytable RETURN types.cursorType
474> l_cursor types.cursorType;
475> BEGIN
476> OPEN l_cursor FOR **SELECT** * FROM mytable;
477> RETURN l_cursor;
478> END;
479>
480>
481>
482> Program code:
483>
484>
485> #include <sqlrelay sqlrclient.h="">
486> #include <iostream.h>
487>
488> main() {
489>
490> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
491> sqlrcursor *cur= **new** sqlrcursor(con);
492>
493> cur->prepareQuery("begin :curs:=sp_mytable; end;");
494> cur->defineOutputBindCursor("curs");
495> cur->executeQuery();
496>
497> sqlrcursor *bindcur=cur->getOutputBindCursor("curs");
498> bindcur->fetchFromBindCursor();
499>
500> // print fields from table
501> **for** ( **int** i=0; i<bindcur->rowCount(); i++) {
502> **for** ( **int** j=0; j<bindcur->colCount(); j++) {
503> cout << bindcur->getField(i,j) << ", ";
504> }
505> cout << endl;
506> }
507>
508> **delete** bindcur;
509>
510> **delete** cur;
511> **delete** con;
512> }
513>
514
515The number of cursors simultaneously available per-connection is set at compile time and defaults to 5.
516
517Getting Column Information
518
519
520
521For each column, the API supports getting the name, type and length of each field. All databases support these attributes. The API also supports getting the precision, scale, length of the longest field, and whether the column is nullable, the primary key, unique, part of a key, unsigned, zero-filled, binary, or an auto-incrementing field. However, not all databases support these attributes. If a database doesn't support an attribute, it is always returned as false.
522
523>
524> #include <sqlrelay sqlrclient.h="">
525> #include <iostream.h>
526>
527> main() {
528>
529> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
530> sqlrcursor *cur= **new** sqlrcursor(con);
531>
532> cur->sendQuery("select * from my_table");
533> con->endSession();
534>
535> **for** ( **int** i=0; i<cur->colCount(); i++) {
536> cout << "Name: " << cur->getColumnName(i) << endl;
537> cout << "Type: " << cur->getColumnType(i) << endl;
538> cout << "Length: " << cur->getColumnLength(i) << endl;
539> cout << "Precision: " << cur->getColumnPrecision(i) << endl;
540> cout << "Scale: " << cur->getColumnScale(i) << endl;
541> cout << "Longest Field: " << cur->getLongest(i) << endl;
542> cout << "Nullable: " << cur->getColumnIsNullable(i) << endl;
543> cout << "Primary Key: " << cur->getColumnIsPrimaryKey(i) << endl;
544> cout << "Unique: " << cur->getColumnIsUnique(i) << endl;
545> cout << "Part of Key: " << cur->getColumnIsPartOfKey(i) << endl;
546> cout << "Unsigned: " << cur->getColumnIsUnsigned(i) << endl;
547> cout << "Zero Filled: " << cur->getColumnIsZeroFilled(i) << endl;
548> cout << "Binary: " << cur->getColumnIsBinary(i) << endl;
549> cout << "Auto Increment:" << cur->getColumnIsAutoIncrement(i) << endl;
550> cout << endl;
551> }
552>
553> **delete** cur;
554> **delete** con;
555> }
556>
557
558Some databases force column names to upper case, others force column names to lower case, and others still support mixed-case column names. Sometimes, when migrating between databases, you can run into trouble. You can use upperCaseColumnNames() and lowerCaseColumnNames() to cause column names to be converted to upper or lower case, or you can use mixedCaseColumnNames() to cause column names to be returned in the same case as they are defined in the database.
559
560>
561> #include <sqlrelay sqlrclient.h="">
562> #include <iostream.h>
563>
564> main() {
565>
566> sqlrconnection *con= **new** sqlrconnection("host",9000,"","user","password",0,1);
567> sqlrcursor *cur= **new** sqlrcursor(con);
568>
569> // column names will be forced to upper case
570> cur->upperCaseColumnNames();
571> cur->sendQuery("select * from my_table");
572> con->endSession();
573>
574> **for** ( **int** i=0; i<cur->colCount(); i++) {
575> cout << "Name: " << cur->getColumnName(i) << endl;
576> cout << endl;
577> }
578>
579> // column names will be forced to lower case
580> cur->lowerCaseColumnNames();
581> cur->sendQuery("select * from my_table");
582> con->endSession();
583>
584> **for** ( **int** i=0; i<cur->colCount(); i++) {
585> cout << "Name: " << cur->getColumnName(i) << endl;
586> cout << endl;
587> }
588>
589> // column names will be the same as they are in the database
590> cur->mixedCaseColumnNames();
591> cur->sendQuery("select * from my_table");
592> con->endSession();
593>
594> **for** ( **int** i=0; i<cur->colCount(); i++) {
595> cout << "Name: " << cur->getColumnName(i) << endl;
596> cout << endl;
597> }
598>
599> **delete** cur;
600> **delete** con;
601> }
602>
603
604Stored Procedures
605
606
607
608Many databases support stored procedures. Stored procedures are sets of queries and procedural code that are executed inside of the database itself. For example, a stored procedure may select rows from one table, iterate through the result set and, based on the values in each row, insert, update or delete rows in other tables. A client program could do this as well, but a stored procedure is generally more efficient because queries and result sets don't have to be sent back and forth between the client and database. Also, stored procedures are generally stored in the database in a compiled state, while queries may have to be re-parsed and re-compiled each time they are sent.
609
610While many databases support stored procedures. The syntax for creating and executing stored procedures varies greatly between databases.
611
612SQL Relay supports stored procedures for most databases, but there are some caveats. Stored procedures are not currently supported when using FreeTDS against Sybase or Microsoft SQL Server. Blob/Clob bind variables are only supported in Oracle 8i or higher. Sybase stored procedures must use varchar output parameters.
613
614Stored procedures typically take input paramters from client programs through input bind variables and return values back to client programs either through bind variables or result sets. Stored procedures can be broken down into several categories, based on the values that they return. Some stored procedures don't return any values, some return a single value, some return multiple values and some return entire result sets.
615
616No Values
617
618
619Some stored procedures don't return any values. Below are examples, illustrating how to create, execute and drop this kind of stored procedure for each database that SQL Relay supports.
620
621Oracle
622
623
624To create the stored procedure, run a query like the following.
625
626>
627> **create** procedure testproc(in1 **in** **number** , in2 **in** **number** , in3 **in** **varchar2** ) is
628> begin
629> **insert** into mytable values (in1,in2,in3);
630> end;
631>
632
633To execute the stored procedure from an SQL Relay program, use code like the following.
634
635>
636> cur->prepareQuery("begin testproc(:in1,:in2,:in3); end;");
637> cur->inputBind("in1",1);
638> cur->inputBind("in2",1.1,2,1);
639> cur->inputBind("in3","hello");
640> cur->executeQuery();
641>
642
643To drop the stored procedure, run a query like the following.
644
645>
646> **drop** procedure testproc
647>
648
649Sybase and Microsoft SQL Server
650
651
652To create the stored procedure, run a query like the following.
653
654>
655> **create** procedure testproc @in1 int, @in2 **float** , @in3 **varchar** (20) as
656> **insert** into mytable values (@in1,@in2,@in3)
657>
658
659To execute the stored procedure from an SQL Relay program, use code like the following.
660
661>
662> cur->prepareQuery("exec testproc");
663> cur->inputBind("in1",1);
664> cur->inputBind("in2",1.1,2,1);
665> cur->inputBind("in3","hello");
666> cur->executeQuery();
667>
668
669To drop the stored procedure, run a query like the following.
670
671>
672> **drop** procedure testproc
673>
674
675Interbase and Firebird
676
677
678To create the stored procedure, run a query like the following.
679
680>
681> **create** procedure testproc(in1 **integer** , in2 **float** , in3 **varchar** (20)) as
682> begin
683> **insert** into mytable values (in1,in2,in3);
684> suspend;
685> end;
686>
687
688To execute the stored procedure from an SQL Relay program, use code like the following.
689
690>
691> cur->prepareQuery("execute procedure testproc ?, ?, ?");
692> cur->inputBind("1",1);
693> cur->inputBind("2",1.1,2,1);
694> cur->inputBind("3","hello");
695> cur->executeQuery();
696>
697
698To drop the stored procedure, run a query like the following.
699
700>
701> **drop** procedure testproc
702>
703
704DB2
705
706
707To create the stored procedure, run a query like the following.
708
709>
710> **create** procedure testproc( **in** in1 int, **in** in2 double, **in** in3 **varchar** (20)) language sql
711> begin
712> **insert** into mytable values (in1,in2,in3);
713> end;
714>
715
716To execute the stored procedure from an SQL Relay program, use code like the following.
717
718>
719> cur->prepareQuery("call testproc(?,?,?)");
720> cur->inputBind("1",1);
721> cur->inputBind("2",1.1,2,1);
722> cur->inputBind("3","hello");
723> cur->executeQuery();
724>
725
726To drop the stored procedure, run a query like the following.
727
728>
729> **drop** procedure testproc
730>
731
732Postgresql
733
734
735To create the stored procedure, run a query like the following.
736
737>
738> **create** function testproc(int, **float** , **varchar** (20)) returns void as '
739> begin
740> **insert** into mytable values ($1,$2,$3);
741> return;
742> end;' language plpgsql
743>
744
745To execute the stored procedure from an SQL Relay program, use code like the following.
746
747>
748> cur->prepareQuery("select testproc(:in1,:in2,:in3)");
749> cur->inputBind("in1",1);
750> cur->inputBind("in2",1.1,2,1);
751> cur->inputBind("in3","hello");
752> cur->executeQuery();
753>
754
755To drop the stored procedure, run a query like the following.
756
757>
758> **drop** procedure testproc
759>
760
761
762Single Values
763
764
765Some stored procedures return single values. Below are examples, illustrating how to create, execute and drop this kind of stored procedure for each database that SQL Relay supports.
766
767Oracle
768
769
770In Oracle, stored procedures can return values through output parameters or as return values of the procedure itself.
771
772Here is an example where the procedure itself returns a value. Note that Oracle calls these functions.
773
774To create the stored procedure, run a query like the following.
775
776>
777> **create** function testproc(in1 **in** **number** , in2 **in** **number** , in3 **in** **varchar2** ) returns **number** is
778> begin
779> return in1;
780> end;
781>
782
783To execute the stored procedure from an SQL Relay program, use code like the following.
784
785>
786> cur->prepareQuery("select testproc(:in1,:in2,:in3) from dual");
787> cur->inputBind("in1",1);
788> cur->inputBind("in2",1.1,2,1);
789> cur->inputBind("in3","hello");
790> cur->executeQuery();
791> **char** *result=cur->getField(0,0);
792>
793
794To drop the stored procedure, run a query like the following.
795
796>
797> **drop** function testproc
798>
799
800Here is an example where the value is returned through an output parameter.
801
802To create the stored procedure, run a query like the following.
803
804>
805> **create** procedure testproc(in1 **in** **number** , in2 **in** **number** , in3 **in** **varchar2** , out1 **out** **number** ) as
806> begin
807> out1:=in1;
808> end;
809>
810
811To execute the stored procedure from an SQL Relay program, use code like the following.
812
813>
814> cur->prepareQuery("begin testproc(:in1,:in2,:in3,:out1); end;");
815> cur->inputBind("in1",1);
816> cur->inputBind("in2",1.1,2,1);
817> cur->inputBind("in3","hello");
818> cur->defineOutputBind("out1",20);
819> cur->executeQuery();
820> **char** *result=cur->getOutputBind("out1");
821>
822
823To drop the stored procedure, run a query like the following.
824
825>
826> **drop** procedure testproc
827>
828
829Sybase and Microsoft SQL Server
830
831
832In Sybase and Microsoft SQL Server, stored procedures return values through output parameters rather than as return values of the procedure itself.
833
834To create the stored procedure, run a query like the following.
835
836>
837> **create** procedure testproc @in1 int, @in2 **float** , @in3 **varchar** (20), @out1 int output as
838> **select** @out1=convert( **varchar** (20),@in1)
839>
840
841To execute the stored procedure from an SQL Relay program, use code like the following.
842
843>
844> cur->prepareQuery("exec testproc");
845> cur->inputBind("in1",1);
846> cur->inputBind("in2",1.1,2,1);
847> cur->inputBind("in3","hello");
848> cur->defineOutputBind("out1",20);
849> cur->executeQuery();
850> **char** *result=cur->getOutputBind("out1");
851>
852
853To drop the stored procedure, run a query like the following.
854
855>
856> **drop** procedure testproc
857>
858
859Interbase and Firebird
860
861
862To create the stored procedure, run a query like the following.
863
864>
865> **create** procedure testproc(in1 **integer** , in2 **float** , in3 **varchar** (20)) returns (out1 **integer** ) as
866> begin
867> out1=in1;
868> suspend;
869> end;
870>
871
872To execute the stored procedure from an SQL Relay program, use code like the following.
873
874>
875> cur->prepareQuery("select * from testproc(?,?,?)");
876> cur->inputBind("1",1);
877> cur->inputBind("2",1.1,2,1);
878> cur->inputBind("3","hello");
879> cur->executeQuery();
880> **char** *result=cur->getField(0,0);
881>
882
883Alternatively, you can run a query like the following and receive the result using an output bind variable. Note that in Interbase/Firebird, input and output bind variable indices are distict from one another. The index of the output bind variable is 1 rather than 4, even though there were 3 input bind variables.
884
885>
886> cur->prepareQuery("execute procedure testproc ?, ?, ?");
887> cur->inputBind("1",1);
888> cur->inputBind("2",1.1,2,1);
889> cur->inputBind("3","hello");
890> cur->defineOutputBind("1",20);
891> cur->executeQuery();
892> **char** *result=cur->getOutputBind("1");
893>
894
895To drop the stored procedure, run a query like the following.
896
897>
898> **drop** procedure testproc
899>
900
901DB2
902
903
904In DB2, stored procedures return values through output parameters rather than as return values of the procedure itself.
905
906To create the stored procedure, run a query like the following.
907
908>
909> **create** procedure testproc( **in** in1 int, **in** in2 double, **in** in3 **varchar** (20), **out** out1 int) language sql
910> begin
911> **set** out1 = in1;
912> end
913>
914
915To execute the stored procedure from an SQL Relay program, use code like the following.
916
917>
918> cur->prepareQuery("call testproc(?,?,?,?)");
919> cur->inputBind("1",1);
920> cur->inputBind("2",1.1,2,1);
921> cur->inputBind("3","hello");
922> cur->defineOutputBind("4",25);
923> cur->executeQuery();
924> **char** *result=cur->getOutputBind("4");
925>
926
927To drop the stored procedure, run a query like the following.
928
929>
930> **drop** procedure testproc
931>
932
933Postgresql
934
935
936To create the stored procedure, run a query like the following.
937
938>
939> **create** function testfunc(int, **float** , **char** (20)) returns int as '
940> declare
941> in1 int;
942> in2 **float** ;
943> in3 **char** (20);
944> begin
945> in1:=$1;
946> return;
947> end;
948> ' language plpgsql
949>
950
951To execute the stored procedure from an SQL Relay program, use code like the following.
952
953>
954> cur->prepareQuery("select * from testfunc(:in1,:in2,:in3)");
955> cur->inputBind("in1",1);
956> cur->inputBind("in2",1.1,4,2);
957> cur->inputBind("in3","hello");
958> cur->executeQuery();
959> **char** *result=cur->getField(0,0);
960>
961
962To drop the stored procedure, run a query like the following.
963
964>
965> **drop** function testfunc(int, **float** , **char** (20))
966>
967
968
969Multiple Values
970
971
972Some stored procedures return multiple values. Below are examples, illustrating how to create, execute and drop this kind of stored procedure for each database that SQL Relay supports.
973
974Oracle
975
976
977In Oracle, stored procedures can return values through output parameters or as return values of the procedure itself. If a procedure needs to return multiple values, it can return one of them as the return value of the procedure itself, but the rest must be returned through output parameters.
978
979To create the stored procedure, run a query like the following.
980
981>
982> **create** procedure testproc(in1 **in** **number** , in2 **in** **number** , in3 **in** **varchar2** , out1 **out** **number** , out2 **out** **number** , out3 **out** **varchar2** ) is
983> begin
984> out1:=in1;
985> out2:=in2;
986> out3:=in3;
987> end;
988>
989
990To execute the stored procedure from an SQL Relay program, use code like the following.
991
992>
993> cur->prepareQuery("begin testproc(:in1,:in2,:in3,:out1,:out2,:out3); end;");
994> cur->inputBind("in1",1);
995> cur->inputBind("in2",1.1,2,1);
996> cur->inputBind("in3","hello");
997> cur->defineOutputBind("out1",20);
998> cur->defineOutputBind("out2",20);
999> cur->defineOutputBind("out3",20);
1000> cur->executeQuery();
1001> **char** *out1=cur->getOutputBind("out1");
1002> **char** *out2=cur->getOutputBind("out2");
1003> **char** *out3=cu</cur-></cur-></cur-></iostream.h></sqlrelay></cur-></iostream.h></sqlrelay></bindcur-></bindcur-></iostream.h></sqlrelay></sqlrelay></cur-></iostream.h></sqlrelay></cur-></cur-></iostream.h></sqlrelay></cur-></cur-></iostream.h></sqlrelay></sqlrelay></sqlrelay></sqlrelay></sqlrelay></sqlrelay></iostream.h></sqlrelay></sqlrelay></sqlrelay></sqlrelay>