~ubuntu-branches/ubuntu/vivid/edbrowse/vivid

« back to all changes in this revision

Viewing changes to src/dbinfx.ec

  • Committer: Bazaar Package Importer
  • Author(s): Kapil Hari Paranjape
  • Date: 2009-03-01 16:55:12 UTC
  • mfrom: (1.1.5 upstream) (3.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090301165512-97yhte20cy3c4q2w
Tags: 3.4.1-1
* New upstream version (3.4.1).
* debian/rules:
  - add "touch build-stamp" to build-stamp target.
  - modify clean target to use clean target in src/makefile.
  - remove debian/edbrowse.1 in clean target.
  - added "-lcurl" to linker flags.
* debian/control:
  - added libcurl4-openssl-dev to Build-Depends
  - Standards Version 3.8.0. No changes required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
57
57
static bool badtrans;
58
58
 
59
59
/* Through globals, make error info available to the application. */
60
 
int rv_lastStatus, rv_vendorStatus, rv_stmtOffset;
 
60
int rv_lastStatus, rv_stmtOffset;
 
61
long rv_vendorStatus;
61
62
char *rv_badToken;
62
63
 
63
64
static void debugStatement(void)
90
91
        exclist = list;
91
92
} /* sql_exception */
92
93
 
93
 
/* text descriptions corresponding to our generic SQL error codes */
94
 
static char *sqlErrorList[] = {0,
95
 
        "miscelaneous SQL error",
96
 
        "syntax error in SQL statement",
97
 
        "filename cannot be used by SQL",
98
 
        "cannot convert/compare the columns/constants in the SQL statement",
99
 
        "bad string subscripting",
100
 
        "bad use of the rowid construct",
101
 
        "bad use of a blob column",
102
 
        "bad use of aggregate operators or columns",
103
 
        "bad use of a view",
104
 
        "bad use of a serial column",
105
 
        "bad use of a temp table",
106
 
        "operation cannot cross databases",
107
 
        "database is fucked up",
108
 
        "query interrupted by user",
109
 
        "could not connect to the database",
110
 
        "database has not yet been selected",
111
 
        "table not found",
112
 
        "duplicate table",
113
 
        "ambiguous table",
114
 
        "column not found",
115
 
        "duplicate column",
116
 
        "ambiguous column",
117
 
        "index not found",
118
 
        "duplicate index",
119
 
        "constraint not found",
120
 
        "duplicate constraint",
121
 
        "stored procedure not found",
122
 
        "duplicate stored procedure",
123
 
        "synonym not found",
124
 
        "duplicate synonym",
125
 
        "table has no primary or unique key",
126
 
        "duplicate primary or unique key",
127
 
        "cursor not specified, or cursor is not available",
128
 
        "duplicate cursor",
129
 
        "the database lacks the resources needed to complete this query",
130
 
        "check constrain violated",
131
 
        "referential integrity violated",
132
 
        "cannot manage or complete the transaction",
133
 
        "long transaction, too much log data generated",
134
 
        "this operation must be run inside a transaction",
135
 
        "cannot open, read, write, close, or otherwise manage a blob",
136
 
        "row, table, page, or database is already locked, or cannot be locked",
137
 
        "inserting null into a not null column",
138
 
        "no permission to modify the database in this way",
139
 
        "no current row established",
140
 
        "many rows were found where one was expected",
141
 
        "cannot union these select statements together",
142
 
        "cannot access or write the audit trail",
143
 
        "could not run SQL or gather data from a remote host",
144
 
        "where clause is semantically unmanageable",
145
 
        "deadlock detected",
146
 
0};
147
 
 
148
 
/* map Informix errors to our own exception codes, as defined in c_sql.h. */
149
 
static struct ERRORMAP {
 
94
/* map Informix errors to our own exception codes, as defined in dbapi.h. */
 
95
static const struct ERRORMAP {
150
96
        short infcode;
151
97
        short excno;
152
98
} errormap[] = {
510
456
 
511
457
static int errTranslate(int code)
512
458
{
513
 
        struct ERRORMAP *e;
 
459
        const struct ERRORMAP *e;
514
460
 
515
461
        for(e=errormap; e->infcode; ++e) {
516
462
                if(e->infcode == code)
523
469
{
524
470
short i;
525
471
 
526
 
rv_lastStatus = rv_vendorStatus = 0; /* innocent until proven guilty */
 
472
/* innocent until proven guilty */
 
473
rv_lastStatus = 0;
 
474
rv_vendorStatus = 0;
527
475
rv_stmtOffset = 0;
528
476
rv_badToken = 0;
529
477
if(ENGINE_ERRCODE >= 0) return false; /* no problem */
530
478
 
 
479
        /* log the SQL statement that elicitted the error */
531
480
showStatement();
532
481
rv_vendorStatus = -ENGINE_ERRCODE;
533
482
rv_lastStatus = errTranslate(rv_vendorStatus);
536
485
if(!rv_badToken[0]) rv_badToken = 0;
537
486
 
538
487
/* if the application didn't trap for this exception, blow up! */
539
 
if(exclist) {
540
 
for(i=0; exclist[i]; ++i) {
 
488
if(exclist)
 
489
for(i=0; exclist[i]; ++i)
541
490
if(exclist[i] == rv_lastStatus) {
542
491
exclist = 0; /* we've spent that exception */
543
492
return true;
544
493
}
545
 
}
546
 
}
547
494
 
 
495
/* Remember, errorPrint() should not return. */
548
496
errorPrint("2SQL error %d, %s", rv_vendorStatus, sqlErrorList[rv_lastStatus]);
549
497
return true; /* make the compiler happy */
550
498
} /* errorTrap */
562
510
struct sqlda *desc;
563
511
char rv_type[NUMRETS];
564
512
long rownum;
565
 
short alloc;
566
513
short cid; /* cursor ID */
567
514
char flag;
568
515
char numRets;
569
 
char **fl; /* array of fetched lines */
570
516
} ocurs[NUMCURSORS];
571
517
 
572
518
/* values for struct OCURS.flag */
605
551
return o;
606
552
} /* findCursor */
607
553
 
 
554
/* This doesn't close/free anything; it simply puts variables in an initial state. */
608
555
/* part of the disconnect() procedure */
609
556
static void clearAllCursors(void)
610
557
{
615
562
                if(o->flag == CURSOR_NONE) continue;
616
563
                o->flag = CURSOR_NONE;
617
564
o->rownum = 0;
618
 
                if(o->fl) {
619
 
                        for(j=0; j<o->alloc; ++j)
620
 
                                nzFree(o->fl[j]);
621
 
                        nzFree(o->fl);
622
 
                        o->fl = 0;
623
 
                }
624
565
        } /* loop over cursors */
625
566
 
626
567
        translevel = 0;
705
646
{
706
647
        rv_lastStatus = 0;
707
648
        checkConnect();
708
 
                stmt_text = "begin work";
709
 
                debugStatement();
 
649
stmt_text = 0;
 
650
 
710
651
        /* count the nesting level of transactions. */
711
652
        if(!translevel) {
712
653
                badtrans = false;
 
654
                stmt_text = "begin work";
 
655
                debugStatement();
713
656
                $begin work;
714
657
                if(errorTrap()) return;
715
658
        }
722
665
{
723
666
        rv_lastStatus = 0;
724
667
        checkConnect();
 
668
stmt_text = 0;
725
669
 
726
670
        if(translevel == 0)
727
671
                errorPrint("2end transaction without a matching begTrans()");
788
732
which makes sense only when the formatted string is destined for SQL.
789
733
*********************************************************************/
790
734
 
791
 
/* information about the blob being fetched */
792
 
const char *rv_blobFile;
793
 
bool rv_blobAppend;
794
 
void *rv_blobLoc; /* location of blob in memory */
795
 
int rv_blobSize; /* size of blob in bytes */
796
735
static loc_t blobstruct; /* Informix structure to manage the blob */
797
736
 
798
737
/* insert a blob into the database */
861
800
The same for time intervals, money, etc.
862
801
*********************************************************************/
863
802
 
864
 
/* Arrays that hold the return values from a select statement. */
865
 
int rv_numRets; /* number of returned values */
866
 
char rv_type[NUMRETS+1]; /* datatypes of returned values */
867
 
char rv_name[NUMRETS+1][COLNAMELEN]; /* column names */
868
 
LF  rv_data[NUMRETS]; /* the returned values */
869
 
int rv_lastNrows, rv_lastRowid, rv_lastSerial;
870
803
/* Temp area to read the Informix values, as strings */
871
804
static char retstring[NUMRETS][STRINGLEN+4];
872
805
static va_list sqlargs;
1073
1006
if(!first) va_end(sqlargs);
1074
1007
} /* retsCopy */
1075
1008
 
1076
 
/* convert column name into column index */
1077
 
int findcol_byname(const char *name)
1078
 
{
1079
 
        int i;
1080
 
        for(i=0; rv_name[i][0]; ++i)
1081
 
                if(stringEqual(name, rv_name[i])) break;
1082
 
        if(!rv_name[i][0])
1083
 
                errorPrint("2Column %s not found in the columns or aliases of your select statement", name);
1084
 
        return i;
1085
 
} /* findcol_byname */
1086
 
 
1087
1009
/* make sure we got one return value, and it is integer compatible */
1088
1010
static long oneRetValue(void)
1089
1011
{
1281
1203
 
1282
1204
/* pointer to vararg list; most of these are vararg functions */
1283
1205
/* execute a stand-alone statement with no % formatting of the string */
1284
 
void sql_execNF(const char *stmt)
 
1206
bool sql_execNF(const char *stmt)
1285
1207
{
1286
 
        execInternal(stmt, 1);
 
1208
        return execInternal(stmt, 1);
1287
1209
} /* sql_execNF */
1288
1210
 
1289
1211
/* execute a stand-alone statement with % formatting */
1290
 
void sql_exec(const char *stmt, ...)
 
1212
bool sql_exec(const char *stmt, ...)
1291
1213
{
 
1214
bool ok;
1292
1215
        va_start(sqlargs, stmt);
1293
1216
        stmt = lineFormatStack(stmt, 0, &sqlargs);
1294
 
        execInternal(stmt, 1);
 
1217
        ok = execInternal(stmt, 1);
1295
1218
        va_end(sqlargs);
 
1219
return ok;
1296
1220
} /* sql_exec */
1297
1221
 
1298
1222
/* run a select statement with no % formatting of the string */
1399
1323
o->numRets = rv_numRets;
1400
1324
memcpy(o->rv_type, rv_type, NUMRETS);
1401
1325
o->flag = CURSOR_PREPARED;
1402
 
o->fl = 0; /* just to make sure */
1403
1326
return o->cid;
1404
1327
} /* prepareCursor */
1405
1328
 
1434
1357
$open :internal_cname;
1435
1358
if(!errorTrap()) o->flag = CURSOR_OPENED;
1436
1359
o->rownum = 0;
1437
 
if(o->fl)
1438
 
for(i=0; i<o->alloc; ++i) {
1439
 
nzFree(o->fl[i]);
1440
 
o->fl[i] = 0;
1441
 
}
1442
1360
exclist = 0;
1443
1361
} /* sql_open */
1444
1362
 
1474
1392
exclist = 0;
1475
1393
} /* sql_close */
1476
1394
 
1477
 
void sql_free( int cid)
 
1395
void
 
1396
sql_free( int cid)
1478
1397
{
1479
1398
$char *internal_sname, *internal_cname;
1480
1399
struct OCURS *o = findCursor(cid);
1489
1408
rv_numRets = 0;
1490
1409
memset(rv_name, 0, sizeof(rv_name));
1491
1410
memset(rv_type, 0, sizeof(rv_type));
1492
 
if(o->fl) { /* free any cached lines */
1493
 
short i;
1494
 
for(i=0; i<o->alloc; ++i)
1495
 
nzFree(o->fl[i]);
1496
 
nzFree(o->fl);
1497
 
o->fl = 0;
1498
 
o->alloc = 0;
1499
 
}
1500
1411
exclist = 0;
1501
1412
} /* sql_free */
1502
1413
 
1512
1423
 
1513
1424
/* fetch row n from the open cursor.
1514
1425
 * Flag can be used to fetch first, last, next, or previous. */
1515
 
bool fetchInternal(int cid, long n, int flag, bool remember)
 
1426
static bool
 
1427
fetchInternal(int cid, long n, int flag, bool remember)
1516
1428
{
1517
1429
$char *internal_sname, *internal_cname;
1518
1430
$long nextrow, lastrow;
1542
1454
}
1543
1455
if(flag == 4) { /* fetch the last row */
1544
1456
nextrow = nullint; /* we just lost track */
1545
 
if(o->fl && o->flag == CURSOR_PREPARED) {
1546
 
/* I'll assume you've read in all the rows, cursor is closed */
1547
 
for(nextrow=o->alloc-1; nextrow>=0; --nextrow)
1548
 
if(o->fl[nextrow]) break;
1549
 
++nextrow;
1550
 
}
1551
1457
}
1552
1458
 
1553
1459
if(!nextrow) goto fetchZero;
1554
1460
 
1555
 
/* see if we have cached this row */
1556
 
if(isnotnull(nextrow) && o->fl &&
1557
 
nextrow <= o->alloc && o->fl[nextrow-1]) {
1558
 
sql_mkload(o->fl[nextrow-1], '\177');
1559
 
/* don't run retsCleanup() here */
1560
 
rv_blobLoc = 0;
1561
 
rv_blobSize = nullint;
1562
 
o->rownum = nextrow;
1563
 
exclist = 0;
1564
 
return true;
1565
 
} /* bringing row out of cache */
1566
 
 
1567
1461
if(o->flag != CURSOR_OPENED)
1568
1462
errorPrint("2cannot fetch from cursor %d, not yet opened", cid);
1569
1463
 
1612
1506
if(ENGINE_ERRCODE == 100) return false; /* not found */
1613
1507
o->rownum = nextrow;
1614
1508
 
1615
 
/* remember the unload image of this line */
1616
 
if(remember)
1617
 
sql_cursorUpdLine(cid, cloneString(sql_mkunld('\177')));
1618
1509
return true;
1619
1510
} /* fetchInternal */
1620
1511
 
1664
1555
} /* sql_fetchAbs */
1665
1556
 
1666
1557
 
1667
 
/* the inverse of sql_mkunld() */
1668
 
void sql_mkload(const char *line, char delim)
1669
 
{
1670
 
char *s, *t;
1671
 
int data;
1672
 
short i;
1673
 
 
1674
 
for(i = 0, s = (char*)line; *s; ++i, *t=delim, s = t+1) {
1675
 
t = strchr(s, delim);
1676
 
if(!t) errorPrint("2sql load line does not end in a delimiter");
1677
 
*t = 0;
1678
 
if(i >= rv_numRets)
1679
 
errorPrint("2sql load line contains more than %d fields", rv_numRets);
1680
 
 
1681
 
switch(rv_type[i]) {
1682
 
case 'N':
1683
 
if(!*s) { data = nullint; break; }
1684
 
data = strtol(s, &s, 10);
1685
 
if(*s) errorPrint("2sql load, cannot convert string to integer");
1686
 
break;
1687
 
 
1688
 
case 'S':
1689
 
if((unsigned)strlen(s) > STRINGLEN)
1690
 
errorPrint("2sql load line has a string that is too long");
1691
 
strcpy(retstring[i], s);
1692
 
data = (int) retstring[i];
1693
 
if(!*s) data = 0;
1694
 
break;
1695
 
 
1696
 
case 'F':
1697
 
rv_data[i].f = *s ? atof(s) : nullfloat;
1698
 
continue;
1699
 
 
1700
 
case 'D':
1701
 
data = stringDate(s,0);
1702
 
if(data == -1)
1703
 
errorPrint("2sql load, cannot convert string to date");
1704
 
break;
1705
 
 
1706
 
case 'C':
1707
 
data = *s;
1708
 
if(data && s[1])
1709
 
errorPrint("2sql load, character field contains more than one character");
1710
 
break;
1711
 
 
1712
 
case 'I':
1713
 
data = stringTime(s);
1714
 
if(data == -1)
1715
 
errorPrint("2sql load, cannot convert string to time interval");
1716
 
break;
1717
 
 
1718
 
default:
1719
 
errorPrint("2sql load cannot convert into type %c", rv_type[i]);
1720
 
} /* switch on type */
1721
 
 
1722
 
rv_data[i].l = data;
1723
 
} /* loop over fields in line */
1724
 
 
1725
 
if(i != rv_numRets)
1726
 
errorPrint("2sql load line contains %d fields, %d expected", i, rv_numRets);
1727
 
} /* sql_mkload */
1728
 
 
1729
 
 
1730
 
/*********************************************************************
1731
 
We maintain our own cache of fetched lines.
1732
 
Why?  You might ask.
1733
 
After all, Informix already maintains a cache of fetched lines.
1734
 
That's what the open cursor is for.
1735
 
Looks like serious wheel reinvention to me.
1736
 
Perhaps, but you can't change the data in the cache that Informix maintains.
1737
 
This is something Powerbuild et al discovered over a decade ago.
1738
 
Consider a simple spreadsheet application.
1739
 
You update the value in one of the cells, thereby updating the row
1740
 
in the database.  Now scroll down to the next page, and then back again.
1741
 
If you fetch from the open cursor you will get the old data, before the
1742
 
change was made, even though the new data is safely ensconsed in the database.
1743
 
Granted one could reopen the cursor and fetch the new data,
1744
 
but this can be slow for certain queries (sometimes a couple minutes).
1745
 
In other words, rebuilding the cursor is not really an option.
1746
 
So we are forced to retain a copy of the data in our program and change it
1747
 
whenever we update the database.
1748
 
Unfortunately the following 3 routines were developed separately,
1749
 
and they are wildly inconsistent.  Some take a row number while
1750
 
others assume you are modifying the current row as stored in o->rownum.
1751
 
Some accept a line of tex, the unload image of the fetch data, while
1752
 
others build the line of text from the fetched data in rv_data[].
1753
 
I apologize for this confusion; clearly a redesign is called for.
1754
 
*********************************************************************/
1755
 
 
1756
 
/* update the text of a fetched line,
1757
 
 * so we get this same text again when we refetch the line later.
1758
 
 * These text changes corespond to the database changes that form an update.
1759
 
 * We assume the line has been allocated using malloc(). */
1760
 
void sql_cursorUpdLine(int cid, const char *line)
1761
 
{
1762
 
struct OCURS *o = findCursor(cid);
1763
 
int n = o->rownum-1;
1764
 
 
1765
 
if(n >= CACHELIMIT)
1766
 
errorPrint("2SQL cursor caches too many lines, limit %d", CACHELIMIT);
1767
 
 
1768
 
if(n >= o->alloc) {
1769
 
/* running off the end, allocate 128 at a time */
1770
 
short oldalloc = o->alloc;
1771
 
o->alloc = n + 128;
1772
 
if(!oldalloc)
1773
 
o->fl = (char **) allocMem(o->alloc*sizeof(char*));
1774
 
else
1775
 
o->fl = (char**) reallocMem((void*)o->fl, o->alloc*sizeof(char*));
1776
 
memset(o->fl+oldalloc, 0, (o->alloc-oldalloc)*sizeof(char*));
1777
 
} /* allocating more space */
1778
 
 
1779
 
nzFree(o->fl[n]);
1780
 
o->fl[n] = (char*)line;
1781
 
} /* sql_cursorUpdLine */
1782
 
 
1783
 
void sql_cursorDelLine(int cid, int rownum)
1784
 
{
1785
 
struct OCURS *o = findCursor(cid);
1786
 
o->rownum = rownum;
1787
 
--rownum;
1788
 
if(rownum >= o->alloc || !o->fl[rownum])
1789
 
errorPrint("2cursorDelLine(%d)", rownum);
1790
 
nzFree(o->fl[rownum]);
1791
 
if(rownum < o->alloc-1)
1792
 
memcpy(o->fl+rownum, o->fl+rownum+1, (o->alloc-rownum-1)*sizeof(char *));
1793
 
o->fl[o->alloc-1] = 0;
1794
 
/* back up the row number if we deleted the last row */
1795
 
if(!o->fl[rownum]) --o->rownum;
1796
 
} /* sql_cursorDelLine */
1797
 
 
1798
 
void sql_cursorInsLine(int cid, int rownum)
1799
 
{
1800
 
struct OCURS *o = findCursor(cid);
1801
 
short i;
1802
 
 
1803
 
/* must insert a row within or immediately following the current data */
1804
 
if(rownum > o->alloc)
1805
 
errorPrint("2cursorInsLine(%d)", rownum);
1806
 
/* newly inserted row becomes the current row */
1807
 
o->rownum = rownum+1;
1808
 
 
1809
 
if(!o->alloc || o->fl[o->alloc-1]) { /* need to make room */
1810
 
o->alloc += 128;
1811
 
if(!o->fl)
1812
 
o->fl = (char **) allocMem(o->alloc*sizeof(char*));
1813
 
else
1814
 
o->fl = (char**) reallocMem((void*)o->fl, o->alloc*sizeof(char*));
1815
 
memset(o->fl+o->alloc-128, 0, 128*sizeof(char*));
1816
 
} /* allocating more space */
1817
 
 
1818
 
/* move the rest of the lines down */
1819
 
for(i=o->alloc-1; i>rownum; --i)
1820
 
o->fl[i] = o->fl[i-1];
1821
 
o->fl[i] = cloneString(sql_mkunld('\177'));
1822
 
} /* sql_cursorInsLine */
1823
 
 
1824
 
 
1825
 
/*********************************************************************
1826
 
run the analog of /bin/comm on two open cursors,
1827
 
rather than two Unix files.
1828
 
This assumes a common unique key that we use to sync up the rows.
1829
 
The cursors should be sorted by this key.
1830
 
*********************************************************************/
1831
 
 
1832
 
void cursor_comm(
1833
 
const char *stmt1, const char *stmt2, /* the two select statements */
1834
 
const char *orderby, /* which fetched column is the unique key */
1835
 
fnptr f, /* call this function for differences */
1836
 
char delim) /* sql_mkunld() delimiter, or call mkinsupd if delim = 0 */
1837
 
{
1838
 
short cid1, cid2; /* the cursor ID numbers */
1839
 
char *line1, *line2, *s; /* the two fetched rows */
1840
 
void *blob1, *blob2; /* one blob per table */
1841
 
int blob1size, blob2size;
1842
 
bool eof1, eof2, get1, get2;
1843
 
int sortval1, sortval2;
1844
 
char sortstring1[80], sortstring2[80];
1845
 
int sortcol;
1846
 
char sorttype;
1847
 
int passkey1, passkey2;
1848
 
static const char sortnull[] = "cursor_comm, sortval%d is null";
1849
 
static const char sortlong[] = "cursor_comm cannot key on strings longer than %d";
1850
 
static const char noblob[] = "sorry, cursor_comm cannot handle blobs yet";
1851
 
 
1852
 
cid1 = sql_prepOpen(stmt1);
1853
 
cid2 = sql_prepOpen(stmt2);
1854
 
 
1855
 
sortcol = findcol_byname(orderby);
1856
 
sorttype = rv_type[sortcol];
1857
 
if(charInList("NDIS", sorttype) < 0)
1858
 
errorPrint("2cursor_com(), column %s has bad type %c", orderby, sorttype);
1859
 
if(sorttype == 'S')
1860
 
passkey1 = (int)sortstring1, passkey2 = (int)sortstring2;
1861
 
 
1862
 
eof1 = eof2 = false;
1863
 
get1 = get2 = true;
1864
 
rv_blobFile = 0; /* in case the cursor has a blob */
1865
 
line1 = line2 = 0;
1866
 
blob1 = blob2 = 0;
1867
 
 
1868
 
while(true) {
1869
 
if(get1) { /* fetch first row */
1870
 
eof1 = !sql_fetchNext(cid1, 0);
1871
 
nzFree(line1);
1872
 
line1 = 0;
1873
 
nzFree(blob1);
1874
 
blob1 = 0;
1875
 
if(!eof1) {
1876
 
if(sorttype == 'S') {
1877
 
s = rv_data[sortcol].ptr;
1878
 
if(isnullstring(s)) errorPrint(sortnull, 1);
1879
 
if(strlen(s) >= sizeof(sortstring1))
1880
 
errorPrint(sortlong, sizeof(sortstring1));
1881
 
strcpy(sortstring1, s);
1882
 
} else {
1883
 
passkey1 = sortval1 = rv_data[sortcol].l;
1884
 
if(isnull(sortval1))
1885
 
errorPrint(sortnull, 1);
1886
 
}
1887
 
line1 = cloneString(delim ? sql_mkunld(delim) : sql_mkinsupd());
1888
 
if(rv_blobLoc) {
1889
 
blob1 = rv_blobLoc;
1890
 
blob1size = rv_blobSize;
1891
 
errorPrint(noblob);
1892
 
}
1893
 
} /* not eof */
1894
 
} /* looking for first line */
1895
 
 
1896
 
if(get2) { /* fetch second row */
1897
 
eof2 = !sql_fetchNext(cid2, 0);
1898
 
nzFree(line2);
1899
 
line2 = 0;
1900
 
nzFree(blob2);
1901
 
blob2 = 0;
1902
 
if(!eof2) {
1903
 
if(sorttype == 'S') {
1904
 
s = rv_data[sortcol].ptr;
1905
 
if(isnullstring(s)) errorPrint(sortnull, 2);
1906
 
if(strlen(s) >= sizeof(sortstring2))
1907
 
errorPrint(sortlong, sizeof(sortstring2));
1908
 
strcpy(sortstring2, rv_data[sortcol].ptr);
1909
 
} else {
1910
 
passkey2 = sortval2 = rv_data[sortcol].l;
1911
 
if(isnull(sortval2))
1912
 
errorPrint(sortnull, 2);
1913
 
}
1914
 
line2 = cloneString(delim ? sql_mkunld(delim) : sql_mkinsupd());
1915
 
if(rv_blobLoc) {
1916
 
blob2 = rv_blobLoc;
1917
 
blob2size = rv_blobSize;
1918
 
errorPrint(noblob);
1919
 
}
1920
 
} /* not eof */
1921
 
} /* looking for second line */
1922
 
 
1923
 
if(eof1 & eof2) break; /* done */
1924
 
get1 = get2 = false;
1925
 
 
1926
 
/* in cid2, but not in cid1 */
1927
 
if(eof1 || !eof2 &&
1928
 
(sorttype == 'S' && strcmp(sortstring1, sortstring2) > 0 ||
1929
 
sorttype != 'S' && sortval1 > sortval2)) {
1930
 
(*f)('>', line1, line2, passkey2);
1931
 
get2 = true;
1932
 
continue;
1933
 
}
1934
 
 
1935
 
/* in cid1, but not in cid2 */
1936
 
if(eof2 || !eof1 &&
1937
 
(sorttype == 'S' && strcmp(sortstring1, sortstring2) < 0 ||
1938
 
sorttype != 'S' && sortval1 < sortval2)) {
1939
 
(*f)('<', line1, line2, passkey1);
1940
 
get1 = true;
1941
 
continue;
1942
 
} /* insert case */
1943
 
 
1944
 
get1 = get2 = true;
1945
 
/* perhaps the lines are equal */
1946
 
if(stringEqual(line1, line2)) continue;
1947
 
 
1948
 
/* lines are different between the two cursors */
1949
 
(*f)('*', line1, line2, passkey2);
1950
 
} /* loop over parallel cursors */
1951
 
 
1952
 
nzFree(line1);
1953
 
nzFree(line2);
1954
 
nzFree(blob1);
1955
 
nzFree(blob2);
1956
 
sql_closeFree(cid1);
1957
 
sql_closeFree(cid2);
1958
 
} /* cursor_comm */
1959
 
 
1960
1558
/*********************************************************************
1961
1559
Get the primary key for a table.
1962
1560
In informix, you can use system tables to get this information.
1963
 
There's a way to do it in odbc, but I don't remember.
 
1561
I haven't yet expanded this to a 3 part key.
1964
1562
*********************************************************************/
1965
1563
 
1966
1564
void
1967
 
getPrimaryKey(char *tname, int *part1, int *part2)
 
1565
getPrimaryKey(char *tname, int *part1, int *part2, int *part3)
1968
1566
{
1969
 
int p1, p2, rc;
1970
 
char *s = strchr(tname, ':');
1971
 
*part1 = *part2 = 0;
1972
 
if(!s) {
1973
 
rc = sql_select("select part1, part2 \
 
1567
    int p1, p2, rc;
 
1568
    char *s = strchr(tname, ':');
 
1569
    *part1 = *part2 = *part3 = 0;
 
1570
    if(!s) {
 
1571
        rc = sql_select("select part1, part2 \
1974
1572
from sysconstraints c, systables t, sysindexes i \
1975
1573
where tabname = %S and t.tabid = c.tabid \
1976
 
and constrtype = 'P' and c.idxname = i.idxname",
1977
 
tname, &p1, &p2);
1978
 
} else {
1979
 
*s = 0;
1980
 
rc = sql_select("select part1, part2 \
 
1574
and constrtype = 'P' and c.idxname = i.idxname", tname, &p1, &p2);
 
1575
    } else {
 
1576
        *s = 0;
 
1577
        rc = sql_select("select part1, part2 \
1981
1578
from %s:sysconstraints c, %s:systables t, %s:sysindexes i \
1982
1579
where tabname = %S and t.tabid = c.tabid \
1983
 
and constrtype = 'P' and c.idxname = i.idxname",
1984
 
tname, tname, tname, s+1, &p1, &p2);
1985
 
*s = ':';
1986
 
}
1987
 
if(rc) *part1 = p1, *part2 = p2;
1988
 
} /* getPrimaryKey */
1989
 
 
 
1580
and constrtype = 'P' and c.idxname = i.idxname", tname, tname, tname, s + 1, &p1, &p2);
 
1581
        *s = ':';
 
1582
    }
 
1583
    if(rc)
 
1584
        *part1 = p1, *part2 = p2;
 
1585
}                               /* getPrimaryKey */
 
1586
 
 
1587
bool
 
1588
showTables(void)
 
1589
{
 
1590
puts("Not implemented in Informix, but certainly doable through systables");
 
1591
}                               /* showTables */
 
1592
 
 
1593
/* This can also be done by catalogs; it's on my list of things to do. */
 
1594
bool
 
1595
fetchForeign(char *tname)
 
1596
{
 
1597
i_puts(MSG_NYI);
 
1598
} /* fetchForeign */