1102
1103
exclist = list;
1103
1104
} /* 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 {
1106
/* map Informix errors to our own exception codes, as defined in dbapi.h. */
1107
static const struct ERRORMAP {
1164
1110
} errormap[] = {
2987
2878
} /* 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
2881
/*********************************************************************
3284
2882
Get the primary key for a table.
3285
2883
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.
2884
I haven't yet expanded this to a 3 part key.
3287
2885
*********************************************************************/
3290
getPrimaryKey(char *tname, int *part1, int *part2)
2888
getPrimaryKey(char *tname, int *part1, int *part2, int *part3)
3293
char *s = strchr(tname, ':');
3294
*part1 = *part2 = 0;
3296
rc = sql_select("select part1, part2 \
2891
char *s = strchr(tname, ':');
2892
*part1 = *part2 = *part3 = 0;
2894
rc = sql_select("select part1, part2 \
3297
2895
from sysconstraints c, systables t, sysindexes i \
3298
2896
where tabname = %S and t.tabid = c.tabid \
3299
and constrtype = 'P' and c.idxname = i.idxname",
3303
rc = sql_select("select part1, part2 \
2897
and constrtype = 'P' and c.idxname = i.idxname", tname, &p1, &p2);
2900
rc = sql_select("select part1, part2 \
3304
2901
from %s:sysconstraints c, %s:systables t, %s:sysindexes i \
3305
2902
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"
2903
and constrtype = 'P' and c.idxname = i.idxname", tname, tname, tname, s + 1, &p1, &p2);
2907
*part1 = p1, *part2 = p2;
2908
} /* getPrimaryKey */
2913
puts("Not implemented in Informix, but certainly doable through systables");
2916
/* This can also be done by catalogs; it's on my list of things to do. */
2918
fetchForeign(char *tname)
2921
} /* fetchForeign */
2923
#line 1598 "dbinfx.ec"