1
/*********************************************************************
2
dbinfx.ec: C-level interface to SQL.
3
This is a layer above esql/c,
4
since embedded SQL is often difficult to use, especially for new programmers.
5
Most SQL queries are relatively simple, whence the esql API is overkill.
6
Why mess with cryptic $directives when you can write:
7
sql_select("select this, that from table1, table2 where keycolumn = %d",
10
More important, this API automatically aborts (or longjumps) if an error
11
occurs, unless that error has been specifically trapped by the program.
12
This minimizes application-level error-leg programming,
13
thereby reducing the code by as much as 1/3.
14
To accomplish this, the errorPrint() function,
15
supplied by the application, must never return.
16
We assume it passes the error message
17
to stderr and to a logfile,
18
and then exits, or longjumps to a recovery point.
20
Note that this API works within the context of our own C programming
23
Note that dbapi.h does NOT include the Informix header files.
24
That would violate the spirit of this layer,
25
which attempts to sheild the application from the details of the SQL API.
26
If the application needed to see anything in the Informix header files,
27
we would be doing something wrong.
28
*********************************************************************/
30
/* bring in the necessary Informix headers */
39
#define CACHELIMIT 10000 /* number of cached lines */
41
#define ENGINE_ERRCODE sqlca.sqlcode
44
/*********************************************************************
45
The status variable ENGINE_ERRCODE holds the return code from an Informix call.
46
This is then used by the function errorTrap() below.
47
If ENGINE_ERRCODE != 0, errorTrap() aborts the program, or performs
48
a recovery longjmp, as directed by the generic error function errorPrint().
49
errorTrap() returns true if an SQL error occurred, but that error
50
was trapped by the application.
51
In this case the calling routine should clean up as best it can and return.
52
*********************************************************************/
54
static const char *stmt_text = 0; /* text of the SQL statement */
55
static const short *exclist; /* list of error codes trapped by the application */
56
static short translevel;
59
/* Through globals, make error info available to the application. */
60
int rv_lastStatus, rv_vendorStatus, rv_stmtOffset;
63
static void debugStatement(void)
65
if(sql_debug && stmt_text)
66
appendFileNF(sql_debuglog, stmt_text);
67
} /* debugStatement */
69
static void debugExtra(const char *s)
72
appendFileNF(sql_debuglog, s);
75
/* Append the SQL statement to the debug log. This is not strictly necessary
76
* if sql_debug is set, since the statement has already been appended. */
77
static void showStatement(void)
79
if(!sql_debug && stmt_text)
80
appendFileNF(sql_debuglog, stmt_text);
83
/* application sets the exception list */
84
void sql_exclist(const short *list) { exclist = list; }
86
void sql_exception(int errnum)
93
/* text descriptions corresponding to our generic SQL error codes */
94
static char *sqlErrorList[] = {0,
95
"miscelaneous SQL error",
96
"syntax error in SQL statement",
97
"filename cannot be used by SQL",
98
"cannot convert/compare the columns/constants in the SQL statement",
99
"bad string subscripting",
100
"bad use of the rowid construct",
101
"bad use of a blob column",
102
"bad use of aggregate operators or columns",
104
"bad use of a serial column",
105
"bad use of a temp table",
106
"operation cannot cross databases",
107
"database is fucked up",
108
"query interrupted by user",
109
"could not connect to the database",
110
"database has not yet been selected",
119
"constraint not found",
120
"duplicate constraint",
121
"stored procedure not found",
122
"duplicate stored procedure",
125
"table has no primary or unique key",
126
"duplicate primary or unique key",
127
"cursor not specified, or cursor is not available",
129
"the database lacks the resources needed to complete this query",
130
"check constrain violated",
131
"referential integrity violated",
132
"cannot manage or complete the transaction",
133
"long transaction, too much log data generated",
134
"this operation must be run inside a transaction",
135
"cannot open, read, write, close, or otherwise manage a blob",
136
"row, table, page, or database is already locked, or cannot be locked",
137
"inserting null into a not null column",
138
"no permission to modify the database in this way",
139
"no current row established",
140
"many rows were found where one was expected",
141
"cannot union these select statements together",
142
"cannot access or write the audit trail",
143
"could not run SQL or gather data from a remote host",
144
"where clause is semantically unmanageable",
148
/* map Informix errors to our own exception codes, as defined in c_sql.h. */
149
static struct ERRORMAP {
184
{231, EXCAGGREGATEUSE},
190
{237, EXCMANAGETRANS},
191
{238, EXCMANAGETRANS},
194
{241, EXCMANAGETRANS},
200
{255, EXCNOTINTRANS},
201
{256, EXCMANAGETRANS},
210
{265, EXCNOTINTRANS},
214
{269, EXCNOTNULLCOLUMN},
217
{272, EXCPERMISSION},
218
{273, EXCPERMISSION},
219
{274, EXCPERMISSION},
220
{275, EXCPERMISSION},
224
{281, EXCTEMPTABLEUSE},
229
{286, EXCNOTNULLCOLUMN},
234
{292, EXCNOTNULLCOLUMN},
236
{294, EXCAGGREGATEUSE},
240
{298, EXCPERMISSION},
241
{299, EXCPERMISSION},
244
{302, EXCPERMISSION},
245
{ 303, EXCAGGREGATEUSE},
246
{304, EXCAGGREGATEUSE},
255
{313, EXCPERMISSION},
257
{315, EXCPERMISSION},
262
{320, EXCPERMISSION},
263
{321, EXCAGGREGATEUSE},
264
{323, EXCTEMPTABLEUSE},
277
{336, EXCTEMPTABLEUSE},
278
{337, EXCTEMPTABLEUSE},
302
{367, EXCAGGREGATEUSE},
310
{375, EXCMANAGETRANS},
311
{376, EXCMANAGETRANS},
312
{377, EXCMANAGETRANS},
315
{383, EXCAGGREGATEUSE},
318
{386, EXCNOTNULLCOLUMN},
319
{387, EXCPERMISSION},
320
{388, EXCPERMISSION},
321
{389, EXCPERMISSION},
322
{390, EXCDUPSYNONYM},
323
{391, EXCNOTNULLCOLUMN},
325
{393, EXCWHERECLAUSE},
327
{395, EXCWHERECLAUSE},
328
{396, EXCWHERECLAUSE},
330
{398, EXCNOTINTRANS},
331
{399, EXCMANAGETRANS},
378
{506, EXCPERMISSION},
380
{508, EXCTEMPTABLEUSE},
381
{509, EXCTEMPTABLEUSE},
382
{510, EXCTEMPTABLEUSE},
383
{512, EXCPERMISSION},
384
{514, EXCPERMISSION},
385
{515, EXCNOCONSTRAINT},
387
{518, EXCNOCONSTRAINT},
391
{524, EXCNOTINTRANS},
398
{532, EXCTEMPTABLEUSE},
400
{535, EXCMANAGETRANS},
402
{537, EXCNOCONSTRAINT},
406
{541, EXCPERMISSION},
409
{544, EXCAGGREGATEUSE},
410
{545, EXCPERMISSION},
411
{548, EXCTEMPTABLEUSE},
416
{559, EXCDUPSYNONYM},
418
{561, EXCAGGREGATEUSE},
429
{573, EXCMANAGETRANS},
431
{576, EXCTEMPTABLEUSE},
432
{577, EXCDUPCONSTRAINT},
434
{579, EXCPERMISSION},
435
{580, EXCPERMISSION},
436
{582, EXCMANAGETRANS},
437
{583, EXCPERMISSION},
442
{592, EXCNOTNULLCOLUMN},
445
{595, EXCAGGREGATEUSE},
449
{600, EXCMANAGEBLOB},
450
{601, EXCMANAGEBLOB},
451
{602, EXCMANAGEBLOB},
452
{603, EXCMANAGEBLOB},
453
{604, EXCMANAGEBLOB},
454
{605, EXCMANAGEBLOB},
455
{606, EXCMANAGEBLOB},
466
{618, EXCMANAGEBLOB},
468
{623, EXCNOCONSTRAINT},
469
{625, EXCDUPCONSTRAINT},
470
{628, EXCMANAGETRANS},
471
{629, EXCMANAGETRANS},
472
{630, EXCMANAGETRANS},
474
{635, EXCPERMISSION},
482
/* I'm not about to map all possible compile/runtime SPL errors. */
495
{703, EXCNOTNULLCOLUMN},
496
{704, EXCDUPCONSTRAINT},
497
{706, EXCPERMISSION},
504
{25553, EXCNOCONNECT},
505
{25587, EXCNOCONNECT},
506
{25588, EXCNOCONNECT},
507
{25596, EXCNOCONNECT},
509
}; /* ends of list */
511
static int errTranslate(int code)
515
for(e=errormap; e->infcode; ++e) {
516
if(e->infcode == code)
522
static bool errorTrap(void)
526
rv_lastStatus = rv_vendorStatus = 0; /* innocent until proven guilty */
529
if(ENGINE_ERRCODE >= 0) return false; /* no problem */
532
rv_vendorStatus = -ENGINE_ERRCODE;
533
rv_lastStatus = errTranslate(rv_vendorStatus);
534
rv_stmtOffset = sqlca.sqlerrd[4];
535
rv_badToken = sqlca.sqlerrm;
536
if(!rv_badToken[0]) rv_badToken = 0;
538
/* if the application didn't trap for this exception, blow up! */
540
for(i=0; exclist[i]; ++i) {
541
if(exclist[i] == rv_lastStatus) {
542
exclist = 0; /* we've spent that exception */
548
errorPrint("2SQL error %d, %s", rv_vendorStatus, sqlErrorList[rv_lastStatus]);
549
return true; /* make the compiler happy */
553
/*********************************************************************
554
The OCURS structure given below maintains an open SQL cursor.
555
A static array of these structures allows multiple cursors
556
to be opened simultaneously.
557
*********************************************************************/
559
static struct OCURS {
560
char sname[8]; /* statement name */
561
char cname[8]; /* cursor name */
563
char rv_type[NUMRETS];
566
short cid; /* cursor ID */
569
char **fl; /* array of fetched lines */
572
/* values for struct OCURS.flag */
573
#define CURSOR_NONE 0
574
#define CURSOR_PREPARED 1
575
#define CURSOR_OPENED 2
577
/* find a free cursor structure */
578
static struct OCURS *findNewCursor(void)
582
for(o=ocurs, i=0; i<NUMCURSORS; ++i, ++o) {
583
if(o->flag != CURSOR_NONE) continue;
584
sprintf(o->cname, "c%u", i);
585
sprintf(o->sname, "s%u", i);
589
errorPrint("2more than %d cursors opend concurrently", NUMCURSORS);
590
return 0; /* make the compiler happy */
591
} /* findNewCursor */
593
/* dereference an existing cursor */
594
static struct OCURS *findCursor(int cid)
597
if(cid < 6000 || cid >= 6000+NUMCURSORS)
598
errorPrint("2cursor number %d is out of range", cid);
601
if(o->flag == CURSOR_NONE)
602
errorPrint("2cursor %d is not currently active", cid);
603
rv_numRets = o->numRets;
604
memcpy(rv_type, o->rv_type, NUMRETS);
608
/* part of the disconnect() procedure */
609
static void clearAllCursors(void)
614
for(i=0, o=ocurs; i<NUMCURSORS; ++i, ++o) {
615
if(o->flag == CURSOR_NONE) continue;
616
o->flag = CURSOR_NONE;
619
for(j=0; j<o->alloc; ++j)
624
} /* loop over cursors */
628
} /* clearAllCursors */
631
/*********************************************************************
632
Connect and disconect to SQL databases.
633
*********************************************************************/
635
void sql_connect(const char *db, const char *login, const char *pw)
637
$char *dblocal = (char*)db;
638
login = pw = 0; /* not used here, so make the compiler happy */
639
if(isnullstring(dblocal)) {
640
dblocal = getenv("DBNAME");
641
if(isnullstring(dblocal))
642
errorPrint("2sql_connect receives no database, check $DBNAME");
646
stmt_text = "disconnect";
653
stmt_text = "connect";
655
$connect to :dblocal;
656
if(errorTrap()) return;
657
sql_database = dblocal;
659
/* set default lock mode and isolation level for transaction management */
660
stmt_text = "lock isolation";
662
$ set lock mode to wait;
668
$ set isolation to committed read;
669
if(errorTrap()) goto abort;
673
void sql_disconnect(void)
676
stmt_text = "disconnect";
683
} /* sql_disconnect */
685
/* make sure we're connected to a database */
686
static void checkConnect(void)
689
errorPrint("2SQL command issued, but no database selected");
693
/*********************************************************************
694
Begin, commit, and abort transactions.
695
SQL does not permit nested transactions; this API does, to a limited degree.
696
An inner transaction cannot fail while an outer one succeeds;
697
that would require SQL support which is not forthcoming.
698
However, as long as all transactions succeed, or the outer most fails,
699
everything works properly.
700
The static variable transLevel holds the number of nested transactions.
701
*********************************************************************/
703
/* begin a transaction */
704
void sql_begTrans(void)
708
stmt_text = "begin work";
710
/* count the nesting level of transactions. */
714
if(errorTrap()) return;
720
/* end a transaction */
721
static void endTrans(bool commit)
727
errorPrint("2end transaction without a matching begTrans()");
731
stmt_text = "commit work";
734
errorPrint("2Cannot commit a transaction around an aborted transaction");
735
if(translevel == 0) {
737
if(ENGINE_ERRCODE) ++translevel;
740
} else { /* success or failure */
741
stmt_text = "rollback work";
744
if(!translevel) { /* bottom level */
746
if(ENGINE_ERRCODE) --translevel;
750
} /* success or failure */
752
/* At this point I will make a bold assumption --
753
* that all cursors are declared with hold.
754
* Hence they remain valid after the transaction is closed,
755
* and we don't have to change any of the OCURS structures. */
760
void sql_commitWork(void) { endTrans(true); }
761
void sql_rollbackWork(void) { endTrans(false); }
763
void sql_deferConstraints(void)
766
errorPrint("2Cannot defer constraints unless inside a transaction");
767
stmt_text = "defer constraints";
769
$set constraints all deferred;
772
} /* sql_deferConstraints */
775
/*********************************************************************
776
Blob management routines, a somewhat awkward interface.
777
Global variables tell SQL where to unload the next fetched blob:
778
either a file (truncate or append) or an allocated chunk of memory.
779
This assumes each fetch or select statement retrieves at most one blob.
780
Since there is no %blob directive in lineFormat(),
781
one cannot simply slip a blob in with the rest of the data as a row is
782
updated or inserted. Instead the row must be created first,
783
then the blob is entered separately, using blobInsert().
784
This means every blob column must permit nulls, at least within the schema.
785
Also, what use to be an atomic insert might become a multi-statement
786
transaction if data integrity is important.
787
Future versions of our line formatting software may support a %blob directive,
788
which makes sense only when the formatted string is destined for SQL.
789
*********************************************************************/
791
/* information about the blob being fetched */
792
const char *rv_blobFile;
794
void *rv_blobLoc; /* location of blob in memory */
795
int rv_blobSize; /* size of blob in bytes */
796
static loc_t blobstruct; /* Informix structure to manage the blob */
798
/* insert a blob into the database */
799
void sql_blobInsert(const char *tabname, const char *colname, int rowid,
800
const char *filename, void *offset, int length)
805
/* basic sanity checks */
807
if(isnullstring(tabname)) errorPrint("2blobInsert, missing table name");
808
if(isnullstring(colname)) errorPrint("2blobInsert, missing column name");
809
if(rowid <= 0) errorPrint("2invalid rowid in blobInsert");
810
if(length < 0) errorPrint("2invalid length in blobInsert");
811
if(strlen(tabname) + strlen(colname) + 42 >= sizeof(blobcmd))
812
errorPrint("2internal blobInsert command too long");
814
/* set up the blob structure */
815
memset(&insblob, 0, sizeof(insblob));
817
insblob.loc_loctype = LOCMEMORY;
819
if(length == 0) offset = 0;
821
if(!offset) length = -1;
822
insblob.loc_buffer = offset;
823
insblob.loc_bufsize = length;
824
insblob.loc_size = length;
825
if(!offset) insblob.loc_indicator = -1;
827
insblob.loc_loctype = LOCFNAME;
828
insblob.loc_fname = (char*)filename;
829
insblob.loc_oflags = LOC_RONLY;
830
insblob.loc_size = -1;
833
/* set up the blob insert command, using one host variable */
834
sprintf(blobcmd, "update %s set %s = ? where rowid = %d",
835
tabname, colname, rowid);
838
$prepare blobinsert from :blobcmd;
839
if(errorTrap()) return;
840
$execute blobinsert using :insblob;
842
rv_lastNrows = sqlca.sqlerrd[2];
843
rv_lastRowid = sqlca.sqlerrd[5];
844
if(sql_debug) appendFile(sql_debuglog, "%d rows affected", rv_lastNrows);
846
} /* sql_blobInsert */
849
/*********************************************************************
850
When an SQL statement is prepared, the engine tells us the types and lengths
851
of the columns. Use this information to "normalize" the sqlda
852
structure, so that columns are fetched using our preferred formats.
853
For instance, smallints and ints both map into int variables,
854
varchars become chars, dates map into strings (so that we can convert
855
them into our own vendor-independent binary representations later), etc.
856
We assume the number and types of returns have been established.
857
Once retsSetup has "normalized" the sqlda structure,
858
run the select or fetch, and then call retsCleanup to post-process the data.
859
This will, for example, turn dates, fetched into strings,
860
into our own 4-byte representations.
861
The same for time intervals, money, etc.
862
*********************************************************************/
864
/* Arrays that hold the return values from a select statement. */
865
int rv_numRets; /* number of returned values */
866
char rv_type[NUMRETS+1]; /* datatypes of returned values */
867
char rv_name[NUMRETS+1][COLNAMELEN]; /* column names */
868
LF rv_data[NUMRETS]; /* the returned values */
869
int rv_lastNrows, rv_lastRowid, rv_lastSerial;
870
/* Temp area to read the Informix values, as strings */
871
static char retstring[NUMRETS][STRINGLEN+4];
872
static va_list sqlargs;
874
static void retsSetup(struct sqlda *desc)
877
bool blobpresent = false;
878
struct sqlvar_struct *v;
880
for(i=0; (unsigned)i< NUMRETS; ++i) {
881
rv_data[i].l = nullint;
887
for(i=0,v=desc->sqlvar; i<rv_numRets; ++i,++v ) {
888
strncpy(rv_name[i], v->sqlname, COLNAMELEN);
894
v->sqltype = CCHARTYPE;
895
v->sqllen = STRINGLEN+2;
896
v->sqldata = retstring[i];
897
rv_data[i].ptr = retstring[i];
901
v->sqltype = CINTTYPE;
903
v->sqldata = (char *) &rv_data[i].l;
908
v->sqltype = CDOUBLETYPE;
910
v->sqldata = (char*) &rv_data[i].f;
911
rv_data[i].f = nullfloat;
917
errorPrint("2Cannot select more than one blob at a time");
919
v->sqltype = CLOCATORTYPE;
920
v->sqllen = sizeof(blobstruct);
921
v->sqldata = (char*) &blobstruct;
922
memset(&blobstruct, 0, sizeof(blobstruct));
924
blobstruct.loc_loctype = LOCMEMORY;
925
blobstruct.loc_mflags = LOC_ALLOC;
926
blobstruct.loc_bufsize = -1;
928
blobstruct.loc_loctype = LOCFNAME;
929
blobstruct.loc_fname = (char*)rv_blobFile;
930
blobstruct.lc_union.lc_file.lc_mode = 0600;
931
blobstruct.loc_oflags =
932
(rv_blobAppend ? LOC_WONLY|LOC_APPEND : LOC_WONLY);
937
errorPrint("@bad character %c in retsSetup", rv_type[i]);
939
} /* loop over fetched columns */
942
/* clean up fetched values, eg. convert date to our proprietary format. */
943
static void retsCleanup(void)
948
/* no blobs unless proven otherwise */
950
rv_blobSize = nullint;
952
for(i=0; i<rv_numRets; ++i) {
953
clipString(retstring[i]);
957
if(retstring[i][4] == '-') yearfirst = true;
958
rv_data[i].l = stringDate(retstring[i],yearfirst);
962
/* thanks to stringTime(), this works for either hh:mm or hh:mm:ss */
963
if(retstring[i][0] == 0) rv_data[i].l = nullint;
965
/* convert space to 0 */
966
if(retstring[i][1] == ' ') retstring[i][1] = '0';
967
/* skip the leading space that is produced when Informix converts interval to string */
968
rv_data[i].l = stringTime(retstring[i]+1);
973
rv_data[i].l = retstring[i][0];
978
/* null floats look different from null dates and ints. */
979
if(rv_data[i].l == 0xffffffff) {
980
rv_data[i].f = nullfloat;
981
if(rv_type[i] == 'M') rv_data[i].l = nullint;
984
/* represent monitary amounts as an integer number of pennies. */
985
if(rv_type[i] == 'M')
986
rv_data[i].l = rv_data[i].f * 100.0 + 0.5;
990
/* map the empty string into the null string */
991
l = strlen(retstring[i]);
992
if(!l) rv_data[i].ptr = 0;
993
if(l > STRINGLEN) errorPrint("2fetched string is too long, limit %d chars", STRINGLEN);
998
if(blobstruct.loc_indicator >= 0) { /* not null blob */
999
rv_blobSize = blobstruct.loc_size;
1000
if(!rv_blobFile) rv_blobLoc = blobstruct.loc_buffer;
1001
if(rv_blobSize == 0) { /* turn empty blob into null blob */
1004
rv_blobSize = nullint;
1007
rv_data[i].l = rv_blobSize;
1011
/* Convert from Informix null to our nullint */
1012
if(rv_data[i].l == 0x80000000) rv_data[i].l = nullint;
1016
errorPrint("@bad character %c in retsCleanup", rv_type[i]);
1017
} /* switch on datatype */
1018
} /* loop over columsn fetched */
1021
void retsCopy(bool allstrings, void *first, ...)
1027
errorPrint("@calling retsCopy() with no returns pending");
1029
for(i=0; i<rv_numRets; ++i) {
1032
va_start(sqlargs, first);
1035
q = va_arg(sqlargs, void*);
1038
if((int)q < 1000 && (int)q > -1000)
1039
errorPrint("2retsCopy, pointer too close to 0");
1041
if(allstrings) *(char*)q = 0;
1043
if(rv_type[i] == 'S') {
1046
strcpy(q, rv_data[i].ptr);
1047
} else if(rv_type[i] == 'C') {
1048
*(char *)q = rv_data[i].l;
1049
if(allstrings) ((char*)q)[1] = 0;
1050
} else if(rv_type[i] == 'F') {
1052
if(isnotnullfloat(rv_data[i].f)) sprintf(q, "%lf", rv_data[i].f);
1054
*(double *)q = rv_data[i].f;
1056
} else if(allstrings) {
1057
char type = rv_type[i];
1058
long l = rv_data[i].l;
1061
strcpy(q, dateString(l, DTDELIMIT));
1062
} else if(type == 'I') {
1063
strcpy(q, timeString(l, DTDELIMIT));
1064
} else if(type == 'M') {
1065
sprintf(q, "%ld.%02d", l/100, l%100);
1066
} else sprintf(q, "%ld", l);
1069
*(long *)q = rv_data[i].l;
1071
} /* loop over result parameters */
1073
if(!first) va_end(sqlargs);
1076
/* convert column name into column index */
1077
int findcol_byname(const char *name)
1080
for(i=0; rv_name[i][0]; ++i)
1081
if(stringEqual(name, rv_name[i])) break;
1083
errorPrint("2Column %s not found in the columns or aliases of your select statement", name);
1085
} /* findcol_byname */
1087
/* make sure we got one return value, and it is integer compatible */
1088
static long oneRetValue(void)
1090
char coltype = rv_type[0];
1091
long n = rv_data[0].l;
1093
errorPrint("2SQL statement has %d return values, 1 value expected", rv_numRets);
1094
if(!strchr("MNFDIC", coltype))
1095
errorPrint("2SQL statement returns a value whose type is not compatible with a 4-byte integer");
1096
if(coltype == 'F') n = rv_data[0].f;
1101
/*********************************************************************
1102
Prepare a formatted SQL statement.
1103
Gather the types and names of the fetched columns and make this information
1104
available to the rest of the C routines in this file, and to the application.
1105
Returns the populated sqlda structure for the statement.
1106
Returns null if the prepare failed.
1107
*********************************************************************/
1109
static struct sqlda *prepare(const char *stmt_parm, const char *sname_parm)
1111
$char*stmt = (char*)stmt_parm;
1112
$char*sname = (char*)sname_parm;
1114
struct sqlvar_struct *v;
1118
if(isnullstring(stmt)) errorPrint("2null SQL statement");
1122
/* look for delete with no where clause */
1123
while(*stmt == ' ') ++stmt;
1124
if(!strncmp(stmt, "delete", 6) || !strncmp(stmt, "update", 6))
1125
/* delete or update */
1126
if(!strstr(stmt, "where") && !strstr(stmt, "WHERE")) {
1128
errorPrint("2Old Mcdonald bug");
1131
/* set things up to nulls, in case the prepare fails */
1134
memset(rv_type, 0, NUMRETS);
1135
rv_lastNrows = rv_lastRowid = rv_lastSerial = 0;
1137
$prepare :sname from :stmt;
1138
if(errorTrap()) return 0;
1140
/* gather types and column headings */
1141
$describe: sname into desc;
1142
if(!desc) errorPrint("2$describe couldn't allocate descriptor");
1143
rv_numRets = desc->sqld;
1144
if(rv_numRets > NUMRETS) {
1146
errorPrint("2cannot select more than %d values", NUMRETS);
1149
for(i=0,v=desc->sqlvar; i<rv_numRets; ++i,++v ) {
1150
coltype = v->sqltype & SQLTYPE;
1151
/* kludge, count(*) should be int, not float, in my humble opinion */
1152
if(stringEqual(v->sqlname, "(count(*))"))
1164
/* We only process datetime year to minute, for databases
1165
* other than Informix, which don't have a date type. */
1166
if(v->sqllen != 5) errorPrint("2datetime field must be year to minute");
1201
errorPrint ("@Unknown informix sql datatype %d", coltype);
1202
} /* switch on type */
1203
} /* loop over returns */
1210
/*********************************************************************
1211
Run an SQL statement internally, and gather any fetched values.
1212
This statement stands alone; it fetches at most one row.
1213
You might simply know this, perhaps because of a unique key,
1214
or you might be running a stored procedure.
1215
For efficiency we do not look for a second row, so this is really
1216
like the "select first" construct that some databases support.
1217
A mode variable says whether execution or selection or both are allowed.
1218
Return true if data was successfully fetched.
1219
*********************************************************************/
1221
static bool execInternal(const char *stmt, int mode)
1224
$static char singlestatement[] = "single_use_stmt";
1225
$static char singlecursor[] = "single_use_cursor";
1227
bool notfound = false;
1228
short errorcode = 0;
1230
desc = prepare(stmt, singlestatement);
1231
if(!desc) return false; /* error */
1236
errorPrint("2SQL select statement returns no values");
1238
$execute :singlestatement;
1240
} else { /* end no return values */
1244
errorPrint("2SQL statement returns %d values", rv_numRets);
1246
$execute: singlestatement into descriptor desc;
1250
errorcode = rv_vendorStatus;
1252
/* select or execute ran properly */
1253
/* error 100 means not found in Informix */
1254
if(ENGINE_ERRCODE == 100) notfound = true;
1255
/* set "last" parameters, in case the application is interested */
1256
rv_lastNrows = sqlca.sqlerrd[2];
1257
rv_lastRowid = sqlca.sqlerrd[5];
1258
rv_lastSerial = sqlca.sqlerrd[1];
1259
} /* successful run */
1261
$free :singlestatement;
1268
rv_vendorStatus = errorcode;
1269
rv_lastStatus = errTranslate(rv_vendorStatus);
1275
} /* execInternal */
1278
/*********************************************************************
1279
Run individual select or execute statements, using the above internal routine.
1280
*********************************************************************/
1282
/* pointer to vararg list; most of these are vararg functions */
1283
/* execute a stand-alone statement with no % formatting of the string */
1284
void sql_execNF(const char *stmt)
1286
execInternal(stmt, 1);
1289
/* execute a stand-alone statement with % formatting */
1290
void sql_exec(const char *stmt, ...)
1292
va_start(sqlargs, stmt);
1293
stmt = lineFormatStack(stmt, 0, &sqlargs);
1294
execInternal(stmt, 1);
1298
/* run a select statement with no % formatting of the string */
1299
/* return true if the row was found */
1300
bool sql_selectNF(const char *stmt, ...)
1303
va_start(sqlargs, stmt);
1304
rc = execInternal(stmt, 2);
1307
} /* sql_selectNF */
1309
/* run a select statement with % formatting */
1310
bool sql_select(const char *stmt, ...)
1313
va_start(sqlargs, stmt);
1314
stmt = lineFormatStack(stmt, 0, &sqlargs);
1315
rc = execInternal(stmt, 2);
1320
/* run a select statement with one return value */
1321
int sql_selectOne(const char *stmt, ...)
1324
va_start(sqlargs, stmt);
1325
stmt = lineFormatStack(stmt, 0, &sqlargs);
1326
rc = execInternal(stmt, 2);
1327
if(!rc) { va_end(sqlargs); return nullint; }
1328
return oneRetValue();
1329
} /* sql_selectOne */
1331
/* run a stored procedure with no % formatting */
1332
static bool sql_procNF(const char *stmt)
1335
char *s = allocMem(20+strlen(stmt));
1336
strcpy(s, "execute procedure ");
1338
rc = execInternal(s, 3);
1339
/* if execInternal doesn't return, we have a memory leak */
1344
/* run a stored procedure */
1345
bool sql_proc(const char *stmt, ...)
1348
va_start(sqlargs, stmt);
1349
stmt = lineFormatStack(stmt, 0, &sqlargs);
1350
rc = sql_procNF(stmt);
1351
if(rv_numRets) retsCopy(false, 0);
1355
/* run a stored procedure with one return */
1356
int sql_procOne(const char *stmt, ...)
1359
va_start(sqlargs, stmt);
1360
stmt = lineFormatStack(stmt, 0, &sqlargs);
1361
rc = sql_procNF(stmt);
1362
if(!rc) { va_end(sqlargs); return 0; }
1363
return oneRetValue();
1367
/*********************************************************************
1368
Prepare, open, close, and free SQL cursors.
1369
*********************************************************************/
1371
/* prepare a cursor; return the ID number of that cursor */
1372
static int prepareCursor(const char *stmt, bool scrollflag)
1374
$char *internal_sname, *internal_cname;
1375
struct OCURS *o = findNewCursor();
1377
stmt = lineFormatStack(stmt, 0, &sqlargs);
1379
internal_sname = o->sname;
1380
internal_cname = o->cname;
1381
o->desc = prepare(stmt, internal_sname);
1382
if(!o->desc) return -1;
1383
if(o->desc->sqld == 0) {
1385
errorPrint("2statement passed to sql_prepare has no returns");
1388
/* declare with hold;
1389
* you might run transactions within this cursor. */
1391
$declare :internal_cname scroll cursor with hold for :internal_sname;
1393
$declare :internal_cname cursor with hold for :internal_sname;
1399
o->numRets = rv_numRets;
1400
memcpy(o->rv_type, rv_type, NUMRETS);
1401
o->flag = CURSOR_PREPARED;
1402
o->fl = 0; /* just to make sure */
1404
} /* prepareCursor */
1406
int sql_prepare(const char *stmt, ...)
1409
va_start(sqlargs, stmt);
1410
n = prepareCursor(stmt, false);
1415
int sql_prepareScrolling(const char *stmt, ...)
1418
va_start(sqlargs, stmt);
1419
n = prepareCursor(stmt, true);
1422
} /* sql_prepareScrolling */
1424
void sql_open(int cid)
1427
$char *internal_sname, *internal_cname;
1428
struct OCURS *o = findCursor(cid);
1429
if(o->flag == CURSOR_OPENED)
1430
errorPrint("2cannot open cursor %d, already opened", cid);
1431
internal_sname = o->sname;
1432
internal_cname = o->cname;
1434
$open :internal_cname;
1435
if(!errorTrap()) o->flag = CURSOR_OPENED;
1438
for(i=0; i<o->alloc; ++i) {
1445
int sql_prepOpen(const char *stmt, ...)
1448
va_start(sqlargs, stmt);
1449
n = prepareCursor(stmt, false);
1453
short ev = rv_vendorStatus;
1454
short el = rv_lastStatus;
1456
rv_vendorStatus = ev;
1461
} /* sql_prepOpen */
1463
void sql_close(int cid)
1465
$char *internal_sname, *internal_cname;
1466
struct OCURS *o = findCursor(cid);
1467
if(o->flag < CURSOR_OPENED)
1468
errorPrint("2cannot close cursor %d, not yet opened", cid);
1469
internal_cname = o->cname;
1470
debugExtra("close");
1471
$close :internal_cname;
1472
if(errorTrap()) return;
1473
o->flag = CURSOR_PREPARED;
1477
void sql_free( int cid)
1479
$char *internal_sname, *internal_cname;
1480
struct OCURS *o = findCursor(cid);
1481
if(o->flag == CURSOR_OPENED)
1482
errorPrint("2cannot free cursor %d, not yet closed", cid);
1483
internal_sname = o->sname;
1485
$free :internal_sname;
1486
if(errorTrap()) return;
1487
o->flag = CURSOR_NONE;
1490
memset(rv_name, 0, sizeof(rv_name));
1491
memset(rv_type, 0, sizeof(rv_type));
1492
if(o->fl) { /* free any cached lines */
1494
for(i=0; i<o->alloc; ++i)
1503
void sql_closeFree(int cid)
1505
const short *exc = exclist;
1507
if(!rv_lastStatus) {
1511
} /* sql_closeFree */
1513
/* fetch row n from the open cursor.
1514
* Flag can be used to fetch first, last, next, or previous. */
1515
bool fetchInternal(int cid, long n, int flag, bool remember)
1517
$char *internal_sname, *internal_cname;
1518
$long nextrow, lastrow;
1519
struct sqlda *internal_desc;
1520
struct OCURS *o = findCursor(cid);
1522
internal_cname = o->cname;
1523
internal_desc = o->desc;
1524
retsSetup(internal_desc);
1526
/* don't do the fetch if we're looking for row 0 absolute,
1527
* that just nulls out the return values */
1528
if(flag == 6 && !n) {
1536
lastrow = nextrow = o->rownum;
1537
if(flag == 6) nextrow = n;
1538
if(flag == 3) nextrow = 1;
1539
if(isnotnull(lastrow)) { /* we haven't lost track yet */
1540
if(flag == 1) ++nextrow;
1541
if(flag == 2 && nextrow) --nextrow;
1543
if(flag == 4) { /* fetch the last row */
1544
nextrow = nullint; /* we just lost track */
1545
if(o->fl && o->flag == CURSOR_PREPARED) {
1546
/* I'll assume you've read in all the rows, cursor is closed */
1547
for(nextrow=o->alloc-1; nextrow>=0; --nextrow)
1548
if(o->fl[nextrow]) break;
1553
if(!nextrow) goto fetchZero;
1555
/* see if we have cached this row */
1556
if(isnotnull(nextrow) && o->fl &&
1557
nextrow <= o->alloc && o->fl[nextrow-1]) {
1558
sql_mkload(o->fl[nextrow-1], '\177');
1559
/* don't run retsCleanup() here */
1561
rv_blobSize = nullint;
1562
o->rownum = nextrow;
1565
} /* bringing row out of cache */
1567
if(o->flag != CURSOR_OPENED)
1568
errorPrint("2cannot fetch from cursor %d, not yet opened", cid);
1570
/* The next line of code is very subtle.
1571
I use to declare all cursors as scroll cursors.
1572
It's a little inefficient, but who cares.
1573
Then I discovered you can't fetch blobs from scroll cursors.
1574
You can however fetch them from regular cursors,
1575
even with an order by clause.
1576
So cursors became non-scrolling by default.
1577
If the programmer chooses to fetch by absolute number,
1578
but he is really going in sequence, I turn them into
1579
fetch-next statements, so that the cursor need not be a scroll cursor. */
1581
isnotnull(lastrow) && isnotnull(nextrow) &&
1582
nextrow == lastrow+1)
1585
debugExtra("fetch");
1589
$fetch :internal_cname using descriptor internal_desc;
1592
$fetch previous :internal_cname using descriptor internal_desc;
1595
$fetch first :internal_cname using descriptor internal_desc;
1598
$fetch last :internal_cname using descriptor internal_desc;
1602
errorPrint("2sql fetches absolute row using null index");
1603
$fetch absolute :nextrow :internal_cname using descriptor internal_desc;
1606
errorPrint("@fetchInternal() receives bad flag %d", flag);
1610
if(errorTrap()) return false;
1612
if(ENGINE_ERRCODE == 100) return false; /* not found */
1613
o->rownum = nextrow;
1615
/* remember the unload image of this line */
1617
sql_cursorUpdLine(cid, cloneString(sql_mkunld('\177')));
1619
} /* fetchInternal */
1621
bool sql_fetchFirst(int cid, ...)
1624
va_start(sqlargs, cid);
1625
rc = fetchInternal(cid, 0L, 3, false);
1628
} /* sql_fetchFirst */
1630
bool sql_fetchLast(int cid, ...)
1633
va_start(sqlargs, cid);
1634
rc = fetchInternal(cid, 0L, 4, false);
1637
} /* sql_fetchLast */
1639
bool sql_fetchNext(int cid, ...)
1642
va_start(sqlargs, cid);
1643
rc = fetchInternal(cid, 0L, 1, false);
1646
} /* sql_fetchNext */
1648
bool sql_fetchPrev(int cid, ...)
1651
va_start(sqlargs, cid);
1652
rc = fetchInternal(cid, 0L, 2, false);
1655
} /* sql_fetchPrev */
1657
bool sql_fetchAbs(int cid, long rownum, ...)
1660
va_start(sqlargs, rownum);
1661
rc = fetchInternal(cid, rownum, 6, false);
1664
} /* sql_fetchAbs */
1667
/* the inverse of sql_mkunld() */
1668
void sql_mkload(const char *line, char delim)
1674
for(i = 0, s = (char*)line; *s; ++i, *t=delim, s = t+1) {
1675
t = strchr(s, delim);
1676
if(!t) errorPrint("2sql load line does not end in a delimiter");
1679
errorPrint("2sql load line contains more than %d fields", rv_numRets);
1681
switch(rv_type[i]) {
1683
if(!*s) { data = nullint; break; }
1684
data = strtol(s, &s, 10);
1685
if(*s) errorPrint("2sql load, cannot convert string to integer");
1689
if((unsigned)strlen(s) > STRINGLEN)
1690
errorPrint("2sql load line has a string that is too long");
1691
strcpy(retstring[i], s);
1692
data = (int) retstring[i];
1697
rv_data[i].f = *s ? atof(s) : nullfloat;
1701
data = stringDate(s,0);
1703
errorPrint("2sql load, cannot convert string to date");
1709
errorPrint("2sql load, character field contains more than one character");
1713
data = stringTime(s);
1715
errorPrint("2sql load, cannot convert string to time interval");
1719
errorPrint("2sql load cannot convert into type %c", rv_type[i]);
1720
} /* switch on type */
1722
rv_data[i].l = data;
1723
} /* loop over fields in line */
1726
errorPrint("2sql load line contains %d fields, %d expected", i, rv_numRets);
1730
/*********************************************************************
1731
We maintain our own cache of fetched lines.
1733
After all, Informix already maintains a cache of fetched lines.
1734
That's what the open cursor is for.
1735
Looks like serious wheel reinvention to me.
1736
Perhaps, but you can't change the data in the cache that Informix maintains.
1737
This is something Powerbuild et al discovered over a decade ago.
1738
Consider a simple spreadsheet application.
1739
You update the value in one of the cells, thereby updating the row
1740
in the database. Now scroll down to the next page, and then back again.
1741
If you fetch from the open cursor you will get the old data, before the
1742
change was made, even though the new data is safely ensconsed in the database.
1743
Granted one could reopen the cursor and fetch the new data,
1744
but this can be slow for certain queries (sometimes a couple minutes).
1745
In other words, rebuilding the cursor is not really an option.
1746
So we are forced to retain a copy of the data in our program and change it
1747
whenever we update the database.
1748
Unfortunately the following 3 routines were developed separately,
1749
and they are wildly inconsistent. Some take a row number while
1750
others assume you are modifying the current row as stored in o->rownum.
1751
Some accept a line of tex, the unload image of the fetch data, while
1752
others build the line of text from the fetched data in rv_data[].
1753
I apologize for this confusion; clearly a redesign is called for.
1754
*********************************************************************/
1756
/* update the text of a fetched line,
1757
* so we get this same text again when we refetch the line later.
1758
* These text changes corespond to the database changes that form an update.
1759
* We assume the line has been allocated using malloc(). */
1760
void sql_cursorUpdLine(int cid, const char *line)
1762
struct OCURS *o = findCursor(cid);
1763
int n = o->rownum-1;
1766
errorPrint("2SQL cursor caches too many lines, limit %d", CACHELIMIT);
1769
/* running off the end, allocate 128 at a time */
1770
short oldalloc = o->alloc;
1773
o->fl = (char **) allocMem(o->alloc*sizeof(char*));
1775
o->fl = (char**) reallocMem((void*)o->fl, o->alloc*sizeof(char*));
1776
memset(o->fl+oldalloc, 0, (o->alloc-oldalloc)*sizeof(char*));
1777
} /* allocating more space */
1780
o->fl[n] = (char*)line;
1781
} /* sql_cursorUpdLine */
1783
void sql_cursorDelLine(int cid, int rownum)
1785
struct OCURS *o = findCursor(cid);
1788
if(rownum >= o->alloc || !o->fl[rownum])
1789
errorPrint("2cursorDelLine(%d)", rownum);
1790
nzFree(o->fl[rownum]);
1791
if(rownum < o->alloc-1)
1792
memcpy(o->fl+rownum, o->fl+rownum+1, (o->alloc-rownum-1)*sizeof(char *));
1793
o->fl[o->alloc-1] = 0;
1794
/* back up the row number if we deleted the last row */
1795
if(!o->fl[rownum]) --o->rownum;
1796
} /* sql_cursorDelLine */
1798
void sql_cursorInsLine(int cid, int rownum)
1800
struct OCURS *o = findCursor(cid);
1803
/* must insert a row within or immediately following the current data */
1804
if(rownum > o->alloc)
1805
errorPrint("2cursorInsLine(%d)", rownum);
1806
/* newly inserted row becomes the current row */
1807
o->rownum = rownum+1;
1809
if(!o->alloc || o->fl[o->alloc-1]) { /* need to make room */
1812
o->fl = (char **) allocMem(o->alloc*sizeof(char*));
1814
o->fl = (char**) reallocMem((void*)o->fl, o->alloc*sizeof(char*));
1815
memset(o->fl+o->alloc-128, 0, 128*sizeof(char*));
1816
} /* allocating more space */
1818
/* move the rest of the lines down */
1819
for(i=o->alloc-1; i>rownum; --i)
1820
o->fl[i] = o->fl[i-1];
1821
o->fl[i] = cloneString(sql_mkunld('\177'));
1822
} /* sql_cursorInsLine */
1825
/*********************************************************************
1826
run the analog of /bin/comm on two open cursors,
1827
rather than two Unix files.
1828
This assumes a common unique key that we use to sync up the rows.
1829
The cursors should be sorted by this key.
1830
*********************************************************************/
1833
const char *stmt1, const char *stmt2, /* the two select statements */
1834
const char *orderby, /* which fetched column is the unique key */
1835
fnptr f, /* call this function for differences */
1836
char delim) /* sql_mkunld() delimiter, or call mkinsupd if delim = 0 */
1838
short cid1, cid2; /* the cursor ID numbers */
1839
char *line1, *line2, *s; /* the two fetched rows */
1840
void *blob1, *blob2; /* one blob per table */
1841
int blob1size, blob2size;
1842
bool eof1, eof2, get1, get2;
1843
int sortval1, sortval2;
1844
char sortstring1[80], sortstring2[80];
1847
int passkey1, passkey2;
1848
static const char sortnull[] = "cursor_comm, sortval%d is null";
1849
static const char sortlong[] = "cursor_comm cannot key on strings longer than %d";
1850
static const char noblob[] = "sorry, cursor_comm cannot handle blobs yet";
1852
cid1 = sql_prepOpen(stmt1);
1853
cid2 = sql_prepOpen(stmt2);
1855
sortcol = findcol_byname(orderby);
1856
sorttype = rv_type[sortcol];
1857
if(charInList("NDIS", sorttype) < 0)
1858
errorPrint("2cursor_com(), column %s has bad type %c", orderby, sorttype);
1860
passkey1 = (int)sortstring1, passkey2 = (int)sortstring2;
1862
eof1 = eof2 = false;
1864
rv_blobFile = 0; /* in case the cursor has a blob */
1869
if(get1) { /* fetch first row */
1870
eof1 = !sql_fetchNext(cid1, 0);
1876
if(sorttype == 'S') {
1877
s = rv_data[sortcol].ptr;
1878
if(isnullstring(s)) errorPrint(sortnull, 1);
1879
if(strlen(s) >= sizeof(sortstring1))
1880
errorPrint(sortlong, sizeof(sortstring1));
1881
strcpy(sortstring1, s);
1883
passkey1 = sortval1 = rv_data[sortcol].l;
1884
if(isnull(sortval1))
1885
errorPrint(sortnull, 1);
1887
line1 = cloneString(delim ? sql_mkunld(delim) : sql_mkinsupd());
1890
blob1size = rv_blobSize;
1894
} /* looking for first line */
1896
if(get2) { /* fetch second row */
1897
eof2 = !sql_fetchNext(cid2, 0);
1903
if(sorttype == 'S') {
1904
s = rv_data[sortcol].ptr;
1905
if(isnullstring(s)) errorPrint(sortnull, 2);
1906
if(strlen(s) >= sizeof(sortstring2))
1907
errorPrint(sortlong, sizeof(sortstring2));
1908
strcpy(sortstring2, rv_data[sortcol].ptr);
1910
passkey2 = sortval2 = rv_data[sortcol].l;
1911
if(isnull(sortval2))
1912
errorPrint(sortnull, 2);
1914
line2 = cloneString(delim ? sql_mkunld(delim) : sql_mkinsupd());
1917
blob2size = rv_blobSize;
1921
} /* looking for second line */
1923
if(eof1 & eof2) break; /* done */
1924
get1 = get2 = false;
1926
/* in cid2, but not in cid1 */
1928
(sorttype == 'S' && strcmp(sortstring1, sortstring2) > 0 ||
1929
sorttype != 'S' && sortval1 > sortval2)) {
1930
(*f)('>', line1, line2, passkey2);
1935
/* in cid1, but not in cid2 */
1937
(sorttype == 'S' && strcmp(sortstring1, sortstring2) < 0 ||
1938
sorttype != 'S' && sortval1 < sortval2)) {
1939
(*f)('<', line1, line2, passkey1);
1945
/* perhaps the lines are equal */
1946
if(stringEqual(line1, line2)) continue;
1948
/* lines are different between the two cursors */
1949
(*f)('*', line1, line2, passkey2);
1950
} /* loop over parallel cursors */
1956
sql_closeFree(cid1);
1957
sql_closeFree(cid2);
1960
/*********************************************************************
1961
Get the primary key for a table.
1962
In informix, you can use system tables to get this information.
1963
There's a way to do it in odbc, but I don't remember.
1964
*********************************************************************/
1967
getPrimaryKey(char *tname, int *part1, int *part2)
1970
char *s = strchr(tname, ':');
1971
*part1 = *part2 = 0;
1973
rc = sql_select("select part1, part2 \
1974
from sysconstraints c, systables t, sysindexes i \
1975
where tabname = %S and t.tabid = c.tabid \
1976
and constrtype = 'P' and c.idxname = i.idxname",
1980
rc = sql_select("select part1, part2 \
1981
from %s:sysconstraints c, %s:systables t, %s:sysindexes i \
1982
where tabname = %S and t.tabid = c.tabid \
1983
and constrtype = 'P' and c.idxname = i.idxname",
1984
tname, tname, tname, s+1, &p1, &p2);
1987
if(rc) *part1 = p1, *part2 = p2;
1988
} /* getPrimaryKey */