~ubuntu-branches/ubuntu/karmic/edbrowse/karmic

« back to all changes in this revision

Viewing changes to dbinfx.ec

  • Committer: Bazaar Package Importer
  • Author(s): Kapil Hari Paranjape
  • Date: 2007-05-09 07:33:04 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20070509073304-ywptg9g6iiitsg17
Tags: 3.2.1-1
* New upstream version (3.2.1). Closes: #421451.
  - can fetch and execute a local javascript file
    if required by local html file.
  - provide COPYING and CHANGES files.
* debian/rules:
  - add CHANGES to dh_installchangelogs line.
  - add dh_installman entry to install the man page.
* debian/copyright: updated to include the COPYING file.
* debian/edbrowse.1: added a basic man page.

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