3
static const char _Cn1[] = "blobinsert";
5
/*********************************************************************
6
dbinfx.ec: C-level interface to SQL.
7
This is a layer above esql/c,
8
since embedded SQL is often difficult to use, especially for new programmers.
9
Most SQL queries are relatively simple, whence the esql API is overkill.
10
Why mess with cryptic $directives when you can write:
11
sql_select("select this, that from table1, table2 where keycolumn = %d",
14
More important, this API automatically aborts (or longjumps) if an error
15
occurs, unless that error has been specifically trapped by the program.
16
This minimizes application-level error-leg programming,
17
thereby reducing the code by as much as 1/3.
18
To accomplish this, the errorPrint() function,
19
supplied by the application, must never return.
20
We assume it passes the error message
21
to stderr and to a logfile,
22
and then exits, or longjumps to a recovery point.
24
Note that this API works within the context of our own C programming
27
Note that dbapi.h does NOT include the Informix header files.
28
That would violate the spirit of this layer,
29
which attempts to sheild the application from the details of the SQL API.
30
If the application needed to see anything in the Informix header files,
31
we would be doing something wrong.
32
*********************************************************************/
34
/* bring in the necessary Informix headers */
41
#line 1 "/opt/informix/incl/esql/sqlca.h"
42
/****************************************************************************
48
* Licensed Material - Property Of IBM
50
* "Restricted Materials of IBM"
52
* IBM Informix Client SDK
54
* (c) Copyright IBM Corporation 1997, 2004. All rights reserved.
60
***************************************************************************
72
typedef struct sqlca_s
76
char sqlerrm[600]; /* error message parameters */
78
char sqlerrm[72]; /* error message parameters */
82
/* 0 - estimated number of rows returned */
83
/* 1 - serial value after insert or ISAM error code */
84
/* 2 - number of rows processed */
85
/* 3 - estimated cost */
86
/* 4 - offset of the error into the SQL statement */
87
/* 5 - rowid after insert */
93
char sqlwarn0; /* = W if any of sqlwarn[1-7] = W */
94
char sqlwarn1; /* = W if any truncation occurred or
95
database has transactions or
96
no privileges revoked */
97
char sqlwarn2; /* = W if a null value returned or
99
char sqlwarn3; /* = W if no. in select list != no. in into list or
100
turbo backend or no privileges granted */
101
char sqlwarn4; /* = W if no where clause on prepared update, delete or
102
incompatible float format */
103
char sqlwarn5; /* = W if non-ANSI statement */
104
char sqlwarn6; /* = W if server is in data replication secondary mode */
105
char sqlwarn7; /* = W if database locale is different from proc_locale
106
= W if backend XPS and if explain avoid_execute is set
107
(for select, insert, delete and update only)
113
/* NOTE: 4gl assumes that the sqlwarn structure can be defined as
114
* sqlawarn -- an 8 character string, because single-char
115
* variables are not recognized in 4gl.
117
* If this structure should change, the code generated by 4gl compiler
125
#define SQLNOTFOUND 100
129
#define sqlca ifmxsqlca
130
extern struct sqlca_s sqlca;
132
extern struct sqlca_s sqlca;
137
extern char SQLSTATE[];
138
#else /* IFX_THREAD */
139
extern int4 * ifx_sqlcode();
140
extern struct sqlca_s * ifx_sqlca();
141
#define SQLCODE (*(ifx_sqlcode()))
142
#define SQLSTATE ((char *)(ifx_sqlstate()))
143
#define sqlca (*(ifx_sqlca()))
144
#endif /* IFX_THREAD */
150
#endif /* SQLCA_INCL */
152
#line 110 "/opt/informix/incl/esql/sqlca.h"
159
#line 1 "/opt/informix/incl/esql/sqltypes.h"
160
/****************************************************************************
166
* Licensed Material - Property Of IBM
168
* "Restricted Materials of IBM"
170
* IBM Informix Client SDK
172
* (c) Copyright IBM Corporation 1985, 2004. All rights reserved.
175
* Description: type definition
177
***************************************************************************
182
#include "ifxtypes.h"
184
/***********************
186
* Any new type to be added to the following lists should not
187
* have the following bit pattern (binary short):
191
* where x can be either 0 or 1.
193
* This is due to the use of the bits as SQLNONULL, SQLHOST and SQLNETFLT
196
* FAILURE TO DO SO WOULD RESULT IN POSSIBLE ERRORS DURING CONVERSIONS.
198
***********************/
200
/* C language types */
202
#define CCHARTYPE 100
203
#define CSHORTTYPE 101
205
#define CLONGTYPE 103
206
#define CFLOATTYPE 104
207
#define CDOUBLETYPE 105
208
#define CDECIMALTYPE 107
209
#define CFIXCHARTYPE 108
210
#define CSTRINGTYPE 109
211
#define CDATETYPE 110
212
#define CMONEYTYPE 111
213
#define CDTIMETYPE 112
214
#define CLOCATORTYPE 113
215
#define CVCHARTYPE 114
217
#define CFILETYPE 116
218
#define CINT8TYPE 117
219
#define CCOLLTYPE 118
220
#define CLVCHARTYPE 119
221
#define CFIXBINTYPE 120
222
#define CVARBINTYPE 121
223
#define CBOOLTYPE 122
225
#define CLVCHARPTRTYPE 124
228
#define USERCOLL(x) ((x))
230
#define COLLMASK 0x007F /* mask out CTYPEDCOLL or CCLIENTCOLL */
231
/* bit set for CCOLLTYPE or CROWTYPE */
232
#define ISCOLLECTIONVAR(n) (((n) & COLLMASK) == CCOLLTYPE)
233
#define ISROWVAR(n) (((n) & COLLMASK) == CROWTYPE)
234
#define ISCOLL_OR_ROWVAR(n) ((ISCOLLECTIONVAR(n)) || (ISROWVAR(n)))
235
#define CCLIENTCOLL SQLCLIENTCOLL /* client collection bit */
236
#define ISCLIENTCOLLECTION(n) (ISCOLLECTIONVAR(n) && ((n) & CCLIENTCOLL))
237
#define ISCLIENTCOMPLEX(n) ((ISCLIENTCOLLECTION(n)) || (ISROWVAR(n)))
240
* The following are for client side only. They are included here
241
* because of the above related definitions.
243
#define CTYPEDCOLL 0x0080 /* typed collection bit */
244
#define CTYPEDCOLLUNMASK 0xFF7F /* unmask typed collection bit */
245
#define ISTYPEDCOLLECTION(n) (ISCOLLECTIONVAR(n) && ((n) & CTYPEDCOLL))
246
#define ISTYPEDROW(n) (ISROWVAR(n) && ((n) & CTYPEDCOLL))
247
#define ISTYPEDCOLL_OR_ROW(n) ( (ISTYPEDCOLLECTION(n)) || (ISTYPEDROW(n)) )
250
* Define all possible database types
251
* include C-ISAM types here as well as in isam.h
268
#define SQLINTERVAL 14
272
#define SQLSERIAL8 18
274
#define SQLMULTISET 20
277
#define SQLCOLLECTION 23
280
* Note: SQLXXX values from 25 through 39 are reserved to avoid collision
281
* with reserved PTXXX values in that same range. See p_types_t.h
283
* REFSER8: create tab with ref: referenced serial 8 rsam counter
284
* this is essentially a SERIAL8, but is an additional rsam counter
285
* this type only lives in the system catalogs and when read from
286
* disk is converted to SQLSERIAL8 with CD_REFSER8 set in ddcol_t
287
* ddc_flags we must distinguish from SERIAL8 to allow both
288
* counters in one tab
291
#define SQLUDTFIXED 41
292
#define SQLREFSER8 42
294
/* These types are used by FE, they are not real major types in BE */
295
#define SQLLVARCHAR 43
296
#define SQLSENDRECV 44
299
#define SQLIMPEXPBIN 47
301
/* This type is used by the UDR code to track default parameters,
302
it is not a real major type in BE */
303
#define SQLUDRDEFAULT 48
304
#define SQLDBSENDRECV 49
305
#define SQLSRVSENDRECV 50
307
/* Type used by DESCRIBE INPUT stmt to indicate input parameters whose
308
types cannot be determined by the server */
309
#define SQLUNKNOWN 51
311
#define SQLMAXTYPES 52
313
#define SQLLABEL SQLINT
315
#define SQLTYPE 0xFF /* type mask */
317
#define SQLNONULL 0x0100 /* disallow nulls */
318
/* a bit to show that the value is from a host variable */
319
#define SQLHOST 0x0200 /* Value is from host var. */
320
#define SQLNETFLT 0x0400 /* float-to-decimal for networked backend */
321
#define SQLDISTINCT 0x0800 /* distinct bit */
322
#define SQLNAMED 0x1000 /* Named row type vs row type */
323
#define SQLDLVARCHAR 0x2000 /* Distinct of lvarchar */
324
#define SQLDBOOLEAN 0x4000 /* Distinct of boolean */
325
#define SQLCLIENTCOLL 0x8000 /* Collection is processed on client */
327
/* we are overloading SQLDBOOLEAN for use with row types */
328
#define SQLVARROWTYPE 0x4000 /* varlen row type */
330
/* We overload SQLNAMED for use with constructor type, this flag
331
* distinguish constructor types from other UDT types.
333
* Please do not test this bit directly, use macro ISCSTTYPE() instead.
335
#define SQLCSTTYPE 0x1000 /* constructor type flag */
337
#define TYPEIDMASK (SQLTYPE | SQLDISTINCT | SQLNAMED | \
338
SQLDLVARCHAR | SQLDBOOLEAN )
343
#define SIZFLOAT (sizeof(double))
344
#define SIZSMFLOAT (sizeof(float))
345
#define SIZDECIMAL 17 /* decimal(32) */
348
#define SIZMONEY 17 /* decimal(32) */
349
#define SIZDTIME 7 /* decimal(12,0) */
351
#define SIZINT8 (sizeof(short) + sizeof(muint) * 2)
352
#define SIZSERIAL8 SIZINT8
353
#define SIZCOLL sizeof (ifx_collection_t)
354
#define SIZSET SIZCOLL
355
#define SIZMULTISET SIZCOLL
356
#define SIZLIST SIZCOLL
357
#define SIZROWREF sizeof (ifx_ref_t)
359
#define MASKNONULL(t) ((t) & (SQLTYPE))
362
* As part of an sqlda structure from DESCRIBE, you can test whether a
363
* column accepts or can return nulls, using the expression:
364
* ISCOLUMNULLABLE(ud->sqlvar[n].sqlflags)
365
* (for sqlda structure pointer ud and column number n).
368
#define ISCOLUMNULLABLE(t) (((t) & (SQLNONULL)) ? 0 : 1)
369
#define ISSQLTYPE(t) (MASKNONULL(t) >= SQLCHAR && MASKNONULL(t) < SQLMAXTYPES)
371
#define DECCOLLEN 8192 /* decimal size definition for DEC(32,0) in syscolumns */
377
#define ISDECTYPE(t) (MASKNONULL(t) == SQLDECIMAL || \
378
MASKNONULL(t) == SQLMONEY || \
379
MASKNONULL(t) == SQLDTIME || \
380
MASKNONULL(t) == SQLINTERVAL)
381
#define ISNUMERICTYPE(t) (MASKNONULL(t) == SQLSMINT || \
382
MASKNONULL(t) == SQLINT || \
383
MASKNONULL(t) == SQLINT8 || \
384
MASKNONULL(t) == SQLFLOAT || \
385
MASKNONULL(t) == SQLSMFLOAT || \
386
MASKNONULL(t) == SQLMONEY || \
387
MASKNONULL(t) == SQLSERIAL || \
388
MASKNONULL(t) == SQLSERIAL8 || \
389
MASKNONULL(t) == SQLDECIMAL)
390
#define ISBLOBTYPE(type) (ISBYTESTYPE (type) || ISTEXTTYPE(type))
391
#define ISBYTESTYPE(type) (MASKNONULL(type) == SQLBYTES)
392
#define ISTEXTTYPE(type) (MASKNONULL(type) == SQLTEXT)
393
#define ISSQLHOST(t) (((t) & SQLHOST) == SQLHOST)
396
#define ISVCTYPE(t) (MASKNONULL(t) == SQLVCHAR)
397
#define ISCHARTYPE(t) (MASKNONULL(t) == SQLCHAR)
399
#define ISVCTYPE(t) (MASKNONULL(t) == SQLVCHAR || \
400
MASKNONULL(t) == SQLNVCHAR)
401
#define ISCHARTYPE(t) (MASKNONULL(t) == SQLCHAR || \
402
MASKNONULL(t) == SQLNCHAR)
403
#define ISNSTRINGTYPE(t) (MASKNONULL(t) == SQLNCHAR || \
404
MASKNONULL(t) == SQLNVCHAR)
407
#define ISSTRINGTYPE(t) (ISVCTYPE(t) || ISCHARTYPE(t))
409
#define ISUDTVARTYPE(t) (MASKNONULL(t) == SQLUDTVAR)
410
#define ISUDTFIXEDTYPE(t) (MASKNONULL(t) == SQLUDTFIXED)
411
#define ISUDTTYPE(t) (ISUDTVARTYPE(t) || ISUDTFIXEDTYPE(t))
413
#define ISCOMPLEXTYPE(t) (ISROWTYPE(t) || ISCOLLTYPE(t))
414
#define ISROWTYPE(t) (MASKNONULL(t) == SQLROW)
415
#define ISLISTTYPE(t) (MASKNONULL(t) == SQLLIST)
416
#define ISMULTISETTYPE(t) (MASKNONULL(t) == SQLMULTISET)
417
#define ISREFTYPE(t) (MASKNONULL(t) == SQLROWREF)
418
#define ISSETTYPE(t) (MASKNONULL(t) == SQLSET)
419
#define ISCOLLECTTYPE(t) (MASKNONULL(t) == SQLCOLLECTION)
420
#define ISCOLLTYPE(t) (ISSETTYPE(t) || ISMULTISETTYPE(t) ||\
421
ISLISTTYPE(t) || ISCOLLECTTYPE(t))
423
#define ISSERIALTYPE(t) (((t) & SQLTYPE) == SQLSERIAL || \
424
((t) & SQLTYPE) == SQLSERIAL8 || \
425
((t) & SQLTYPE) == SQLREFSER8)
427
#define ISDISTINCTTYPE(t) ((t) & SQLDISTINCT)
428
#define ISCSTTYPE(t) (ISUDTTYPE(t) && ((t) & SQLCSTTYPE))
430
/* these macros are used to distinguish NLS char types and non-nls (ASCII)
433
#define ISNONNLSCHAR(t) (MASKNONULL(t) == SQLCHAR || \
434
MASKNONULL(t) == SQLVCHAR)
436
/* these macros should be used in case statements
439
#define CHARCASE SQLCHAR
440
#define VCHARCASE SQLVCHAR
442
#define CHARCASE SQLCHAR: case SQLNCHAR
443
#define VCHARCASE SQLVCHAR: case SQLNVCHAR
446
#define UDTCASE SQLUDTVAR: case SQLUDTFIXED
451
#define ISBLOBCTYPE(type) (ISLOCTYPE(type) || ISFILETYPE(type))
452
#define ISLOCTYPE(type) (MASKNONULL(type) == CLOCATORTYPE)
453
#define ISFILETYPE(type) (MASKNONULL(type) == CFILETYPE)
454
#define ISLVCHARCTYPE(type) (MASKNONULL(type) == CLVCHARTYPE)
455
#define ISLVCHARCPTRTYPE(type) (MASKNONULL(type) == CLVCHARPTRTYPE)
456
#define ISFIXBINCTYPE(type) (MASKNONULL(type) == CFIXBINTYPE)
457
#define ISVARBINCTYPE(type) (MASKNONULL(type) == CVARBINTYPE)
458
#define ISBOOLCTYPE(type) (MASKNONULL(type) == CBOOLTYPE)
461
#define ISOPTICALCOL(type) (type == 'O')
463
#define DEFDECIMAL 9 /* default decimal(16) size */
464
#define DEFMONEY 9 /* default decimal(16) size */
466
#define SYSPUBLIC "public"
469
* if an SQL type is specified, convert to default C type
470
* map C int to either short or long
474
#define TYPEMAX SQLMAXTYPES
476
extern int2 sqlctype[];
478
#define toctype(ctype, type) \
480
if (type == CINTTYPE) \
482
if (sizeof(mint) == sizeof(mlong)) \
483
{ ctype = type = CLONGTYPE; } \
484
else if (sizeof(mint) == sizeof(int2)) \
485
{ ctype = type = CSHORTTYPE; } \
487
{ ctype = CLONGTYPE; type = CINTTYPE; } \
489
else if (type >= 0 && type < TYPEMAX) \
490
ctype = sqlctype[type]; \
497
/* Extended ID definitions for predefined UDT's */
498
/* These can not be changed because sqli is using
499
* them. If we change them, the client has to recompile.
500
* NOTE: This order must match the definitions in boot90.sql
503
#define XID_LVARCHAR 1
504
#define XID_SENDRECV 2
506
#define XID_IMPEXPBIN 4
507
#define XID_BOOLEAN 5
508
#define XID_POINTER 6
509
#define XID_INDEXKEYARRAY 7
510
#define XID_RTNPARAMTYPES 8
511
#define XID_SELFUNCARGS 9
514
#define XID_LOLIST 12
515
#define XID_IFX_LO_SPEC 13
516
#define XID_IFX_LO_STAT 14
518
#define XID_CLIENTBINVAL 16
519
#define XID_UDTMODIFIERS 17
520
#define XID_AGGMODIFIERS 18
521
#define XID_UDRMODIFIERS 19
523
#define XID_DBSENDRECV 21
524
#define XID_SRVSENDRECV 22
525
#define XID_FUNCARG 23
527
/* Max size definitions for the predefined UDT's.
528
* Only a few are currently defined.
530
#define SIZINDEXKEYARRAY 1024
531
#define SIZLVARCHAR 2048
532
#define SIZRTNPARAMTYPES 4096
536
/* Alignment required by predefined UDT's.
537
* Only a few are currently defined. At a minimum,
538
* all with alignment not 1 should be defined here
539
* and used throughout the code.
541
#define ALNINDEXKEYARRAY 4
545
#define USER_XTDTYPEID_START 2048
547
/* These macros should be used to test lvarchar and distinct of lvarchar */
549
#define ISLVARCHARXTYPE(t, xid) (ISUDTTYPE((t)) && (xid) == XID_LVARCHAR)
550
#define ISDISTINCTLVARCHAR(t) ((t) & SQLDLVARCHAR)
551
#define LIKELVARCHARXTYPE(t,xid) ((ISLVARCHARXTYPE(t,xid))||\
552
ISDISTINCTLVARCHAR(t))
554
#define ISSMARTBLOB(type, xid) (ISUDTTYPE((type)) && \
555
((xid == XID_CLOB) || (xid == XID_BLOB)))
557
/* These macros should be used to test boolean and distinct of boolean */
558
#define ISBOOLEANXTYPE(t, xid) (ISUDTTYPE((t)) && (xid) == XID_BOOLEAN)
559
#define ISDISTINCTBOOLEAN(t) (((t) & SQLDBOOLEAN) && \
561
#define LIKEBOOLEANXTYPE(t,xid) ((ISBOOLEANXTYPE(t,xid))||\
562
ISDISTINCTBOOLEAN(t))
564
#define ISFIXLENGTHTYPE(t) (!ISBYTESTYPE(t) && !ISTEXTTYPE(t) \
565
&& !ISCOMPLEXTYPE(t) \
566
&& !ISUDTVARTYPE(t) \
568
#endif /* CCHARTYPE */
569
#line 409 "/opt/informix/incl/esql/sqltypes.h"
576
#line 1 "/opt/informix/incl/esql/sqlda.h"
577
/****************************************************************************
583
* Licensed Material - Property Of IBM
585
* "Restricted Materials of IBM"
587
* IBM Informix Client SDK
589
* (c) Copyright IBM Corporation 1985, 2004. All rights reserved.
592
* Description: SQL Data Description Area
594
***************************************************************************
601
#include "ifxtypes.h"
603
typedef struct sqlvar_struct
605
int2 sqltype; /* variable type */
606
int4 sqllen; /* length in bytes */
607
char *sqldata; /* pointer to data */
608
int2 *sqlind; /* pointer to indicator */
609
char *sqlname; /* variable name */
610
char *sqlformat; /* reserved for future use */
611
int2 sqlitype; /* ind variable type */
612
int2 sqlilen; /* ind length in bytes */
613
char *sqlidata; /* ind data pointer */
614
int4 sqlxid; /* extended id type */
615
char *sqltypename; /* extended type name */
616
int2 sqltypelen; /* length of extended type name */
617
int2 sqlownerlen; /* length of owner name */
618
int2 sqlsourcetype; /* source type for distinct of built-ins */
619
char *sqlownername; /* owner name */
620
int4 sqlsourceid; /* extended id of source type */
623
* sqlilongdata is new. It supports data that exceeds the 32k
624
* limit. sqlilen and sqlidata are for backward compatibility
625
* and they have maximum value of <32K.
627
char *sqlilongdata; /* for data field beyond 32K */
630
* As part of an SQL DESCRIPTOR (ALLOCATE DESCRIPTOR, etc), sqlflags is
631
* reserved for internal use.
632
* As part of an sqlda structure from DESCRIBE, you can test whether a
633
* column accepts or can return nulls, using the expression:
634
* ISCOLUMNULLABLE(ud->sqlvar[n].sqlflags)
635
* (for sqlda structure pointer ud and column number n).
639
void *sqlreserved; /* reserved for future use */
645
ifx_sqlvar_t *sqlvar;
646
char desc_name[19]; /* descriptor name */
647
int2 desc_occ; /* size of sqlda structure */
648
struct sqlda *desc_next; /* pointer to next sqlda struct */
649
void *reserved; /* reserved for future use */
653
#line 76 "/opt/informix/incl/esql/sqlda.h"
660
#line 1 "/opt/informix/incl/esql/locator.h"
661
/****************************************************************************
667
* Licensed Material - Property Of IBM
669
* "Restricted Materials of IBM"
671
* IBM Informix Client SDK
673
* (c) Copyright IBM Corporation 1997, 2004. All rights reserved.
678
* 'locator.h' defines 'loc_t' the locator struct.
680
***************************************************************************
683
#ifndef LOCATOR_INCL /* avoid multiple includes */
686
#include "ifxtypes.h"
691
Locators are used to store TEXT or BYTE fields (blobs) in ESQL
692
programs. The "loc_t" structure is described below. Fields denoted
693
USER should be set by the user program and will be examined by the DBMS
694
system. Those denoted SYSTEM are set by the system and may be examined
695
by the user program. Those denoted INTERNAL contain data only the
696
system manupilates and examines.
698
If "loc_loctype" is set to LOCMEMORY, then the blob is stored in
699
primary memory. The memory buffer is pointed to by the variant
700
"loc_buffer". The field "loc_bufsize" gives the size of "loc_buffer".
701
If the "loc_bufsize" is set to "-1" and "loc_mflags" is set to "0"
702
and the locator is used for a fetch, memory is obtained using "malloc"
703
and "loc_buffer" and "loc_bufsize" are set.
705
If "loc_loctype" is set to LOCFILE, then the blob is stored in a file.
706
The file descriptor of an open operating system file is specified in
709
If "loc_loctype" is set to LOCFNAME, the the blob is stored in a file
710
and the name of the file is given. The DBMS will open or created the
711
file at the correct time and in the correct mode.
713
If the "loc_loctype" is set to LOCUSER, "loc_(open/close/read/write)"
714
are called. If the blob is an input to a SQL statement, "loc_open" is
715
called with the parameter "LOC_RONLY". If the blob is an output target
716
for an SQL statement, "loc_open" is called with the parameter
719
"loc_size" specifies the maximum number of bytes to use when the
720
locator is an input target to an SQL statement. It specifies the number
721
of bytes returned if the locator is an output target. If "loc_loctype"
722
is LOCFILE or LOCUSER, it can be set to -1 to indicate transfer until
725
"loc_indicator" is set by the user to -1 to indicate a NULL blob. It
726
will be set to -1 if a NULL blob is retrieved. If the blob to be
727
retrieved will not fit in the space provided, the indicator contains
728
the size of the blob.
730
"loc_status" is the status return of locator operations.
732
"loc_type" is the "blob" type (SQLTEXT, SQLBYTES, ...).
734
"loc_user_env" is a pointer for the user's private use. It is neither
735
set nor examined by the system. "loc_user_env" as well as the
736
"loc_union" fieds may be used by user supplied routines to store and
737
communicate information.
740
typedef struct tag_loc_t
742
int2 loc_loctype; /* USER: type of locator - see below */
743
union /* variant on 'loc' */
745
struct /* case LOCMEMORY */
747
int4 lc_bufsize; /* USER: buffer size */
748
char *lc_buffer; /* USER: memory buffer to use */
749
char *lc_currdata_p;/* INTERNAL: current memory buffer */
750
mint lc_mflags; /* USER/INTERNAL: memory flags */
754
struct /* cases L0CFNAME & LOCFILE */
756
char *lc_fname; /* USER: file name */
757
mint lc_mode; /* USER: perm. bits used if creating */
758
mint lc_fd; /* USER: os file descriptior */
759
int4 lc_position; /* INTERNAL: seek position */
763
int4 loc_indicator; /* USER SYSTEM: indicator */
764
int4 loc_type; /* USER SYSTEM: type of blob */
765
int4 loc_size; /* USER SYSTEM: num bytes in blob or -1 */
766
mint loc_status; /* SYSTEM: status return of locator ops */
767
char *loc_user_env; /* USER: for the user's PRIVATE use */
768
int4 loc_xfercount; /* INTERNAL/SYSTEM: Transfer count */
770
#if defined(__STDC__) || defined(__cplusplus)
771
mint (*loc_open)(struct tag_loc_t *loc, mint flag, mint bsize);
772
mint (*loc_close)(struct tag_loc_t *loc);
773
mint (*loc_read)(struct tag_loc_t *loc, char *buffer, mint buflen);
774
mint (*loc_write)(struct tag_loc_t *loc, char *buffer, mint buflen);
780
#endif /* defined(__STDC__) || defined(__cplusplus) */
782
mint loc_oflags; /* USER/INTERNAL: see flag definitions below */
785
#define loc_fname lc_union.lc_file.lc_fname
786
#define loc_fd lc_union.lc_file.lc_fd
787
#define loc_position lc_union.lc_file.lc_position
788
#define loc_bufsize lc_union.lc_mem.lc_bufsize
789
#define loc_buffer lc_union.lc_mem.lc_buffer
790
#define loc_currdata_p lc_union.lc_mem.lc_currdata_p
791
#define loc_mflags lc_union.lc_mem.lc_mflags
793
/* Enumeration literals for loc_loctype */
795
#define LOCMEMORY 1 /* memory storage */
796
#define LOCFNAME 2 /* File storage with file name */
797
#define LOCFILE 3 /* File storage with fd */
798
#define LOCUSER 4 /* User define functions */
800
/* passed to loc_open and stored in loc_oflags */
801
#define LOC_RONLY 0x1 /* read only */
802
#define LOC_WONLY 0x2 /* write only */
804
/* LOC_APPEND can be set when the locator is created
805
* if the file is to be appended to instead of created
807
#define LOC_APPEND 0x4 /* write with append */
808
#define LOC_TEMPFILE 0x8 /* 4GL tempfile blob */
810
/* LOC_USEALL can be set to force the maximum size of the blob to always be
811
* used when the blob is an input source. This is the same as setting the
812
* loc_size field to -1. Good for LOCFILE or LOCFNAME blobs only.
814
#define LOC_USEALL 0x10 /* ignore loc_size field */
815
#define LOC_DESCRIPTOR 0x20 /* BLOB is optical descriptor */
817
/* length of the encoded descriptor text */
818
#define LOC_DESCRIPTOR_SIZE 112
820
/* passed to loc_open and stored in loc_mflags */
821
#define LOC_ALLOC 0x1 /* free and alloc memory */
823
/* Flags to indicate if file is on the server or client */
824
#define LO_CLIENT_FILE 0x20000000
825
#define LO_SERVER_FILE 0x10000000
827
/*******************************************************************************
828
* File open flags used for operating system file open via
829
* - ifx_lo_copy_to_lo
830
* - ifx_lo_copy_to_file
833
******************************************************************************/
835
#define LO_O_EXCL 0x00000001 /* fail if file exists */
836
#define LO_O_APPEND 0x00000002 /* append to end of file */
837
#define LO_O_TRUNC 0x00000004 /* turncate to 0 if file exists */
838
#define LO_O_RDWR 0x00000008 /* read/write (default) */
839
#define LO_O_RDONLY 0x00000010 /* read-only (from-flags only) */
840
#define LO_O_WRONLY 0x00000020 /* write-only (to-flags only) */
841
#define LO_O_BINARY 0x00000040 /* binary-mode (default) */
842
#define LO_O_TEXT 0x00000080 /* text-mode (default off)*/
845
/*******************************************************************************
847
* Smartblob Definitions
849
******************************************************************************/
851
/******************************************************************************
852
* Open flags: see ESQL/C documentation for further explanation.
854
* LO_APPEND - Positions the seek position to end-of-file + 1. Affects write
855
* operations. Reads can still seek anywhere in the LO. Writes always append.
857
* LO_SEQUENTIAL - If set overrides optimizer decision. Indicates that
858
* reads are sequential in either forward or reverse direction.
860
* LO_RANDOM - If set overrides optimizer decision. Indicates that I/O is
861
* random and that the system should not read-ahead.
862
* LO_FORWARD - Only used for sequential access. Indicates that the sequential
863
* access will be in a forward direction, i.e. from low offset to higher offset.
864
* LO_REVERSE - Only used for sequential access. Indicates that the sequential
865
* access will be in a reverse direction.
867
* LO_BUFFER - If set overrides optimizer decision. I/O goes through the
870
* LO_NOBUFFER - If set then I/O does not use the buffer pool.
871
******************************************************************************/
873
#define LO_APPEND 0x1
874
#define LO_WRONLY 0x2
875
#define LO_RDONLY 0x4 /* default */
877
#define LO_DIRTY_READ 0x10
879
#define LO_RANDOM 0x20 /* default is determined by optimizer */
880
#define LO_SEQUENTIAL 0x40 /* default is determined by optimizer */
882
#define LO_FORWARD 0x80 /* default */
883
#define LO_REVERSE 0x100
885
#define LO_BUFFER 0x200 /* default is determined by optimizer */
886
#define LO_NOBUFFER 0x400 /* default is determined by optimizer */
887
#define LO_NODIRTY_READ 0x800
889
#define LO_LOCKALL 0x1000 /* default */
890
#define LO_LOCKRANGE 0x2000
892
#ifndef REMOVE_VECTORIO
893
#define LO_VECTORIO 0x4000
897
* Another set of open flags are defined to make the flags more meaningful
900
#define LO_OPEN_APPEND LO_APPEND
901
#define LO_OPEN_WRONLY LO_WRONLY
902
#define LO_OPEN_RDONLY LO_RDONLY /* default */
903
#define LO_OPEN_RDWR LO_RDWR
904
#define LO_OPEN_DIRTY_READ LO_DIRTY_READ
906
#define LO_OPEN_RANDOM LO_RANDOM /* default is determined by optimizer */
907
#define LO_OPEN_SEQUENTIAL LO_SEQUENTIAL /* default is determined by optimizer */
909
#define LO_OPEN_FORWARD LO_FORWARD /* default */
910
#define LO_OPEN_REVERSE LO_REVERSE
912
#define LO_OPEN_BUFFER LO_BUFFER /* default is determined by optimizer */
913
#define LO_OPEN_NOBUFFER LO_NOBUFFER /* default is determined by optimizer */
914
#define LO_OPEN_NODIRTY_READ LO_NODIRTY_READ
916
#define LO_OPEN_LOCKALL LO_LOCKALL /* default */
917
#define LO_OPEN_LOCKRANGE LO_LOCKRANGE
919
/* When setting the MI_LO_NOBUFFER flag for write operations, please
920
* don't set this flag if the object is small. It usually causes a synchronous
921
* flush of the log and a synchronous flush of pages written - this is
922
* very slow. Instead use buffered I/O for small writes.
924
#define LO_NOBUFFER_SIZE_THRESHOLD 8080
926
/*see Informix internal use file /vobs/tristarp/incl/sblob.h for other flags*/
928
/*******************************************************************************
929
* LO create-time flags:
931
* Bitmask - Set/Get via ifx_lo_specset_flags() on ifx_lo_create_spec_t.
933
* New applications should use the flags which begin LO_ATTR_
934
* The second set of flags are defined for backward compatibility only.
935
******************************************************************************/
937
#define LO_ATTR_LOG 0x0001
938
#define LO_ATTR_NOLOG 0x0002
939
#define LO_ATTR_DELAY_LOG 0x0004
940
#define LO_ATTR_KEEP_LASTACCESS_TIME 0x0008
941
#define LO_ATTR_NOKEEP_LASTACCESS_TIME 0x0010
942
#define LO_ATTR_HIGH_INTEG 0x0020
943
#define LO_ATTR_MODERATE_INTEG 0x0040
944
#define LO_ATTR_TEMP 0x0080
946
/* these 7 values are defined for backward compatibility only */
947
#define LO_LOG 0x0001
948
#define LO_NOLOG 0x0002
949
#define LO_DELAY_LOG 0x0004
950
#define LO_KEEP_LASTACCESS_TIME 0x0008
951
#define LO_NOKEEP_LASTACCESS_TIME 0x0010
952
#define LO_HIGH_INTEG 0x0020
953
#define LO_MODERATE_INTEG 0x0040
954
#define LO_TEMP 0x0080
956
/* these flags are defined to make the create flags more meaningful */
957
#define LO_CREATE_LOG 0x0001
958
#define LO_CREATE_NOLOG 0x0002
959
#define LO_CREATE_DELAY_LOG 0x0004
960
#define LO_CREATE_KEEP_LASTACCESS_TIME 0x0008
961
#define LO_CREATE_NOKEEP_LASTACCESS_TIME 0x0010
962
#define LO_CREATE_HIGH_INTEG 0x0020
963
#define LO_CREATE_MODERATE_INTEG 0x0040
964
#define LO_CREATE_TEMP 0x0080
966
/*******************************************************************************
967
* Symbolic constants for the "lseek" routine
968
******************************************************************************/
970
#define LO_SEEK_SET 0 /* Set curr. pos. to "offset" */
971
#define LO_SEEK_CUR 1 /* Set curr. pos. to current + "offset" */
972
#define LO_SEEK_END 2 /* Set curr. pos. to EOF + "offset" */
975
/*******************************************************************************
976
* Symbolic constants for lo_lock and lo_unlock routines.
977
******************************************************************************/
979
#define LO_SHARED_MODE 1 /* ISSLOCK */
980
#define LO_EXCLUSIVE_MODE 2 /* ISXLOCK */
982
#define LO_MAX_END -1
983
#define LO_CURRENT_END -2
985
/*******************************************************************************
986
* ifx_lo_create_spec_t:
988
* This is an opaque structure used for creating smartblobs. The
989
* user may examin and/or set certain fields herein by using
990
* ifx_lo_spec[set|get]_* accessor functions. Prototypes for these acessors
991
* are in incl/sqlhdr.h
993
******************************************************************************/
995
typedef struct ifx_lo_create_spec_s ifx_lo_create_spec_t;
999
/*******************************************************************************
1000
* ifx_lo_t: A dummy opaque representation of the smartblob structure
1002
* This can be used for stack or in-line structure declarations.
1004
******************************************************************************/
1005
#define SB_LOCSIZE 72 /* length of ifx_lo_t */
1007
typedef struct ifx_lo_ts
1009
char dummy[SB_LOCSIZE];
1012
/*******************************************************************************
1015
* This is an opaque structure used in querying attribtes of smartblobs. The
1016
* user may examin fields herein by using ifx_lo_stat_* accessor functions.
1017
* Prototypes for these acessors are in incl/sqlhdr.h
1019
* The accessors are defined as follows:
1020
* ifx_lo_stat_size: contains the size of the LO in bytes.
1021
* ifx_lo_stat_uid: reserved for future use: the user id for the
1023
* ifx_lo_stat_atime: the time of last access. This is only maintained if
1024
* the LO_KEEP_LASTACCESS_TIME flag is set for the LO.
1025
* Resolution is seconds.
1026
* ifx_lo_stat_mtime: the time of last modification. Resolution is
1028
* ifx_lo_stat_ctime: the time of the last status change (this includes
1029
* updates, changes in ownership, and changes to the number of
1030
* references). Resolution is seconds. See Appendix B2.11,
1031
* Future Embedded- language feature issues, Smartblob API
1032
* functions using lofd, for enhancements that extend support
1033
* for named and external LOs.
1034
* ifx_lo_stat_refcnt: the number of pointers to this LO - when 0 the LO is
1035
* typically deleted. See deletion criteria.
1036
* ifx_lo_stat_cspec: a pointer to the opaque create spec for this object.
1037
* (see ifx_lo_spec[get|set]_ accessors for details.)
1038
* ifx_lo_stat_type: the 8 byte code for the LO's type
1040
******************************************************************************/
1042
typedef struct ifx_lo_stat_s ifx_lo_stat_t;
1044
#endif /* LOCATOR_INCL */
1045
#line 384 "/opt/informix/incl/esql/locator.h"
1046
#line 35 "dbinfx.ec"
1051
#define CACHELIMIT 10000 /* number of cached lines */
1053
#define ENGINE_ERRCODE sqlca.sqlcode
1056
/*********************************************************************
1057
The status variable ENGINE_ERRCODE holds the return code from an Informix call.
1058
This is then used by the function errorTrap() below.
1059
If ENGINE_ERRCODE != 0, errorTrap() aborts the program, or performs
1060
a recovery longjmp, as directed by the generic error function errorPrint().
1061
errorTrap() returns true if an SQL error occurred, but that error
1062
was trapped by the application.
1063
In this case the calling routine should clean up as best it can and return.
1064
*********************************************************************/
1066
static const char *stmt_text = 0; /* text of the SQL statement */
1067
static const short *exclist; /* list of error codes trapped by the application */
1068
static short translevel;
1069
static bool badtrans;
1071
/* Through globals, make error info available to the application. */
1072
int rv_lastStatus, rv_vendorStatus, rv_stmtOffset;
1075
static void debugStatement(void)
1077
if(sql_debug && stmt_text)
1078
appendFileNF(sql_debuglog, stmt_text);
1079
} /* debugStatement */
1081
static void debugExtra(const char *s)
1084
appendFileNF(sql_debuglog, s);
1087
/* Append the SQL statement to the debug log. This is not strictly necessary
1088
* if sql_debug is set, since the statement has already been appended. */
1089
static void showStatement(void)
1091
if(!sql_debug && stmt_text)
1092
appendFileNF(sql_debuglog, stmt_text);
1093
} /* showStatement */
1095
/* application sets the exception list */
1096
void sql_exclist(const short *list) { exclist = list; }
1098
void sql_exception(int errnum)
1100
static short list[2];
1103
} /* sql_exception */
1105
/* text descriptions corresponding to our generic SQL error codes */
1106
static char *sqlErrorList[] = {0,
1107
"miscelaneous SQL error",
1108
"syntax error in SQL statement",
1109
"filename cannot be used by SQL",
1110
"cannot convert/compare the columns/constants in the SQL statement",
1111
"bad string subscripting",
1112
"bad use of the rowid construct",
1113
"bad use of a blob column",
1114
"bad use of aggregate operators or columns",
1115
"bad use of a view",
1116
"bad use of a serial column",
1117
"bad use of a temp table",
1118
"operation cannot cross databases",
1119
"database is fucked up",
1120
"query interrupted by user",
1121
"could not connect to the database",
1122
"database has not yet been selected",
1131
"constraint not found",
1132
"duplicate constraint",
1133
"stored procedure not found",
1134
"duplicate stored procedure",
1135
"synonym not found",
1136
"duplicate synonym",
1137
"table has no primary or unique key",
1138
"duplicate primary or unique key",
1139
"cursor not specified, or cursor is not available",
1141
"the database lacks the resources needed to complete this query",
1142
"check constrain violated",
1143
"referential integrity violated",
1144
"cannot manage or complete the transaction",
1145
"long transaction, too much log data generated",
1146
"this operation must be run inside a transaction",
1147
"cannot open, read, write, close, or otherwise manage a blob",
1148
"row, table, page, or database is already locked, or cannot be locked",
1149
"inserting null into a not null column",
1150
"no permission to modify the database in this way",
1151
"no current row established",
1152
"many rows were found where one was expected",
1153
"cannot union these select statements together",
1154
"cannot access or write the audit trail",
1155
"could not run SQL or gather data from a remote host",
1156
"where clause is semantically unmanageable",
1157
"deadlock detected",
1160
/* map Informix errors to our own exception codes, as defined in c_sql.h. */
1161
static struct ERRORMAP {
1174
{209, EXCDBCORRUPT},
1176
{211, EXCDBCORRUPT},
1178
{213, EXCINTERRUPT},
1179
{214, EXCDBCORRUPT},
1180
{215, EXCDBCORRUPT},
1181
{216, EXCDBCORRUPT},
1183
{218, EXCNOSYNONYM},
1195
{230, EXCDBCORRUPT},
1196
{231, EXCAGGREGATEUSE},
1199
{234, EXCAMBCOLUMN},
1202
{237, EXCMANAGETRANS},
1203
{238, EXCMANAGETRANS},
1205
{240, EXCDBCORRUPT},
1206
{241, EXCMANAGETRANS},
1207
{249, EXCAMBCOLUMN},
1208
{250, EXCDBCORRUPT},
1212
{255, EXCNOTINTRANS},
1213
{256, EXCMANAGETRANS},
1215
{258, EXCDBCORRUPT},
1222
{265, EXCNOTINTRANS},
1226
{269, EXCNOTNULLCOLUMN},
1227
{270, EXCDBCORRUPT},
1228
{271, EXCDBCORRUPT},
1229
{272, EXCPERMISSION},
1230
{273, EXCPERMISSION},
1231
{274, EXCPERMISSION},
1232
{275, EXCPERMISSION},
1236
{281, EXCTEMPTABLEUSE},
1241
{286, EXCNOTNULLCOLUMN},
1246
{292, EXCNOTNULLCOLUMN},
1248
{294, EXCAGGREGATEUSE},
1252
{298, EXCPERMISSION},
1253
{299, EXCPERMISSION},
1256
{302, EXCPERMISSION},
1257
{ 303, EXCAGGREGATEUSE},
1258
{304, EXCAGGREGATEUSE},
1259
{305, EXCSUBSCRIPT},
1260
{306, EXCSUBSCRIPT},
1261
{307, EXCSUBSCRIPT},
1263
{309, EXCAMBCOLUMN},
1265
{311, EXCDBCORRUPT},
1266
{312, EXCDBCORRUPT},
1267
{313, EXCPERMISSION},
1269
{315, EXCPERMISSION},
1274
{320, EXCPERMISSION},
1275
{321, EXCAGGREGATEUSE},
1276
{323, EXCTEMPTABLEUSE},
1277
{324, EXCAMBCOLUMN},
1281
{328, EXCDUPCOLUMN},
1282
{329, EXCNOCONNECT},
1284
{331, EXCDBCORRUPT},
1289
{336, EXCTEMPTABLEUSE},
1290
{337, EXCTEMPTABLEUSE},
1299
{346, EXCDBCORRUPT},
1301
{348, EXCDBCORRUPT},
1307
{355, EXCDBCORRUPT},
1314
{367, EXCAGGREGATEUSE},
1315
{368, EXCDBCORRUPT},
1317
{370, EXCAMBCOLUMN},
1322
{375, EXCMANAGETRANS},
1323
{376, EXCMANAGETRANS},
1324
{377, EXCMANAGETRANS},
1327
{383, EXCAGGREGATEUSE},
1330
{386, EXCNOTNULLCOLUMN},
1331
{387, EXCPERMISSION},
1332
{388, EXCPERMISSION},
1333
{389, EXCPERMISSION},
1334
{390, EXCDUPSYNONYM},
1335
{391, EXCNOTNULLCOLUMN},
1336
{392, EXCDBCORRUPT},
1337
{393, EXCWHERECLAUSE},
1339
{395, EXCWHERECLAUSE},
1340
{396, EXCWHERECLAUSE},
1341
{397, EXCDBCORRUPT},
1342
{398, EXCNOTINTRANS},
1343
{399, EXCMANAGETRANS},
1348
{407, EXCDBCORRUPT},
1349
{408, EXCDBCORRUPT},
1350
{409, EXCNOCONNECT},
1360
{424, EXCDUPCURSOR},
1370
{453, EXCDBCORRUPT},
1371
{454, EXCDBCORRUPT},
1374
{458, EXCLONGTRANS},
1378
{468, EXCNOCONNECT},
1390
{506, EXCPERMISSION},
1392
{508, EXCTEMPTABLEUSE},
1393
{509, EXCTEMPTABLEUSE},
1394
{510, EXCTEMPTABLEUSE},
1395
{512, EXCPERMISSION},
1396
{514, EXCPERMISSION},
1397
{515, EXCNOCONSTRAINT},
1399
{518, EXCNOCONSTRAINT},
1403
{524, EXCNOTINTRANS},
1407
{529, EXCNOCONNECT},
1409
{531, EXCDUPCOLUMN},
1410
{532, EXCTEMPTABLEUSE},
1412
{535, EXCMANAGETRANS},
1414
{537, EXCNOCONSTRAINT},
1415
{538, EXCDUPCURSOR},
1417
{540, EXCDBCORRUPT},
1418
{541, EXCPERMISSION},
1419
{543, EXCAMBCOLUMN},
1421
{544, EXCAGGREGATEUSE},
1422
{545, EXCPERMISSION},
1423
{548, EXCTEMPTABLEUSE},
1428
{559, EXCDUPSYNONYM},
1429
{560, EXCDBCORRUPT},
1430
{561, EXCAGGREGATEUSE},
1441
{573, EXCMANAGETRANS},
1442
{574, EXCAMBCOLUMN},
1443
{576, EXCTEMPTABLEUSE},
1444
{577, EXCDUPCONSTRAINT},
1446
{579, EXCPERMISSION},
1447
{580, EXCPERMISSION},
1448
{582, EXCMANAGETRANS},
1449
{583, EXCPERMISSION},
1450
{586, EXCDUPCURSOR},
1452
{590, EXCDBCORRUPT},
1454
{592, EXCNOTNULLCOLUMN},
1457
{595, EXCAGGREGATEUSE},
1458
{597, EXCDBCORRUPT},
1461
{600, EXCMANAGEBLOB},
1462
{601, EXCMANAGEBLOB},
1463
{602, EXCMANAGEBLOB},
1464
{603, EXCMANAGEBLOB},
1465
{604, EXCMANAGEBLOB},
1466
{605, EXCMANAGEBLOB},
1467
{606, EXCMANAGEBLOB},
1468
{607, EXCSUBSCRIPT},
1478
{618, EXCMANAGEBLOB},
1480
{623, EXCNOCONSTRAINT},
1481
{625, EXCDUPCONSTRAINT},
1482
{628, EXCMANAGETRANS},
1483
{629, EXCMANAGETRANS},
1484
{630, EXCMANAGETRANS},
1486
{635, EXCPERMISSION},
1490
{640, EXCDBCORRUPT},
1494
/* I'm not about to map all possible compile/runtime SPL errors. */
1500
{678, EXCSUBSCRIPT},
1501
{681, EXCDUPCOLUMN},
1507
{703, EXCNOTNULLCOLUMN},
1508
{704, EXCDUPCONSTRAINT},
1509
{706, EXCPERMISSION},
1516
{25553, EXCNOCONNECT},
1517
{25587, EXCNOCONNECT},
1518
{25588, EXCNOCONNECT},
1519
{25596, EXCNOCONNECT},
1521
}; /* ends of list */
1523
static int errTranslate(int code)
1527
for(e=errormap; e->infcode; ++e) {
1528
if(e->infcode == code)
1532
} /* errTranslate */
1534
static bool errorTrap(void)
1538
rv_lastStatus = rv_vendorStatus = 0; /* innocent until proven guilty */
1541
if(ENGINE_ERRCODE >= 0) return false; /* no problem */
1544
rv_vendorStatus = -ENGINE_ERRCODE;
1545
rv_lastStatus = errTranslate(rv_vendorStatus);
1546
rv_stmtOffset = sqlca.sqlerrd[4];
1547
rv_badToken = sqlca.sqlerrm;
1548
if(!rv_badToken[0]) rv_badToken = 0;
1550
/* if the application didn't trap for this exception, blow up! */
1552
for(i=0; exclist[i]; ++i) {
1553
if(exclist[i] == rv_lastStatus) {
1554
exclist = 0; /* we've spent that exception */
1560
errorPrint("2SQL error %d, %s", rv_vendorStatus, sqlErrorList[rv_lastStatus]);
1561
return true; /* make the compiler happy */
1565
/*********************************************************************
1566
The OCURS structure given below maintains an open SQL cursor.
1567
A static array of these structures allows multiple cursors
1568
to be opened simultaneously.
1569
*********************************************************************/
1571
static struct OCURS {
1572
char sname[8]; /* statement name */
1573
char cname[8]; /* cursor name */
1575
char rv_type[NUMRETS];
1578
short cid; /* cursor ID */
1581
char **fl; /* array of fetched lines */
1582
} ocurs[NUMCURSORS];
1584
/* values for struct OCURS.flag */
1585
#define CURSOR_NONE 0
1586
#define CURSOR_PREPARED 1
1587
#define CURSOR_OPENED 2
1589
/* find a free cursor structure */
1590
static struct OCURS *findNewCursor(void)
1594
for(o=ocurs, i=0; i<NUMCURSORS; ++i, ++o) {
1595
if(o->flag != CURSOR_NONE) continue;
1596
sprintf(o->cname, "c%u", i);
1597
sprintf(o->sname, "s%u", i);
1601
errorPrint("2more than %d cursors opend concurrently", NUMCURSORS);
1602
return 0; /* make the compiler happy */
1603
} /* findNewCursor */
1605
/* dereference an existing cursor */
1606
static struct OCURS *findCursor(int cid)
1609
if(cid < 6000 || cid >= 6000+NUMCURSORS)
1610
errorPrint("2cursor number %d is out of range", cid);
1613
if(o->flag == CURSOR_NONE)
1614
errorPrint("2cursor %d is not currently active", cid);
1615
rv_numRets = o->numRets;
1616
memcpy(rv_type, o->rv_type, NUMRETS);
1620
/* part of the disconnect() procedure */
1621
static void clearAllCursors(void)
1626
for(i=0, o=ocurs; i<NUMCURSORS; ++i, ++o) {
1627
if(o->flag == CURSOR_NONE) continue;
1628
o->flag = CURSOR_NONE;
1631
for(j=0; j<o->alloc; ++j)
1636
} /* loop over cursors */
1640
} /* clearAllCursors */
1643
/*********************************************************************
1644
Connect and disconect to SQL databases.
1645
*********************************************************************/
1647
void sql_connect(const char *db, const char *login, const char *pw)
1650
* $char *dblocal = (char*)db;
1652
#line 637 "dbinfx.ec"
1653
char *dblocal = (char*)db;
1654
login = pw = 0; /* not used here, so make the compiler happy */
1655
if(isnullstring(dblocal)) {
1656
dblocal = getenv("DBNAME");
1657
if(isnullstring(dblocal))
1658
errorPrint("2sql_connect receives no database, check $DBNAME");
1662
stmt_text = "disconnect";
1665
* $disconnect current;
1667
#line 648 "dbinfx.ec"
1669
#line 648 "dbinfx.ec"
1670
sqli_connect_close(3, (char *)0, 0, 0);
1671
#line 648 "dbinfx.ec"
1677
stmt_text = "connect";
1680
* $connect to :dblocal;
1682
#line 655 "dbinfx.ec"
1684
#line 655 "dbinfx.ec"
1685
sqli_connect_open(ESQLINTVERSION, 0, dblocal, (char *)0, (ifx_conn_t *)0, 0);
1686
#line 655 "dbinfx.ec"
1688
if(errorTrap()) return;
1689
sql_database = dblocal;
1691
/* set default lock mode and isolation level for transaction management */
1692
stmt_text = "lock isolation";
1695
* $ set lock mode to wait;
1697
#line 662 "dbinfx.ec"
1699
#line 662 "dbinfx.ec"
1700
static const char *sqlcmdtxt[] =
1701
#line 662 "dbinfx.ec"
1703
#line 662 "dbinfx.ec"
1704
"set lock mode to wait",
1707
#line 662 "dbinfx.ec"
1708
static ifx_statement_t _SQ0 = {0};
1709
#line 662 "dbinfx.ec"
1710
sqli_stmt(ESQLINTVERSION, &_SQ0, sqlcmdtxt, 0, (ifx_sqlvar_t *)0, (struct value *)0, (ifx_literal_t *)0, (ifx_namelist_t *)0, (ifx_cursor_t *)0, -1, 0, 0);
1711
#line 662 "dbinfx.ec"
1719
* $ set isolation to committed read;
1721
#line 668 "dbinfx.ec"
1723
#line 668 "dbinfx.ec"
1724
static const char *sqlcmdtxt[] =
1725
#line 668 "dbinfx.ec"
1727
#line 668 "dbinfx.ec"
1728
"set isolation to committed read",
1731
#line 668 "dbinfx.ec"
1732
static ifx_statement_t _SQ0 = {0};
1733
#line 668 "dbinfx.ec"
1734
sqli_stmt(ESQLINTVERSION, &_SQ0, sqlcmdtxt, 0, (ifx_sqlvar_t *)0, (struct value *)0, (ifx_literal_t *)0, (ifx_namelist_t *)0, (ifx_cursor_t *)0, -1, 0, 0);
1735
#line 668 "dbinfx.ec"
1737
if(errorTrap()) goto abort;
1741
void sql_disconnect(void)
1744
stmt_text = "disconnect";
1747
* $disconnect current;
1749
#line 678 "dbinfx.ec"
1751
#line 678 "dbinfx.ec"
1752
sqli_connect_close(3, (char *)0, 0, 0);
1753
#line 678 "dbinfx.ec"
1759
} /* sql_disconnect */
1761
/* make sure we're connected to a database */
1762
static void checkConnect(void)
1765
errorPrint("2SQL command issued, but no database selected");
1766
} /* checkConnect */
1769
/*********************************************************************
1770
Begin, commit, and abort transactions.
1771
SQL does not permit nested transactions; this API does, to a limited degree.
1772
An inner transaction cannot fail while an outer one succeeds;
1773
that would require SQL support which is not forthcoming.
1774
However, as long as all transactions succeed, or the outer most fails,
1775
everything works properly.
1776
The static variable transLevel holds the number of nested transactions.
1777
*********************************************************************/
1779
/* begin a transaction */
1780
void sql_begTrans(void)
1784
stmt_text = "begin work";
1786
/* count the nesting level of transactions. */
1792
#line 713 "dbinfx.ec"
1794
#line 713 "dbinfx.ec"
1795
sqli_trans_begin2((mint)1);
1796
#line 713 "dbinfx.ec"
1798
if(errorTrap()) return;
1802
} /* sql_begTrans */
1804
/* end a transaction */
1805
static void endTrans(bool commit)
1811
errorPrint("2end transaction without a matching begTrans()");
1815
stmt_text = "commit work";
1818
errorPrint("2Cannot commit a transaction around an aborted transaction");
1819
if(translevel == 0) {
1823
#line 736 "dbinfx.ec"
1825
#line 736 "dbinfx.ec"
1826
sqli_trans_commit();
1827
#line 736 "dbinfx.ec"
1829
if(ENGINE_ERRCODE) ++translevel;
1832
} else { /* success or failure */
1833
stmt_text = "rollback work";
1836
if(!translevel) { /* bottom level */
1840
#line 745 "dbinfx.ec"
1842
#line 745 "dbinfx.ec"
1843
sqli_trans_rollback();
1844
#line 745 "dbinfx.ec"
1846
if(ENGINE_ERRCODE) --translevel;
1850
} /* success or failure */
1852
/* At this point I will make a bold assumption --
1853
* that all cursors are declared with hold.
1854
* Hence they remain valid after the transaction is closed,
1855
* and we don't have to change any of the OCURS structures. */
1860
void sql_commitWork(void) { endTrans(true); }
1861
void sql_rollbackWork(void) { endTrans(false); }
1863
void sql_deferConstraints(void)
1866
errorPrint("2Cannot defer constraints unless inside a transaction");
1867
stmt_text = "defer constraints";
1870
* $set constraints all deferred;
1872
#line 769 "dbinfx.ec"
1874
#line 769 "dbinfx.ec"
1875
static const char *sqlcmdtxt[] =
1876
#line 769 "dbinfx.ec"
1878
#line 769 "dbinfx.ec"
1879
"set constraints all deferred",
1882
#line 769 "dbinfx.ec"
1883
static ifx_statement_t _SQ0 = {0};
1884
#line 769 "dbinfx.ec"
1885
sqli_stmt(ESQLINTVERSION, &_SQ0, sqlcmdtxt, 0, (ifx_sqlvar_t *)0, (struct value *)0, (ifx_literal_t *)0, (ifx_namelist_t *)0, (ifx_cursor_t *)0, -1, 0, 0);
1886
#line 769 "dbinfx.ec"
1890
} /* sql_deferConstraints */
1893
/*********************************************************************
1894
Blob management routines, a somewhat awkward interface.
1895
Global variables tell SQL where to unload the next fetched blob:
1896
either a file (truncate or append) or an allocated chunk of memory.
1897
This assumes each fetch or select statement retrieves at most one blob.
1898
Since there is no %blob directive in lineFormat(),
1899
one cannot simply slip a blob in with the rest of the data as a row is
1900
updated or inserted. Instead the row must be created first,
1901
then the blob is entered separately, using blobInsert().
1902
This means every blob column must permit nulls, at least within the schema.
1903
Also, what use to be an atomic insert might become a multi-statement
1904
transaction if data integrity is important.
1905
Future versions of our line formatting software may support a %blob directive,
1906
which makes sense only when the formatted string is destined for SQL.
1907
*********************************************************************/
1909
/* information about the blob being fetched */
1910
const char *rv_blobFile;
1912
void *rv_blobLoc; /* location of blob in memory */
1913
int rv_blobSize; /* size of blob in bytes */
1914
static loc_t blobstruct; /* Informix structure to manage the blob */
1916
/* insert a blob into the database */
1917
void sql_blobInsert(const char *tabname, const char *colname, int rowid,
1918
const char *filename, void *offset, int length)
1921
* $char blobcmd[100];
1923
#line 802 "dbinfx.ec"
1928
#line 803 "dbinfx.ec"
1931
/* basic sanity checks */
1933
if(isnullstring(tabname)) errorPrint("2blobInsert, missing table name");
1934
if(isnullstring(colname)) errorPrint("2blobInsert, missing column name");
1935
if(rowid <= 0) errorPrint("2invalid rowid in blobInsert");
1936
if(length < 0) errorPrint("2invalid length in blobInsert");
1937
if(strlen(tabname) + strlen(colname) + 42 >= sizeof(blobcmd))
1938
errorPrint("2internal blobInsert command too long");
1940
/* set up the blob structure */
1941
memset(&insblob, 0, sizeof(insblob));
1943
insblob.loc_loctype = LOCMEMORY;
1945
if(length == 0) offset = 0;
1947
if(!offset) length = -1;
1948
insblob.loc_buffer = offset;
1949
insblob.loc_bufsize = length;
1950
insblob.loc_size = length;
1951
if(!offset) insblob.loc_indicator = -1;
1953
insblob.loc_loctype = LOCFNAME;
1954
insblob.loc_fname = (char*)filename;
1955
insblob.loc_oflags = LOC_RONLY;
1956
insblob.loc_size = -1;
1959
/* set up the blob insert command, using one host variable */
1960
sprintf(blobcmd, "update %s set %s = ? where rowid = %d",
1961
tabname, colname, rowid);
1962
stmt_text = blobcmd;
1965
* $prepare blobinsert from :blobcmd;
1967
#line 838 "dbinfx.ec"
1969
#line 838 "dbinfx.ec"
1970
sqli_prep(ESQLINTVERSION, _Cn1, blobcmd,(ifx_literal_t *)0, (ifx_namelist_t *)0, -1, 0, 0 );
1971
#line 838 "dbinfx.ec"
1973
if(errorTrap()) return;
1975
* $execute blobinsert using :insblob;
1977
#line 840 "dbinfx.ec"
1979
#line 840 "dbinfx.ec"
1980
static ifx_sqlvar_t _sqibind[] =
1982
{ 113, sizeof(insblob), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
1983
#line 840 "dbinfx.ec"
1985
static ifx_sqlda_t _SD0 = { 1, _sqibind, {0}, 1, 0 };
1986
#line 840 "dbinfx.ec"
1987
_sqibind[0].sqldata = (char *) &insblob;
1988
sqli_exec(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, _Cn1, 769), &_SD0, (char *)0, (struct value *)0, (ifx_sqlda_t *)0, (char *)0, (struct value *)0, 0);
1989
#line 840 "dbinfx.ec"
1992
rv_lastNrows = sqlca.sqlerrd[2];
1993
rv_lastRowid = sqlca.sqlerrd[5];
1994
if(sql_debug) appendFile(sql_debuglog, "%d rows affected", rv_lastNrows);
1996
} /* sql_blobInsert */
1999
/*********************************************************************
2000
When an SQL statement is prepared, the engine tells us the types and lengths
2001
of the columns. Use this information to "normalize" the sqlda
2002
structure, so that columns are fetched using our preferred formats.
2003
For instance, smallints and ints both map into int variables,
2004
varchars become chars, dates map into strings (so that we can convert
2005
them into our own vendor-independent binary representations later), etc.
2006
We assume the number and types of returns have been established.
2007
Once retsSetup has "normalized" the sqlda structure,
2008
run the select or fetch, and then call retsCleanup to post-process the data.
2009
This will, for example, turn dates, fetched into strings,
2010
into our own 4-byte representations.
2011
The same for time intervals, money, etc.
2012
*********************************************************************/
2014
/* Arrays that hold the return values from a select statement. */
2015
int rv_numRets; /* number of returned values */
2016
char rv_type[NUMRETS+1]; /* datatypes of returned values */
2017
char rv_name[NUMRETS+1][COLNAMELEN]; /* column names */
2018
LF rv_data[NUMRETS]; /* the returned values */
2019
int rv_lastNrows, rv_lastRowid, rv_lastSerial;
2020
/* Temp area to read the Informix values, as strings */
2021
static char retstring[NUMRETS][STRINGLEN+4];
2022
static va_list sqlargs;
2024
static void retsSetup(struct sqlda *desc)
2027
bool blobpresent = false;
2028
struct sqlvar_struct *v;
2030
for(i=0; (unsigned)i< NUMRETS; ++i) {
2031
rv_data[i].l = nullint;
2032
retstring[i][0] = 0;
2037
for(i=0,v=desc->sqlvar; i<rv_numRets; ++i,++v ) {
2038
strncpy(rv_name[i], v->sqlname, COLNAMELEN);
2039
switch(rv_type[i]) {
2044
v->sqltype = CCHARTYPE;
2045
v->sqllen = STRINGLEN+2;
2046
v->sqldata = retstring[i];
2047
rv_data[i].ptr = retstring[i];
2051
v->sqltype = CINTTYPE;
2053
v->sqldata = (char *) &rv_data[i].l;
2058
v->sqltype = CDOUBLETYPE;
2060
v->sqldata = (char*) &rv_data[i].f;
2061
rv_data[i].f = nullfloat;
2067
errorPrint("2Cannot select more than one blob at a time");
2069
v->sqltype = CLOCATORTYPE;
2070
v->sqllen = sizeof(blobstruct);
2071
v->sqldata = (char*) &blobstruct;
2072
memset(&blobstruct, 0, sizeof(blobstruct));
2074
blobstruct.loc_loctype = LOCMEMORY;
2075
blobstruct.loc_mflags = LOC_ALLOC;
2076
blobstruct.loc_bufsize = -1;
2078
blobstruct.loc_loctype = LOCFNAME;
2079
blobstruct.loc_fname = (char*)rv_blobFile;
2080
blobstruct.lc_union.lc_file.lc_mode = 0600;
2081
blobstruct.loc_oflags =
2082
(rv_blobAppend ? LOC_WONLY|LOC_APPEND : LOC_WONLY);
2087
errorPrint("@bad character %c in retsSetup", rv_type[i]);
2089
} /* loop over fetched columns */
2092
/* clean up fetched values, eg. convert date to our proprietary format. */
2093
static void retsCleanup(void)
2098
/* no blobs unless proven otherwise */
2100
rv_blobSize = nullint;
2102
for(i=0; i<rv_numRets; ++i) {
2103
clipString(retstring[i]);
2104
switch(rv_type[i]) {
2107
if(retstring[i][4] == '-') yearfirst = true;
2108
rv_data[i].l = stringDate(retstring[i],yearfirst);
2112
/* thanks to stringTime(), this works for either hh:mm or hh:mm:ss */
2113
if(retstring[i][0] == 0) rv_data[i].l = nullint;
2115
/* convert space to 0 */
2116
if(retstring[i][1] == ' ') retstring[i][1] = '0';
2117
/* skip the leading space that is produced when Informix converts interval to string */
2118
rv_data[i].l = stringTime(retstring[i]+1);
2123
rv_data[i].l = retstring[i][0];
2128
/* null floats look different from null dates and ints. */
2129
if(rv_data[i].l == 0xffffffff) {
2130
rv_data[i].f = nullfloat;
2131
if(rv_type[i] == 'M') rv_data[i].l = nullint;
2134
/* represent monitary amounts as an integer number of pennies. */
2135
if(rv_type[i] == 'M')
2136
rv_data[i].l = rv_data[i].f * 100.0 + 0.5;
2140
/* map the empty string into the null string */
2141
l = strlen(retstring[i]);
2142
if(!l) rv_data[i].ptr = 0;
2143
if(l > STRINGLEN) errorPrint("2fetched string is too long, limit %d chars", STRINGLEN);
2148
if(blobstruct.loc_indicator >= 0) { /* not null blob */
2149
rv_blobSize = blobstruct.loc_size;
2150
if(!rv_blobFile) rv_blobLoc = blobstruct.loc_buffer;
2151
if(rv_blobSize == 0) { /* turn empty blob into null blob */
2154
rv_blobSize = nullint;
2157
rv_data[i].l = rv_blobSize;
2161
/* Convert from Informix null to our nullint */
2162
if(rv_data[i].l == 0x80000000) rv_data[i].l = nullint;
2166
errorPrint("@bad character %c in retsCleanup", rv_type[i]);
2167
} /* switch on datatype */
2168
} /* loop over columsn fetched */
2171
void retsCopy(bool allstrings, void *first, ...)
2177
errorPrint("@calling retsCopy() with no returns pending");
2179
for(i=0; i<rv_numRets; ++i) {
2182
va_start(sqlargs, first);
2185
q = va_arg(sqlargs, void*);
2188
if((int)q < 1000 && (int)q > -1000)
2189
errorPrint("2retsCopy, pointer too close to 0");
2191
if(allstrings) *(char*)q = 0;
2193
if(rv_type[i] == 'S') {
2196
strcpy(q, rv_data[i].ptr);
2197
} else if(rv_type[i] == 'C') {
2198
*(char *)q = rv_data[i].l;
2199
if(allstrings) ((char*)q)[1] = 0;
2200
} else if(rv_type[i] == 'F') {
2202
if(isnotnullfloat(rv_data[i].f)) sprintf(q, "%lf", rv_data[i].f);
2204
*(double *)q = rv_data[i].f;
2206
} else if(allstrings) {
2207
char type = rv_type[i];
2208
long l = rv_data[i].l;
2211
strcpy(q, dateString(l, DTDELIMIT));
2212
} else if(type == 'I') {
2213
strcpy(q, timeString(l, DTDELIMIT));
2214
} else if(type == 'M') {
2215
sprintf(q, "%ld.%02d", l/100, l%100);
2216
} else sprintf(q, "%ld", l);
2219
*(long *)q = rv_data[i].l;
2221
} /* loop over result parameters */
2223
if(!first) va_end(sqlargs);
2226
/* convert column name into column index */
2227
int findcol_byname(const char *name)
2230
for(i=0; rv_name[i][0]; ++i)
2231
if(stringEqual(name, rv_name[i])) break;
2233
errorPrint("2Column %s not found in the columns or aliases of your select statement", name);
2235
} /* findcol_byname */
2237
/* make sure we got one return value, and it is integer compatible */
2238
static long oneRetValue(void)
2240
char coltype = rv_type[0];
2241
long n = rv_data[0].l;
2243
errorPrint("2SQL statement has %d return values, 1 value expected", rv_numRets);
2244
if(!strchr("MNFDIC", coltype))
2245
errorPrint("2SQL statement returns a value whose type is not compatible with a 4-byte integer");
2246
if(coltype == 'F') n = rv_data[0].f;
2251
/*********************************************************************
2252
Prepare a formatted SQL statement.
2253
Gather the types and names of the fetched columns and make this information
2254
available to the rest of the C routines in this file, and to the application.
2255
Returns the populated sqlda structure for the statement.
2256
Returns null if the prepare failed.
2257
*********************************************************************/
2259
static struct sqlda *prepare(const char *stmt_parm, const char *sname_parm)
2262
* $char*stmt = (char*)stmt_parm;
2264
#line 1111 "dbinfx.ec"
2265
char *stmt = (char*)stmt_parm;
2267
* $char*sname = (char*)sname_parm;
2269
#line 1112 "dbinfx.ec"
2270
char *sname = (char*)sname_parm;
2272
struct sqlvar_struct *v;
2276
if(isnullstring(stmt)) errorPrint("2null SQL statement");
2280
/* look for delete with no where clause */
2281
while(*stmt == ' ') ++stmt;
2282
if(!strncmp(stmt, "delete", 6) || !strncmp(stmt, "update", 6))
2283
/* delete or update */
2284
if(!strstr(stmt, "where") && !strstr(stmt, "WHERE")) {
2286
errorPrint("2Old Mcdonald bug");
2289
/* set things up to nulls, in case the prepare fails */
2292
memset(rv_type, 0, NUMRETS);
2293
rv_lastNrows = rv_lastRowid = rv_lastSerial = 0;
2296
* $prepare :sname from :stmt;
2298
#line 1137 "dbinfx.ec"
2300
#line 1137 "dbinfx.ec"
2301
sqli_prep(ESQLINTVERSION, sname, stmt,(ifx_literal_t *)0, (ifx_namelist_t *)0, -1, 0, 0 );
2302
#line 1137 "dbinfx.ec"
2304
if(errorTrap()) return 0;
2306
/* gather types and column headings */
2308
* $describe: sname into desc;
2310
#line 1141 "dbinfx.ec"
2312
#line 1141 "dbinfx.ec"
2313
sqli_describe_stmt(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, sname, 257), &desc, 0);
2314
#line 1141 "dbinfx.ec"
2316
if(!desc) errorPrint("2$describe couldn't allocate descriptor");
2317
rv_numRets = desc->sqld;
2318
if(rv_numRets > NUMRETS) {
2320
errorPrint("2cannot select more than %d values", NUMRETS);
2323
for(i=0,v=desc->sqlvar; i<rv_numRets; ++i,++v ) {
2324
coltype = v->sqltype & SQLTYPE;
2325
/* kludge, count(*) should be int, not float, in my humble opinion */
2326
if(stringEqual(v->sqlname, "(count(*))"))
2338
/* We only process datetime year to minute, for databases
2339
* other than Informix, which don't have a date type. */
2340
if(v->sqllen != 5) errorPrint("2datetime field must be year to minute");
2375
errorPrint ("@Unknown informix sql datatype %d", coltype);
2376
} /* switch on type */
2377
} /* loop over returns */
2384
/*********************************************************************
2385
Run an SQL statement internally, and gather any fetched values.
2386
This statement stands alone; it fetches at most one row.
2387
You might simply know this, perhaps because of a unique key,
2388
or you might be running a stored procedure.
2389
For efficiency we do not look for a second row, so this is really
2390
like the "select first" construct that some databases support.
2391
A mode variable says whether execution or selection or both are allowed.
2392
Return true if data was successfully fetched.
2393
*********************************************************************/
2395
static bool execInternal(const char *stmt, int mode)
2399
* $static char singlestatement[] = "single_use_stmt";
2401
#line 1224 "dbinfx.ec"
2402
static char singlestatement[] = "single_use_stmt";
2404
* $static char singlecursor[] = "single_use_cursor";
2406
#line 1225 "dbinfx.ec"
2407
static char singlecursor[] = "single_use_cursor";
2409
bool notfound = false;
2410
short errorcode = 0;
2412
desc = prepare(stmt, singlestatement);
2413
if(!desc) return false; /* error */
2418
errorPrint("2SQL select statement returns no values");
2421
* $execute :singlestatement;
2423
#line 1238 "dbinfx.ec"
2425
#line 1238 "dbinfx.ec"
2426
sqli_exec(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, singlestatement, 257), (ifx_sqlda_t *)0, (char *)0, (struct value *)0, (ifx_sqlda_t *)0, (char *)0, (struct value *)0, 0);
2427
#line 1238 "dbinfx.ec"
2430
} else { /* end no return values */
2434
errorPrint("2SQL statement returns %d values", rv_numRets);
2437
* $execute: singlestatement into descriptor desc;
2439
#line 1246 "dbinfx.ec"
2441
#line 1246 "dbinfx.ec"
2442
sqli_exec(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, singlestatement, 257), (ifx_sqlda_t *)0, (char *)0, (struct value *)0, desc, (char *)0, (struct value *)0, 0);
2443
#line 1246 "dbinfx.ec"
2448
errorcode = rv_vendorStatus;
2450
/* select or execute ran properly */
2451
/* error 100 means not found in Informix */
2452
if(ENGINE_ERRCODE == 100) notfound = true;
2453
/* set "last" parameters, in case the application is interested */
2454
rv_lastNrows = sqlca.sqlerrd[2];
2455
rv_lastRowid = sqlca.sqlerrd[5];
2456
rv_lastSerial = sqlca.sqlerrd[1];
2457
} /* successful run */
2460
* $free :singlestatement;
2462
#line 1261 "dbinfx.ec"
2464
#line 1261 "dbinfx.ec"
2465
sqli_curs_free(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, singlestatement, 258));
2466
#line 1261 "dbinfx.ec"
2474
rv_vendorStatus = errorcode;
2475
rv_lastStatus = errTranslate(rv_vendorStatus);
2481
} /* execInternal */
2484
/*********************************************************************
2485
Run individual select or execute statements, using the above internal routine.
2486
*********************************************************************/
2488
/* pointer to vararg list; most of these are vararg functions */
2489
/* execute a stand-alone statement with no % formatting of the string */
2490
void sql_execNF(const char *stmt)
2492
execInternal(stmt, 1);
2495
/* execute a stand-alone statement with % formatting */
2496
void sql_exec(const char *stmt, ...)
2498
va_start(sqlargs, stmt);
2499
stmt = lineFormatStack(stmt, 0, &sqlargs);
2500
execInternal(stmt, 1);
2504
/* run a select statement with no % formatting of the string */
2505
/* return true if the row was found */
2506
bool sql_selectNF(const char *stmt, ...)
2509
va_start(sqlargs, stmt);
2510
rc = execInternal(stmt, 2);
2513
} /* sql_selectNF */
2515
/* run a select statement with % formatting */
2516
bool sql_select(const char *stmt, ...)
2519
va_start(sqlargs, stmt);
2520
stmt = lineFormatStack(stmt, 0, &sqlargs);
2521
rc = execInternal(stmt, 2);
2526
/* run a select statement with one return value */
2527
int sql_selectOne(const char *stmt, ...)
2530
va_start(sqlargs, stmt);
2531
stmt = lineFormatStack(stmt, 0, &sqlargs);
2532
rc = execInternal(stmt, 2);
2533
if(!rc) { va_end(sqlargs); return nullint; }
2534
return oneRetValue();
2535
} /* sql_selectOne */
2537
/* run a stored procedure with no % formatting */
2538
static bool sql_procNF(const char *stmt)
2541
char *s = allocMem(20+strlen(stmt));
2542
strcpy(s, "execute procedure ");
2544
rc = execInternal(s, 3);
2545
/* if execInternal doesn't return, we have a memory leak */
2550
/* run a stored procedure */
2551
bool sql_proc(const char *stmt, ...)
2554
va_start(sqlargs, stmt);
2555
stmt = lineFormatStack(stmt, 0, &sqlargs);
2556
rc = sql_procNF(stmt);
2557
if(rv_numRets) retsCopy(false, 0);
2561
/* run a stored procedure with one return */
2562
int sql_procOne(const char *stmt, ...)
2565
va_start(sqlargs, stmt);
2566
stmt = lineFormatStack(stmt, 0, &sqlargs);
2567
rc = sql_procNF(stmt);
2568
if(!rc) { va_end(sqlargs); return 0; }
2569
return oneRetValue();
2573
/*********************************************************************
2574
Prepare, open, close, and free SQL cursors.
2575
*********************************************************************/
2577
/* prepare a cursor; return the ID number of that cursor */
2578
static int prepareCursor(const char *stmt, bool scrollflag)
2581
* $char *internal_sname, *internal_cname;
2583
#line 1374 "dbinfx.ec"
2584
char *internal_sname, *internal_cname;
2585
struct OCURS *o = findNewCursor();
2587
stmt = lineFormatStack(stmt, 0, &sqlargs);
2589
internal_sname = o->sname;
2590
internal_cname = o->cname;
2591
o->desc = prepare(stmt, internal_sname);
2592
if(!o->desc) return -1;
2593
if(o->desc->sqld == 0) {
2595
errorPrint("2statement passed to sql_prepare has no returns");
2598
/* declare with hold;
2599
* you might run transactions within this cursor. */
2602
* $declare :internal_cname scroll cursor with hold for :internal_sname;
2604
#line 1391 "dbinfx.ec"
2606
#line 1391 "dbinfx.ec"
2607
sqli_curs_decl_dynm(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, internal_cname, 0), internal_cname, sqli_curs_locate(ESQLINTVERSION, internal_sname, 1), 4128, 0);
2608
#line 1391 "dbinfx.ec"
2612
* $declare :internal_cname cursor with hold for :internal_sname;
2614
#line 1393 "dbinfx.ec"
2616
#line 1393 "dbinfx.ec"
2617
sqli_curs_decl_dynm(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, internal_cname, 0), internal_cname, sqli_curs_locate(ESQLINTVERSION, internal_sname, 1), 4096, 0);
2618
#line 1393 "dbinfx.ec"
2625
o->numRets = rv_numRets;
2626
memcpy(o->rv_type, rv_type, NUMRETS);
2627
o->flag = CURSOR_PREPARED;
2628
o->fl = 0; /* just to make sure */
2630
} /* prepareCursor */
2632
int sql_prepare(const char *stmt, ...)
2635
va_start(sqlargs, stmt);
2636
n = prepareCursor(stmt, false);
2641
int sql_prepareScrolling(const char *stmt, ...)
2644
va_start(sqlargs, stmt);
2645
n = prepareCursor(stmt, true);
2648
} /* sql_prepareScrolling */
2650
void sql_open(int cid)
2654
* $char *internal_sname, *internal_cname;
2656
#line 1427 "dbinfx.ec"
2657
char *internal_sname, *internal_cname;
2658
struct OCURS *o = findCursor(cid);
2659
if(o->flag == CURSOR_OPENED)
2660
errorPrint("2cannot open cursor %d, already opened", cid);
2661
internal_sname = o->sname;
2662
internal_cname = o->cname;
2665
* $open :internal_cname;
2667
#line 1434 "dbinfx.ec"
2669
#line 1434 "dbinfx.ec"
2670
sqli_curs_open(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, internal_cname, 256), (ifx_sqlda_t *)0, (char *)0, (struct value *)0, 0, 0);
2671
#line 1434 "dbinfx.ec"
2673
if(!errorTrap()) o->flag = CURSOR_OPENED;
2676
for(i=0; i<o->alloc; ++i) {
2683
int sql_prepOpen(const char *stmt, ...)
2686
va_start(sqlargs, stmt);
2687
n = prepareCursor(stmt, false);
2691
short ev = rv_vendorStatus;
2692
short el = rv_lastStatus;
2694
rv_vendorStatus = ev;
2699
} /* sql_prepOpen */
2701
void sql_close(int cid)
2704
* $char *internal_sname, *internal_cname;
2706
#line 1465 "dbinfx.ec"
2707
char *internal_sname, *internal_cname;
2708
struct OCURS *o = findCursor(cid);
2709
if(o->flag < CURSOR_OPENED)
2710
errorPrint("2cannot close cursor %d, not yet opened", cid);
2711
internal_cname = o->cname;
2712
debugExtra("close");
2714
* $close :internal_cname;
2716
#line 1471 "dbinfx.ec"
2718
#line 1471 "dbinfx.ec"
2719
sqli_curs_close(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, internal_cname, 256));
2720
#line 1471 "dbinfx.ec"
2722
if(errorTrap()) return;
2723
o->flag = CURSOR_PREPARED;
2727
void sql_free( int cid)
2730
* $char *internal_sname, *internal_cname;
2732
#line 1479 "dbinfx.ec"
2733
char *internal_sname, *internal_cname;
2734
struct OCURS *o = findCursor(cid);
2735
if(o->flag == CURSOR_OPENED)
2736
errorPrint("2cannot free cursor %d, not yet closed", cid);
2737
internal_sname = o->sname;
2740
* $free :internal_sname;
2742
#line 1485 "dbinfx.ec"
2744
#line 1485 "dbinfx.ec"
2745
sqli_curs_free(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, internal_sname, 258));
2746
#line 1485 "dbinfx.ec"
2748
if(errorTrap()) return;
2749
o->flag = CURSOR_NONE;
2752
memset(rv_name, 0, sizeof(rv_name));
2753
memset(rv_type, 0, sizeof(rv_type));
2754
if(o->fl) { /* free any cached lines */
2756
for(i=0; i<o->alloc; ++i)
2765
void sql_closeFree(int cid)
2767
const short *exc = exclist;
2769
if(!rv_lastStatus) {
2773
} /* sql_closeFree */
2775
/* fetch row n from the open cursor.
2776
* Flag can be used to fetch first, last, next, or previous. */
2777
bool fetchInternal(int cid, long n, int flag, bool remember)
2780
* $char *internal_sname, *internal_cname;
2782
#line 1517 "dbinfx.ec"
2783
char *internal_sname, *internal_cname;
2785
* $long nextrow, lastrow;
2787
#line 1518 "dbinfx.ec"
2788
long nextrow, lastrow;
2789
struct sqlda *internal_desc;
2790
struct OCURS *o = findCursor(cid);
2792
internal_cname = o->cname;
2793
internal_desc = o->desc;
2794
retsSetup(internal_desc);
2796
/* don't do the fetch if we're looking for row 0 absolute,
2797
* that just nulls out the return values */
2798
if(flag == 6 && !n) {
2806
lastrow = nextrow = o->rownum;
2807
if(flag == 6) nextrow = n;
2808
if(flag == 3) nextrow = 1;
2809
if(isnotnull(lastrow)) { /* we haven't lost track yet */
2810
if(flag == 1) ++nextrow;
2811
if(flag == 2 && nextrow) --nextrow;
2813
if(flag == 4) { /* fetch the last row */
2814
nextrow = nullint; /* we just lost track */
2815
if(o->fl && o->flag == CURSOR_PREPARED) {
2816
/* I'll assume you've read in all the rows, cursor is closed */
2817
for(nextrow=o->alloc-1; nextrow>=0; --nextrow)
2818
if(o->fl[nextrow]) break;
2823
if(!nextrow) goto fetchZero;
2825
/* see if we have cached this row */
2826
if(isnotnull(nextrow) && o->fl &&
2827
nextrow <= o->alloc && o->fl[nextrow-1]) {
2828
sql_mkload(o->fl[nextrow-1], '\177');
2829
/* don't run retsCleanup() here */
2831
rv_blobSize = nullint;
2832
o->rownum = nextrow;
2835
} /* bringing row out of cache */
2837
if(o->flag != CURSOR_OPENED)
2838
errorPrint("2cannot fetch from cursor %d, not yet opened", cid);
2840
/* The next line of code is very subtle.
2841
I use to declare all cursors as scroll cursors.
2842
It's a little inefficient, but who cares.
2843
Then I discovered you can't fetch blobs from scroll cursors.
2844
You can however fetch them from regular cursors,
2845
even with an order by clause.
2846
So cursors became non-scrolling by default.
2847
If the programmer chooses to fetch by absolute number,
2848
but he is really going in sequence, I turn them into
2849
fetch-next statements, so that the cursor need not be a scroll cursor. */
2851
isnotnull(lastrow) && isnotnull(nextrow) &&
2852
nextrow == lastrow+1)
2855
debugExtra("fetch");
2860
* $fetch :internal_cname using descriptor internal_desc;
2862
#line 1589 "dbinfx.ec"
2864
#line 1589 "dbinfx.ec"
2865
static _FetchSpec _FS0 = { 0, 1, 0 };
2866
sqli_curs_fetch(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, internal_cname, 256), (ifx_sqlda_t *)0, internal_desc, (char *)0, &_FS0);
2867
#line 1589 "dbinfx.ec"
2872
* $fetch previous :internal_cname using descriptor internal_desc;
2874
#line 1592 "dbinfx.ec"
2876
#line 1592 "dbinfx.ec"
2877
static _FetchSpec _FS0 = { 0, 2, 0 };
2878
sqli_curs_fetch(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, internal_cname, 256), (ifx_sqlda_t *)0, internal_desc, (char *)0, &_FS0);
2879
#line 1592 "dbinfx.ec"
2884
* $fetch first :internal_cname using descriptor internal_desc;
2886
#line 1595 "dbinfx.ec"
2888
#line 1595 "dbinfx.ec"
2889
static _FetchSpec _FS0 = { 0, 3, 0 };
2890
sqli_curs_fetch(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, internal_cname, 256), (ifx_sqlda_t *)0, internal_desc, (char *)0, &_FS0);
2891
#line 1595 "dbinfx.ec"
2896
* $fetch last :internal_cname using descriptor internal_desc;
2898
#line 1598 "dbinfx.ec"
2900
#line 1598 "dbinfx.ec"
2901
static _FetchSpec _FS0 = { 0, 4, 0 };
2902
sqli_curs_fetch(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, internal_cname, 256), (ifx_sqlda_t *)0, internal_desc, (char *)0, &_FS0);
2903
#line 1598 "dbinfx.ec"
2908
errorPrint("2sql fetches absolute row using null index");
2910
* $fetch absolute :nextrow :internal_cname using descriptor internal_desc;
2912
#line 1603 "dbinfx.ec"
2914
#line 1603 "dbinfx.ec"
2915
static ifx_sqlvar_t _sqibind[] =
2917
{ 103, sizeof(nextrow), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
2918
#line 1603 "dbinfx.ec"
2920
static ifx_sqlda_t _SD0 = { 1, _sqibind, {0}, 1, 0 };
2921
static _FetchSpec _FS1 = { 0, 6, 0 };
2922
#line 1603 "dbinfx.ec"
2923
_sqibind[0].sqldata = (char *) &nextrow;
2924
sqli_curs_fetch(ESQLINTVERSION, sqli_curs_locate(ESQLINTVERSION, internal_cname, 256), &_SD0, internal_desc, (char *)0, &_FS1);
2925
#line 1603 "dbinfx.ec"
2929
errorPrint("@fetchInternal() receives bad flag %d", flag);
2933
if(errorTrap()) return false;
2935
if(ENGINE_ERRCODE == 100) return false; /* not found */
2936
o->rownum = nextrow;
2938
/* remember the unload image of this line */
2940
sql_cursorUpdLine(cid, cloneString(sql_mkunld('\177')));
2942
} /* fetchInternal */
2944
bool sql_fetchFirst(int cid, ...)
2947
va_start(sqlargs, cid);
2948
rc = fetchInternal(cid, 0L, 3, false);
2951
} /* sql_fetchFirst */
2953
bool sql_fetchLast(int cid, ...)
2956
va_start(sqlargs, cid);
2957
rc = fetchInternal(cid, 0L, 4, false);
2960
} /* sql_fetchLast */
2962
bool sql_fetchNext(int cid, ...)
2965
va_start(sqlargs, cid);
2966
rc = fetchInternal(cid, 0L, 1, false);
2969
} /* sql_fetchNext */
2971
bool sql_fetchPrev(int cid, ...)
2974
va_start(sqlargs, cid);
2975
rc = fetchInternal(cid, 0L, 2, false);
2978
} /* sql_fetchPrev */
2980
bool sql_fetchAbs(int cid, long rownum, ...)
2983
va_start(sqlargs, rownum);
2984
rc = fetchInternal(cid, rownum, 6, false);
2987
} /* sql_fetchAbs */
2990
/* the inverse of sql_mkunld() */
2991
void sql_mkload(const char *line, char delim)
2997
for(i = 0, s = (char*)line; *s; ++i, *t=delim, s = t+1) {
2998
t = strchr(s, delim);
2999
if(!t) errorPrint("2sql load line does not end in a delimiter");
3002
errorPrint("2sql load line contains more than %d fields", rv_numRets);
3004
switch(rv_type[i]) {
3006
if(!*s) { data = nullint; break; }
3007
data = strtol(s, &s, 10);
3008
if(*s) errorPrint("2sql load, cannot convert string to integer");
3012
if((unsigned)strlen(s) > STRINGLEN)
3013
errorPrint("2sql load line has a string that is too long");
3014
strcpy(retstring[i], s);
3015
data = (int) retstring[i];
3020
rv_data[i].f = *s ? atof(s) : nullfloat;
3024
data = stringDate(s,0);
3026
errorPrint("2sql load, cannot convert string to date");
3032
errorPrint("2sql load, character field contains more than one character");
3036
data = stringTime(s);
3038
errorPrint("2sql load, cannot convert string to time interval");
3042
errorPrint("2sql load cannot convert into type %c", rv_type[i]);
3043
} /* switch on type */
3045
rv_data[i].l = data;
3046
} /* loop over fields in line */
3049
errorPrint("2sql load line contains %d fields, %d expected", i, rv_numRets);
3053
/*********************************************************************
3054
We maintain our own cache of fetched lines.
3056
After all, Informix already maintains a cache of fetched lines.
3057
That's what the open cursor is for.
3058
Looks like serious wheel reinvention to me.
3059
Perhaps, but you can't change the data in the cache that Informix maintains.
3060
This is something Powerbuild et al discovered over a decade ago.
3061
Consider a simple spreadsheet application.
3062
You update the value in one of the cells, thereby updating the row
3063
in the database. Now scroll down to the next page, and then back again.
3064
If you fetch from the open cursor you will get the old data, before the
3065
change was made, even though the new data is safely ensconsed in the database.
3066
Granted one could reopen the cursor and fetch the new data,
3067
but this can be slow for certain queries (sometimes a couple minutes).
3068
In other words, rebuilding the cursor is not really an option.
3069
So we are forced to retain a copy of the data in our program and change it
3070
whenever we update the database.
3071
Unfortunately the following 3 routines were developed separately,
3072
and they are wildly inconsistent. Some take a row number while
3073
others assume you are modifying the current row as stored in o->rownum.
3074
Some accept a line of tex, the unload image of the fetch data, while
3075
others build the line of text from the fetched data in rv_data[].
3076
I apologize for this confusion; clearly a redesign is called for.
3077
*********************************************************************/
3079
/* update the text of a fetched line,
3080
* so we get this same text again when we refetch the line later.
3081
* These text changes corespond to the database changes that form an update.
3082
* We assume the line has been allocated using malloc(). */
3083
void sql_cursorUpdLine(int cid, const char *line)
3085
struct OCURS *o = findCursor(cid);
3086
int n = o->rownum-1;
3089
errorPrint("2SQL cursor caches too many lines, limit %d", CACHELIMIT);
3092
/* running off the end, allocate 128 at a time */
3093
short oldalloc = o->alloc;
3096
o->fl = (char **) allocMem(o->alloc*sizeof(char*));
3098
o->fl = (char**) reallocMem((void*)o->fl, o->alloc*sizeof(char*));
3099
memset(o->fl+oldalloc, 0, (o->alloc-oldalloc)*sizeof(char*));
3100
} /* allocating more space */
3103
o->fl[n] = (char*)line;
3104
} /* sql_cursorUpdLine */
3106
void sql_cursorDelLine(int cid, int rownum)
3108
struct OCURS *o = findCursor(cid);
3111
if(rownum >= o->alloc || !o->fl[rownum])
3112
errorPrint("2cursorDelLine(%d)", rownum);
3113
nzFree(o->fl[rownum]);
3114
if(rownum < o->alloc-1)
3115
memcpy(o->fl+rownum, o->fl+rownum+1, (o->alloc-rownum-1)*sizeof(char *));
3116
o->fl[o->alloc-1] = 0;
3117
/* back up the row number if we deleted the last row */
3118
if(!o->fl[rownum]) --o->rownum;
3119
} /* sql_cursorDelLine */
3121
void sql_cursorInsLine(int cid, int rownum)
3123
struct OCURS *o = findCursor(cid);
3126
/* must insert a row within or immediately following the current data */
3127
if(rownum > o->alloc)
3128
errorPrint("2cursorInsLine(%d)", rownum);
3129
/* newly inserted row becomes the current row */
3130
o->rownum = rownum+1;
3132
if(!o->alloc || o->fl[o->alloc-1]) { /* need to make room */
3135
o->fl = (char **) allocMem(o->alloc*sizeof(char*));
3137
o->fl = (char**) reallocMem((void*)o->fl, o->alloc*sizeof(char*));
3138
memset(o->fl+o->alloc-128, 0, 128*sizeof(char*));
3139
} /* allocating more space */
3141
/* move the rest of the lines down */
3142
for(i=o->alloc-1; i>rownum; --i)
3143
o->fl[i] = o->fl[i-1];
3144
o->fl[i] = cloneString(sql_mkunld('\177'));
3145
} /* sql_cursorInsLine */
3148
/*********************************************************************
3149
run the analog of /bin/comm on two open cursors,
3150
rather than two Unix files.
3151
This assumes a common unique key that we use to sync up the rows.
3152
The cursors should be sorted by this key.
3153
*********************************************************************/
3156
const char *stmt1, const char *stmt2, /* the two select statements */
3157
const char *orderby, /* which fetched column is the unique key */
3158
fnptr f, /* call this function for differences */
3159
char delim) /* sql_mkunld() delimiter, or call mkinsupd if delim = 0 */
3161
short cid1, cid2; /* the cursor ID numbers */
3162
char *line1, *line2, *s; /* the two fetched rows */
3163
void *blob1, *blob2; /* one blob per table */
3164
int blob1size, blob2size;
3165
bool eof1, eof2, get1, get2;
3166
int sortval1, sortval2;
3167
char sortstring1[80], sortstring2[80];
3170
int passkey1, passkey2;
3171
static const char sortnull[] = "cursor_comm, sortval%d is null";
3172
static const char sortlong[] = "cursor_comm cannot key on strings longer than %d";
3173
static const char noblob[] = "sorry, cursor_comm cannot handle blobs yet";
3175
cid1 = sql_prepOpen(stmt1);
3176
cid2 = sql_prepOpen(stmt2);
3178
sortcol = findcol_byname(orderby);
3179
sorttype = rv_type[sortcol];
3180
if(charInList("NDIS", sorttype) < 0)
3181
errorPrint("2cursor_com(), column %s has bad type %c", orderby, sorttype);
3183
passkey1 = (int)sortstring1, passkey2 = (int)sortstring2;
3185
eof1 = eof2 = false;
3187
rv_blobFile = 0; /* in case the cursor has a blob */
3192
if(get1) { /* fetch first row */
3193
eof1 = !sql_fetchNext(cid1, 0);
3199
if(sorttype == 'S') {
3200
s = rv_data[sortcol].ptr;
3201
if(isnullstring(s)) errorPrint(sortnull, 1);
3202
if(strlen(s) >= sizeof(sortstring1))
3203
errorPrint(sortlong, sizeof(sortstring1));
3204
strcpy(sortstring1, s);
3206
passkey1 = sortval1 = rv_data[sortcol].l;
3207
if(isnull(sortval1))
3208
errorPrint(sortnull, 1);
3210
line1 = cloneString(delim ? sql_mkunld(delim) : sql_mkinsupd());
3213
blob1size = rv_blobSize;
3217
} /* looking for first line */
3219
if(get2) { /* fetch second row */
3220
eof2 = !sql_fetchNext(cid2, 0);
3226
if(sorttype == 'S') {
3227
s = rv_data[sortcol].ptr;
3228
if(isnullstring(s)) errorPrint(sortnull, 2);
3229
if(strlen(s) >= sizeof(sortstring2))
3230
errorPrint(sortlong, sizeof(sortstring2));
3231
strcpy(sortstring2, rv_data[sortcol].ptr);
3233
passkey2 = sortval2 = rv_data[sortcol].l;
3234
if(isnull(sortval2))
3235
errorPrint(sortnull, 2);
3237
line2 = cloneString(delim ? sql_mkunld(delim) : sql_mkinsupd());
3240
blob2size = rv_blobSize;
3244
} /* looking for second line */
3246
if(eof1 & eof2) break; /* done */
3247
get1 = get2 = false;
3249
/* in cid2, but not in cid1 */
3251
(sorttype == 'S' && strcmp(sortstring1, sortstring2) > 0 ||
3252
sorttype != 'S' && sortval1 > sortval2)) {
3253
(*f)('>', line1, line2, passkey2);
3258
/* in cid1, but not in cid2 */
3260
(sorttype == 'S' && strcmp(sortstring1, sortstring2) < 0 ||
3261
sorttype != 'S' && sortval1 < sortval2)) {
3262
(*f)('<', line1, line2, passkey1);
3268
/* perhaps the lines are equal */
3269
if(stringEqual(line1, line2)) continue;
3271
/* lines are different between the two cursors */
3272
(*f)('*', line1, line2, passkey2);
3273
} /* loop over parallel cursors */
3279
sql_closeFree(cid1);
3280
sql_closeFree(cid2);
3283
/*********************************************************************
3284
Get the primary key for a table.
3285
In informix, you can use system tables to get this information.
3286
There's a way to do it in odbc, but I don't remember.
3287
*********************************************************************/
3290
getPrimaryKey(char *tname, int *part1, int *part2)
3293
char *s = strchr(tname, ':');
3294
*part1 = *part2 = 0;
3296
rc = sql_select("select part1, part2 \
3297
from sysconstraints c, systables t, sysindexes i \
3298
where tabname = %S and t.tabid = c.tabid \
3299
and constrtype = 'P' and c.idxname = i.idxname",
3303
rc = sql_select("select part1, part2 \
3304
from %s:sysconstraints c, %s:systables t, %s:sysindexes i \
3305
where tabname = %S and t.tabid = c.tabid \
3306
and constrtype = 'P' and c.idxname = i.idxname",
3307
tname, tname, tname, s+1, &p1, &p2);
3310
if(rc) *part1 = p1, *part2 = p2;
3311
} /* getPrimaryKey */
3314
#line 1989 "dbinfx.ec"