]> granicus.if.org Git - postgresql/blob - src/interfaces/odbc/info.c
Version 06-30-0248
[postgresql] / src / interfaces / odbc / info.c
1
2 /* Module:          info.c
3  *
4  * Description:     This module contains routines related to
5  *                  ODBC informational functions.
6  *
7  * Classes:         n/a
8  *
9  * API functions:   SQLGetInfo, SQLGetTypeInfo, SQLGetFunctions, 
10  *                  SQLTables, SQLColumns, SQLStatistics, SQLSpecialColumns,
11  *                  SQLPrimaryKeys, SQLForeignKeys, 
12  *                  SQLProcedureColumns(NI), SQLProcedures(NI), 
13  *                  SQLTablePrivileges(NI), SQLColumnPrivileges(NI)
14  *
15  * Comments:        See "notice.txt" for copyright and license information.
16  *
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <string.h>
24 #include <stdio.h>
25 #include "psqlodbc.h"
26
27 #ifdef HAVE_IODBC
28 #include "iodbc.h"
29 #include "isql.h"
30 #include "isqlext.h"
31 #else
32 #include <windows.h>
33 #include <sql.h> 
34 #include <sqlext.h>
35 #endif
36
37 #include "tuple.h"
38 #include "pgtypes.h"
39
40 #include "environ.h"
41 #include "connection.h"
42 #include "statement.h"
43 #include "qresult.h"
44 #include "bind.h"
45 #include "misc.h"
46 #include "pgtypes.h"
47
48
49 extern GLOBAL_VALUES globals;
50
51 //      -       -       -       -       -       -       -       -       -
52
53 RETCODE SQL_API SQLGetInfo(
54         HDBC      hdbc,
55         UWORD     fInfoType,
56         PTR       rgbInfoValue,
57         SWORD     cbInfoValueMax,
58         SWORD FAR *pcbInfoValue)
59 {
60 char *func = "SQLGetInfo";
61 ConnectionClass *conn = (ConnectionClass *) hdbc;
62 char *p;
63
64         if ( ! conn) {
65                 CC_log_error(func, "", NULL);
66                 return SQL_INVALID_HANDLE;
67         }
68
69     if (NULL == (char *)rgbInfoValue) {
70                 CC_log_error(func, "Bad rgbInfoValue", conn);
71         return SQL_INVALID_HANDLE;
72         }
73
74
75     switch (fInfoType) {
76     case SQL_ACCESSIBLE_PROCEDURES: /* ODBC 1.0 */
77         // can the user call all functions returned by SQLProcedures?
78         // I assume access permissions could prevent this in some cases(?)
79         // anyway, SQLProcedures doesn't exist yet.
80         if (pcbInfoValue) *pcbInfoValue = 1;
81         strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax);
82         break;
83
84     case SQL_ACCESSIBLE_TABLES: /* ODBC 1.0 */
85         // is the user guaranteed "SELECT" on every table?
86         if (pcbInfoValue) *pcbInfoValue = 1;
87         strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax);
88         break;
89
90     case SQL_ACTIVE_CONNECTIONS: /* ODBC 1.0 */
91         // how many simultaneous connections do we support?
92         *((WORD *)rgbInfoValue) = MAX_CONNECTIONS;
93         if(pcbInfoValue) { *pcbInfoValue = 2; }
94         break;
95
96     case SQL_ACTIVE_STATEMENTS: /* ODBC 1.0 */
97         // no limit on the number of active statements.
98         *((WORD *)rgbInfoValue) = (WORD)0;
99         if(pcbInfoValue) { *pcbInfoValue = 2; }
100         break;
101
102     case SQL_ALTER_TABLE: /* ODBC 2.0 */
103         // what does 'alter table' support? (bitmask)
104         // postgres doesn't seem to let you drop columns.
105         *((DWORD *)rgbInfoValue) = SQL_AT_ADD_COLUMN;
106         if(pcbInfoValue) { *pcbInfoValue = 4; }
107         break;
108
109     case SQL_BOOKMARK_PERSISTENCE: /* ODBC 2.0 */
110         // through what operations do bookmarks persist? (bitmask)
111         // bookmarks don't exist yet, so they're not very persistent.
112         *((DWORD *)rgbInfoValue) = 0;
113         if(pcbInfoValue) { *pcbInfoValue = 4; }
114         break;
115
116     case SQL_COLUMN_ALIAS: /* ODBC 2.0 */
117         // do we support column aliases?  guess not.
118         if (pcbInfoValue) *pcbInfoValue = 1;
119         strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax);
120         break;
121
122     case SQL_CONCAT_NULL_BEHAVIOR: /* ODBC 1.0 */
123         // how does concatenation work with NULL columns?
124         // not sure how you do concatentation, but this way seems
125         // more reasonable
126         *((WORD *)rgbInfoValue) = SQL_CB_NON_NULL;
127         if(pcbInfoValue) { *pcbInfoValue = 2; }
128         break;
129
130         // which types of data-conversion do we support?
131         // currently we don't support any, except converting a type
132         // to itself.
133     case SQL_CONVERT_BIGINT:
134     case SQL_CONVERT_BINARY:
135     case SQL_CONVERT_BIT:
136     case SQL_CONVERT_CHAR:
137     case SQL_CONVERT_DATE:
138     case SQL_CONVERT_DECIMAL:
139     case SQL_CONVERT_DOUBLE:
140     case SQL_CONVERT_FLOAT:
141     case SQL_CONVERT_INTEGER:
142     case SQL_CONVERT_LONGVARBINARY:
143     case SQL_CONVERT_LONGVARCHAR:
144     case SQL_CONVERT_NUMERIC:
145     case SQL_CONVERT_REAL:
146     case SQL_CONVERT_SMALLINT:
147     case SQL_CONVERT_TIME:
148     case SQL_CONVERT_TIMESTAMP:
149     case SQL_CONVERT_TINYINT:
150     case SQL_CONVERT_VARBINARY:
151     case SQL_CONVERT_VARCHAR: /* ODBC 1.0 */
152         // only return the type we were called with (bitmask)
153         *((DWORD *)rgbInfoValue) = fInfoType;
154         if(pcbInfoValue) { *pcbInfoValue = 4; }
155         break;
156
157     case SQL_CONVERT_FUNCTIONS: /* ODBC 1.0 */
158         // which conversion functions do we support? (bitmask)
159         *((DWORD *)rgbInfoValue) = 0;
160         if(pcbInfoValue) { *pcbInfoValue = 4; }
161         break;
162
163     case SQL_CORRELATION_NAME: /* ODBC 1.0 */
164         // I don't know what a correlation name is, so I guess we don't
165         // support them.
166
167         // *((WORD *)rgbInfoValue) = (WORD)SQL_CN_NONE;
168
169         // well, let's just say we do--otherwise Query won't work.
170         *((WORD *)rgbInfoValue) = (WORD)SQL_CN_ANY;
171         if(pcbInfoValue) { *pcbInfoValue = 2; }
172
173         break;
174
175     case SQL_CURSOR_COMMIT_BEHAVIOR: /* ODBC 1.0 */
176         // postgres definitely closes cursors when a transaction ends,
177         // but you shouldn't have to re-prepare a statement after
178         // commiting a transaction (I don't think)
179         *((WORD *)rgbInfoValue) = (WORD)SQL_CB_CLOSE;
180         if(pcbInfoValue) { *pcbInfoValue = 2; }
181         break;
182
183     case SQL_CURSOR_ROLLBACK_BEHAVIOR: /* ODBC 1.0 */
184         // see above
185         *((WORD *)rgbInfoValue) = (WORD)SQL_CB_CLOSE;
186         if(pcbInfoValue) { *pcbInfoValue = 2; }
187         break;
188
189     case SQL_DATA_SOURCE_NAME: /* ODBC 1.0 */
190                 p = CC_get_DSN(conn);
191                 if (pcbInfoValue) *pcbInfoValue = strlen(p);
192                 strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax);
193         break;
194
195     case SQL_DATA_SOURCE_READ_ONLY: /* ODBC 1.0 */
196         if (pcbInfoValue) *pcbInfoValue = 1;
197                 sprintf((char *)rgbInfoValue, "%c", CC_is_readonly(conn) ? 'Y' : 'N');
198         break;
199
200     case SQL_DATABASE_NAME: /* Support for old ODBC 1.0 Apps */
201         // case SQL_CURRENT_QUALIFIER:
202         // this tag doesn't seem to be in ODBC 2.0, and it conflicts
203         // with a valid tag (SQL_TIMEDATE_ADD_INTERVALS).
204
205                 /*      Returning the database name causes problems in MS Query.
206                         It generates query like: "SELECT DISTINCT a FROM byronncrap3 crap3"
207                 */
208                 p = "";    // CC_get_database(conn);
209                 if (pcbInfoValue) *pcbInfoValue = strlen(p);
210                 strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax);
211                 break;
212
213     case SQL_DBMS_NAME: /* ODBC 1.0 */
214         if (pcbInfoValue) *pcbInfoValue = 10;
215         strncpy_null((char *)rgbInfoValue, DBMS_NAME, (size_t)cbInfoValueMax);
216         break;
217
218     case SQL_DBMS_VER: /* ODBC 1.0 */
219         if (pcbInfoValue) *pcbInfoValue = 25;
220         strncpy_null((char *)rgbInfoValue, DBMS_VERSION, (size_t)cbInfoValueMax);
221         break;
222
223     case SQL_DEFAULT_TXN_ISOLATION: /* ODBC 1.0 */
224         // are dirty reads, non-repeatable reads, and phantoms possible? (bitmask)
225         // by direct experimentation they are not.  postgres forces
226         // the newer transaction to wait before doing something that
227         // would cause one of these problems.
228         *((DWORD *)rgbInfoValue) = SQL_TXN_SERIALIZABLE;
229         if(pcbInfoValue) { *pcbInfoValue = 4; }
230         break;
231
232     case SQL_DRIVER_NAME: /* ODBC 1.0 */
233         // this should be the actual filename of the driver
234         p = DRIVER_FILE_NAME;
235         if (pcbInfoValue)  *pcbInfoValue = strlen(p);
236         strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax);
237         break;
238
239     case SQL_DRIVER_ODBC_VER:
240         if (pcbInfoValue) *pcbInfoValue = 5;
241         strncpy_null((char *)rgbInfoValue, "02.00", (size_t)cbInfoValueMax);
242         break;
243
244     case SQL_DRIVER_VER: /* ODBC 1.0 */
245         p = POSTGRESDRIVERVERSION;
246         if (pcbInfoValue) *pcbInfoValue = strlen(p);
247         strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax);
248         break;
249
250     case SQL_EXPRESSIONS_IN_ORDERBY: /* ODBC 1.0 */
251         // can you have expressions in an 'order by' clause?
252         // not sure about this.  say no for now.
253         if (pcbInfoValue) *pcbInfoValue = 1;
254         strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax);
255         break;
256
257     case SQL_FETCH_DIRECTION: /* ODBC 1.0 */
258         // which fetch directions are supported? (bitmask)
259         *((DWORD *)rgbInfoValue) = globals.use_declarefetch ? 0 : (SQL_FD_FETCH_NEXT |
260                                    SQL_FD_FETCH_FIRST |
261                                    SQL_FD_FETCH_LAST |
262                                    SQL_FD_FETCH_PRIOR |
263                                    SQL_FD_FETCH_ABSOLUTE);
264         if(pcbInfoValue) { *pcbInfoValue = 4; }
265         break;
266
267     case SQL_FILE_USAGE: /* ODBC 2.0 */
268         // we are a two-tier driver, not a file-based one.
269         *((WORD *)rgbInfoValue) = (WORD)SQL_FILE_NOT_SUPPORTED;
270         if(pcbInfoValue) { *pcbInfoValue = 2; }
271         break;
272
273     case SQL_GETDATA_EXTENSIONS: /* ODBC 2.0 */
274         // (bitmask)
275         *((DWORD *)rgbInfoValue) = (SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND);
276         if(pcbInfoValue) { *pcbInfoValue = 4; }
277         break;
278
279     case SQL_GROUP_BY: /* ODBC 2.0 */
280         // how do the columns selected affect the columns you can group by?
281         *((WORD *)rgbInfoValue) = SQL_GB_GROUP_BY_EQUALS_SELECT;
282         if(pcbInfoValue) { *pcbInfoValue = 2; }
283         break;
284
285     case SQL_IDENTIFIER_CASE: /* ODBC 1.0 */
286         // are identifiers case-sensitive (yes)
287         *((WORD *)rgbInfoValue) = SQL_IC_SENSITIVE;
288         if(pcbInfoValue) { *pcbInfoValue = 2; }
289         break;
290
291     case SQL_IDENTIFIER_QUOTE_CHAR: /* ODBC 1.0 */
292         // the character used to quote "identifiers" (what are they?)
293         // the manual index lists 'owner names' and 'qualifiers' as
294         // examples of identifiers.  it says return a blank for no
295         // quote character, we'll try that...
296         if (pcbInfoValue) *pcbInfoValue = 1;
297         strncpy_null((char *)rgbInfoValue, "\"", (size_t)cbInfoValueMax);
298         break;
299
300     case SQL_KEYWORDS: /* ODBC 2.0 */
301         // do this later
302         conn->errormsg = "SQL_KEYWORDS parameter to SQLGetInfo not implemented.";
303         conn->errornumber = CONN_NOT_IMPLEMENTED_ERROR;
304                 CC_log_error(func, "", conn);
305         return SQL_ERROR;
306         break;
307
308     case SQL_LIKE_ESCAPE_CLAUSE: /* ODBC 2.0 */
309         // is there a character that escapes '%' and '_' in a LIKE clause?
310         // not as far as I can tell
311         if (pcbInfoValue) *pcbInfoValue = 1;
312         strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax);
313         break;
314
315     case SQL_LOCK_TYPES: /* ODBC 2.0 */
316         // which lock types does SQLSetPos support? (bitmask)
317         // SQLSetPos doesn't exist yet
318         *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_LCK_NO_CHANGE | SQL_LCK_EXCLUSIVE | SQL_LCK_UNLOCK) : 0;
319         if(pcbInfoValue) { *pcbInfoValue = 4; }
320         break;
321
322     case SQL_MAX_BINARY_LITERAL_LEN: /* ODBC 2.0 */
323         // the maximum length of a query is 2k, so maybe we should
324         // set the maximum length of all these literals to that value?
325         // for now just use zero for 'unknown or no limit'
326
327         // maximum length of a binary literal
328         *((DWORD *)rgbInfoValue) = 0;
329         if(pcbInfoValue) { *pcbInfoValue = 4; }
330         break;
331
332     case SQL_MAX_CHAR_LITERAL_LEN: /* ODBC 2.0 */
333         // maximum length of a character literal
334         *((DWORD *)rgbInfoValue) = 0;
335         if(pcbInfoValue) { *pcbInfoValue = 4; }
336         break;
337
338     case SQL_MAX_COLUMN_NAME_LEN: /* ODBC 1.0 */
339         // maximum length of a column name
340         *((WORD *)rgbInfoValue) = MAX_COLUMN_LEN;
341         if(pcbInfoValue) { *pcbInfoValue = 2; }
342         break;
343
344     case SQL_MAX_COLUMNS_IN_GROUP_BY: /* ODBC 2.0 */
345         // maximum number of columns in a 'group by' clause
346         *((WORD *)rgbInfoValue) = 0;
347         if(pcbInfoValue) { *pcbInfoValue = 2; }
348         break;
349
350     case SQL_MAX_COLUMNS_IN_INDEX: /* ODBC 2.0 */
351         // maximum number of columns in an index
352         *((WORD *)rgbInfoValue) = 0;
353         if(pcbInfoValue) { *pcbInfoValue = 2; }
354         break;
355
356     case SQL_MAX_COLUMNS_IN_ORDER_BY: /* ODBC 2.0 */
357         // maximum number of columns in an ORDER BY statement
358         *((WORD *)rgbInfoValue) = 0;
359         if(pcbInfoValue) { *pcbInfoValue = 2; }
360         break;
361
362     case SQL_MAX_COLUMNS_IN_SELECT: /* ODBC 2.0 */
363         *((WORD *)rgbInfoValue) = 0;
364         if(pcbInfoValue) { *pcbInfoValue = 2; }
365         break;
366
367     case SQL_MAX_COLUMNS_IN_TABLE: /* ODBC 2.0 */
368         *((WORD *)rgbInfoValue) = 0;
369         if(pcbInfoValue) { *pcbInfoValue = 2; }
370         break;
371
372     case SQL_MAX_CURSOR_NAME_LEN: /* ODBC 1.0 */
373         *((WORD *)rgbInfoValue) = MAX_CURSOR_LEN;
374         if(pcbInfoValue) { *pcbInfoValue = 2; }
375         break;
376
377     case SQL_MAX_INDEX_SIZE: /* ODBC 2.0 */
378         *((DWORD *)rgbInfoValue) = 0;
379         if(pcbInfoValue) { *pcbInfoValue = 4; }
380         break;
381
382     case SQL_MAX_OWNER_NAME_LEN: /* ODBC 1.0 */
383         // the maximum length of a table owner's name.  (0 == none)
384         // (maybe this should be 8)
385         *((WORD *)rgbInfoValue) = (WORD)0;
386         if(pcbInfoValue) { *pcbInfoValue = 2; }
387         break;
388
389     case SQL_MAX_PROCEDURE_NAME_LEN: /* ODBC 1.0 */
390         *((WORD *)rgbInfoValue) = 0;
391         if(pcbInfoValue) { *pcbInfoValue = 2; }
392         break;
393
394     case SQL_MAX_QUALIFIER_NAME_LEN: /* ODBC 1.0 */
395         *((WORD *)rgbInfoValue) = 0;
396         if(pcbInfoValue) { *pcbInfoValue = 2; }
397         break;
398
399     case SQL_MAX_ROW_SIZE: /* ODBC 2.0 */
400         // the maximum size of one row
401         // here I do know a definite value
402         *((DWORD *)rgbInfoValue) = 8192;
403         if(pcbInfoValue) { *pcbInfoValue = 4; }
404         break;
405
406     case SQL_MAX_ROW_SIZE_INCLUDES_LONG: /* ODBC 2.0 */
407         // does the preceding value include LONGVARCHAR and LONGVARBINARY
408         // fields?   Well, it does include longvarchar, but not longvarbinary.
409         if (pcbInfoValue) *pcbInfoValue = 1;
410         strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax);
411         break;
412
413     case SQL_MAX_STATEMENT_LEN: /* ODBC 2.0 */
414         // there should be a definite value here (2k?)
415         *((DWORD *)rgbInfoValue) = 0;
416         if(pcbInfoValue) { *pcbInfoValue = 4; }
417         break;
418
419     case SQL_MAX_TABLE_NAME_LEN: /* ODBC 1.0 */
420         *((WORD *)rgbInfoValue) = MAX_TABLE_LEN;
421         if(pcbInfoValue) { *pcbInfoValue = 2; }
422         break;
423
424     case SQL_MAX_TABLES_IN_SELECT: /* ODBC 2.0 */
425         *((WORD *)rgbInfoValue) = 0;
426         if(pcbInfoValue) { *pcbInfoValue = 2; }
427         break;
428
429     case SQL_MAX_USER_NAME_LEN:
430         *(SWORD FAR *)rgbInfoValue = 0;
431         if(pcbInfoValue) { *pcbInfoValue = 2; }
432         break;
433
434     case SQL_MULT_RESULT_SETS: /* ODBC 1.0 */
435         // do we support multiple result sets?  Not really, but say yes anyway?
436         if (pcbInfoValue) *pcbInfoValue = 1;
437         strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax);
438         break;
439
440     case SQL_MULTIPLE_ACTIVE_TXN: /* ODBC 1.0 */
441         // do we support multiple simultaneous transactions?
442         if (pcbInfoValue) *pcbInfoValue = 1;
443         strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax);
444         break;
445
446     case SQL_NEED_LONG_DATA_LEN: /* ODBC 2.0 */
447         if (pcbInfoValue) *pcbInfoValue = 1;
448                 /*      Dont need the length, SQLPutData can handle any size and multiple calls */
449         strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax);
450         break;
451
452     case SQL_NON_NULLABLE_COLUMNS: /* ODBC 1.0 */
453         *((WORD *)rgbInfoValue) = (WORD)SQL_NNC_NON_NULL;
454         if(pcbInfoValue) { *pcbInfoValue = 2; }
455         break;
456
457     case SQL_NULL_COLLATION: /* ODBC 2.0 */
458         // where are nulls sorted?
459         *((WORD *)rgbInfoValue) = (WORD)SQL_NC_END;
460         if(pcbInfoValue) { *pcbInfoValue = 2; }
461         break;
462
463     case SQL_NUMERIC_FUNCTIONS: /* ODBC 1.0 */
464         // what numeric functions are supported? (bitmask)
465         // I'm not sure if any of these are actually supported
466         *((DWORD *)rgbInfoValue) = 0;
467         if(pcbInfoValue) { *pcbInfoValue = 4; }
468         break;
469
470     case SQL_ODBC_API_CONFORMANCE: /* ODBC 1.0 */
471         *((WORD *)rgbInfoValue) = SQL_OAC_LEVEL1;
472         if(pcbInfoValue) { *pcbInfoValue = 2; }
473         break;
474
475     case SQL_ODBC_SAG_CLI_CONFORMANCE: /* ODBC 1.0 */
476         // can't find any reference to SAG in the ODBC reference manual
477         // (although it's in the index, it doesn't actually appear on
478         // the pages referenced)
479         *((WORD *)rgbInfoValue) = SQL_OSCC_NOT_COMPLIANT;
480         if(pcbInfoValue) { *pcbInfoValue = 2; }
481         break;
482
483     case SQL_ODBC_SQL_CONFORMANCE: /* ODBC 1.0 */
484         *((WORD *)rgbInfoValue) = SQL_OSC_CORE;
485         if(pcbInfoValue) { *pcbInfoValue = 2; }
486         break;
487
488     case SQL_ODBC_SQL_OPT_IEF: /* ODBC 1.0 */
489         // do we support the "Integrity Enhancement Facility" (?)
490         // (something to do with referential integrity?)
491         if (pcbInfoValue) *pcbInfoValue = 1;
492         strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax);
493         break;
494
495     case SQL_ORDER_BY_COLUMNS_IN_SELECT: /* ODBC 2.0 */
496         // do the columns sorted by have to be in the list of
497         // columns selected?
498         if (pcbInfoValue) *pcbInfoValue = 1;
499         strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax);
500         break;
501
502     case SQL_OUTER_JOINS: /* ODBC 1.0 */
503         // do we support outer joins?
504         if (pcbInfoValue) *pcbInfoValue = 1;
505         strncpy_null((char *)rgbInfoValue, "N", (size_t)cbInfoValueMax);
506         break;
507
508     case SQL_OWNER_TERM: /* ODBC 1.0 */
509         // what we call an owner
510         if (pcbInfoValue) *pcbInfoValue = 5;
511         strncpy_null((char *)rgbInfoValue, "owner", (size_t)cbInfoValueMax);
512         break;
513
514     case SQL_OWNER_USAGE: /* ODBC 2.0 */
515         // in which statements can "owners be used"?  (what does that mean?
516         // specifying 'owner.table' instead of just 'table' or something?)
517         // (bitmask)
518         *((DWORD *)rgbInfoValue) = 0;
519         if(pcbInfoValue) { *pcbInfoValue = 4; }
520         break;
521
522     case SQL_POS_OPERATIONS: /* ODBC 2.0 */
523         // what functions does SQLSetPos support? (bitmask)
524         // SQLSetPos does not exist yet
525         *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_POS_POSITION | SQL_POS_REFRESH | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD) : 0;
526         if(pcbInfoValue) { *pcbInfoValue = 4; }
527         break;
528
529     case SQL_POSITIONED_STATEMENTS: /* ODBC 2.0 */
530         // what 'positioned' functions are supported? (bitmask)
531         *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_PS_POSITIONED_DELETE | 
532                                                                                         SQL_PS_POSITIONED_UPDATE | 
533                                                                                         SQL_PS_SELECT_FOR_UPDATE) : 0;
534         if(pcbInfoValue) { *pcbInfoValue = 4; }
535         break;
536
537     case SQL_PROCEDURE_TERM: /* ODBC 1.0 */
538         // what do we call a procedure?
539         if (pcbInfoValue) *pcbInfoValue = 9;
540         strncpy_null((char *)rgbInfoValue, "procedure", (size_t)cbInfoValueMax);
541         break;
542
543     case SQL_PROCEDURES: /* ODBC 1.0 */
544         // do we support procedures?
545         if (pcbInfoValue) *pcbInfoValue = 1;
546         strncpy_null((char *)rgbInfoValue, "Y", (size_t)cbInfoValueMax);
547         break;
548
549     case SQL_QUALIFIER_LOCATION: /* ODBC 2.0 */
550         // where does the qualifier go (before or after the table name?)
551         // we don't really use qualifiers, so...
552         *((WORD *)rgbInfoValue) = SQL_QL_START;
553         if(pcbInfoValue) { *pcbInfoValue = 2; }
554         break;
555
556     case SQL_QUALIFIER_NAME_SEPARATOR: /* ODBC 1.0 */
557         // not really too sure what a qualifier is supposed to do either
558         // (specify the name of a database in certain cases?), so nix
559         // on that, too.
560         if (pcbInfoValue) *pcbInfoValue = 0;
561         strncpy_null((char *)rgbInfoValue, "", (size_t)cbInfoValueMax);
562         break;
563
564     case SQL_QUALIFIER_TERM: /* ODBC 1.0 */
565         // what we call a qualifier
566         if (pcbInfoValue) *pcbInfoValue = 0;
567         strncpy_null((char *)rgbInfoValue, "", (size_t)cbInfoValueMax);
568         break;
569
570     case SQL_QUALIFIER_USAGE: /* ODBC 2.0 */
571         // where can qualifiers be used? (bitmask)
572         // nowhere
573         *((DWORD *)rgbInfoValue) = 0;
574         if(pcbInfoValue) { *pcbInfoValue = 4; }
575         break;
576
577     case SQL_QUOTED_IDENTIFIER_CASE: /* ODBC 2.0 */
578         // are "quoted" identifiers case-sensitive?
579         // well, we don't really let you quote identifiers, so...
580         *((WORD *)rgbInfoValue) = SQL_IC_SENSITIVE;
581         if(pcbInfoValue) { *pcbInfoValue = 2; }
582         break;
583
584     case SQL_ROW_UPDATES: /* ODBC 1.0 */
585         //  Driver doesn't support keyset-driven or mixed cursors, so
586                 //      not much point in saying row updates are supported
587         if (pcbInfoValue) *pcbInfoValue = 1;
588         strncpy_null((char *)rgbInfoValue, globals.lie ? "Y" : "N", (size_t)cbInfoValueMax);
589         break;
590
591     case SQL_SCROLL_CONCURRENCY: /* ODBC 1.0 */
592         // what concurrency options are supported BY THE CURSOR? (bitmask)
593         *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_SCCO_READ_ONLY | 
594                                                                         SQL_SCCO_LOCK | 
595                                                                         SQL_SCCO_OPT_ROWVER | 
596                                                                         SQL_SCCO_OPT_VALUES) : (SQL_SCCO_READ_ONLY);
597         if(pcbInfoValue) { *pcbInfoValue = 4; }
598         break;
599
600     case SQL_SCROLL_OPTIONS: /* ODBC 1.0 */
601         // what options are supported for scrollable cursors? (bitmask)
602                 // for declare/fetch, only FORWARD scrolling is allowed
603                 // otherwise, the result set is STATIC (to SQLExtendedFetch for example)
604         *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_SO_FORWARD_ONLY | 
605                                                                         SQL_SO_STATIC | 
606                                                                         SQL_SO_KEYSET_DRIVEN | 
607                                                                         SQL_SO_DYNAMIC | 
608                                                                         SQL_SO_MIXED) : (globals.use_declarefetch ? SQL_SO_FORWARD_ONLY : (SQL_SO_FORWARD_ONLY | SQL_SO_STATIC));
609         if(pcbInfoValue) { *pcbInfoValue = 4; }
610         break;
611
612     case SQL_SEARCH_PATTERN_ESCAPE: /* ODBC 1.0 */
613         // this is supposed to be the character that escapes '_' or '%'
614         // in LIKE clauses.  as far as I can tell postgres doesn't have one
615         // (backslash generates an error).  returning an empty string means
616         // no escape character is supported.
617         if (pcbInfoValue) *pcbInfoValue = 0;
618         strncpy_null((char *)rgbInfoValue, "", (size_t)cbInfoValueMax);
619         break;
620
621     case SQL_SERVER_NAME: /* ODBC 1.0 */
622                 p = CC_get_server(conn);
623                 if (pcbInfoValue)  *pcbInfoValue = strlen(p);
624                 strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax);
625         break;
626
627     case SQL_SPECIAL_CHARACTERS: /* ODBC 2.0 */
628         // what special characters can be used in table and column names, etc.?
629         // probably more than just this...
630         if (pcbInfoValue) *pcbInfoValue = 1;
631         strncpy_null((char *)rgbInfoValue, "_", (size_t)cbInfoValueMax);
632         break;
633
634     case SQL_STATIC_SENSITIVITY: /* ODBC 2.0 */
635         // can changes made inside a cursor be detected? (or something like that)
636         // (bitmask)
637         // only applies to SQLSetPos, which doesn't exist yet.
638         *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES) : 0;
639         if(pcbInfoValue) { *pcbInfoValue = 4; }
640         break;
641
642     case SQL_STRING_FUNCTIONS: /* ODBC 1.0 */
643         // what string functions exist? (bitmask)
644         *((DWORD *)rgbInfoValue) = (SQL_FN_STR_CONCAT |
645                                                 SQL_FN_STR_LCASE | 
646                                                                         SQL_FN_STR_LENGTH | 
647                                                                         SQL_FN_STR_LOCATE | 
648                                                                         SQL_FN_STR_LTRIM | 
649                                                                         SQL_FN_STR_RTRIM |
650                                                                         SQL_FN_STR_SUBSTRING |
651                                                                         SQL_FN_STR_UCASE);
652         if(pcbInfoValue) { *pcbInfoValue = 4; }
653         break;
654
655     case SQL_SUBQUERIES: /* ODBC 2.0 */
656                 /* postgres 6.3 supports subqueries */
657         *((DWORD *)rgbInfoValue) = (SQL_SQ_QUANTIFIED |
658                                                 SQL_SQ_IN |
659                                     SQL_SQ_EXISTS |
660                                                                     SQL_SQ_COMPARISON);
661         if(pcbInfoValue) { *pcbInfoValue = 4; }
662         break;
663
664     case SQL_SYSTEM_FUNCTIONS: /* ODBC 1.0 */
665         // what system functions are supported? (bitmask)
666         // none of these seem to be supported, either
667         *((DWORD *)rgbInfoValue) = 0;
668         if(pcbInfoValue) { *pcbInfoValue = 4; }
669         break;
670
671     case SQL_TABLE_TERM: /* ODBC 1.0 */
672         // what we call a table
673         if (pcbInfoValue) *pcbInfoValue = 5;
674         strncpy_null((char *)rgbInfoValue, "table", (size_t)cbInfoValueMax);
675         break;
676
677     case SQL_TIMEDATE_ADD_INTERVALS: /* ODBC 2.0 */
678         // what resolutions are supported by the "TIMESTAMPADD scalar
679         // function" (whatever that is)? (bitmask)
680         *((DWORD *)rgbInfoValue) = 0;
681         if(pcbInfoValue) { *pcbInfoValue = 4; }
682         break;
683
684     case SQL_TIMEDATE_DIFF_INTERVALS: /* ODBC 2.0 */
685         // what resolutions are supported by the "TIMESTAMPDIFF scalar
686         // function" (whatever that is)? (bitmask)
687         *((DWORD *)rgbInfoValue) = 0;
688         if(pcbInfoValue) { *pcbInfoValue = 4; }
689         break;
690
691     case SQL_TIMEDATE_FUNCTIONS: /* ODBC 1.0 */
692         // what time and date functions are supported? (bitmask)
693         *((DWORD *)rgbInfoValue) = (SQL_FN_TD_NOW);
694         if(pcbInfoValue) { *pcbInfoValue = 4; }
695         break;
696
697     case SQL_TXN_CAPABLE: /* ODBC 1.0 */
698         *((WORD *)rgbInfoValue) = (WORD)SQL_TC_ALL;
699         // Postgres can deal with create or drop table statements in a transaction
700         if(pcbInfoValue) { *pcbInfoValue = 2; }
701         break;
702
703     case SQL_TXN_ISOLATION_OPTION: /* ODBC 1.0 */
704         // what transaction isolation options are available? (bitmask)
705         // only the default--serializable transactions.
706         *((DWORD *)rgbInfoValue) = SQL_TXN_SERIALIZABLE;
707         if(pcbInfoValue) { *pcbInfoValue = 4; }
708         break;
709
710     case SQL_UNION: /* ODBC 2.0 */
711                 /*  unions with all supported in postgres 6.3 */
712         *((DWORD *)rgbInfoValue) = (SQL_U_UNION | SQL_U_UNION_ALL);
713         if(pcbInfoValue) { *pcbInfoValue = 4; }
714         break;
715
716     case SQL_USER_NAME: /* ODBC 1.0 */
717                 p = CC_get_username(conn);
718         if (pcbInfoValue) *pcbInfoValue = strlen(p);
719         strncpy_null((char *)rgbInfoValue, p, (size_t)cbInfoValueMax);
720         break;
721
722     default:
723         /* unrecognized key */
724         conn->errormsg = "Unrecognized key passed to SQLGetInfo.";
725         conn->errornumber = CONN_NOT_IMPLEMENTED_ERROR;
726                 CC_log_error(func, "", conn);
727         return SQL_ERROR;
728     }
729
730     return SQL_SUCCESS;
731 }
732
733 //      -       -       -       -       -       -       -       -       -
734
735
736 RETCODE SQL_API SQLGetTypeInfo(
737         HSTMT   hstmt,
738         SWORD   fSqlType)
739 {
740 char *func = "SQLGetTypeInfo";
741 StatementClass *stmt = (StatementClass *) hstmt;
742 TupleNode *row;
743 int i;
744 Int4 type;
745
746         mylog("**** in SQLGetTypeInfo: fSqlType = %d\n", fSqlType);
747
748         if( ! stmt) {
749                 SC_log_error(func, "", NULL);
750                 return SQL_INVALID_HANDLE;
751         }
752
753         stmt->manual_result = TRUE;
754         stmt->result = QR_Constructor();
755         if( ! stmt->result) {
756                 SC_log_error(func, "Error creating result.", stmt);
757                 return SQL_ERROR;
758         }
759
760         extend_bindings(stmt, 15);
761
762         QR_set_num_fields(stmt->result, 15);
763         QR_set_field_info(stmt->result, 0, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
764         QR_set_field_info(stmt->result, 1, "DATA_TYPE", PG_TYPE_INT2, 2);
765         QR_set_field_info(stmt->result, 2, "PRECISION", PG_TYPE_INT4, 4);
766         QR_set_field_info(stmt->result, 3, "LITERAL_PREFIX", PG_TYPE_TEXT, MAX_INFO_STRING);
767         QR_set_field_info(stmt->result, 4, "LITERAL_SUFFIX", PG_TYPE_TEXT, MAX_INFO_STRING);
768         QR_set_field_info(stmt->result, 5, "CREATE_PARAMS", PG_TYPE_TEXT, MAX_INFO_STRING);
769         QR_set_field_info(stmt->result, 6, "NULLABLE", PG_TYPE_INT2, 2);
770         QR_set_field_info(stmt->result, 7, "CASE_SENSITIVE", PG_TYPE_INT2, 2);
771         QR_set_field_info(stmt->result, 8, "SEARCHABLE", PG_TYPE_INT2, 2);
772         QR_set_field_info(stmt->result, 9, "UNSIGNED_ATTRIBUTE", PG_TYPE_INT2, 2);
773         QR_set_field_info(stmt->result, 10, "MONEY", PG_TYPE_INT2, 2);
774         QR_set_field_info(stmt->result, 11, "AUTO_INCREMENT", PG_TYPE_INT2, 2);
775         QR_set_field_info(stmt->result, 12, "LOCAL_TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
776         QR_set_field_info(stmt->result, 13, "MINIMUM_SCALE", PG_TYPE_INT2, 2);
777         QR_set_field_info(stmt->result, 14, "MAXIMUM_SCALE", PG_TYPE_INT2, 2);
778
779     // cycle through the types
780     for(i=0, type = pgtypes_defined[0]; type; type = pgtypes_defined[++i]) {
781
782                 if(fSqlType == SQL_ALL_TYPES || fSqlType == pgtype_to_sqltype(stmt, type)) {
783
784                         row = (TupleNode *)malloc(sizeof(TupleNode) + (15 - 1)*sizeof(TupleField));
785
786                         /*      These values can't be NULL */
787                         set_tuplefield_string(&row->tuple[0], pgtype_to_name(stmt, type));
788                         set_tuplefield_int2(&row->tuple[1], pgtype_to_sqltype(stmt, type));
789                         set_tuplefield_int2(&row->tuple[6], pgtype_nullable(stmt, type));
790                         set_tuplefield_int2(&row->tuple[7], pgtype_case_sensitive(stmt, type));
791                         set_tuplefield_int2(&row->tuple[8], pgtype_searchable(stmt, type));
792                         set_tuplefield_int2(&row->tuple[10], pgtype_money(stmt, type));
793
794                         /*      Localized data-source dependent data type name (always NULL) */
795                         set_tuplefield_null(&row->tuple[12]);   
796
797                         /*      These values can be NULL */
798                         set_nullfield_int4(&row->tuple[2], pgtype_precision(stmt, type, PG_STATIC, PG_STATIC));
799                         set_nullfield_string(&row->tuple[3], pgtype_literal_prefix(stmt, type));
800                         set_nullfield_string(&row->tuple[4], pgtype_literal_suffix(stmt, type));
801                         set_nullfield_string(&row->tuple[5], pgtype_create_params(stmt, type));
802                         set_nullfield_int2(&row->tuple[9], pgtype_unsigned(stmt, type));
803                         set_nullfield_int2(&row->tuple[11], pgtype_auto_increment(stmt, type));
804                         set_nullfield_int2(&row->tuple[13], pgtype_scale(stmt, type));
805                         set_nullfield_int2(&row->tuple[14], pgtype_scale(stmt, type));
806
807                         QR_add_tuple(stmt->result, row);
808                 }
809     }
810
811     stmt->status = STMT_FINISHED;
812     stmt->currTuple = -1;
813         stmt->current_col = -1;
814
815     return SQL_SUCCESS;
816 }
817
818 //      -       -       -       -       -       -       -       -       -
819
820 RETCODE SQL_API SQLGetFunctions(
821         HDBC      hdbc,
822         UWORD     fFunction,
823         UWORD FAR *pfExists)
824 {
825     if (fFunction == SQL_API_ALL_FUNCTIONS) {
826
827                 if (globals.lie) {
828                         int i;
829                         memset(pfExists, 0, sizeof(UWORD)*100);
830
831                         pfExists[SQL_API_SQLALLOCENV] = TRUE;
832                         pfExists[SQL_API_SQLFREEENV] = TRUE;
833                         for (i = SQL_API_SQLALLOCCONNECT; i <= SQL_NUM_FUNCTIONS; i++)
834                                 pfExists[i] = TRUE;
835                         for (i = SQL_EXT_API_START; i <= SQL_EXT_API_LAST; i++)
836                                 pfExists[i] = TRUE;
837                 }
838                 else {
839                         memset(pfExists, 0, sizeof(UWORD)*100);
840
841                         // ODBC core functions
842                         pfExists[SQL_API_SQLALLOCCONNECT]     = TRUE;
843                         pfExists[SQL_API_SQLALLOCENV]         = TRUE;
844                         pfExists[SQL_API_SQLALLOCSTMT]        = TRUE;
845                         pfExists[SQL_API_SQLBINDCOL]          = TRUE;  
846                         pfExists[SQL_API_SQLCANCEL]           = TRUE;
847                         pfExists[SQL_API_SQLCOLATTRIBUTES]    = TRUE;
848                         pfExists[SQL_API_SQLCONNECT]          = TRUE;
849                         pfExists[SQL_API_SQLDESCRIBECOL]      = TRUE;  // partial
850                         pfExists[SQL_API_SQLDISCONNECT]       = TRUE;
851                         pfExists[SQL_API_SQLERROR]            = TRUE;
852                         pfExists[SQL_API_SQLEXECDIRECT]       = TRUE;
853                         pfExists[SQL_API_SQLEXECUTE]          = TRUE;
854                         pfExists[SQL_API_SQLFETCH]            = TRUE;
855                         pfExists[SQL_API_SQLFREECONNECT]      = TRUE;
856                         pfExists[SQL_API_SQLFREEENV]          = TRUE;
857                         pfExists[SQL_API_SQLFREESTMT]         = TRUE;
858                         pfExists[SQL_API_SQLGETCURSORNAME]    = TRUE;
859                         pfExists[SQL_API_SQLNUMRESULTCOLS]    = TRUE;
860                         pfExists[SQL_API_SQLPREPARE]          = TRUE;  // complete?
861                         pfExists[SQL_API_SQLROWCOUNT]         = TRUE;
862                         pfExists[SQL_API_SQLSETCURSORNAME]    = TRUE;
863                         pfExists[SQL_API_SQLSETPARAM]         = FALSE; // odbc 1.0
864                         pfExists[SQL_API_SQLTRANSACT]         = TRUE;
865
866                         // ODBC level 1 functions
867                         pfExists[SQL_API_SQLBINDPARAMETER]    = TRUE;
868                         pfExists[SQL_API_SQLCOLUMNS]          = TRUE;
869                         pfExists[SQL_API_SQLDRIVERCONNECT]    = TRUE;
870                         pfExists[SQL_API_SQLGETCONNECTOPTION] = TRUE;  // partial
871                         pfExists[SQL_API_SQLGETDATA]          = TRUE;
872                         pfExists[SQL_API_SQLGETFUNCTIONS]     = TRUE;                                                       
873                         pfExists[SQL_API_SQLGETINFO]          = TRUE;
874                         pfExists[SQL_API_SQLGETSTMTOPTION]    = TRUE;  // partial
875                         pfExists[SQL_API_SQLGETTYPEINFO]      = TRUE;
876                         pfExists[SQL_API_SQLPARAMDATA]        = TRUE;
877                         pfExists[SQL_API_SQLPUTDATA]          = TRUE;
878                         pfExists[SQL_API_SQLSETCONNECTOPTION] = TRUE;  // partial
879                         pfExists[SQL_API_SQLSETSTMTOPTION]    = TRUE;
880                         pfExists[SQL_API_SQLSPECIALCOLUMNS]   = TRUE;
881                         pfExists[SQL_API_SQLSTATISTICS]       = TRUE;
882                         pfExists[SQL_API_SQLTABLES]           = TRUE;
883
884                         // ODBC level 2 functions
885                         pfExists[SQL_API_SQLBROWSECONNECT]    = FALSE;
886                         pfExists[SQL_API_SQLCOLUMNPRIVILEGES] = FALSE;
887                         pfExists[SQL_API_SQLDATASOURCES]      = FALSE;  // only implemented by DM
888                         pfExists[SQL_API_SQLDESCRIBEPARAM]    = FALSE;  // not properly implemented
889                         pfExists[SQL_API_SQLDRIVERS]          = FALSE;  // only implemented by DM
890                         pfExists[SQL_API_SQLEXTENDEDFETCH]    = globals.use_declarefetch ? FALSE : TRUE;
891                         pfExists[SQL_API_SQLFOREIGNKEYS]      = TRUE;
892                         pfExists[SQL_API_SQLMORERESULTS]      = TRUE;
893                         pfExists[SQL_API_SQLNATIVESQL]        = TRUE;
894                         pfExists[SQL_API_SQLNUMPARAMS]        = TRUE;
895                         pfExists[SQL_API_SQLPARAMOPTIONS]     = FALSE;
896                         pfExists[SQL_API_SQLPRIMARYKEYS]      = TRUE;
897                         pfExists[SQL_API_SQLPROCEDURECOLUMNS] = FALSE;
898                         pfExists[SQL_API_SQLPROCEDURES]       = FALSE;
899                         pfExists[SQL_API_SQLSETPOS]           = FALSE;
900                         pfExists[SQL_API_SQLSETSCROLLOPTIONS] = FALSE;  // odbc 1.0
901                         pfExists[SQL_API_SQLTABLEPRIVILEGES]  = FALSE;
902                 }
903     } else {
904
905                 if (globals.lie)
906                         *pfExists = TRUE;
907
908                 else {
909
910                         switch(fFunction) {
911                         case SQL_API_SQLALLOCCONNECT:     *pfExists = TRUE; break;
912                         case SQL_API_SQLALLOCENV:         *pfExists = TRUE; break;
913                         case SQL_API_SQLALLOCSTMT:        *pfExists = TRUE; break;
914                         case SQL_API_SQLBINDCOL:          *pfExists = TRUE; break;
915                         case SQL_API_SQLCANCEL:           *pfExists = TRUE; break;
916                         case SQL_API_SQLCOLATTRIBUTES:    *pfExists = TRUE; break;
917                         case SQL_API_SQLCONNECT:          *pfExists = TRUE; break;
918                         case SQL_API_SQLDESCRIBECOL:      *pfExists = TRUE; break;  // partial
919                         case SQL_API_SQLDISCONNECT:       *pfExists = TRUE; break;
920                         case SQL_API_SQLERROR:            *pfExists = TRUE; break;
921                         case SQL_API_SQLEXECDIRECT:       *pfExists = TRUE; break;
922                         case SQL_API_SQLEXECUTE:          *pfExists = TRUE; break;
923                         case SQL_API_SQLFETCH:            *pfExists = TRUE; break;
924                         case SQL_API_SQLFREECONNECT:      *pfExists = TRUE; break;
925                         case SQL_API_SQLFREEENV:          *pfExists = TRUE; break;
926                         case SQL_API_SQLFREESTMT:         *pfExists = TRUE; break;
927                         case SQL_API_SQLGETCURSORNAME:    *pfExists = TRUE; break;
928                         case SQL_API_SQLNUMRESULTCOLS:    *pfExists = TRUE; break;
929                         case SQL_API_SQLPREPARE:          *pfExists = TRUE; break;
930                         case SQL_API_SQLROWCOUNT:         *pfExists = TRUE; break;
931                         case SQL_API_SQLSETCURSORNAME:    *pfExists = TRUE; break;
932                         case SQL_API_SQLSETPARAM:         *pfExists = FALSE; break; // odbc 1.0
933                         case SQL_API_SQLTRANSACT:         *pfExists = TRUE; break;
934
935                                 // ODBC level 1 functions
936                         case SQL_API_SQLBINDPARAMETER:    *pfExists = TRUE; break;
937                         case SQL_API_SQLCOLUMNS:          *pfExists = TRUE; break;
938                         case SQL_API_SQLDRIVERCONNECT:    *pfExists = TRUE; break;
939                         case SQL_API_SQLGETCONNECTOPTION: *pfExists = TRUE; break;  // partial
940                         case SQL_API_SQLGETDATA:          *pfExists = TRUE; break;
941                         case SQL_API_SQLGETFUNCTIONS:     *pfExists = TRUE; break;
942                         case SQL_API_SQLGETINFO:          *pfExists = TRUE; break;
943                         case SQL_API_SQLGETSTMTOPTION:    *pfExists = TRUE; break;  // partial
944                         case SQL_API_SQLGETTYPEINFO:      *pfExists = TRUE; break;
945                         case SQL_API_SQLPARAMDATA:        *pfExists = TRUE; break;
946                         case SQL_API_SQLPUTDATA:          *pfExists = TRUE; break;
947                         case SQL_API_SQLSETCONNECTOPTION: *pfExists = TRUE; break;  // partial
948                         case SQL_API_SQLSETSTMTOPTION:    *pfExists = TRUE; break;
949                         case SQL_API_SQLSPECIALCOLUMNS:   *pfExists = TRUE; break;
950                         case SQL_API_SQLSTATISTICS:       *pfExists = TRUE; break;
951                         case SQL_API_SQLTABLES:           *pfExists = TRUE; break;
952
953                                 // ODBC level 2 functions
954                         case SQL_API_SQLBROWSECONNECT:    *pfExists = FALSE; break;
955                         case SQL_API_SQLCOLUMNPRIVILEGES: *pfExists = FALSE; break;
956                         case SQL_API_SQLDATASOURCES:      *pfExists = FALSE; break;  // only implemented by DM
957                         case SQL_API_SQLDESCRIBEPARAM:    *pfExists = FALSE; break;  // not properly implemented
958                         case SQL_API_SQLDRIVERS:          *pfExists = FALSE; break;  // only implemented by DM
959                         case SQL_API_SQLEXTENDEDFETCH:    *pfExists = globals.use_declarefetch ? FALSE : TRUE; break;
960                         case SQL_API_SQLFOREIGNKEYS:      *pfExists = TRUE; break;
961                         case SQL_API_SQLMORERESULTS:      *pfExists = TRUE; break;
962                         case SQL_API_SQLNATIVESQL:        *pfExists = TRUE; break;
963                         case SQL_API_SQLNUMPARAMS:        *pfExists = TRUE; break;
964                         case SQL_API_SQLPARAMOPTIONS:     *pfExists = FALSE; break;
965                         case SQL_API_SQLPRIMARYKEYS:      *pfExists = TRUE; break;
966                         case SQL_API_SQLPROCEDURECOLUMNS: *pfExists = FALSE; break;
967                         case SQL_API_SQLPROCEDURES:       *pfExists = FALSE; break;
968                         case SQL_API_SQLSETPOS:           *pfExists = FALSE; break;
969                         case SQL_API_SQLSETSCROLLOPTIONS: *pfExists = FALSE; break;     // odbc 1.0
970                         case SQL_API_SQLTABLEPRIVILEGES:  *pfExists = FALSE; break;
971                         }
972                 }
973     }
974
975     return SQL_SUCCESS;
976 }
977
978
979
980 RETCODE SQL_API SQLTables(
981                           HSTMT       hstmt,
982                           UCHAR FAR * szTableQualifier,
983                           SWORD       cbTableQualifier,
984                           UCHAR FAR * szTableOwner,
985                           SWORD       cbTableOwner,
986                           UCHAR FAR * szTableName,
987                           SWORD       cbTableName,
988                           UCHAR FAR * szTableType,
989                           SWORD       cbTableType)
990 {
991 char *func = "SQLTables";
992 StatementClass *stmt = (StatementClass *) hstmt;
993 StatementClass *tbl_stmt;
994 TupleNode *row;
995 HSTMT htbl_stmt;
996 RETCODE result;
997 char *tableType;
998 char tables_query[MAX_STATEMENT_LEN];
999 char table_name[MAX_INFO_STRING], table_owner[MAX_INFO_STRING], relhasrules[MAX_INFO_STRING];
1000 ConnInfo *ci;
1001 char *prefix[32], prefixes[MEDIUM_REGISTRY_LEN];
1002 char *table_type[32], table_types[MAX_INFO_STRING];
1003 char show_system_tables, show_regular_tables, show_views;
1004 char regular_table, view, systable;
1005 int i;
1006
1007 mylog("**** SQLTables(): ENTER, stmt=%u\n", stmt);
1008
1009         if( ! stmt) {
1010                 SC_log_error(func, "", NULL);
1011                 return SQL_INVALID_HANDLE;
1012         }
1013
1014         stmt->manual_result = TRUE;
1015         stmt->errormsg_created = TRUE;
1016
1017         ci = &stmt->hdbc->connInfo;
1018
1019         result = SQLAllocStmt( stmt->hdbc, &htbl_stmt);
1020         if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1021                 stmt->errornumber = STMT_NO_MEMORY_ERROR;
1022                 stmt->errormsg = "Couldn't allocate statement for SQLTables result.";
1023                 SC_log_error(func, "", stmt);
1024                 return SQL_ERROR;
1025         }
1026         tbl_stmt = (StatementClass *) htbl_stmt;
1027
1028         // **********************************************************************
1029         //      Create the query to find out the tables
1030         // **********************************************************************
1031
1032         strcpy(tables_query, "select relname, usename, relhasrules from pg_class, pg_user where relkind = 'r' ");
1033
1034         my_strcat(tables_query, " and usename like '%.*s'", szTableOwner, cbTableOwner);
1035         my_strcat(tables_query, " and relname like '%.*s'", szTableName, cbTableName);
1036
1037
1038         //      Parse the extra systable prefix 
1039         strcpy(prefixes, globals.extra_systable_prefixes);
1040         i = 0;
1041         prefix[i] = strtok(prefixes, ";");
1042         while (prefix[i] && i<32) {
1043                 prefix[++i] = strtok(NULL, ";");
1044         }
1045
1046         /*      Parse the desired table types to return */
1047         show_system_tables = FALSE;
1048         show_regular_tables = FALSE;
1049         show_views = FALSE;
1050
1051         /*      make_string mallocs memory */
1052         tableType = make_string(szTableType, cbTableType, NULL);
1053         if (tableType) {
1054                 strcpy(table_types, tableType);
1055                 free(tableType);
1056                 i = 0;
1057                 table_type[i] = strtok(table_types, ",");
1058                 while (table_type[i] && i<32) {
1059                         table_type[++i] = strtok(NULL, ",");
1060                 }
1061
1062                 /*      Check for desired table types to return */
1063                 i = 0;
1064                 while (table_type[i]) {
1065                         if ( strstr(table_type[i], "SYSTEM TABLE"))
1066                                 show_system_tables = TRUE;
1067                         else if ( strstr(table_type[i], "TABLE"))
1068                                 show_regular_tables = TRUE;
1069                         else if ( strstr(table_type[i], "VIEW"))
1070                                 show_views = TRUE;
1071
1072                         i++;
1073                 }
1074         }
1075         else {
1076                 show_regular_tables = TRUE;
1077                 show_views = TRUE;
1078         }
1079
1080         /*  If not interested in SYSTEM TABLES then filter them out
1081                 to save some time on the query.  If treating system tables
1082                 as regular tables, then dont filter either.
1083         */
1084         if ( ! atoi(ci->show_system_tables) && ! show_system_tables) {
1085                 strcat(tables_query, " and relname !~ '^" POSTGRES_SYS_PREFIX);
1086
1087                 /*      Also filter out user-defined system table types */
1088                 i = 0;
1089                 while(prefix[i]) {
1090                         strcat(tables_query, "|^");
1091                         strcat(tables_query, prefix[i]);
1092                         i++;
1093                 }
1094
1095                 strcat(tables_query, "'");
1096         }
1097
1098
1099         /*      filter out large objects unconditionally (they are not system tables) and match users */
1100         strcat(tables_query, " and relname !~ '^xinv[0-9]+' and int4out(usesysid) = int4out(relowner) order by relname");
1101
1102         // **********************************************************************
1103
1104         result = SQLExecDirect(htbl_stmt, tables_query, strlen(tables_query));
1105         if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1106                 stmt->errormsg = SC_create_errormsg(htbl_stmt);
1107                 stmt->errornumber = tbl_stmt->errornumber;
1108                 SC_log_error(func, "", stmt);
1109                 SQLFreeStmt(htbl_stmt, SQL_DROP);
1110                 return SQL_ERROR;
1111         }
1112
1113     result = SQLBindCol(htbl_stmt, 1, SQL_C_CHAR,
1114                         table_name, MAX_INFO_STRING, NULL);
1115     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1116                 stmt->errormsg = tbl_stmt->errormsg;
1117                 stmt->errornumber = tbl_stmt->errornumber;
1118                 SC_log_error(func, "", stmt);
1119                 SQLFreeStmt(htbl_stmt, SQL_DROP);
1120         return SQL_ERROR;
1121     }
1122
1123     result = SQLBindCol(htbl_stmt, 2, SQL_C_CHAR,
1124                         table_owner, MAX_INFO_STRING, NULL);
1125     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1126                 stmt->errormsg = tbl_stmt->errormsg;
1127                 stmt->errornumber = tbl_stmt->errornumber;
1128                 SC_log_error(func, "", stmt);
1129                 SQLFreeStmt(htbl_stmt, SQL_DROP);
1130         return SQL_ERROR;
1131     }
1132     result = SQLBindCol(htbl_stmt, 3, SQL_C_CHAR,
1133                         relhasrules, MAX_INFO_STRING, NULL);
1134     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1135                 stmt->errormsg = tbl_stmt->errormsg;
1136                 stmt->errornumber = tbl_stmt->errornumber;
1137                 SC_log_error(func, "", stmt);
1138                 SQLFreeStmt(htbl_stmt, SQL_DROP);
1139         return SQL_ERROR;
1140     }
1141
1142         stmt->result = QR_Constructor();
1143         if(!stmt->result) {
1144                 stmt->errormsg = "Couldn't allocate memory for SQLTables result.";
1145                 stmt->errornumber = STMT_NO_MEMORY_ERROR;
1146                 SC_log_error(func, "", stmt);
1147                 SQLFreeStmt(htbl_stmt, SQL_DROP);
1148                 return SQL_ERROR;
1149         }
1150
1151     // the binding structure for a statement is not set up until
1152     // a statement is actually executed, so we'll have to do this ourselves.
1153         extend_bindings(stmt, 5);
1154         
1155     // set the field names
1156         QR_set_num_fields(stmt->result, 5);
1157         QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
1158         QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
1159         QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
1160         QR_set_field_info(stmt->result, 3, "TABLE_TYPE", PG_TYPE_TEXT, MAX_INFO_STRING);
1161         QR_set_field_info(stmt->result, 4, "REMARKS", PG_TYPE_TEXT, 254);
1162
1163     // add the tuples
1164         result = SQLFetch(htbl_stmt);
1165         while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) {
1166
1167                 /*      Determine if this table name is a system table.
1168                         If treating system tables as regular tables, then 
1169                         no need to do this test.
1170                 */              
1171                 systable = FALSE;
1172                 if( ! atoi(ci->show_system_tables)) {
1173
1174                         if ( strncmp(table_name, POSTGRES_SYS_PREFIX, strlen(POSTGRES_SYS_PREFIX)) == 0)
1175                                 systable = TRUE;
1176
1177                         else {                  /* Check extra system table prefixes */
1178                                 i = 0;
1179                                 while (prefix[i]) {
1180                                         mylog("table_name='%s', prefix[%d]='%s'\n", table_name, i, prefix[i]);
1181                                         if (strncmp(table_name, prefix[i], strlen(prefix[i])) == 0) {
1182                                                 systable = TRUE;
1183                                                 break;
1184                                         }
1185                                         i++;
1186                                 }
1187                         }
1188                 }
1189
1190                 /*      Determine if the table name is a view */
1191                 view = (relhasrules[0] == '1');
1192
1193                 /*      It must be a regular table */
1194                 regular_table = ( ! systable && ! view);
1195
1196
1197                 /*      Include the row in the result set if meets all criteria */
1198                 /*      NOTE: Unsupported table types (i.e., LOCAL TEMPORARY, ALIAS, etc)
1199                                         will return nothing */
1200                 if ( (systable && show_system_tables) ||
1201                          (view && show_views) || 
1202                          (regular_table && show_regular_tables)) {
1203
1204                         row = (TupleNode *)malloc(sizeof(TupleNode) + (5 - 1) * sizeof(TupleField));
1205
1206                         set_tuplefield_string(&row->tuple[0], "");
1207
1208                         // I have to hide the table owner from Access, otherwise it
1209                         // insists on referring to the table as 'owner.table'.
1210                         // (this is valid according to the ODBC SQL grammar, but
1211                         // Postgres won't support it.)
1212                         // set_tuplefield_string(&row->tuple[1], table_owner);
1213
1214                         mylog("SQLTables: table_name = '%s'\n", table_name);
1215
1216                         set_tuplefield_string(&row->tuple[1], "");
1217                         set_tuplefield_string(&row->tuple[2], table_name);
1218                         set_tuplefield_string(&row->tuple[3], systable ? "SYSTEM TABLE" : (view ? "VIEW" : "TABLE"));
1219                         set_tuplefield_string(&row->tuple[4], "");
1220
1221                         QR_add_tuple(stmt->result, row);
1222                 }
1223                 result = SQLFetch(htbl_stmt);
1224     }
1225         if(result != SQL_NO_DATA_FOUND) {
1226                 stmt->errormsg = SC_create_errormsg(htbl_stmt);
1227                 stmt->errornumber = tbl_stmt->errornumber;
1228                 SC_log_error(func, "", stmt);
1229                 SQLFreeStmt(htbl_stmt, SQL_DROP);
1230                 return SQL_ERROR;
1231         }
1232
1233     // also, things need to think that this statement is finished so
1234     // the results can be retrieved.
1235         stmt->status = STMT_FINISHED;
1236
1237     // set up the current tuple pointer for SQLFetch
1238         stmt->currTuple = -1;
1239         stmt->current_col = -1;
1240
1241         SQLFreeStmt(htbl_stmt, SQL_DROP);
1242         mylog("SQLTables(): EXIT,  stmt=%u\n", stmt);
1243         return SQL_SUCCESS;
1244 }
1245
1246
1247
1248
1249 RETCODE SQL_API SQLColumns(
1250                            HSTMT        hstmt,
1251                            UCHAR FAR *  szTableQualifier,
1252                            SWORD        cbTableQualifier,
1253                            UCHAR FAR *  szTableOwner,
1254                            SWORD        cbTableOwner,
1255                            UCHAR FAR *  szTableName,
1256                            SWORD        cbTableName,
1257                            UCHAR FAR *  szColumnName,
1258                            SWORD        cbColumnName)
1259 {
1260 char *func = "SQLColumns";
1261 StatementClass *stmt = (StatementClass *) hstmt;
1262 TupleNode *row;
1263 HSTMT hcol_stmt;
1264 StatementClass *col_stmt;
1265 char columns_query[MAX_STATEMENT_LEN];
1266 RETCODE result;
1267 char table_owner[MAX_INFO_STRING], table_name[MAX_INFO_STRING], field_name[MAX_INFO_STRING], field_type_name[MAX_INFO_STRING];
1268 Int2 field_number, field_length, mod_length, result_cols;
1269 Int4 field_type, the_type;
1270 char not_null[MAX_INFO_STRING];
1271 ConnInfo *ci;
1272
1273
1274
1275 mylog("**** SQLColumns(): ENTER, stmt=%u\n", stmt);
1276
1277         if( ! stmt) {
1278                 SC_log_error(func, "", NULL);
1279                 return SQL_INVALID_HANDLE;
1280         }
1281
1282         stmt->manual_result = TRUE;
1283         stmt->errormsg_created = TRUE;
1284
1285         ci = &stmt->hdbc->connInfo;
1286
1287         // **********************************************************************
1288         //      Create the query to find out the columns (Note: pre 6.3 did not have the atttypmod field)
1289         // **********************************************************************
1290         sprintf(columns_query, "select u.usename, c.relname, a.attname, a.atttypid,t.typname, a.attnum, a.attlen, %s, a.attnotnull from pg_user u, pg_class c, pg_attribute a, pg_type t where "
1291                 "int4out(u.usesysid) = int4out(c.relowner) and c.oid= a.attrelid and a.atttypid = t.oid and (a.attnum > 0)",
1292                 PROTOCOL_62(ci) ? "a.attlen" : "a.atttypmod");
1293
1294         my_strcat(columns_query, " and c.relname like '%.*s'", szTableName, cbTableName);
1295         my_strcat(columns_query, " and u.usename like '%.*s'", szTableOwner, cbTableOwner);
1296         my_strcat(columns_query, " and a.attname like '%.*s'", szColumnName, cbColumnName);
1297
1298     // give the output in the order the columns were defined
1299     // when the table was created
1300     strcat(columns_query, " order by attnum");
1301         // **********************************************************************
1302
1303     result = SQLAllocStmt( stmt->hdbc, &hcol_stmt);
1304     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1305                 stmt->errornumber = STMT_NO_MEMORY_ERROR;
1306                 stmt->errormsg = "Couldn't allocate statement for SQLColumns result.";
1307                 SC_log_error(func, "", stmt);
1308         return SQL_ERROR;
1309     }
1310         col_stmt = (StatementClass *) hcol_stmt;
1311
1312     result = SQLExecDirect(hcol_stmt, columns_query,
1313                            strlen(columns_query));
1314     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1315                 stmt->errormsg = SC_create_errormsg(hcol_stmt);
1316                 stmt->errornumber = col_stmt->errornumber;
1317                 SC_log_error(func, "", stmt);
1318                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1319         return SQL_ERROR;
1320     }
1321
1322     result = SQLBindCol(hcol_stmt, 1, SQL_C_CHAR,
1323                         table_owner, MAX_INFO_STRING, NULL);
1324     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1325                 stmt->errormsg = col_stmt->errormsg;
1326                 stmt->errornumber = col_stmt->errornumber;
1327                 SC_log_error(func, "", stmt);
1328                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1329         return SQL_ERROR;
1330     }
1331
1332     result = SQLBindCol(hcol_stmt, 2, SQL_C_CHAR,
1333                         table_name, MAX_INFO_STRING, NULL);
1334     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1335                 stmt->errormsg = col_stmt->errormsg;
1336                 stmt->errornumber = col_stmt->errornumber;
1337                 SC_log_error(func, "", stmt);
1338                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1339         return SQL_ERROR;
1340     }
1341
1342     result = SQLBindCol(hcol_stmt, 3, SQL_C_CHAR,
1343                         field_name, MAX_INFO_STRING, NULL);
1344     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1345                 stmt->errormsg = col_stmt->errormsg;
1346                 stmt->errornumber = col_stmt->errornumber;
1347                 SC_log_error(func, "", stmt);
1348                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1349         return SQL_ERROR;
1350     }
1351
1352     result = SQLBindCol(hcol_stmt, 4, SQL_C_DEFAULT,
1353                         &field_type, 4, NULL);
1354     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1355                 stmt->errormsg = col_stmt->errormsg;
1356                 stmt->errornumber = col_stmt->errornumber;
1357                 SC_log_error(func, "", stmt);
1358                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1359         return SQL_ERROR;
1360     }
1361
1362     result = SQLBindCol(hcol_stmt, 5, SQL_C_CHAR,
1363                         field_type_name, MAX_INFO_STRING, NULL);
1364     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1365                 stmt->errormsg = col_stmt->errormsg;
1366                 stmt->errornumber = col_stmt->errornumber;
1367                 SC_log_error(func, "", stmt);
1368                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1369         return SQL_ERROR;
1370     }
1371
1372     result = SQLBindCol(hcol_stmt, 6, SQL_C_DEFAULT,
1373                         &field_number, MAX_INFO_STRING, NULL);
1374     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1375                 stmt->errormsg = col_stmt->errormsg;
1376                 stmt->errornumber = col_stmt->errornumber;
1377                 SC_log_error(func, "", stmt);
1378                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1379         return SQL_ERROR;
1380     }
1381
1382     result = SQLBindCol(hcol_stmt, 7, SQL_C_DEFAULT,
1383                         &field_length, MAX_INFO_STRING, NULL);
1384     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1385                 stmt->errormsg = col_stmt->errormsg;
1386                 stmt->errornumber = col_stmt->errornumber;
1387                 SC_log_error(func, "", stmt);
1388                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1389         return SQL_ERROR;
1390     }
1391
1392     result = SQLBindCol(hcol_stmt, 8, SQL_C_DEFAULT,
1393                         &mod_length, MAX_INFO_STRING, NULL);
1394     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1395                 stmt->errormsg = col_stmt->errormsg;
1396                 stmt->errornumber = col_stmt->errornumber;
1397                 SC_log_error(func, "", stmt);
1398                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1399         return SQL_ERROR;
1400     }
1401
1402     result = SQLBindCol(hcol_stmt, 9, SQL_C_CHAR,
1403                         not_null, MAX_INFO_STRING, NULL);
1404     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1405                 stmt->errormsg = col_stmt->errormsg;
1406                 stmt->errornumber = col_stmt->errornumber;
1407                 SC_log_error(func, "", stmt);
1408                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1409         return SQL_ERROR;
1410     }
1411
1412     stmt->result = QR_Constructor();
1413     if(!stmt->result) {
1414                 stmt->errormsg = "Couldn't allocate memory for SQLColumns result.";
1415         stmt->errornumber = STMT_NO_MEMORY_ERROR;
1416                 SC_log_error(func, "", stmt);
1417                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1418         return SQL_ERROR;
1419     }
1420
1421     // the binding structure for a statement is not set up until
1422     // a statement is actually executed, so we'll have to do this ourselves.
1423         result_cols = 14;
1424     extend_bindings(stmt, result_cols);
1425
1426     // set the field names
1427     QR_set_num_fields(stmt->result, result_cols);
1428     QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
1429     QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
1430     QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
1431     QR_set_field_info(stmt->result, 3, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
1432     QR_set_field_info(stmt->result, 4, "DATA_TYPE", PG_TYPE_INT2, 2);
1433     QR_set_field_info(stmt->result, 5, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
1434     QR_set_field_info(stmt->result, 6, "PRECISION", PG_TYPE_INT4, 4);
1435     QR_set_field_info(stmt->result, 7, "LENGTH", PG_TYPE_INT4, 4);
1436     QR_set_field_info(stmt->result, 8, "SCALE", PG_TYPE_INT2, 2);
1437     QR_set_field_info(stmt->result, 9, "RADIX", PG_TYPE_INT2, 2);
1438     QR_set_field_info(stmt->result, 10, "NULLABLE", PG_TYPE_INT2, 2);
1439     QR_set_field_info(stmt->result, 11, "REMARKS", PG_TYPE_TEXT, 254);
1440
1441         //      User defined fields
1442     QR_set_field_info(stmt->result, 12, "DISPLAY_SIZE", PG_TYPE_INT4, 4);
1443         QR_set_field_info(stmt->result, 13, "FIELD_TYPE", PG_TYPE_INT4, 4);
1444
1445         
1446         result = SQLFetch(hcol_stmt);
1447
1448         /*      Only show oid if option AND there are other columns AND 
1449                 its not being called by SQLStatistics .
1450                 Always show OID if its a system table
1451         */
1452
1453         if (result != SQL_ERROR && ! stmt->internal) {
1454
1455                 if (atoi(ci->show_oid_column) || strncmp(table_name, POSTGRES_SYS_PREFIX, strlen(POSTGRES_SYS_PREFIX)) == 0) {
1456
1457                         /*      For OID fields */
1458                         the_type = PG_TYPE_OID;
1459                         row = (TupleNode *)malloc(sizeof(TupleNode) +
1460                                                                           (result_cols - 1) * sizeof(TupleField));
1461
1462                         set_tuplefield_string(&row->tuple[0], "");
1463                         // see note in SQLTables()
1464                         //      set_tuplefield_string(&row->tuple[1], table_owner);
1465                         set_tuplefield_string(&row->tuple[1], "");
1466                         set_tuplefield_string(&row->tuple[2], table_name);
1467                         set_tuplefield_string(&row->tuple[3], "oid");
1468                         set_tuplefield_int2(&row->tuple[4], pgtype_to_sqltype(stmt, the_type));
1469                         set_tuplefield_string(&row->tuple[5], "OID");
1470
1471                         set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC));
1472                         set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, the_type, PG_STATIC, PG_STATIC));
1473
1474                         set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, the_type));
1475                         set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, the_type));
1476                         set_tuplefield_int2(&row->tuple[10], SQL_NO_NULLS);
1477                         set_tuplefield_string(&row->tuple[11], "");
1478
1479                         set_tuplefield_int4(&row->tuple[12], pgtype_display_size(stmt, the_type, PG_STATIC, PG_STATIC));
1480                         set_tuplefield_int4(&row->tuple[13], the_type);
1481
1482                         QR_add_tuple(stmt->result, row);
1483                 }
1484
1485         }
1486
1487     while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) {
1488         row = (TupleNode *)malloc(sizeof(TupleNode) +
1489                                   (result_cols - 1) * sizeof(TupleField));
1490
1491         set_tuplefield_string(&row->tuple[0], "");
1492         // see note in SQLTables()
1493         //      set_tuplefield_string(&row->tuple[1], table_owner);
1494         set_tuplefield_string(&row->tuple[1], "");
1495         set_tuplefield_string(&row->tuple[2], table_name);
1496         set_tuplefield_string(&row->tuple[3], field_name);
1497         set_tuplefield_int2(&row->tuple[4], pgtype_to_sqltype(stmt, field_type));
1498         set_tuplefield_string(&row->tuple[5], field_type_name);
1499
1500
1501                 /*      Some Notes about Postgres Data Types:
1502
1503                         VARCHAR - the length is stored in the pg_attribute.atttypmod field
1504                         BPCHAR  - the length is also stored as varchar is
1505
1506                 */
1507         if((field_type == PG_TYPE_VARCHAR) ||
1508                    (field_type == PG_TYPE_BPCHAR)) {
1509
1510                         if (mod_length >= 4)
1511                                 mod_length -= 4;                        // the length is in atttypmod - 4
1512
1513                         if (mod_length > globals.max_varchar_size || mod_length <= 0)
1514                                 mod_length = globals.max_varchar_size;
1515
1516                         mylog("SQLColumns: field type is VARCHAR,BPCHAR: field_type = %d, mod_length = %d\n", field_type, mod_length);
1517
1518             set_tuplefield_int4(&row->tuple[7], mod_length);
1519                         set_tuplefield_int4(&row->tuple[6], mod_length);
1520                         set_tuplefield_int4(&row->tuple[12], mod_length);
1521         } else {
1522                         mylog("SQLColumns: field type is OTHER: field_type = %d, pgtype_length = %d\n", field_type, pgtype_length(stmt, field_type, PG_STATIC, PG_STATIC));
1523
1524             set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, field_type, PG_STATIC, PG_STATIC));
1525                         set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, field_type, PG_STATIC, PG_STATIC));
1526                         set_tuplefield_int4(&row->tuple[12], pgtype_display_size(stmt, field_type, PG_STATIC, PG_STATIC));
1527
1528         }
1529
1530                 set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, field_type));
1531                 set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, field_type));
1532                 set_tuplefield_int2(&row->tuple[10], (Int2) (not_null[0] == '1' ? SQL_NO_NULLS : pgtype_nullable(stmt, field_type)));
1533                 set_tuplefield_string(&row->tuple[11], "");
1534                 set_tuplefield_int4(&row->tuple[13], field_type);
1535
1536         QR_add_tuple(stmt->result, row);
1537
1538         result = SQLFetch(hcol_stmt);
1539     }
1540     if(result != SQL_NO_DATA_FOUND) {
1541                 stmt->errormsg = SC_create_errormsg(hcol_stmt);
1542                 stmt->errornumber = col_stmt->errornumber;
1543                 SC_log_error(func, "", stmt);
1544                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1545         return SQL_ERROR;
1546     }
1547
1548         //      Put the row version column at the end so it might not be
1549         //      mistaken for a key field.
1550         if ( ! stmt->internal && atoi(ci->row_versioning)) {
1551                 /*      For Row Versioning fields */
1552                 the_type = PG_TYPE_INT4;
1553
1554                 row = (TupleNode *)malloc(sizeof(TupleNode) +
1555                                                                   (result_cols - 1) * sizeof(TupleField));
1556
1557                 set_tuplefield_string(&row->tuple[0], "");
1558                 set_tuplefield_string(&row->tuple[1], "");
1559                 set_tuplefield_string(&row->tuple[2], table_name);
1560                 set_tuplefield_string(&row->tuple[3], "xmin");
1561                 set_tuplefield_int2(&row->tuple[4], pgtype_to_sqltype(stmt, the_type));
1562                 set_tuplefield_string(&row->tuple[5], pgtype_to_name(stmt, the_type));
1563                 set_tuplefield_int4(&row->tuple[6], pgtype_precision(stmt, the_type, PG_STATIC, PG_STATIC));
1564                 set_tuplefield_int4(&row->tuple[7], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC));
1565                 set_nullfield_int2(&row->tuple[8], pgtype_scale(stmt, the_type));
1566                 set_nullfield_int2(&row->tuple[9], pgtype_radix(stmt, the_type));
1567                 set_tuplefield_int2(&row->tuple[10], SQL_NO_NULLS);
1568                 set_tuplefield_string(&row->tuple[11], "");
1569                 set_tuplefield_int4(&row->tuple[12], pgtype_display_size(stmt, the_type, PG_STATIC, PG_STATIC));
1570                 set_tuplefield_int4(&row->tuple[13], the_type);
1571
1572                 QR_add_tuple(stmt->result, row);
1573         }
1574
1575     // also, things need to think that this statement is finished so
1576     // the results can be retrieved.
1577     stmt->status = STMT_FINISHED;
1578
1579     // set up the current tuple pointer for SQLFetch
1580     stmt->currTuple = -1;
1581         stmt->current_col = -1;
1582
1583         SQLFreeStmt(hcol_stmt, SQL_DROP);
1584         mylog("SQLColumns(): EXIT,  stmt=%u\n", stmt);
1585     return SQL_SUCCESS;
1586 }
1587
1588 RETCODE SQL_API SQLSpecialColumns(
1589                                   HSTMT        hstmt,
1590                                   UWORD        fColType,
1591                                   UCHAR FAR *  szTableQualifier,
1592                                   SWORD        cbTableQualifier,
1593                                   UCHAR FAR *  szTableOwner,
1594                                   SWORD        cbTableOwner,
1595                                   UCHAR FAR *  szTableName,
1596                                   SWORD        cbTableName,
1597                                   UWORD        fScope,
1598                                   UWORD        fNullable)
1599 {
1600 char *func = "SQLSpecialColumns";
1601 TupleNode *row;
1602 StatementClass *stmt = (StatementClass *) hstmt;
1603 ConnInfo *ci;
1604
1605
1606 mylog("**** SQLSpecialColumns(): ENTER,  stmt=%u\n", stmt);
1607
1608     if( ! stmt) {
1609                 SC_log_error(func, "", NULL);
1610         return SQL_INVALID_HANDLE;
1611     }
1612         ci = &stmt->hdbc->connInfo;
1613
1614         stmt->manual_result = TRUE;
1615     stmt->result = QR_Constructor();
1616     extend_bindings(stmt, 8);
1617
1618     QR_set_num_fields(stmt->result, 8);
1619     QR_set_field_info(stmt->result, 0, "SCOPE", PG_TYPE_INT2, 2);
1620     QR_set_field_info(stmt->result, 1, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
1621     QR_set_field_info(stmt->result, 2, "DATA_TYPE", PG_TYPE_INT2, 2);
1622     QR_set_field_info(stmt->result, 3, "TYPE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
1623     QR_set_field_info(stmt->result, 4, "PRECISION", PG_TYPE_INT4, 4);
1624     QR_set_field_info(stmt->result, 5, "LENGTH", PG_TYPE_INT4, 4);
1625     QR_set_field_info(stmt->result, 6, "SCALE", PG_TYPE_INT2, 2);
1626     QR_set_field_info(stmt->result, 7, "PSEUDO_COLUMN", PG_TYPE_INT2, 2);
1627
1628     /* use the oid value for the rowid */
1629     if(fColType == SQL_BEST_ROWID) {
1630         row = (TupleNode *)malloc(sizeof(TupleNode) + (8 - 1) * sizeof(TupleField));
1631
1632         set_tuplefield_int2(&row->tuple[0], SQL_SCOPE_SESSION);
1633         set_tuplefield_string(&row->tuple[1], "oid");
1634         set_tuplefield_int2(&row->tuple[2], pgtype_to_sqltype(stmt, PG_TYPE_OID));
1635         set_tuplefield_string(&row->tuple[3], "OID");
1636         set_tuplefield_int4(&row->tuple[4], pgtype_precision(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC));
1637         set_tuplefield_int4(&row->tuple[5], pgtype_length(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC));
1638         set_tuplefield_int2(&row->tuple[6], pgtype_scale(stmt, PG_TYPE_OID));
1639         set_tuplefield_int2(&row->tuple[7], SQL_PC_PSEUDO);
1640
1641         QR_add_tuple(stmt->result, row);
1642
1643     } else if(fColType == SQL_ROWVER) {
1644
1645                 Int2 the_type = PG_TYPE_INT4;
1646
1647                 if (atoi(ci->row_versioning)) {
1648                         row = (TupleNode *)malloc(sizeof(TupleNode) + (8 - 1) * sizeof(TupleField));
1649
1650                         set_tuplefield_null(&row->tuple[0]);
1651                         set_tuplefield_string(&row->tuple[1], "xmin");
1652                         set_tuplefield_int2(&row->tuple[2], pgtype_to_sqltype(stmt, the_type));
1653                         set_tuplefield_string(&row->tuple[3], pgtype_to_name(stmt, the_type));
1654                         set_tuplefield_int4(&row->tuple[4], pgtype_precision(stmt, the_type, PG_STATIC, PG_STATIC));
1655                         set_tuplefield_int4(&row->tuple[5], pgtype_length(stmt, the_type, PG_STATIC, PG_STATIC));
1656                         set_tuplefield_int2(&row->tuple[6], pgtype_scale(stmt, the_type));
1657                         set_tuplefield_int2(&row->tuple[7], SQL_PC_PSEUDO);
1658
1659                         QR_add_tuple(stmt->result, row);
1660                 }
1661         }
1662     stmt->status = STMT_FINISHED;
1663     stmt->currTuple = -1;
1664         stmt->current_col = -1;
1665
1666         mylog("SQLSpecialColumns(): EXIT,  stmt=%u\n", stmt);
1667     return SQL_SUCCESS;
1668 }
1669
1670 RETCODE SQL_API SQLStatistics(
1671                               HSTMT         hstmt,
1672                               UCHAR FAR *   szTableQualifier,
1673                               SWORD         cbTableQualifier,
1674                               UCHAR FAR *   szTableOwner,
1675                               SWORD         cbTableOwner,
1676                               UCHAR FAR *   szTableName,
1677                               SWORD         cbTableName,
1678                               UWORD         fUnique,
1679                               UWORD         fAccuracy)
1680 {
1681 char *func="SQLStatistics";
1682 StatementClass *stmt = (StatementClass *) hstmt;
1683 char index_query[MAX_STATEMENT_LEN];
1684 HSTMT hindx_stmt;
1685 RETCODE result;
1686 char *table_name;
1687 char index_name[MAX_INFO_STRING];
1688 short fields_vector[8];
1689 char isunique[10], isclustered[10];
1690 SDWORD index_name_len, fields_vector_len;
1691 TupleNode *row;
1692 int i;
1693 HSTMT hcol_stmt;
1694 StatementClass *col_stmt, *indx_stmt;
1695 char column_name[MAX_INFO_STRING];
1696 char **column_names = 0;
1697 Int4 column_name_len;
1698 int total_columns = 0;
1699 char error = TRUE;
1700 ConnInfo *ci;
1701 char buf[256];
1702
1703 mylog("**** SQLStatistics(): ENTER,  stmt=%u\n", stmt);
1704
1705     if( ! stmt) {
1706                 SC_log_error(func, "", NULL);
1707         return SQL_INVALID_HANDLE;
1708     }
1709
1710         stmt->manual_result = TRUE;
1711         stmt->errormsg_created = TRUE;
1712
1713         ci = &stmt->hdbc->connInfo;
1714
1715     stmt->result = QR_Constructor();
1716     if(!stmt->result) {
1717         stmt->errormsg = "Couldn't allocate memory for SQLStatistics result.";
1718         stmt->errornumber = STMT_NO_MEMORY_ERROR;
1719                 SC_log_error(func, "", stmt);
1720         return SQL_ERROR;
1721     }
1722
1723     // the binding structure for a statement is not set up until
1724     // a statement is actually executed, so we'll have to do this ourselves.
1725     extend_bindings(stmt, 13);
1726
1727     // set the field names
1728     QR_set_num_fields(stmt->result, 13);
1729     QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
1730     QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
1731     QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
1732     QR_set_field_info(stmt->result, 3, "NON_UNIQUE", PG_TYPE_INT2, 2);
1733     QR_set_field_info(stmt->result, 4, "INDEX_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
1734     QR_set_field_info(stmt->result, 5, "INDEX_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
1735     QR_set_field_info(stmt->result, 6, "TYPE", PG_TYPE_INT2, 2);
1736     QR_set_field_info(stmt->result, 7, "SEQ_IN_INDEX", PG_TYPE_INT2, 2);
1737     QR_set_field_info(stmt->result, 8, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
1738     QR_set_field_info(stmt->result, 9, "COLLATION", PG_TYPE_CHAR, 1);
1739     QR_set_field_info(stmt->result, 10, "CARDINALITY", PG_TYPE_INT4, 4);
1740     QR_set_field_info(stmt->result, 11, "PAGES", PG_TYPE_INT4, 4);
1741     QR_set_field_info(stmt->result, 12, "FILTER_CONDITION", PG_TYPE_TEXT, MAX_INFO_STRING);
1742
1743
1744     // only use the table name... the owner should be redundant, and
1745     // we never use qualifiers.
1746         table_name = make_string(szTableName, cbTableName, NULL);
1747         if ( ! table_name) {
1748         stmt->errormsg = "No table name passed to SQLStatistics.";
1749         stmt->errornumber = STMT_INTERNAL_ERROR;
1750                 SC_log_error(func, "", stmt);
1751         return SQL_ERROR;
1752     }
1753
1754         // we need to get a list of the field names first,
1755         // so we can return them later.
1756         result = SQLAllocStmt( stmt->hdbc, &hcol_stmt);
1757         if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1758                 stmt->errormsg = "SQLAllocStmt failed in SQLStatistics for columns.";
1759                 stmt->errornumber = STMT_NO_MEMORY_ERROR;
1760                 goto SEEYA;
1761         }
1762
1763         col_stmt = (StatementClass *) hcol_stmt;
1764
1765         /*      "internal" prevents SQLColumns from returning the oid if it is being shown.
1766                 This would throw everything off.
1767         */
1768         col_stmt->internal = TRUE;
1769         result = SQLColumns(hcol_stmt, "", 0, "", 0, 
1770                                 table_name, (SWORD) strlen(table_name), "", 0);
1771         col_stmt->internal = FALSE;
1772
1773         if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1774                         stmt->errormsg = col_stmt->errormsg;        // "SQLColumns failed in SQLStatistics.";
1775                         stmt->errornumber = col_stmt->errornumber;  // STMT_EXEC_ERROR;
1776                         SQLFreeStmt(hcol_stmt, SQL_DROP);
1777                         goto SEEYA;
1778         }
1779         result = SQLBindCol(hcol_stmt, 4, SQL_C_CHAR,
1780                                 column_name, MAX_INFO_STRING, &column_name_len);
1781         if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1782                 stmt->errormsg = col_stmt->errormsg;
1783                 stmt->errornumber = col_stmt->errornumber;
1784                 SQLFreeStmt(hcol_stmt, SQL_DROP);
1785                 goto SEEYA;
1786
1787         }
1788
1789         result = SQLFetch(hcol_stmt);
1790         while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) {
1791                 total_columns++;
1792
1793                 column_names = 
1794                 (char **)realloc(column_names, 
1795                                  total_columns * sizeof(char *));
1796                 column_names[total_columns-1] = 
1797                 (char *)malloc(strlen(column_name)+1);
1798                 strcpy(column_names[total_columns-1], column_name);
1799
1800                 mylog("SQLStatistics: column_name = '%s'\n", column_name);
1801
1802                 result = SQLFetch(hcol_stmt);
1803         }
1804         if(result != SQL_NO_DATA_FOUND || total_columns == 0) {
1805                         stmt->errormsg = SC_create_errormsg(hcol_stmt); // "Couldn't get column names in SQLStatistics.";
1806                         stmt->errornumber = col_stmt->errornumber;
1807                         SQLFreeStmt(hcol_stmt, SQL_DROP);
1808                         goto SEEYA;
1809
1810         }
1811         
1812         SQLFreeStmt(hcol_stmt, SQL_DROP);
1813
1814     // get a list of indexes on this table
1815     result = SQLAllocStmt( stmt->hdbc, &hindx_stmt);
1816     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1817                 stmt->errormsg = "SQLAllocStmt failed in SQLStatistics for indices.";
1818                 stmt->errornumber = STMT_NO_MEMORY_ERROR;
1819                 goto SEEYA;
1820
1821     }
1822         indx_stmt = (StatementClass *) hindx_stmt;
1823
1824         sprintf(index_query, "select c.relname, i.indkey, i.indisunique, i.indisclustered from pg_index i, pg_class c, pg_class d where c.oid = i.indexrelid and d.relname = '%s' and d.oid = i.indrelid", 
1825                 table_name);
1826
1827     result = SQLExecDirect(hindx_stmt, index_query, strlen(index_query));
1828     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1829                 stmt->errormsg = SC_create_errormsg(hindx_stmt); // "Couldn't execute index query (w/SQLExecDirect) in SQLStatistics.";
1830                 stmt->errornumber = indx_stmt->errornumber;
1831                 SQLFreeStmt(hindx_stmt, SQL_DROP);
1832                 goto SEEYA;
1833
1834     }
1835
1836     // bind the index name column
1837     result = SQLBindCol(hindx_stmt, 1, SQL_C_CHAR,
1838                         index_name, MAX_INFO_STRING, &index_name_len);
1839     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1840                 stmt->errormsg = indx_stmt->errormsg; // "Couldn't bind column in SQLStatistics.";
1841                 stmt->errornumber = indx_stmt->errornumber;
1842                 SQLFreeStmt(hindx_stmt, SQL_DROP);
1843                 goto SEEYA;
1844
1845     }
1846     // bind the vector column
1847     result = SQLBindCol(hindx_stmt, 2, SQL_C_DEFAULT,
1848                         fields_vector, 16, &fields_vector_len);
1849     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1850                 stmt->errormsg = indx_stmt->errormsg;  // "Couldn't bind column in SQLStatistics.";
1851                 stmt->errornumber = indx_stmt->errornumber;
1852                 SQLFreeStmt(hindx_stmt, SQL_DROP);
1853                 goto SEEYA;
1854
1855     }
1856     // bind the "is unique" column
1857     result = SQLBindCol(hindx_stmt, 3, SQL_C_CHAR,
1858                         isunique, sizeof(isunique), NULL);
1859     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1860                 stmt->errormsg = indx_stmt->errormsg;  // "Couldn't bind column in SQLStatistics.";
1861                 stmt->errornumber = indx_stmt->errornumber;
1862                 SQLFreeStmt(hindx_stmt, SQL_DROP);
1863                 goto SEEYA;
1864     }
1865
1866     // bind the "is clustered" column
1867     result = SQLBindCol(hindx_stmt, 4, SQL_C_CHAR,
1868                         isclustered, sizeof(isclustered), NULL);
1869     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
1870                 stmt->errormsg = indx_stmt->errormsg;  // "Couldn't bind column in SQLStatistics.";
1871                 stmt->errornumber = indx_stmt->errornumber;
1872                 SQLFreeStmt(hindx_stmt, SQL_DROP);
1873                 goto SEEYA;
1874
1875     }
1876
1877         /*      fake index of OID */
1878         if (atoi(ci->show_oid_column) && atoi(ci->fake_oid_index)) {
1879                 row = (TupleNode *)malloc(sizeof(TupleNode) + 
1880                                           (13 - 1) * sizeof(TupleField));
1881
1882                 // no table qualifier
1883                 set_tuplefield_string(&row->tuple[0], "");
1884                 // don't set the table owner, else Access tries to use it
1885                 set_tuplefield_string(&row->tuple[1], "");
1886                 set_tuplefield_string(&row->tuple[2], table_name);
1887
1888                 // non-unique index?
1889                 set_tuplefield_int2(&row->tuple[3], (Int2) (globals.unique_index ? FALSE : TRUE));
1890                 
1891                 // no index qualifier
1892                 set_tuplefield_string(&row->tuple[4], "");
1893
1894                 sprintf(buf, "%s_idx_fake_oid", table_name);
1895                 set_tuplefield_string(&row->tuple[5], buf);
1896
1897                 // Clustered index?  I think non-clustered should be type OTHER not HASHED
1898                 set_tuplefield_int2(&row->tuple[6], (Int2) SQL_INDEX_OTHER);
1899                 set_tuplefield_int2(&row->tuple[7], (Int2) 1);
1900
1901                 set_tuplefield_string(&row->tuple[8], "oid");
1902                 set_tuplefield_string(&row->tuple[9], "A");
1903                 set_tuplefield_null(&row->tuple[10]);
1904                 set_tuplefield_null(&row->tuple[11]);
1905                 set_tuplefield_null(&row->tuple[12]);
1906
1907                 QR_add_tuple(stmt->result, row);
1908         }
1909
1910     result = SQLFetch(hindx_stmt);
1911     while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) {
1912
1913                 //      If only requesting unique indexs, then just return those.
1914                 if (fUnique == SQL_INDEX_ALL || 
1915                         (fUnique == SQL_INDEX_UNIQUE && atoi(isunique))) {
1916                         i = 0;
1917                         // add a row in this table for each field in the index
1918                         while(i < 8 && fields_vector[i] != 0) {
1919
1920                                 row = (TupleNode *)malloc(sizeof(TupleNode) + 
1921                                                           (13 - 1) * sizeof(TupleField));
1922
1923                                 // no table qualifier
1924                                 set_tuplefield_string(&row->tuple[0], "");
1925                                 // don't set the table owner, else Access tries to use it
1926                                 set_tuplefield_string(&row->tuple[1], "");
1927                                 set_tuplefield_string(&row->tuple[2], table_name);
1928
1929                                 // non-unique index?
1930                                 if (globals.unique_index)
1931                                         set_tuplefield_int2(&row->tuple[3], (Int2) (atoi(isunique) ? FALSE : TRUE));
1932                                 else
1933                                         set_tuplefield_int2(&row->tuple[3], TRUE);
1934                                 
1935                                 // no index qualifier
1936                                 set_tuplefield_string(&row->tuple[4], "");
1937                                 set_tuplefield_string(&row->tuple[5], index_name);
1938
1939                                 // Clustered index?  I think non-clustered should be type OTHER not HASHED
1940                                 set_tuplefield_int2(&row->tuple[6], (Int2) (atoi(isclustered) ? SQL_INDEX_CLUSTERED : SQL_INDEX_OTHER));
1941                                 set_tuplefield_int2(&row->tuple[7], (Int2) (i+1));
1942
1943                                 if(fields_vector[i] == OID_ATTNUM) {
1944                                         set_tuplefield_string(&row->tuple[8], "oid");
1945                                         mylog("SQLStatistics: column name = oid\n");
1946                                 }
1947                                 else if(fields_vector[i] < 0 || fields_vector[i] > total_columns) {
1948                                         set_tuplefield_string(&row->tuple[8], "UNKNOWN");
1949                                         mylog("SQLStatistics: column name = UNKNOWN\n");
1950                                 }
1951                                 else {
1952                                         set_tuplefield_string(&row->tuple[8], column_names[fields_vector[i]-1]);
1953                                         mylog("SQLStatistics: column name = '%s'\n", column_names[fields_vector[i]-1]);
1954                                 }
1955
1956                                 set_tuplefield_string(&row->tuple[9], "A");
1957                                 set_tuplefield_null(&row->tuple[10]);
1958                                 set_tuplefield_null(&row->tuple[11]);
1959                                 set_tuplefield_null(&row->tuple[12]);
1960
1961                                 QR_add_tuple(stmt->result, row);
1962                                 i++;
1963                         }
1964                 }
1965
1966         result = SQLFetch(hindx_stmt);
1967     }
1968     if(result != SQL_NO_DATA_FOUND) {
1969                 stmt->errormsg = SC_create_errormsg(hindx_stmt); // "SQLFetch failed in SQLStatistics.";
1970                 stmt->errornumber = indx_stmt->errornumber;
1971                 SQLFreeStmt(hindx_stmt, SQL_DROP);
1972                 goto SEEYA;
1973     }
1974
1975         SQLFreeStmt(hindx_stmt, SQL_DROP);
1976
1977     // also, things need to think that this statement is finished so
1978     // the results can be retrieved.
1979     stmt->status = STMT_FINISHED;
1980
1981     // set up the current tuple pointer for SQLFetch
1982     stmt->currTuple = -1;
1983         stmt->current_col = -1;
1984
1985         error = FALSE;
1986
1987 SEEYA:
1988         /* These things should be freed on any error ALSO! */
1989         free(table_name);
1990     for(i = 0; i < total_columns; i++) {
1991                 free(column_names[i]);
1992     }
1993     free(column_names);
1994
1995         mylog("SQLStatistics(): EXIT, %s, stmt=%u\n", error ? "error" : "success", stmt);
1996
1997         if (error) {
1998                 SC_log_error(func, "", stmt);
1999                 return SQL_ERROR;
2000         }
2001         else
2002                 return SQL_SUCCESS;
2003 }
2004
2005 RETCODE SQL_API SQLColumnPrivileges(
2006                                     HSTMT        hstmt,
2007                                     UCHAR FAR *  szTableQualifier,
2008                                     SWORD        cbTableQualifier,
2009                                     UCHAR FAR *  szTableOwner,
2010                                     SWORD        cbTableOwner,
2011                                     UCHAR FAR *  szTableName,
2012                                     SWORD        cbTableName,
2013                                     UCHAR FAR *  szColumnName,
2014                                     SWORD        cbColumnName)
2015 {
2016 char *func="SQLColumnPrivileges";
2017 /*      Neither Access or Borland care about this. */
2018
2019         SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
2020     return SQL_ERROR;
2021 }
2022
2023 RETCODE
2024 getPrimaryKeyString(StatementClass *stmt, char *szTableName, SWORD cbTableName, char *svKey, int *nKey)
2025 {
2026 char *func = "getPrimaryKeyString";
2027 HSTMT htbl_stmt;
2028 StatementClass *tbl_stmt;
2029 RETCODE result;
2030 char tables_query[MAX_STATEMENT_LEN];
2031 char attname[MAX_INFO_STRING];
2032 SDWORD attname_len;
2033 int nk = 0;
2034
2035         if (nKey != NULL)
2036                 *nKey = 0;
2037
2038         svKey[0] = '\0';
2039
2040         stmt->errormsg_created = TRUE;
2041
2042     result = SQLAllocStmt( stmt->hdbc, &htbl_stmt);
2043     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
2044                 stmt->errornumber = STMT_NO_MEMORY_ERROR;
2045                 stmt->errormsg = "Couldn't allocate statement for Primary Key result.";
2046                 SC_log_error(func, "", stmt);
2047         return SQL_ERROR;
2048     }
2049         tbl_stmt = (StatementClass *) htbl_stmt;
2050
2051         tables_query[0] = '\0';
2052         if ( ! my_strcat(tables_query, "select distinct on attnum a2.attname, a2.attnum from pg_attribute a1, pg_attribute a2, pg_class c, pg_index i where c.relname = '%.*s_pkey' AND c.oid = i.indexrelid AND a1.attrelid = c.oid AND a2.attrelid = c.oid AND (i.indkey[0] = a1.attnum OR i.indkey[1] = a1.attnum OR i.indkey[2] = a1.attnum OR i.indkey[3] = a1.attnum OR i.indkey[4] = a1.attnum OR i.indkey[5] = a1.attnum OR i.indkey[6] = a1.attnum OR i.indkey[7] = a1.attnum) order by a2.attnum",
2053                         szTableName, cbTableName)) {
2054
2055                 stmt->errormsg = "No Table specified to getPrimaryKeyString.";
2056             stmt->errornumber = STMT_INTERNAL_ERROR;
2057                 SC_log_error(func, "", stmt);
2058                 SQLFreeStmt(htbl_stmt, SQL_DROP);
2059                 return SQL_ERROR;
2060         }
2061
2062         mylog("getPrimaryKeyString: tables_query='%s'\n", tables_query);
2063
2064     result = SQLExecDirect(htbl_stmt, tables_query, strlen(tables_query));
2065     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
2066                 stmt->errormsg = SC_create_errormsg(htbl_stmt);
2067                 stmt->errornumber = tbl_stmt->errornumber;
2068                 SC_log_error(func, "", stmt);
2069                 SQLFreeStmt(htbl_stmt, SQL_DROP);
2070         return SQL_ERROR;
2071     }
2072
2073     result = SQLBindCol(htbl_stmt, 1, SQL_C_CHAR,
2074                         attname, MAX_INFO_STRING, &attname_len);
2075     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
2076                 stmt->errormsg = tbl_stmt->errormsg;
2077                 stmt->errornumber = tbl_stmt->errornumber;
2078                 SC_log_error(func, "", stmt);
2079                 SQLFreeStmt(htbl_stmt, SQL_DROP);
2080         return SQL_ERROR;
2081     }
2082
2083     result = SQLFetch(htbl_stmt);
2084     while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) {
2085
2086                 if (strlen(svKey) > 0)
2087                         strcat(svKey, "+");
2088                 strcat(svKey, attname);
2089
2090         result = SQLFetch(htbl_stmt);
2091                 nk++;
2092     }
2093
2094     if(result != SQL_NO_DATA_FOUND) {
2095                 stmt->errormsg = SC_create_errormsg(htbl_stmt);
2096                 stmt->errornumber = tbl_stmt->errornumber;
2097                 SC_log_error(func, "", stmt);
2098                 SQLFreeStmt(htbl_stmt, SQL_DROP);
2099         return SQL_ERROR;
2100     }
2101
2102         SQLFreeStmt(htbl_stmt, SQL_DROP);
2103
2104         if (nKey != NULL)
2105                 *nKey = nk;
2106
2107         mylog(">> getPrimaryKeyString: returning nKey=%d, svKey='%s'\n", nk, svKey);
2108         return result;
2109 }
2110
2111 RETCODE
2112 getPrimaryKeyArray(StatementClass *stmt, char *szTableName, SWORD cbTableName, char keyArray[][MAX_INFO_STRING], int *nKey)
2113 {
2114 RETCODE result;
2115 char svKey[MAX_KEYLEN], *svKeyPtr;
2116 int i = 0;
2117
2118         result = getPrimaryKeyString(stmt, szTableName, cbTableName, svKey, nKey);
2119         if (result != SQL_SUCCESS && result != SQL_NO_DATA_FOUND)
2120                 //  error passed from above
2121                 return result;
2122
2123         //      If no keys, return NO_DATA_FOUND
2124         if (svKey[0] == '\0') {
2125                 mylog("!!!!!! getPrimaryKeyArray: svKey was null\n");
2126                 return SQL_NO_DATA_FOUND;
2127         }
2128
2129         // mylog(">> primarykeyArray: nKey=%d, svKey='%s'\n",  *nKey, svKey);
2130
2131         svKeyPtr = strtok(svKey, "+");
2132         while (svKeyPtr != NULL && i < MAX_KEYPARTS) {
2133                 strcpy(keyArray[i++], svKeyPtr);
2134                 svKeyPtr = strtok(NULL, "+");
2135         }
2136
2137         /*
2138         for (i = 0; i < *nKey; i++)
2139                 mylog(">> keyArray[%d] = '%s'\n", i, keyArray[i]);
2140         */
2141
2142         return result;
2143 }
2144
2145
2146 RETCODE SQL_API SQLPrimaryKeys(
2147                                HSTMT         hstmt,
2148                                UCHAR FAR *   szTableQualifier,
2149                                SWORD         cbTableQualifier,
2150                                UCHAR FAR *   szTableOwner,
2151                                SWORD         cbTableOwner,
2152                                UCHAR FAR *   szTableName,
2153                                SWORD         cbTableName)
2154 {
2155 char *func = "SQLPrimaryKeys";
2156 StatementClass *stmt = (StatementClass *) hstmt;
2157 TupleNode *row;
2158 RETCODE result;
2159 char svKey[MAX_KEYLEN], *ptr;
2160 int seq = 1, nkeys = 0;
2161
2162 mylog("**** SQLPrimaryKeys(): ENTER, stmt=%u\n", stmt);
2163
2164     if( ! stmt) {
2165                 SC_log_error(func, "", NULL);
2166         return SQL_INVALID_HANDLE;
2167     }
2168         stmt->manual_result = TRUE;
2169
2170         result = getPrimaryKeyString(stmt, szTableName, cbTableName, svKey, &nkeys);
2171
2172         mylog(">> PrimaryKeys: getPrimaryKeyString() returned %d, nkeys=%d, svKey = '%s'\n", result, nkeys, svKey);
2173
2174         if (result != SQL_SUCCESS && result != SQL_NO_DATA_FOUND) {
2175                 //      error msg passed from above
2176                 return result;
2177         }
2178
2179         //      I'm not sure if this is correct to return when there are no keys or
2180         //      if an empty result set would be better.
2181         if (nkeys == 0) {
2182                 stmt->errornumber = STMT_INFO_ONLY;
2183                 stmt->errormsg = "No primary keys for this table.";
2184                 return SQL_SUCCESS_WITH_INFO;
2185         }
2186
2187     stmt->result = QR_Constructor();
2188     if(!stmt->result) {
2189         stmt->errormsg = "Couldn't allocate memory for SQLPrimaryKeys result.";
2190         stmt->errornumber = STMT_NO_MEMORY_ERROR;
2191                 SC_log_error(func, "", stmt);
2192         return SQL_ERROR;
2193     }
2194
2195
2196     // the binding structure for a statement is not set up until
2197     // a statement is actually executed, so we'll have to do this ourselves.
2198     extend_bindings(stmt, 6);
2199         
2200     // set the field names
2201     QR_set_num_fields(stmt->result, 6);
2202     QR_set_field_info(stmt->result, 0, "TABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
2203     QR_set_field_info(stmt->result, 1, "TABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
2204     QR_set_field_info(stmt->result, 2, "TABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
2205     QR_set_field_info(stmt->result, 3, "COLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
2206     QR_set_field_info(stmt->result, 4, "KEY_SEQ", PG_TYPE_INT2, 2);
2207     QR_set_field_info(stmt->result, 5, "PK_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
2208
2209     // add the tuples
2210         ptr = strtok(svKey, "+");
2211     while( ptr != NULL) {
2212         row = (TupleNode *)malloc(sizeof(TupleNode) + (6 - 1) * sizeof(TupleField));
2213
2214         set_tuplefield_string(&row->tuple[0], "");
2215
2216         // I have to hide the table owner from Access, otherwise it
2217         // insists on referring to the table as 'owner.table'.
2218         // (this is valid according to the ODBC SQL grammar, but
2219         // Postgres won't support it.)
2220
2221                 mylog(">> primaryKeys: ptab = '%s', seq = %d\n", ptr, seq);
2222
2223         set_tuplefield_string(&row->tuple[1], "");
2224         set_tuplefield_string(&row->tuple[2], szTableName);
2225         set_tuplefield_string(&row->tuple[3], ptr);
2226                 set_tuplefield_int2(&row->tuple[4], (Int2) (seq++));
2227                 set_tuplefield_null(&row->tuple[5]);
2228
2229         QR_add_tuple(stmt->result, row);
2230
2231                 ptr = strtok(NULL, "+");
2232         }
2233
2234     // also, things need to think that this statement is finished so
2235     // the results can be retrieved.
2236     stmt->status = STMT_FINISHED;
2237
2238     // set up the current tuple pointer for SQLFetch
2239     stmt->currTuple = -1;
2240         stmt->current_col = -1;
2241
2242         mylog("SQLPrimaryKeys(): EXIT, stmt=%u\n", stmt);
2243     return SQL_SUCCESS;
2244 }
2245
2246 RETCODE SQL_API SQLForeignKeys(
2247                                HSTMT         hstmt,
2248                                UCHAR FAR *   szPkTableQualifier,
2249                                SWORD         cbPkTableQualifier,
2250                                UCHAR FAR *   szPkTableOwner,
2251                                SWORD         cbPkTableOwner,
2252                                UCHAR FAR *   szPkTableName,
2253                                SWORD         cbPkTableName,
2254                                UCHAR FAR *   szFkTableQualifier,
2255                                SWORD         cbFkTableQualifier,
2256                                UCHAR FAR *   szFkTableOwner,
2257                                SWORD         cbFkTableOwner,
2258                                UCHAR FAR *   szFkTableName,
2259                                SWORD         cbFkTableName)
2260 {
2261 char *func = "SQLForeignKeys";
2262 StatementClass *stmt = (StatementClass *) hstmt;
2263 TupleNode *row;
2264 HSTMT htbl_stmt;
2265 StatementClass *tbl_stmt;
2266 RETCODE result;
2267 char tables_query[MAX_STATEMENT_LEN];
2268 char relname[MAX_INFO_STRING], attnames[MAX_INFO_STRING], frelname[MAX_INFO_STRING];
2269 SDWORD relname_len, attnames_len, frelname_len;
2270 char *pktab, *fktab;
2271 char fkey = FALSE;
2272 char primaryKey[MAX_KEYPARTS][MAX_INFO_STRING];
2273 char *attnamePtr;
2274 int pkeys, seq;
2275
2276 mylog("**** SQLForeignKeys(): ENTER, stmt=%u\n", stmt);
2277
2278         memset(primaryKey, 0, sizeof(primaryKey));
2279
2280     if( ! stmt) {
2281                 SC_log_error(func, "", NULL);
2282         return SQL_INVALID_HANDLE;
2283     }
2284         stmt->manual_result = TRUE;
2285         stmt->errormsg_created = TRUE;
2286
2287     result = SQLAllocStmt( stmt->hdbc, &htbl_stmt);
2288     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
2289                 stmt->errornumber = STMT_NO_MEMORY_ERROR;
2290                 stmt->errormsg = "Couldn't allocate statement for SQLForeignKeys result.";
2291                 SC_log_error(func, "", stmt);
2292         return SQL_ERROR;
2293     }
2294
2295         tbl_stmt = (StatementClass *) htbl_stmt;
2296
2297         pktab = make_string(szPkTableName, cbPkTableName, NULL);
2298         fktab = make_string(szFkTableName, cbFkTableName, NULL);
2299
2300         if (pktab && fktab) {
2301         //      Get the primary key of the table listed in szPkTable
2302                 result = getPrimaryKeyArray(stmt, pktab, (SWORD) strlen(pktab), primaryKey, &pkeys);
2303                 if (result != SQL_SUCCESS && result != SQL_NO_DATA_FOUND) {
2304                         //      error msg passed from above
2305                         SQLFreeStmt(htbl_stmt, SQL_DROP);
2306                         free(pktab); free(fktab);
2307                         return result;
2308                 }
2309                 if (pkeys == 0) {
2310                         stmt->errornumber = STMT_INFO_ONLY;
2311                         stmt->errormsg = "No primary keys for this table.";
2312                         SQLFreeStmt(htbl_stmt, SQL_DROP);
2313                         free(pktab); free(fktab);
2314                         return SQL_SUCCESS_WITH_INFO;
2315                 }
2316
2317             sprintf(tables_query, "select relname, attnames, frelname from %s where relname='%s' AND frelname='%s'", KEYS_TABLE, fktab, pktab);
2318                 free(pktab); free(fktab);
2319         }
2320     else if (pktab) {
2321         //      Get the primary key of the table listed in szPkTable
2322                 result = getPrimaryKeyArray(stmt, pktab, (SWORD) strlen(pktab), primaryKey, &pkeys);
2323                 if (result != SQL_SUCCESS && result != SQL_NO_DATA_FOUND) {
2324                         //      error msg passed from above
2325                         SQLFreeStmt(htbl_stmt, SQL_DROP);
2326                         free(pktab);
2327                         return result;
2328                 }
2329                 if (pkeys == 0) {
2330                         stmt->errornumber = STMT_INFO_ONLY;
2331                         stmt->errormsg = "No primary keys for this table.";
2332                         SQLFreeStmt(htbl_stmt, SQL_DROP);
2333                         free(pktab);
2334                         return SQL_SUCCESS_WITH_INFO;
2335                 }
2336
2337             sprintf(tables_query, "select relname, attnames, frelname from %s where frelname='%s'", KEYS_TABLE, pktab);
2338                 free(pktab);
2339     }
2340     else if (fktab) {
2341                 //      This query could involve multiple calls to getPrimaryKey()
2342                 //      so put that off till we know what pktables we need.
2343                 fkey = TRUE;
2344
2345             sprintf(tables_query, "select relname, attnames, frelname from %s where relname='%s'", KEYS_TABLE, fktab);
2346                 free(fktab);
2347     }
2348         else {
2349                 stmt->errormsg = "No tables specified to SQLForeignKeys.";
2350                 stmt->errornumber = STMT_INTERNAL_ERROR;
2351                 SC_log_error(func, "", stmt);
2352                 SQLFreeStmt(htbl_stmt, SQL_DROP);
2353                 return SQL_ERROR;
2354         }
2355
2356     result = SQLExecDirect(htbl_stmt, tables_query, strlen(tables_query));
2357     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
2358                 stmt->errormsg = SC_create_errormsg(htbl_stmt);
2359                 stmt->errornumber = tbl_stmt->errornumber;
2360                 SC_log_error(func, "", stmt);
2361         SQLFreeStmt(htbl_stmt, SQL_DROP);
2362             return SQL_ERROR;
2363     }
2364
2365     result = SQLBindCol(htbl_stmt, 1, SQL_C_CHAR,
2366                         relname, MAX_INFO_STRING, &relname_len);
2367     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
2368                 stmt->errormsg = tbl_stmt->errormsg;
2369                 stmt->errornumber = tbl_stmt->errornumber;
2370                 SC_log_error(func, "", stmt);
2371                 SQLFreeStmt(htbl_stmt, SQL_DROP);
2372         return SQL_ERROR;
2373     }
2374     result = SQLBindCol(htbl_stmt, 2, SQL_C_CHAR,
2375                         attnames, MAX_INFO_STRING, &attnames_len);
2376     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
2377                 stmt->errormsg = tbl_stmt->errormsg;
2378                 stmt->errornumber = tbl_stmt->errornumber;
2379                 SC_log_error(func, "", stmt);
2380                 SQLFreeStmt(htbl_stmt, SQL_DROP);
2381         return SQL_ERROR;
2382     }
2383
2384     result = SQLBindCol(htbl_stmt, 3, SQL_C_CHAR,
2385                         frelname, MAX_INFO_STRING, &frelname_len);
2386     if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
2387                 stmt->errormsg = tbl_stmt->errormsg;
2388                 stmt->errornumber = tbl_stmt->errornumber;
2389                 SC_log_error(func, "", stmt);
2390                 SQLFreeStmt(htbl_stmt, SQL_DROP);
2391         return SQL_ERROR;
2392     }
2393
2394     stmt->result = QR_Constructor();
2395     if(!stmt->result) {
2396                 stmt->errormsg = "Couldn't allocate memory for SQLForeignKeys result.";
2397         stmt->errornumber = STMT_NO_MEMORY_ERROR;
2398                 SC_log_error(func, "", stmt);
2399                 SQLFreeStmt(htbl_stmt, SQL_DROP);
2400         return SQL_ERROR;
2401     }
2402
2403     // the binding structure for a statement is not set up until
2404     // a statement is actually executed, so we'll have to do this ourselves.
2405     extend_bindings(stmt, 13);
2406
2407     // set the field names
2408     QR_set_num_fields(stmt->result, 13);
2409     QR_set_field_info(stmt->result, 0, "PKTABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
2410     QR_set_field_info(stmt->result, 1, "PKTABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
2411     QR_set_field_info(stmt->result, 2, "PKTABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
2412     QR_set_field_info(stmt->result, 3, "PKCOLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
2413     QR_set_field_info(stmt->result, 4, "FKTABLE_QUALIFIER", PG_TYPE_TEXT, MAX_INFO_STRING);
2414     QR_set_field_info(stmt->result, 5, "FKTABLE_OWNER", PG_TYPE_TEXT, MAX_INFO_STRING);
2415     QR_set_field_info(stmt->result, 6, "FKTABLE_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
2416     QR_set_field_info(stmt->result, 7, "FKCOLUMN_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
2417     QR_set_field_info(stmt->result, 8, "KEY_SEQ", PG_TYPE_INT2, 2);
2418     QR_set_field_info(stmt->result, 9, "UPDATE_RULE", PG_TYPE_INT2, 2);
2419     QR_set_field_info(stmt->result, 10, "DELETE_RULE", PG_TYPE_INT2, 2);
2420     QR_set_field_info(stmt->result, 11, "FK_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
2421     QR_set_field_info(stmt->result, 12, "PK_NAME", PG_TYPE_TEXT, MAX_INFO_STRING);
2422
2423     // add the tuples
2424     result = SQLFetch(htbl_stmt);
2425
2426     while((result == SQL_SUCCESS) || (result == SQL_SUCCESS_WITH_INFO)) {
2427
2428                 if (fkey == TRUE) {
2429                         result = getPrimaryKeyArray(stmt, frelname, (SWORD) strlen(frelname), primaryKey, &pkeys);
2430
2431                         //  mylog(">> getPrimaryKeyArray: frelname = '%s', pkeys = %d, result = %d\n", frelname, pkeys, result);
2432
2433                         //      If an error occurs or for some reason there is no primary key for a
2434                         //      table that is a foreign key, then skip that one.
2435                         if ((result != SQL_SUCCESS && result != SQL_NO_DATA_FOUND) || pkeys == 0) {
2436                         result = SQLFetch(htbl_stmt);
2437                                 continue;
2438                         }
2439
2440                         /*
2441                         for (i = 0; i< pkeys; i++)
2442                                 mylog(">> fkey: pkeys=%d, primaryKey[%d] = '%s'\n", pkeys, i, primaryKey[i]);
2443                         mylog(">> !!!!!!!!! pkeys = %d\n", pkeys);
2444                         */
2445                 }
2446
2447                 // mylog(">> attnames='%s'\n", attnames);
2448
2449                 attnamePtr = strtok(attnames, "+");
2450                 seq = 0;
2451
2452                 while (attnamePtr != NULL && seq < pkeys) {
2453
2454                 row = (TupleNode *)malloc(sizeof(TupleNode) + (13 - 1) * sizeof(TupleField));
2455
2456                         set_tuplefield_null(&row->tuple[0]);
2457
2458                         // I have to hide the table owner from Access, otherwise it
2459                         // insists on referring to the table as 'owner.table'.
2460                         // (this is valid according to the ODBC SQL grammar, but
2461                         // Postgres won't support it.)
2462
2463                         mylog(">> foreign keys: pktab='%s' patt='%s' fktab='%s' fatt='%s' seq=%d\n", 
2464                                 frelname, primaryKey[seq], relname, attnamePtr, (seq+1));
2465
2466                         set_tuplefield_string(&row->tuple[1], "");
2467                         set_tuplefield_string(&row->tuple[2], frelname);
2468                         set_tuplefield_string(&row->tuple[3], primaryKey[seq]);
2469                         set_tuplefield_null(&row->tuple[4]);
2470                         set_tuplefield_string(&row->tuple[5], "");
2471                         set_tuplefield_string(&row->tuple[6], relname);
2472                         set_tuplefield_string(&row->tuple[7], attnamePtr);
2473                         set_tuplefield_int2(&row->tuple[8], (Int2) (++seq));
2474                         set_tuplefield_null(&row->tuple[9]);
2475                         set_tuplefield_null(&row->tuple[10]);
2476                         set_tuplefield_null(&row->tuple[11]);
2477                         set_tuplefield_null(&row->tuple[12]);
2478
2479                         QR_add_tuple(stmt->result, row);
2480
2481                         attnamePtr = strtok(NULL, "+");
2482                 }
2483         result = SQLFetch(htbl_stmt);
2484     }
2485
2486     if(result != SQL_NO_DATA_FOUND) {
2487                 stmt->errormsg = SC_create_errormsg(htbl_stmt);
2488                 stmt->errornumber = tbl_stmt->errornumber;
2489                 SC_log_error(func, "", stmt);
2490                 SQLFreeStmt(htbl_stmt, SQL_DROP);
2491         return SQL_ERROR;
2492     }
2493
2494         SQLFreeStmt(htbl_stmt, SQL_DROP);
2495
2496     // also, things need to think that this statement is finished so
2497     // the results can be retrieved.
2498     stmt->status = STMT_FINISHED;
2499
2500     // set up the current tuple pointer for SQLFetch
2501     stmt->currTuple = -1;
2502         stmt->current_col = -1;
2503
2504         mylog("SQLForeignKeys(): EXIT, stmt=%u\n", stmt);
2505     return SQL_SUCCESS;
2506 }
2507
2508
2509
2510 RETCODE SQL_API SQLProcedureColumns(
2511                                     HSTMT         hstmt,
2512                                     UCHAR FAR *   szProcQualifier,
2513                                     SWORD         cbProcQualifier,
2514                                     UCHAR FAR *   szProcOwner,
2515                                     SWORD         cbProcOwner,
2516                                     UCHAR FAR *   szProcName,
2517                                     SWORD         cbProcName,
2518                                     UCHAR FAR *   szColumnName,
2519                                     SWORD         cbColumnName)
2520 {
2521 char *func="SQLProcedureColumns";
2522
2523         SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
2524     return SQL_ERROR;
2525 }
2526
2527 RETCODE SQL_API SQLProcedures(
2528                               HSTMT          hstmt,
2529                               UCHAR FAR *    szProcQualifier,
2530                               SWORD          cbProcQualifier,
2531                               UCHAR FAR *    szProcOwner,
2532                               SWORD          cbProcOwner,
2533                               UCHAR FAR *    szProcName,
2534                               SWORD          cbProcName)
2535 {
2536 char *func="SQLProcedures";
2537
2538         SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
2539     return SQL_ERROR;
2540 }
2541
2542 RETCODE SQL_API SQLTablePrivileges(
2543                                    HSTMT           hstmt,
2544                                    UCHAR FAR *     szTableQualifier,
2545                                    SWORD           cbTableQualifier,
2546                                    UCHAR FAR *     szTableOwner,
2547                                    SWORD           cbTableOwner,
2548                                    UCHAR FAR *     szTableName,
2549                                    SWORD           cbTableName)
2550 {
2551 char *func="SQLTablePrivileges";
2552
2553         SC_log_error(func, "Function not implemented", (StatementClass *) hstmt);
2554     return SQL_ERROR;
2555 }