~ubuntu-branches/ubuntu/jaunty/edbrowse/jaunty-security

« back to all changes in this revision

Viewing changes to dbinfx.ec

  • Committer: Bazaar Package Importer
  • Author(s): Kapil Hari Paranjape
  • Date: 2008-04-09 18:55:23 UTC
  • mfrom: (1.1.4 upstream) (3.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080409185523-dqokcloumyn1ibn4
Tags: 3.3.4-1
* New upstream version (3.3.4).
 - Convert between iso8859-1 and utf-8 on the fly.
 - Support reading of pdf using pdftohtml.
 - French translation of html documentation.
 - Old html documentation renamed to usersguide.
 - Additional documentation on philosophy.
* debian/control:
 - Changed homepage to sourcefource site.
 - Moved homepage from description to its own field.
 - Added "poppler-utils | xpdf-utils" to Recommends.
 - Added "www-browser", "mail-reader" and "editor" to Provides. 
 - Removed "XS-" from Vcs-Svn tag.
 - Standards-Version: 3.7.3
* debian/docs: Added new documentation files
  from "doc/" subdirectory.
* debian/watch: Updated to use sourceforge site.
* debian/edbrowse.doc-base:
  - Changed name of upstream provided html documentation from
    "ebdoc.html" to "usersguide.html".
  - Changed section from "net" to "Network/Web Browsing".
* debian/install: Compiled binary is now in "src/".

Show diffs side-by-side

added added

removed removed

Lines of Context:
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",
8
 
27, &this, &that);
9
 
 
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.
19
 
 
20
 
Note that this API works within the context of our own C programming
21
 
environment.
22
 
 
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
 
*********************************************************************/
29
 
 
30
 
/* bring in the necessary Informix headers */
31
 
$include sqlca;
32
 
$include sqltypes;
33
 
$include sqlda;
34
 
$include locator;
35
 
 
36
 
#include "eb.h"
37
 
#include "dbapi.h"
38
 
 
39
 
#define CACHELIMIT 10000 /* number of cached lines */
40
 
 
41
 
#define ENGINE_ERRCODE sqlca.sqlcode
42
 
 
43
 
 
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
 
*********************************************************************/
53
 
 
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;
57
 
static bool badtrans;
58
 
 
59
 
/* Through globals, make error info available to the application. */
60
 
int rv_lastStatus, rv_vendorStatus, rv_stmtOffset;
61
 
char *rv_badToken;
62
 
 
63
 
static void debugStatement(void)
64
 
{
65
 
        if(sql_debug && stmt_text)
66
 
                appendFileNF(sql_debuglog, stmt_text);
67
 
} /* debugStatement */
68
 
 
69
 
static void debugExtra(const char *s)
70
 
{
71
 
        if(sql_debug)
72
 
                appendFileNF(sql_debuglog, s);
73
 
} /* debugExtra */
74
 
 
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)
78
 
{
79
 
        if(!sql_debug && stmt_text)
80
 
                appendFileNF(sql_debuglog, stmt_text);
81
 
} /* showStatement */
82
 
 
83
 
/* application sets the exception list */
84
 
void sql_exclist(const short *list) { exclist = list; }
85
 
 
86
 
void sql_exception(int errnum)
87
 
{
88
 
        static short list[2];
89
 
        list[0] = errnum;
90
 
        exclist = list;
91
 
} /* sql_exception */
92
 
 
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",
103
 
        "bad use of a view",
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",
111
 
        "table not found",
112
 
        "duplicate table",
113
 
        "ambiguous table",
114
 
        "column not found",
115
 
        "duplicate column",
116
 
        "ambiguous column",
117
 
        "index not found",
118
 
        "duplicate index",
119
 
        "constraint not found",
120
 
        "duplicate constraint",
121
 
        "stored procedure not found",
122
 
        "duplicate stored procedure",
123
 
        "synonym not found",
124
 
        "duplicate synonym",
125
 
        "table has no primary or unique key",
126
 
        "duplicate primary or unique key",
127
 
        "cursor not specified, or cursor is not available",
128
 
        "duplicate cursor",
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",
145
 
        "deadlock detected",
146
 
0};
147
 
 
148
 
/* map Informix errors to our own exception codes, as defined in c_sql.h. */
149
 
static struct ERRORMAP {
150
 
        short infcode;
151
 
        short excno;
152
 
} errormap[] = {
153
 
        {200, EXCSYNTAX},
154
 
        {201, EXCSYNTAX},
155
 
        {202, EXCSYNTAX},
156
 
        {203, EXCSYNTAX},
157
 
        {204, EXCSYNTAX},
158
 
        {205, EXCROWIDUSE},
159
 
        {206, EXCNOTABLE},
160
 
        /* 207 */
161
 
        {208, EXCRESOURCE},
162
 
        {209, EXCDBCORRUPT},
163
 
        {210, EXCFILENAME},
164
 
        {211, EXCDBCORRUPT},
165
 
        {212, EXCRESOURCE},
166
 
        {213, EXCINTERRUPT},
167
 
        {214, EXCDBCORRUPT},
168
 
        {215, EXCDBCORRUPT},
169
 
        {216, EXCDBCORRUPT},
170
 
        {217, EXCNOCOLUMN},
171
 
        {218, EXCNOSYNONYM},
172
 
        {219, EXCCONVERT},
173
 
        {220, EXCSYNTAX},
174
 
        {221, EXCRESOURCE},
175
 
        {222, EXCRESOURCE},
176
 
        {223, EXCAMBTABLE},
177
 
        {224, EXCRESOURCE},
178
 
        {225, EXCRESOURCE},
179
 
        {226, EXCRESOURCE},
180
 
        {227, EXCROWIDUSE},
181
 
        {228, EXCROWIDUSE},
182
 
        {229, EXCRESOURCE},
183
 
        {230, EXCDBCORRUPT},
184
 
        {231, EXCAGGREGATEUSE},
185
 
        {232, EXCSERIAL},
186
 
        {233, EXCITEMLOCK},
187
 
        {234, EXCAMBCOLUMN},
188
 
        {235, EXCCONVERT},
189
 
        {236, EXCSYNTAX},
190
 
        {237, EXCMANAGETRANS},
191
 
        {238, EXCMANAGETRANS},
192
 
        {239, EXCDUPKEY},
193
 
        {240, EXCDBCORRUPT},
194
 
        {241, EXCMANAGETRANS},
195
 
        {249, EXCAMBCOLUMN},
196
 
        {250, EXCDBCORRUPT},
197
 
        {251, EXCSYNTAX},
198
 
        {252, EXCITEMLOCK},
199
 
        {253, EXCSYNTAX},
200
 
        {255, EXCNOTINTRANS},
201
 
        {256, EXCMANAGETRANS},
202
 
        {257, EXCRESOURCE},
203
 
        {258, EXCDBCORRUPT},
204
 
        {259, EXCNOCURSOR},
205
 
        {260, EXCNOCURSOR},
206
 
        {261, EXCRESOURCE},
207
 
        {262, EXCNOCURSOR},
208
 
        {263, EXCRESOURCE},
209
 
        {264, EXCRESOURCE},
210
 
        {265, EXCNOTINTRANS},
211
 
        {266, EXCNOCURSOR},
212
 
        {267, EXCNOCURSOR},
213
 
        {268, EXCDUPKEY},
214
 
        {269, EXCNOTNULLCOLUMN},
215
 
        {270, EXCDBCORRUPT},
216
 
        {271, EXCDBCORRUPT},
217
 
        {272, EXCPERMISSION},
218
 
        {273, EXCPERMISSION},
219
 
        {274, EXCPERMISSION},
220
 
        {275, EXCPERMISSION},
221
 
        {276, EXCNOCURSOR},
222
 
        {277, EXCNOCURSOR},
223
 
        {278, EXCRESOURCE},
224
 
        {281, EXCTEMPTABLEUSE},
225
 
        {282, EXCSYNTAX},
226
 
        {283, EXCSYNTAX},
227
 
        {284, EXCMANYROW},
228
 
        {285, EXCNOCURSOR},
229
 
        {286, EXCNOTNULLCOLUMN},
230
 
        {287, EXCSERIAL},
231
 
        {288, EXCITEMLOCK},
232
 
        {289, EXCITEMLOCK},
233
 
        {290, EXCNOCURSOR},
234
 
        {292, EXCNOTNULLCOLUMN},
235
 
        {293, EXCSYNTAX},
236
 
        {294, EXCAGGREGATEUSE},
237
 
        {295, EXCCROSSDB},
238
 
        {296, EXCNOTABLE},
239
 
        {297, EXCNOKEY},
240
 
        {298, EXCPERMISSION},
241
 
        {299, EXCPERMISSION},
242
 
        {300, EXCRESOURCE},
243
 
        {301, EXCRESOURCE},
244
 
        {302, EXCPERMISSION},
245
 
        { 303, EXCAGGREGATEUSE},
246
 
        {304, EXCAGGREGATEUSE},
247
 
        {305, EXCSUBSCRIPT},
248
 
        {306, EXCSUBSCRIPT},
249
 
        {307, EXCSUBSCRIPT},
250
 
        {308, EXCCONVERT},
251
 
        {309, EXCAMBCOLUMN},
252
 
        {310, EXCDUPTABLE},
253
 
        {311, EXCDBCORRUPT},
254
 
        {312, EXCDBCORRUPT},
255
 
        {313, EXCPERMISSION},
256
 
        {314, EXCDUPTABLE},
257
 
        {315, EXCPERMISSION},
258
 
        {316, EXCDUPINDEX},
259
 
        {317, EXCUNION},
260
 
        {318, EXCFILENAME},
261
 
        {319, EXCNOINDEX},
262
 
        {320, EXCPERMISSION},
263
 
        {321, EXCAGGREGATEUSE},
264
 
        {323, EXCTEMPTABLEUSE},
265
 
        {324, EXCAMBCOLUMN},
266
 
        {325, EXCFILENAME},
267
 
        {326, EXCRESOURCE},
268
 
        {327, EXCITEMLOCK},
269
 
        {328, EXCDUPCOLUMN},
270
 
        {329, EXCNOCONNECT},
271
 
        {330, EXCRESOURCE},
272
 
        {331, EXCDBCORRUPT},
273
 
        {332, EXCTRACE},
274
 
        {333, EXCTRACE},
275
 
        {334, EXCTRACE},
276
 
        {335, EXCTRACE},
277
 
        {336, EXCTEMPTABLEUSE},
278
 
        {337, EXCTEMPTABLEUSE},
279
 
        {338, EXCTRACE},
280
 
        {339, EXCFILENAME},
281
 
        {340, EXCTRACE},
282
 
        {341, EXCTRACE},
283
 
        {342, EXCREMOTE},
284
 
        {343, EXCTRACE},
285
 
        {344, EXCTRACE},
286
 
        {345, EXCTRACE},
287
 
        {346, EXCDBCORRUPT},
288
 
        {347, EXCITEMLOCK},
289
 
        {348, EXCDBCORRUPT},
290
 
        {349, EXCNODB},
291
 
        {350, EXCDUPINDEX},
292
 
        {352, EXCNOCOLUMN},
293
 
        {353, EXCNOTABLE},
294
 
        {354, EXCSYNTAX},
295
 
        {355, EXCDBCORRUPT},
296
 
        {356, EXCCONVERT},
297
 
        {361, EXCRESOURCE},
298
 
        {362, EXCSERIAL},
299
 
        {363, EXCNOCURSOR},
300
 
        {365, EXCNOCURSOR},
301
 
        {366, EXCCONVERT},
302
 
        {367, EXCAGGREGATEUSE},
303
 
        {368, EXCDBCORRUPT},
304
 
        {369, EXCSERIAL},
305
 
        {370, EXCAMBCOLUMN},
306
 
        {371, EXCDUPKEY},
307
 
        {372, EXCTRACE},
308
 
        {373, EXCFILENAME},
309
 
        {374, EXCSYNTAX},
310
 
        {375, EXCMANAGETRANS},
311
 
        {376, EXCMANAGETRANS},
312
 
        {377, EXCMANAGETRANS},
313
 
        {378, EXCITEMLOCK},
314
 
        {382, EXCSYNTAX},
315
 
        {383, EXCAGGREGATEUSE},
316
 
        {384, EXCVIEWUSE},
317
 
        {385, EXCCONVERT},
318
 
        {386, EXCNOTNULLCOLUMN},
319
 
        {387, EXCPERMISSION},
320
 
        {388, EXCPERMISSION},
321
 
        {389, EXCPERMISSION},
322
 
        {390, EXCDUPSYNONYM},
323
 
        {391, EXCNOTNULLCOLUMN},
324
 
        {392, EXCDBCORRUPT},
325
 
        {393, EXCWHERECLAUSE},
326
 
        {394, EXCNOTABLE},
327
 
        {395, EXCWHERECLAUSE},
328
 
        {396, EXCWHERECLAUSE},
329
 
        {397, EXCDBCORRUPT},
330
 
        {398, EXCNOTINTRANS},
331
 
        {399, EXCMANAGETRANS},
332
 
        {400, EXCNOCURSOR},
333
 
        {401, EXCNOCURSOR},
334
 
        {404, EXCNOCURSOR},
335
 
        {406, EXCRESOURCE},
336
 
        {407, EXCDBCORRUPT},
337
 
        {408, EXCDBCORRUPT},
338
 
        {409, EXCNOCONNECT},
339
 
        {410, EXCNOCURSOR},
340
 
        {413, EXCNOCURSOR},
341
 
        {414, EXCNOCURSOR},
342
 
        {415, EXCCONVERT},
343
 
        {417, EXCNOCURSOR},
344
 
        {420, EXCREMOTE},
345
 
        {421, EXCREMOTE},
346
 
        {422, EXCNOCURSOR},
347
 
        {423, EXCNOROW},
348
 
        {424, EXCDUPCURSOR},
349
 
        {425, EXCITEMLOCK},
350
 
        {430, EXCCONVERT},
351
 
        {431, EXCCONVERT},
352
 
        {432, EXCCONVERT},
353
 
        {433, EXCCONVERT},
354
 
        {434, EXCCONVERT},
355
 
        {439, EXCREMOTE},
356
 
        {451, EXCRESOURCE},
357
 
        {452, EXCRESOURCE},
358
 
        {453, EXCDBCORRUPT},
359
 
        {454, EXCDBCORRUPT},
360
 
        {455, EXCRESOURCE},
361
 
        {457, EXCREMOTE},
362
 
        {458, EXCLONGTRANS},
363
 
        {459, EXCREMOTE},
364
 
        {460, EXCRESOURCE},
365
 
        {465, EXCRESOURCE},
366
 
        {468, EXCNOCONNECT},
367
 
        {472, EXCCONVERT},
368
 
        {473, EXCCONVERT},
369
 
        {474, EXCCONVERT},
370
 
        {482, EXCNOCURSOR},
371
 
        {484, EXCFILENAME},
372
 
        {500, EXCDUPINDEX},
373
 
        {501, EXCDUPINDEX},
374
 
        {502, EXCNOINDEX},
375
 
        {503, EXCRESOURCE},
376
 
        {504, EXCVIEWUSE},
377
 
        {505, EXCSYNTAX},
378
 
        {506, EXCPERMISSION},
379
 
        {507, EXCNOCURSOR},
380
 
        {508, EXCTEMPTABLEUSE},
381
 
        {509, EXCTEMPTABLEUSE},
382
 
        {510, EXCTEMPTABLEUSE},
383
 
        {512, EXCPERMISSION},
384
 
        {514, EXCPERMISSION},
385
 
        {515, EXCNOCONSTRAINT},
386
 
        {517, EXCRESOURCE},
387
 
        {518, EXCNOCONSTRAINT},
388
 
        {519, EXCCONVERT},
389
 
        {521, EXCITEMLOCK},
390
 
        {522, EXCNOTABLE},
391
 
        {524, EXCNOTINTRANS},
392
 
        {525, EXCREFINT},
393
 
        {526, EXCNOCURSOR},
394
 
        {528, EXCRESOURCE},
395
 
        {529, EXCNOCONNECT},
396
 
        {530, EXCCHECK},
397
 
        {531, EXCDUPCOLUMN},
398
 
        {532, EXCTEMPTABLEUSE},
399
 
        {534, EXCITEMLOCK},
400
 
        {535, EXCMANAGETRANS},
401
 
        {536, EXCSYNTAX},
402
 
        {537, EXCNOCONSTRAINT},
403
 
        {538, EXCDUPCURSOR},
404
 
        {539, EXCRESOURCE},
405
 
        {540, EXCDBCORRUPT},
406
 
        {541, EXCPERMISSION},
407
 
        {543, EXCAMBCOLUMN},
408
 
        {543, EXCSYNTAX},
409
 
        {544, EXCAGGREGATEUSE},
410
 
        {545, EXCPERMISSION},
411
 
        {548, EXCTEMPTABLEUSE},
412
 
        {549, EXCNOCOLUMN},
413
 
        {550, EXCRESOURCE},
414
 
        {551, EXCRESOURCE},
415
 
        {554, EXCSYNTAX},
416
 
        {559, EXCDUPSYNONYM},
417
 
        {560, EXCDBCORRUPT},
418
 
        {561, EXCAGGREGATEUSE},
419
 
        {562, EXCCONVERT},
420
 
        {536, EXCITEMLOCK},
421
 
        {564, EXCRESOURCE},
422
 
        {565, EXCRESOURCE},
423
 
        {566, EXCRESOURCE},
424
 
        {567, EXCRESOURCE},
425
 
        {568, EXCCROSSDB},
426
 
        {569, EXCCROSSDB},
427
 
        {570, EXCCROSSDB},
428
 
        {571, EXCCROSSDB},
429
 
        {573, EXCMANAGETRANS},
430
 
        {574, EXCAMBCOLUMN},
431
 
        {576, EXCTEMPTABLEUSE},
432
 
        {577, EXCDUPCONSTRAINT},
433
 
        {578, EXCSYNTAX},
434
 
        {579, EXCPERMISSION},
435
 
        {580, EXCPERMISSION},
436
 
        {582, EXCMANAGETRANS},
437
 
        {583, EXCPERMISSION},
438
 
        {586, EXCDUPCURSOR},
439
 
        {589, EXCREMOTE},
440
 
        {590, EXCDBCORRUPT},
441
 
        {591, EXCCONVERT},
442
 
        {592, EXCNOTNULLCOLUMN},
443
 
        {593, EXCSERIAL},
444
 
        {594, EXCBLOBUSE},
445
 
        {595, EXCAGGREGATEUSE},
446
 
        {597, EXCDBCORRUPT},
447
 
        {598, EXCNOCURSOR},
448
 
        {599, EXCSYNTAX},
449
 
        {600, EXCMANAGEBLOB},
450
 
        {601, EXCMANAGEBLOB},
451
 
        {602, EXCMANAGEBLOB},
452
 
        {603, EXCMANAGEBLOB},
453
 
        {604, EXCMANAGEBLOB},
454
 
        {605, EXCMANAGEBLOB},
455
 
        {606, EXCMANAGEBLOB},
456
 
        {607, EXCSUBSCRIPT},
457
 
        {608, EXCCONVERT},
458
 
        {610, EXCBLOBUSE},
459
 
        {611, EXCBLOBUSE},
460
 
        {612, EXCBLOBUSE},
461
 
        {613, EXCBLOBUSE},
462
 
        {614, EXCBLOBUSE},
463
 
        {615, EXCBLOBUSE},
464
 
        {616, EXCBLOBUSE},
465
 
        {617, EXCBLOBUSE},
466
 
        {618, EXCMANAGEBLOB},
467
 
        {622, EXCNOINDEX},
468
 
        {623, EXCNOCONSTRAINT},
469
 
        {625, EXCDUPCONSTRAINT},
470
 
        {628, EXCMANAGETRANS},
471
 
        {629, EXCMANAGETRANS},
472
 
        {630, EXCMANAGETRANS},
473
 
        {631, EXCBLOBUSE},
474
 
        {635, EXCPERMISSION},
475
 
        {636, EXCRESOURCE},
476
 
        {638, EXCBLOBUSE},
477
 
        {639, EXCBLOBUSE},
478
 
        {640, EXCDBCORRUPT},
479
 
        {649, EXCFILENAME},
480
 
        {650, EXCRESOURCE},
481
 
        {651, EXCRESOURCE},
482
 
        /* I'm not about to map all possible compile/runtime SPL errors. */
483
 
        /* Here's a few. */
484
 
        {655, EXCSYNTAX},
485
 
        {667, EXCSYNTAX},
486
 
        {673, EXCDUPSPROC},
487
 
        {674, EXCNOSPROC},
488
 
        {678, EXCSUBSCRIPT},
489
 
        {681, EXCDUPCOLUMN},
490
 
        {686, EXCMANYROW},
491
 
        {690, EXCREFINT},
492
 
        {691, EXCREFINT},
493
 
        {692, EXCREFINT},
494
 
        {702, EXCITEMLOCK},
495
 
        {703, EXCNOTNULLCOLUMN},
496
 
        {704, EXCDUPCONSTRAINT},
497
 
        {706, EXCPERMISSION},
498
 
        {707, EXCBLOBUSE},
499
 
        {722, EXCRESOURCE},
500
 
        {958, EXCDUPTABLE},
501
 
        {1214, EXCCONVERT},
502
 
        {1262, EXCCONVERT},
503
 
        {1264, EXCCONVERT},
504
 
        {25553, EXCNOCONNECT},
505
 
        {25587, EXCNOCONNECT},
506
 
        {25588, EXCNOCONNECT},
507
 
        {25596, EXCNOCONNECT},
508
 
        {0, 0}
509
 
}; /* ends of list */
510
 
 
511
 
static int errTranslate(int code)
512
 
{
513
 
        struct ERRORMAP *e;
514
 
 
515
 
        for(e=errormap; e->infcode; ++e) {
516
 
                if(e->infcode == code)
517
 
                        return e->excno;
518
 
        }
519
 
        return EXCSQLMISC;
520
 
} /* errTranslate */
521
 
 
522
 
static bool errorTrap(void)
523
 
{
524
 
short i;
525
 
 
526
 
rv_lastStatus = rv_vendorStatus = 0; /* innocent until proven guilty */
527
 
rv_stmtOffset = 0;
528
 
rv_badToken = 0;
529
 
if(ENGINE_ERRCODE >= 0) return false; /* no problem */
530
 
 
531
 
showStatement();
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;
537
 
 
538
 
/* if the application didn't trap for this exception, blow up! */
539
 
if(exclist) {
540
 
for(i=0; exclist[i]; ++i) {
541
 
if(exclist[i] == rv_lastStatus) {
542
 
exclist = 0; /* we've spent that exception */
543
 
return true;
544
 
}
545
 
}
546
 
}
547
 
 
548
 
errorPrint("2SQL error %d, %s", rv_vendorStatus, sqlErrorList[rv_lastStatus]);
549
 
return true; /* make the compiler happy */
550
 
} /* errorTrap */
551
 
 
552
 
 
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
 
*********************************************************************/
558
 
 
559
 
static struct OCURS {
560
 
char sname[8]; /* statement name */
561
 
char cname[8]; /* cursor name */
562
 
struct sqlda *desc;
563
 
char rv_type[NUMRETS];
564
 
long rownum;
565
 
short alloc;
566
 
short cid; /* cursor ID */
567
 
char flag;
568
 
char numRets;
569
 
char **fl; /* array of fetched lines */
570
 
} ocurs[NUMCURSORS];
571
 
 
572
 
/* values for struct OCURS.flag */
573
 
#define CURSOR_NONE 0
574
 
#define CURSOR_PREPARED 1
575
 
#define CURSOR_OPENED 2
576
 
 
577
 
/* find a free cursor structure */
578
 
static struct OCURS *findNewCursor(void)
579
 
{
580
 
struct OCURS *o;
581
 
short i;
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);
586
 
o->cid = 6000+i;
587
 
return o;
588
 
}
589
 
errorPrint("2more than %d cursors opend concurrently", NUMCURSORS);
590
 
return 0; /* make the compiler happy */
591
 
} /* findNewCursor */
592
 
 
593
 
/* dereference an existing cursor */
594
 
static struct OCURS *findCursor(int cid)
595
 
{
596
 
struct OCURS *o;
597
 
if(cid < 6000 || cid >= 6000+NUMCURSORS)
598
 
errorPrint("2cursor number %d is out of range", cid);
599
 
cid -= 6000;
600
 
o = ocurs+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);
605
 
return o;
606
 
} /* findCursor */
607
 
 
608
 
/* part of the disconnect() procedure */
609
 
static void clearAllCursors(void)
610
 
{
611
 
        int i, j;
612
 
        struct OCURS *o;
613
 
 
614
 
        for(i=0, o=ocurs; i<NUMCURSORS; ++i, ++o) {
615
 
                if(o->flag == CURSOR_NONE) continue;
616
 
                o->flag = CURSOR_NONE;
617
 
o->rownum = 0;
618
 
                if(o->fl) {
619
 
                        for(j=0; j<o->alloc; ++j)
620
 
                                nzFree(o->fl[j]);
621
 
                        nzFree(o->fl);
622
 
                        o->fl = 0;
623
 
                }
624
 
        } /* loop over cursors */
625
 
 
626
 
        translevel = 0;
627
 
        badtrans = false;
628
 
} /* clearAllCursors */
629
 
 
630
 
 
631
 
/*********************************************************************
632
 
Connect and disconect to SQL databases.
633
 
*********************************************************************/
634
 
 
635
 
void sql_connect(const char *db, const char *login, const char *pw) 
636
 
{
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");
643
 
}
644
 
 
645
 
if(sql_database) {
646
 
        stmt_text = "disconnect";
647
 
        debugStatement();
648
 
$disconnect current;
649
 
clearAllCursors();
650
 
sql_database = 0;
651
 
}
652
 
 
653
 
        stmt_text = "connect";
654
 
        debugStatement();
655
 
$connect to :dblocal;
656
 
if(errorTrap()) return;
657
 
sql_database = dblocal;
658
 
 
659
 
/* set default lock mode and isolation level for transaction management */
660
 
stmt_text = "lock isolation";
661
 
debugStatement();
662
 
$ set lock mode to wait;
663
 
if(errorTrap()) {
664
 
abort:
665
 
sql_disconnect();
666
 
return;
667
 
}
668
 
$ set isolation to committed read;
669
 
if(errorTrap()) goto abort;
670
 
exclist = 0;
671
 
} /* sql_connect */
672
 
 
673
 
void sql_disconnect(void)
674
 
{
675
 
if(sql_database) {
676
 
        stmt_text = "disconnect";
677
 
        debugStatement();
678
 
$disconnect current;
679
 
clearAllCursors();
680
 
sql_database = 0;
681
 
}
682
 
exclist = 0;
683
 
} /* sql_disconnect */
684
 
 
685
 
/* make sure we're connected to a database */
686
 
static void checkConnect(void)
687
 
{
688
 
        if(!sql_database)
689
 
                errorPrint("2SQL command issued, but no database selected");
690
 
} /* checkConnect */
691
 
 
692
 
 
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
 
*********************************************************************/
702
 
 
703
 
/* begin a transaction */
704
 
void sql_begTrans(void)
705
 
{
706
 
        rv_lastStatus = 0;
707
 
        checkConnect();
708
 
                stmt_text = "begin work";
709
 
                debugStatement();
710
 
        /* count the nesting level of transactions. */
711
 
        if(!translevel) {
712
 
                badtrans = false;
713
 
                $begin work;
714
 
                if(errorTrap()) return;
715
 
        }
716
 
        ++translevel;
717
 
        exclist = 0;
718
 
} /* sql_begTrans */
719
 
 
720
 
/* end a transaction */
721
 
static void endTrans(bool commit)
722
 
{
723
 
        rv_lastStatus = 0;
724
 
        checkConnect();
725
 
 
726
 
        if(translevel == 0)
727
 
                errorPrint("2end transaction without a matching begTrans()");
728
 
        --translevel;
729
 
 
730
 
        if(commit) {
731
 
                        stmt_text = "commit work";
732
 
                        debugStatement();
733
 
                if(badtrans)
734
 
                        errorPrint("2Cannot commit a transaction around an aborted transaction");
735
 
                if(translevel == 0) {
736
 
                        $commit work;
737
 
                        if(ENGINE_ERRCODE) ++translevel;
738
 
                        errorTrap();
739
 
                }
740
 
        } else { /* success or failure */
741
 
                        stmt_text = "rollback work";
742
 
                        debugStatement();
743
 
                badtrans = true;
744
 
                if(!translevel) { /* bottom level */
745
 
                        $rollback work;
746
 
                        if(ENGINE_ERRCODE) --translevel;
747
 
                        errorTrap();
748
 
                        badtrans = false;
749
 
                }
750
 
        } /* success or failure */
751
 
 
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. */
756
 
 
757
 
        exclist = 0;
758
 
} /* endTrans */
759
 
 
760
 
void sql_commitWork(void) { endTrans(true); }
761
 
void sql_rollbackWork(void) { endTrans(false); }
762
 
 
763
 
void sql_deferConstraints(void)
764
 
{
765
 
        if(!translevel)
766
 
                errorPrint("2Cannot defer constraints unless inside a transaction");
767
 
        stmt_text = "defer constraints";
768
 
        debugStatement();
769
 
        $set constraints all deferred;
770
 
        errorTrap();
771
 
        exclist = 0;
772
 
} /* sql_deferConstraints */
773
 
 
774
 
 
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
 
*********************************************************************/
790
 
 
791
 
/* information about the blob being fetched */
792
 
const char *rv_blobFile;
793
 
bool rv_blobAppend;
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 */
797
 
 
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)
801
 
{
802
 
$char blobcmd[100];
803
 
$loc_t insblob;
804
 
 
805
 
/* basic sanity checks */
806
 
checkConnect();
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");
813
 
 
814
 
/* set up the blob structure */
815
 
memset(&insblob, 0, sizeof(insblob));
816
 
if(!filename) {
817
 
insblob.loc_loctype = LOCMEMORY;
818
 
if(offset) {
819
 
if(length == 0) offset = 0;
820
 
}
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;
826
 
} else {
827
 
insblob.loc_loctype = LOCFNAME;
828
 
insblob.loc_fname = (char*)filename;
829
 
insblob.loc_oflags = LOC_RONLY;
830
 
insblob.loc_size = -1;
831
 
}
832
 
 
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);
836
 
stmt_text = blobcmd;
837
 
debugStatement();
838
 
$prepare blobinsert from :blobcmd;
839
 
if(errorTrap()) return;
840
 
$execute blobinsert using :insblob;
841
 
errorTrap();
842
 
rv_lastNrows = sqlca.sqlerrd[2];
843
 
rv_lastRowid = sqlca.sqlerrd[5];
844
 
if(sql_debug) appendFile(sql_debuglog, "%d rows affected", rv_lastNrows);
845
 
exclist = 0;
846
 
} /* sql_blobInsert */
847
 
 
848
 
 
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
 
*********************************************************************/
863
 
 
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;
873
 
 
874
 
static void retsSetup(struct sqlda *desc)
875
 
{
876
 
short i;
877
 
bool blobpresent = false;
878
 
struct sqlvar_struct   *v;
879
 
 
880
 
for(i=0; (unsigned)i< NUMRETS; ++i) {
881
 
rv_data[i].l = nullint;
882
 
retstring[i][0] = 0;
883
 
rv_name[i][0] = 0;
884
 
}
885
 
if(!desc) return;
886
 
 
887
 
  for(i=0,v=desc->sqlvar; i<rv_numRets; ++i,++v ) {
888
 
strncpy(rv_name[i], v->sqlname, COLNAMELEN);
889
 
switch(rv_type[i]) {
890
 
case 'S':
891
 
case 'C':
892
 
case 'D':
893
 
case 'I':
894
 
v->sqltype = CCHARTYPE;
895
 
v->sqllen = STRINGLEN+2;
896
 
v->sqldata = retstring[i];
897
 
rv_data[i].ptr = retstring[i];
898
 
break;
899
 
 
900
 
case 'N':
901
 
v->sqltype = CINTTYPE;
902
 
v->sqllen = 4;
903
 
v->sqldata =  (char *) &rv_data[i].l;
904
 
break;
905
 
 
906
 
case 'F':
907
 
case 'M':
908
 
v->sqltype = CDOUBLETYPE;
909
 
v->sqllen = 8;
910
 
v->sqldata = (char*) &rv_data[i].f;
911
 
rv_data[i].f = nullfloat;
912
 
break;
913
 
 
914
 
case 'B':
915
 
case 'T':
916
 
if(blobpresent)
917
 
errorPrint("2Cannot select more than one blob at a time");
918
 
blobpresent = true;
919
 
v->sqltype = CLOCATORTYPE;
920
 
v->sqllen = sizeof(blobstruct);
921
 
v->sqldata = (char*) &blobstruct;
922
 
memset(&blobstruct, 0, sizeof(blobstruct));
923
 
if(!rv_blobFile) {
924
 
blobstruct.loc_loctype = LOCMEMORY;
925
 
blobstruct.loc_mflags = LOC_ALLOC;
926
 
blobstruct.loc_bufsize = -1;
927
 
} else {
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);
933
 
}
934
 
break;
935
 
 
936
 
default:
937
 
errorPrint("@bad character %c in retsSetup", rv_type[i]);
938
 
} /* switch */
939
 
} /* loop over fetched columns */
940
 
} /* retsSetup */
941
 
 
942
 
/* clean up fetched values, eg. convert date to our proprietary format. */
943
 
static void retsCleanup(void)
944
 
{
945
 
short i, l;
946
 
bool yearfirst;
947
 
 
948
 
/* no blobs unless proven otherwise */
949
 
rv_blobLoc = 0;
950
 
rv_blobSize = nullint;
951
 
 
952
 
for(i=0; i<rv_numRets; ++i) {
953
 
clipString(retstring[i]);
954
 
switch(rv_type[i]) {
955
 
case 'D':
956
 
yearfirst = false;
957
 
if(retstring[i][4] == '-') yearfirst = true;
958
 
rv_data[i].l = stringDate(retstring[i],yearfirst);
959
 
break;
960
 
 
961
 
case 'I':
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;
964
 
else {
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);
969
 
}
970
 
break;
971
 
 
972
 
case 'C':
973
 
rv_data[i].l = retstring[i][0];
974
 
break;
975
 
 
976
 
case 'M':
977
 
case 'F':
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;
982
 
break;
983
 
}
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;
987
 
break;
988
 
 
989
 
case 'S':
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);
994
 
break;
995
 
 
996
 
case 'B':
997
 
case 'T':
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 */
1002
 
nzFree(rv_blobLoc);
1003
 
rv_blobLoc = 0;
1004
 
rv_blobSize = nullint;
1005
 
}
1006
 
}
1007
 
rv_data[i].l = rv_blobSize;
1008
 
break;
1009
 
 
1010
 
case 'N':
1011
 
/* Convert from Informix null to our nullint */
1012
 
if(rv_data[i].l == 0x80000000) rv_data[i].l = nullint;
1013
 
break;
1014
 
 
1015
 
default:
1016
 
errorPrint("@bad character %c in retsCleanup", rv_type[i]);
1017
 
} /* switch on datatype */
1018
 
} /* loop over columsn fetched */
1019
 
} /* retsCleanup */
1020
 
 
1021
 
void retsCopy(bool allstrings, void *first, ...)
1022
 
{
1023
 
void *q;
1024
 
int i;
1025
 
 
1026
 
        if(!rv_numRets)
1027
 
                errorPrint("@calling retsCopy() with no returns pending");
1028
 
 
1029
 
        for(i=0; i<rv_numRets; ++i) {
1030
 
                if(first) {
1031
 
                        q = first;
1032
 
                        va_start(sqlargs, first);
1033
 
                        first = 0;
1034
 
                } else {
1035
 
                        q = va_arg(sqlargs, void*);
1036
 
                }
1037
 
                if(!q) break;
1038
 
                if((int)q < 1000 && (int)q > -1000)
1039
 
                        errorPrint("2retsCopy, pointer too close to 0");
1040
 
 
1041
 
if(allstrings) *(char*)q = 0;
1042
 
 
1043
 
if(rv_type[i] == 'S') {
1044
 
*(char*)q = 0;
1045
 
if(rv_data[i].ptr)
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') {
1051
 
if(allstrings) {
1052
 
if(isnotnullfloat(rv_data[i].f)) sprintf(q, "%lf", rv_data[i].f);
1053
 
} else {
1054
 
*(double *)q = rv_data[i].f;
1055
 
}
1056
 
} else if(allstrings) {
1057
 
char type = rv_type[i];
1058
 
long l = rv_data[i].l;
1059
 
if(isnotnull(l)) {
1060
 
if(type == 'D') {
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);
1067
 
}
1068
 
} else {
1069
 
*(long *)q = rv_data[i].l;
1070
 
}
1071
 
} /* loop over result parameters */
1072
 
 
1073
 
if(!first) va_end(sqlargs);
1074
 
} /* retsCopy */
1075
 
 
1076
 
/* convert column name into column index */
1077
 
int findcol_byname(const char *name)
1078
 
{
1079
 
        int i;
1080
 
        for(i=0; rv_name[i][0]; ++i)
1081
 
                if(stringEqual(name, rv_name[i])) break;
1082
 
        if(!rv_name[i][0])
1083
 
                errorPrint("2Column %s not found in the columns or aliases of your select statement", name);
1084
 
        return i;
1085
 
} /* findcol_byname */
1086
 
 
1087
 
/* make sure we got one return value, and it is integer compatible */
1088
 
static long oneRetValue(void)
1089
 
{
1090
 
char coltype = rv_type[0];
1091
 
long n = rv_data[0].l;
1092
 
if(rv_numRets != 1)
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;
1097
 
return n;
1098
 
} /* oneRetValue */
1099
 
 
1100
 
 
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
 
*********************************************************************/
1108
 
 
1109
 
static struct sqlda *prepare(const char *stmt_parm, const char *sname_parm)
1110
 
{
1111
 
$char*stmt = (char*)stmt_parm;
1112
 
$char*sname = (char*)sname_parm;
1113
 
struct sqlda *desc;
1114
 
struct sqlvar_struct   *v;
1115
 
short i, coltype;
1116
 
 
1117
 
checkConnect();
1118
 
if(isnullstring(stmt)) errorPrint("2null SQL statement");
1119
 
stmt_text = stmt;
1120
 
debugStatement();
1121
 
 
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")) {
1127
 
showStatement();
1128
 
errorPrint("2Old Mcdonald bug");
1129
 
}
1130
 
 
1131
 
/* set things up to nulls, in case the prepare fails */
1132
 
retsSetup(0);
1133
 
rv_numRets = 0;
1134
 
memset(rv_type, 0, NUMRETS);
1135
 
rv_lastNrows = rv_lastRowid = rv_lastSerial = 0;
1136
 
 
1137
 
$prepare :sname from :stmt;
1138
 
if(errorTrap()) return 0;
1139
 
 
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) {
1145
 
showStatement();
1146
 
errorPrint("2cannot select more than %d values", NUMRETS);
1147
 
}
1148
 
 
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(*))"))
1153
 
coltype = SQLINT;
1154
 
 
1155
 
switch(coltype) {
1156
 
case SQLCHAR:
1157
 
case SQLVCHAR:
1158
 
rv_type[i] = 'S';
1159
 
if(v->sqllen == 1)
1160
 
rv_type[i] = 'C';
1161
 
break;
1162
 
 
1163
 
case SQLDTIME:
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");
1167
 
case SQLDATE:
1168
 
rv_type[i] = 'D';
1169
 
break;
1170
 
 
1171
 
case SQLINTERVAL:
1172
 
rv_type[i] = 'I';
1173
 
break;
1174
 
 
1175
 
case SQLSMINT:
1176
 
case SQLINT:
1177
 
case SQLSERIAL:
1178
 
case SQLNULL:
1179
 
rv_type[i] = 'N';
1180
 
break;
1181
 
 
1182
 
case SQLFLOAT:
1183
 
case SQLSMFLOAT:
1184
 
case SQLDECIMAL:
1185
 
rv_type[i] = 'F';
1186
 
break;
1187
 
 
1188
 
case SQLMONEY:
1189
 
rv_type[i] = 'M';
1190
 
break;
1191
 
 
1192
 
case SQLBYTES:
1193
 
rv_type[i] = 'B';
1194
 
break;
1195
 
 
1196
 
case SQLTEXT:
1197
 
rv_type[i] = 'T';
1198
 
break;
1199
 
 
1200
 
default:
1201
 
errorPrint ("@Unknown informix sql datatype %d", coltype);
1202
 
} /* switch on type */
1203
 
} /* loop over returns */
1204
 
 
1205
 
retsSetup(desc);
1206
 
return desc;
1207
 
} /* prepare */
1208
 
 
1209
 
 
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
 
*********************************************************************/
1220
 
 
1221
 
static bool execInternal(const char *stmt, int mode)
1222
 
{
1223
 
struct sqlda *desc;
1224
 
$static char singlestatement[] = "single_use_stmt";
1225
 
$static char singlecursor[] = "single_use_cursor";
1226
 
int i;
1227
 
bool notfound = false;
1228
 
short errorcode = 0;
1229
 
 
1230
 
desc = prepare(stmt, singlestatement);
1231
 
if(!desc) return false; /* error */
1232
 
 
1233
 
if(!rv_numRets) {
1234
 
if(!(mode&1)) {
1235
 
showStatement();
1236
 
errorPrint("2SQL select statement returns no values");
1237
 
}
1238
 
$execute :singlestatement;
1239
 
notfound = true;
1240
 
} else { /* end no return values */
1241
 
 
1242
 
if(!(mode&2)) {
1243
 
showStatement();
1244
 
errorPrint("2SQL statement returns %d values", rv_numRets);
1245
 
}
1246
 
$execute: singlestatement into descriptor desc;
1247
 
}
1248
 
 
1249
 
if(errorTrap()) {
1250
 
errorcode = rv_vendorStatus;
1251
 
} else {
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 */
1260
 
 
1261
 
$free :singlestatement;
1262
 
errorTrap();
1263
 
nzFree(desc);
1264
 
 
1265
 
retsCleanup();
1266
 
 
1267
 
if(errorcode) {
1268
 
rv_vendorStatus = errorcode;
1269
 
rv_lastStatus = errTranslate(rv_vendorStatus);
1270
 
return false;
1271
 
}
1272
 
 
1273
 
exclist = 0;
1274
 
return !notfound;
1275
 
} /* execInternal */
1276
 
 
1277
 
 
1278
 
/*********************************************************************
1279
 
Run individual select or execute statements, using the above internal routine.
1280
 
*********************************************************************/
1281
 
 
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)
1285
 
{
1286
 
        execInternal(stmt, 1);
1287
 
} /* sql_execNF */
1288
 
 
1289
 
/* execute a stand-alone statement with % formatting */
1290
 
void sql_exec(const char *stmt, ...)
1291
 
{
1292
 
        va_start(sqlargs, stmt);
1293
 
        stmt = lineFormatStack(stmt, 0, &sqlargs);
1294
 
        execInternal(stmt, 1);
1295
 
        va_end(sqlargs);
1296
 
} /* sql_exec */
1297
 
 
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, ...)
1301
 
{
1302
 
        bool rc;
1303
 
        va_start(sqlargs, stmt);
1304
 
        rc = execInternal(stmt, 2);
1305
 
        retsCopy(false, 0);
1306
 
        return rc;
1307
 
} /* sql_selectNF */
1308
 
 
1309
 
/* run a select statement with % formatting */
1310
 
bool sql_select(const char *stmt, ...)
1311
 
{
1312
 
        bool rc;
1313
 
        va_start(sqlargs, stmt);
1314
 
        stmt = lineFormatStack(stmt, 0, &sqlargs);
1315
 
        rc = execInternal(stmt, 2);
1316
 
        retsCopy(false, 0);
1317
 
        return rc;
1318
 
} /* sql_select */
1319
 
 
1320
 
/* run a select statement with one return value */
1321
 
int sql_selectOne(const char *stmt, ...)
1322
 
{
1323
 
        bool rc;
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 */
1330
 
 
1331
 
/* run a stored procedure with no % formatting */
1332
 
static bool sql_procNF(const char *stmt)
1333
 
{
1334
 
        bool rc;
1335
 
        char *s = allocMem(20+strlen(stmt));
1336
 
        strcpy(s, "execute procedure ");
1337
 
        strcat(s, stmt);
1338
 
        rc = execInternal(s, 3);
1339
 
        /* if execInternal doesn't return, we have a memory leak */
1340
 
        nzFree(s);
1341
 
        return rc;
1342
 
} /* sql_procNF */
1343
 
 
1344
 
/* run a stored procedure */
1345
 
bool sql_proc(const char *stmt, ...)
1346
 
{
1347
 
        bool rc;
1348
 
        va_start(sqlargs, stmt);
1349
 
        stmt = lineFormatStack(stmt, 0, &sqlargs);
1350
 
        rc = sql_procNF(stmt);
1351
 
        if(rv_numRets) retsCopy(false, 0);
1352
 
        return rc;
1353
 
} /* sql_proc */
1354
 
 
1355
 
/* run a stored procedure with one return */
1356
 
int sql_procOne(const char *stmt, ...)
1357
 
{
1358
 
        bool rc;
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();
1364
 
} /* sql_procOne */
1365
 
 
1366
 
 
1367
 
/*********************************************************************
1368
 
Prepare, open, close, and free SQL cursors.
1369
 
*********************************************************************/
1370
 
 
1371
 
/* prepare a cursor; return the ID number of that cursor */
1372
 
static int prepareCursor(const char *stmt, bool scrollflag)
1373
 
{
1374
 
$char *internal_sname, *internal_cname;
1375
 
struct OCURS *o = findNewCursor();
1376
 
 
1377
 
stmt = lineFormatStack(stmt, 0, &sqlargs);
1378
 
va_end(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) {
1384
 
showStatement();
1385
 
errorPrint("2statement passed to sql_prepare has no returns");
1386
 
}
1387
 
 
1388
 
/* declare with hold;
1389
 
 * you might run transactions within this cursor. */
1390
 
if(scrollflag)
1391
 
$declare :internal_cname scroll cursor with hold for :internal_sname;
1392
 
else
1393
 
$declare :internal_cname cursor with hold for :internal_sname;
1394
 
if(errorTrap()) {
1395
 
nzFree(o->desc);
1396
 
return -1;
1397
 
}
1398
 
 
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 */
1403
 
return o->cid;
1404
 
} /* prepareCursor */
1405
 
 
1406
 
int sql_prepare(const char *stmt, ...)
1407
 
{
1408
 
        int n;
1409
 
        va_start(sqlargs, stmt);
1410
 
        n = prepareCursor(stmt, false);
1411
 
        exclist = 0;
1412
 
        return n;
1413
 
} /* sql_prepare */
1414
 
 
1415
 
int sql_prepareScrolling(const char *stmt, ...)
1416
 
{
1417
 
        int n;
1418
 
        va_start(sqlargs, stmt);
1419
 
        n = prepareCursor(stmt, true);
1420
 
        exclist = 0;
1421
 
        return n;
1422
 
} /* sql_prepareScrolling */
1423
 
 
1424
 
void sql_open(int cid)
1425
 
{
1426
 
short i;
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;
1433
 
debugExtra("open");
1434
 
$open :internal_cname;
1435
 
if(!errorTrap()) o->flag = CURSOR_OPENED;
1436
 
o->rownum = 0;
1437
 
if(o->fl)
1438
 
for(i=0; i<o->alloc; ++i) {
1439
 
nzFree(o->fl[i]);
1440
 
o->fl[i] = 0;
1441
 
}
1442
 
exclist = 0;
1443
 
} /* sql_open */
1444
 
 
1445
 
int sql_prepOpen(const char *stmt, ...)
1446
 
{
1447
 
int n;
1448
 
va_start(sqlargs, stmt);
1449
 
n = prepareCursor(stmt, false);
1450
 
if(n < 0) return n;
1451
 
sql_open(n);
1452
 
if(rv_lastStatus) {
1453
 
short ev = rv_vendorStatus;
1454
 
short el = rv_lastStatus;
1455
 
sql_free(n);
1456
 
rv_vendorStatus = ev;
1457
 
rv_lastStatus = el;
1458
 
n = -1;
1459
 
}
1460
 
return n;
1461
 
} /* sql_prepOpen */
1462
 
 
1463
 
void sql_close(int cid)
1464
 
{
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;
1474
 
exclist = 0;
1475
 
} /* sql_close */
1476
 
 
1477
 
void sql_free( int cid)
1478
 
{
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;
1484
 
debugExtra("free");
1485
 
$free :internal_sname;
1486
 
if(errorTrap()) return;
1487
 
o->flag = CURSOR_NONE;
1488
 
nzFree(o->desc);
1489
 
rv_numRets = 0;
1490
 
memset(rv_name, 0, sizeof(rv_name));
1491
 
memset(rv_type, 0, sizeof(rv_type));
1492
 
if(o->fl) { /* free any cached lines */
1493
 
short i;
1494
 
for(i=0; i<o->alloc; ++i)
1495
 
nzFree(o->fl[i]);
1496
 
nzFree(o->fl);
1497
 
o->fl = 0;
1498
 
o->alloc = 0;
1499
 
}
1500
 
exclist = 0;
1501
 
} /* sql_free */
1502
 
 
1503
 
void sql_closeFree(int cid)
1504
 
{
1505
 
const short *exc = exclist;
1506
 
sql_close(cid);
1507
 
if(!rv_lastStatus) {
1508
 
exclist = exc;
1509
 
sql_free(cid);
1510
 
}
1511
 
} /* sql_closeFree */
1512
 
 
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)
1516
 
{
1517
 
$char *internal_sname, *internal_cname;
1518
 
$long nextrow, lastrow;
1519
 
struct sqlda *internal_desc;
1520
 
struct OCURS *o = findCursor(cid);
1521
 
 
1522
 
internal_cname = o->cname;
1523
 
internal_desc = o->desc;
1524
 
retsSetup(internal_desc);
1525
 
 
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) {
1529
 
o->rownum = 0;
1530
 
fetchZero:
1531
 
retsCleanup();
1532
 
exclist = 0;
1533
 
return false;
1534
 
}
1535
 
 
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;
1542
 
}
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;
1549
 
++nextrow;
1550
 
}
1551
 
}
1552
 
 
1553
 
if(!nextrow) goto fetchZero;
1554
 
 
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 */
1560
 
rv_blobLoc = 0;
1561
 
rv_blobSize = nullint;
1562
 
o->rownum = nextrow;
1563
 
exclist = 0;
1564
 
return true;
1565
 
} /* bringing row out of cache */
1566
 
 
1567
 
if(o->flag != CURSOR_OPENED)
1568
 
errorPrint("2cannot fetch from cursor %d, not yet opened", cid);
1569
 
 
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. */
1580
 
if(flag == 6 &&
1581
 
isnotnull(lastrow) && isnotnull(nextrow) &&
1582
 
nextrow == lastrow+1)
1583
 
flag=1;
1584
 
 
1585
 
debugExtra("fetch");
1586
 
 
1587
 
switch(flag) {
1588
 
case 1:
1589
 
$fetch :internal_cname using descriptor internal_desc;
1590
 
break;
1591
 
case 2:
1592
 
$fetch previous :internal_cname using descriptor internal_desc;
1593
 
break;
1594
 
case 3:
1595
 
$fetch first :internal_cname using descriptor internal_desc;
1596
 
break;
1597
 
case 4:
1598
 
$fetch last :internal_cname using descriptor internal_desc;
1599
 
break;
1600
 
case 6:
1601
 
if(isnull(nextrow))
1602
 
errorPrint("2sql fetches absolute row using null index");
1603
 
$fetch absolute :nextrow :internal_cname using descriptor internal_desc;
1604
 
break;
1605
 
default:
1606
 
errorPrint("@fetchInternal() receives bad flag %d", flag);
1607
 
} /* switch */
1608
 
retsCleanup();
1609
 
 
1610
 
if(errorTrap()) return false;
1611
 
exclist = 0;
1612
 
if(ENGINE_ERRCODE == 100) return false; /* not found */
1613
 
o->rownum = nextrow;
1614
 
 
1615
 
/* remember the unload image of this line */
1616
 
if(remember)
1617
 
sql_cursorUpdLine(cid, cloneString(sql_mkunld('\177')));
1618
 
return true;
1619
 
} /* fetchInternal */
1620
 
 
1621
 
bool sql_fetchFirst(int cid, ...)
1622
 
{
1623
 
        bool rc;
1624
 
        va_start(sqlargs, cid);
1625
 
        rc = fetchInternal(cid, 0L, 3, false);
1626
 
        retsCopy(false, 0);
1627
 
        return rc;
1628
 
} /* sql_fetchFirst */
1629
 
 
1630
 
bool sql_fetchLast(int cid, ...)
1631
 
{
1632
 
        bool rc;
1633
 
        va_start(sqlargs, cid);
1634
 
        rc = fetchInternal(cid, 0L, 4, false);
1635
 
        retsCopy(false, 0);
1636
 
        return rc;
1637
 
} /* sql_fetchLast */
1638
 
 
1639
 
bool sql_fetchNext(int cid, ...)
1640
 
{
1641
 
        bool rc;
1642
 
        va_start(sqlargs, cid);
1643
 
        rc = fetchInternal(cid, 0L, 1, false);
1644
 
        retsCopy(false, 0);
1645
 
        return rc;
1646
 
} /* sql_fetchNext */
1647
 
 
1648
 
bool sql_fetchPrev(int cid, ...)
1649
 
{
1650
 
        bool rc;
1651
 
        va_start(sqlargs, cid);
1652
 
        rc = fetchInternal(cid, 0L, 2, false);
1653
 
        retsCopy(false, 0);
1654
 
        return rc;
1655
 
} /* sql_fetchPrev */
1656
 
 
1657
 
bool sql_fetchAbs(int cid, long rownum, ...)
1658
 
{
1659
 
        bool rc;
1660
 
        va_start(sqlargs, rownum);
1661
 
        rc = fetchInternal(cid, rownum, 6, false);
1662
 
        retsCopy(false, 0);
1663
 
        return rc;
1664
 
} /* sql_fetchAbs */
1665
 
 
1666
 
 
1667
 
/* the inverse of sql_mkunld() */
1668
 
void sql_mkload(const char *line, char delim)
1669
 
{
1670
 
char *s, *t;
1671
 
int data;
1672
 
short i;
1673
 
 
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");
1677
 
*t = 0;
1678
 
if(i >= rv_numRets)
1679
 
errorPrint("2sql load line contains more than %d fields", rv_numRets);
1680
 
 
1681
 
switch(rv_type[i]) {
1682
 
case 'N':
1683
 
if(!*s) { data = nullint; break; }
1684
 
data = strtol(s, &s, 10);
1685
 
if(*s) errorPrint("2sql load, cannot convert string to integer");
1686
 
break;
1687
 
 
1688
 
case 'S':
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];
1693
 
if(!*s) data = 0;
1694
 
break;
1695
 
 
1696
 
case 'F':
1697
 
rv_data[i].f = *s ? atof(s) : nullfloat;
1698
 
continue;
1699
 
 
1700
 
case 'D':
1701
 
data = stringDate(s,0);
1702
 
if(data == -1)
1703
 
errorPrint("2sql load, cannot convert string to date");
1704
 
break;
1705
 
 
1706
 
case 'C':
1707
 
data = *s;
1708
 
if(data && s[1])
1709
 
errorPrint("2sql load, character field contains more than one character");
1710
 
break;
1711
 
 
1712
 
case 'I':
1713
 
data = stringTime(s);
1714
 
if(data == -1)
1715
 
errorPrint("2sql load, cannot convert string to time interval");
1716
 
break;
1717
 
 
1718
 
default:
1719
 
errorPrint("2sql load cannot convert into type %c", rv_type[i]);
1720
 
} /* switch on type */
1721
 
 
1722
 
rv_data[i].l = data;
1723
 
} /* loop over fields in line */
1724
 
 
1725
 
if(i != rv_numRets)
1726
 
errorPrint("2sql load line contains %d fields, %d expected", i, rv_numRets);
1727
 
} /* sql_mkload */
1728
 
 
1729
 
 
1730
 
/*********************************************************************
1731
 
We maintain our own cache of fetched lines.
1732
 
Why?  You might ask.
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
 
*********************************************************************/
1755
 
 
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)
1761
 
{
1762
 
struct OCURS *o = findCursor(cid);
1763
 
int n = o->rownum-1;
1764
 
 
1765
 
if(n >= CACHELIMIT)
1766
 
errorPrint("2SQL cursor caches too many lines, limit %d", CACHELIMIT);
1767
 
 
1768
 
if(n >= o->alloc) {
1769
 
/* running off the end, allocate 128 at a time */
1770
 
short oldalloc = o->alloc;
1771
 
o->alloc = n + 128;
1772
 
if(!oldalloc)
1773
 
o->fl = (char **) allocMem(o->alloc*sizeof(char*));
1774
 
else
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 */
1778
 
 
1779
 
nzFree(o->fl[n]);
1780
 
o->fl[n] = (char*)line;
1781
 
} /* sql_cursorUpdLine */
1782
 
 
1783
 
void sql_cursorDelLine(int cid, int rownum)
1784
 
{
1785
 
struct OCURS *o = findCursor(cid);
1786
 
o->rownum = rownum;
1787
 
--rownum;
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 */
1797
 
 
1798
 
void sql_cursorInsLine(int cid, int rownum)
1799
 
{
1800
 
struct OCURS *o = findCursor(cid);
1801
 
short i;
1802
 
 
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;
1808
 
 
1809
 
if(!o->alloc || o->fl[o->alloc-1]) { /* need to make room */
1810
 
o->alloc += 128;
1811
 
if(!o->fl)
1812
 
o->fl = (char **) allocMem(o->alloc*sizeof(char*));
1813
 
else
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 */
1817
 
 
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 */
1823
 
 
1824
 
 
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
 
*********************************************************************/
1831
 
 
1832
 
void cursor_comm(
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 */
1837
 
{
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];
1845
 
int sortcol;
1846
 
char sorttype;
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";
1851
 
 
1852
 
cid1 = sql_prepOpen(stmt1);
1853
 
cid2 = sql_prepOpen(stmt2);
1854
 
 
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);
1859
 
if(sorttype == 'S')
1860
 
passkey1 = (int)sortstring1, passkey2 = (int)sortstring2;
1861
 
 
1862
 
eof1 = eof2 = false;
1863
 
get1 = get2 = true;
1864
 
rv_blobFile = 0; /* in case the cursor has a blob */
1865
 
line1 = line2 = 0;
1866
 
blob1 = blob2 = 0;
1867
 
 
1868
 
while(true) {
1869
 
if(get1) { /* fetch first row */
1870
 
eof1 = !sql_fetchNext(cid1, 0);
1871
 
nzFree(line1);
1872
 
line1 = 0;
1873
 
nzFree(blob1);
1874
 
blob1 = 0;
1875
 
if(!eof1) {
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);
1882
 
} else {
1883
 
passkey1 = sortval1 = rv_data[sortcol].l;
1884
 
if(isnull(sortval1))
1885
 
errorPrint(sortnull, 1);
1886
 
}
1887
 
line1 = cloneString(delim ? sql_mkunld(delim) : sql_mkinsupd());
1888
 
if(rv_blobLoc) {
1889
 
blob1 = rv_blobLoc;
1890
 
blob1size = rv_blobSize;
1891
 
errorPrint(noblob);
1892
 
}
1893
 
} /* not eof */
1894
 
} /* looking for first line */
1895
 
 
1896
 
if(get2) { /* fetch second row */
1897
 
eof2 = !sql_fetchNext(cid2, 0);
1898
 
nzFree(line2);
1899
 
line2 = 0;
1900
 
nzFree(blob2);
1901
 
blob2 = 0;
1902
 
if(!eof2) {
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);
1909
 
} else {
1910
 
passkey2 = sortval2 = rv_data[sortcol].l;
1911
 
if(isnull(sortval2))
1912
 
errorPrint(sortnull, 2);
1913
 
}
1914
 
line2 = cloneString(delim ? sql_mkunld(delim) : sql_mkinsupd());
1915
 
if(rv_blobLoc) {
1916
 
blob2 = rv_blobLoc;
1917
 
blob2size = rv_blobSize;
1918
 
errorPrint(noblob);
1919
 
}
1920
 
} /* not eof */
1921
 
} /* looking for second line */
1922
 
 
1923
 
if(eof1 & eof2) break; /* done */
1924
 
get1 = get2 = false;
1925
 
 
1926
 
/* in cid2, but not in cid1 */
1927
 
if(eof1 || !eof2 &&
1928
 
(sorttype == 'S' && strcmp(sortstring1, sortstring2) > 0 ||
1929
 
sorttype != 'S' && sortval1 > sortval2)) {
1930
 
(*f)('>', line1, line2, passkey2);
1931
 
get2 = true;
1932
 
continue;
1933
 
}
1934
 
 
1935
 
/* in cid1, but not in cid2 */
1936
 
if(eof2 || !eof1 &&
1937
 
(sorttype == 'S' && strcmp(sortstring1, sortstring2) < 0 ||
1938
 
sorttype != 'S' && sortval1 < sortval2)) {
1939
 
(*f)('<', line1, line2, passkey1);
1940
 
get1 = true;
1941
 
continue;
1942
 
} /* insert case */
1943
 
 
1944
 
get1 = get2 = true;
1945
 
/* perhaps the lines are equal */
1946
 
if(stringEqual(line1, line2)) continue;
1947
 
 
1948
 
/* lines are different between the two cursors */
1949
 
(*f)('*', line1, line2, passkey2);
1950
 
} /* loop over parallel cursors */
1951
 
 
1952
 
nzFree(line1);
1953
 
nzFree(line2);
1954
 
nzFree(blob1);
1955
 
nzFree(blob2);
1956
 
sql_closeFree(cid1);
1957
 
sql_closeFree(cid2);
1958
 
} /* cursor_comm */
1959
 
 
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
 
*********************************************************************/
1965
 
 
1966
 
void
1967
 
getPrimaryKey(char *tname, int *part1, int *part2)
1968
 
{
1969
 
int p1, p2, rc;
1970
 
char *s = strchr(tname, ':');
1971
 
*part1 = *part2 = 0;
1972
 
if(!s) {
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",
1977
 
tname, &p1, &p2);
1978
 
} else {
1979
 
*s = 0;
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);
1985
 
*s = ':';
1986
 
}
1987
 
if(rc) *part1 = p1, *part2 = p2;
1988
 
} /* getPrimaryKey */
1989