]> granicus.if.org Git - postgresql/blob - src/interfaces/odbc/options.c
Provide some initial support for building the ODBC driver for
[postgresql] / src / interfaces / odbc / options.c
1 /*--------
2  * Module:                      options.c
3  *
4  * Description:         This module contains routines for getting/setting
5  *                                      connection and statement options.
6  *
7  * Classes:                     n/a
8  *
9  * API functions:       SQLSetConnectOption, SQLSetStmtOption, SQLGetConnectOption,
10  *                                      SQLGetStmtOption
11  *
12  * Comments:            See "notice.txt" for copyright and license information.
13  *--------
14  */
15
16 #include "psqlodbc.h"
17 #include <string.h>
18
19 #include "environ.h"
20 #include "connection.h"
21 #include "statement.h"
22 #include "qresult.h"
23 #include "pgapifunc.h"
24
25
26
27 RETCODE set_statement_option(ConnectionClass *conn,
28                                          StatementClass *stmt,
29                                          UWORD fOption,
30                                          UDWORD vParam);
31
32
33 RETCODE
34 set_statement_option(ConnectionClass *conn,
35                                          StatementClass *stmt,
36                                          UWORD fOption,
37                                          UDWORD vParam)
38 {
39         static char *func = "set_statement_option";
40         char            changed = FALSE;
41         ConnInfo *ci = NULL;
42
43         if (conn)
44                 ci = &(conn->connInfo);
45         else if (stmt)
46                 ci = &(SC_get_conn(stmt)->connInfo);
47         switch (fOption)
48         {
49                 case SQL_ASYNC_ENABLE:  /* ignored */
50                         break;
51
52                 case SQL_BIND_TYPE:
53                         /* now support multi-column and multi-row binding */
54                         if (conn)
55                                 conn->stmtOptions.bind_size = vParam;
56                         if (stmt)
57                                 stmt->options.bind_size = vParam;
58                         break;
59
60                 case SQL_CONCURRENCY:
61
62                         /*
63                          * positioned update isn't supported so cursor concurrency is
64                          * read-only
65                          */
66                         mylog("SetStmtOption(): SQL_CONCURRENCY = %d\n", vParam);
67                         if (ci->drivers.lie || vParam == SQL_CONCUR_READ_ONLY || vParam == SQL_CONCUR_ROWVER)
68                         {
69                                 if (conn)
70                                         conn->stmtOptions.scroll_concurrency = vParam;
71                                 if (stmt)
72                                         stmt->options.scroll_concurrency = vParam;
73                         }
74                         else 
75                         {
76                                 if (conn)
77                                         conn->stmtOptions.scroll_concurrency = SQL_CONCUR_ROWVER;
78                                 if (stmt)
79                                         stmt->options.scroll_concurrency = SQL_CONCUR_ROWVER;
80                                 changed = TRUE;
81                          }
82                          break;
83
84                 case SQL_CURSOR_TYPE:
85
86                         /*
87                          * if declare/fetch, then type can only be forward. otherwise,
88                          * it can only be forward or static.
89                          */
90                         mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam);
91
92                         if (ci->drivers.lie)
93                         {
94                                 if (conn)
95                                         conn->stmtOptions.cursor_type = vParam;
96                                 if (stmt)
97                                         stmt->options.cursor_type = vParam;
98                         }
99                         else
100                         {
101                                 if (ci->drivers.use_declarefetch)
102                                 {
103                                         if (conn)
104                                                 conn->stmtOptions.cursor_type = SQL_CURSOR_FORWARD_ONLY;
105                                         if (stmt)
106                                                 stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
107
108                                         if (vParam != SQL_CURSOR_FORWARD_ONLY)
109                                                 changed = TRUE;
110                                 }
111                                 else
112                                 {
113                                         if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC)
114                                         {
115                                                 if (conn)
116                                                         conn->stmtOptions.cursor_type = vParam;         /* valid type */
117                                                 if (stmt)
118                                                         stmt->options.cursor_type = vParam; /* valid type */
119                                         }
120                                         else
121                                         {
122                                                 if (conn)
123                                                         conn->stmtOptions.cursor_type = SQL_CURSOR_STATIC;
124                                                 if (stmt)
125                                                         stmt->options.cursor_type = SQL_CURSOR_STATIC;
126
127                                                 changed = TRUE;
128                                         }
129                                 }
130                         }
131                         break;
132
133                 case SQL_KEYSET_SIZE:   /* ignored, but saved and returned      */
134                         mylog("SetStmtOption(): SQL_KEYSET_SIZE, vParam = %d\n", vParam);
135
136                         if (conn)
137                                 conn->stmtOptions.keyset_size = vParam;
138                         if (stmt)
139                                 stmt->options.keyset_size = vParam;
140
141                         break;
142
143                         /*-------
144                          *      if (ci->drivers.lie)
145                          *              stmt->keyset_size = vParam;
146                          *      else
147                          *      {
148                          *              stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
149                          *              stmt->errormsg = "Driver does not support keyset size option";
150                          *              SC_log_error(func, "", stmt);
151                          *              return SQL_ERROR;
152                          *      }
153                          *-------
154                          */
155
156                 case SQL_MAX_LENGTH:    /* ignored, but saved */
157                         mylog("SetStmtOption(): SQL_MAX_LENGTH, vParam = %d\n", vParam);
158                         if (conn)
159                                 conn->stmtOptions.maxLength = vParam;
160                         if (stmt)
161                                 stmt->options.maxLength = vParam;
162                         break;
163
164                 case SQL_MAX_ROWS:              /* ignored, but saved */
165                         mylog("SetStmtOption(): SQL_MAX_ROWS, vParam = %d\n", vParam);
166                         if (conn)
167                                 conn->stmtOptions.maxRows = vParam;
168                         if (stmt)
169                                 stmt->options.maxRows = vParam;
170                         break;
171
172                 case SQL_NOSCAN:                /* ignored */
173                         mylog("SetStmtOption: SQL_NOSCAN, vParam = %d\n", vParam);
174                         break;
175
176                 case SQL_QUERY_TIMEOUT:/* ignored */
177                         mylog("SetStmtOption: SQL_QUERY_TIMEOUT, vParam = %d\n", vParam);
178                         /* "0" returned in SQLGetStmtOption */
179                         break;
180
181                 case SQL_RETRIEVE_DATA:/* ignored, but saved */
182                         mylog("SetStmtOption(): SQL_RETRIEVE_DATA, vParam = %d\n", vParam);
183                         if (conn)
184                                 conn->stmtOptions.retrieve_data = vParam;
185                         if (stmt)
186                                 stmt->options.retrieve_data = vParam;
187                         break;
188
189                 case SQL_ROWSET_SIZE:
190                         mylog("SetStmtOption(): SQL_ROWSET_SIZE, vParam = %d\n", vParam);
191
192                         /*
193                          * Save old rowset size for SQLExtendedFetch purposes If the
194                          * rowset_size is being changed since the last call to fetch
195                          * rows.
196                          */
197
198                         if (stmt && stmt->save_rowset_size <= 0 && stmt->last_fetch_count > 0)
199                                 stmt->save_rowset_size = stmt->options.rowset_size;
200
201                         if (vParam < 1)
202                         {
203                                 vParam = 1;
204                                 changed = TRUE;
205                         }
206
207                         if (conn)
208                                 conn->stmtOptions.rowset_size = vParam;
209                         if (stmt)
210                                 stmt->options.rowset_size = vParam;
211                         break;
212
213                 case SQL_SIMULATE_CURSOR:               /* NOT SUPPORTED */
214                         if (stmt)
215                         {
216                                 stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
217                                 stmt->errormsg = "Simulated positioned update/delete not supported.  Use the cursor library.";
218                                 SC_log_error(func, "", stmt);
219                         }
220                         if (conn)
221                         {
222                                 conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
223                                 conn->errormsg = "Simulated positioned update/delete not supported.  Use the cursor library.";
224                                 CC_log_error(func, "", conn);
225                         }
226                         return SQL_ERROR;
227
228                 case SQL_USE_BOOKMARKS:
229                         if (stmt)
230                                 stmt->options.use_bookmarks = vParam;
231                         if (conn)
232                                 conn->stmtOptions.use_bookmarks = vParam;
233                         break;
234
235                 default:
236                         {
237                                 char            option[64];
238
239                                 if (stmt)
240                                 {
241                                         stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
242                                         stmt->errormsg = "Unknown statement option (Set)";
243                                         sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
244                                         SC_log_error(func, option, stmt);
245                                 }
246                                 if (conn)
247                                 {
248                                         conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
249                                         conn->errormsg = "Unknown statement option (Set)";
250                                         sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
251                                         CC_log_error(func, option, conn);
252                                 }
253
254                                 return SQL_ERROR;
255                         }
256         }
257
258         if (changed)
259         {
260                 if (stmt)
261                 {
262                         stmt->errormsg = "Requested value changed.";
263                         stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
264                 }
265                 if (conn)
266                 {
267                         conn->errormsg = "Requested value changed.";
268                         conn->errornumber = STMT_OPTION_VALUE_CHANGED;
269                 }
270                 return SQL_SUCCESS_WITH_INFO;
271         }
272         else
273                 return SQL_SUCCESS;
274 }
275
276
277 /* Implements only SQL_AUTOCOMMIT */
278 RETCODE SQL_API
279 PGAPI_SetConnectOption(
280                                         HDBC hdbc,
281                                         UWORD fOption,
282                                         UDWORD vParam)
283 {
284         static char *func = "PGAPI_SetConnectOption";
285         ConnectionClass *conn = (ConnectionClass *) hdbc;
286         char            changed = FALSE;
287         RETCODE         retval;
288         int                     i;
289
290         mylog("%s: entering fOption = %d vParam = %d\n", func, fOption, vParam);
291         if (!conn)
292         {
293                 CC_log_error(func, "", NULL);
294                 return SQL_INVALID_HANDLE;
295         }
296
297         switch (fOption)
298         {
299
300                         /*
301                          * Statement Options (apply to all stmts on the connection and
302                          * become defaults for new stmts)
303                          */
304                 case SQL_ASYNC_ENABLE:
305                 case SQL_BIND_TYPE:
306                 case SQL_CONCURRENCY:
307                 case SQL_CURSOR_TYPE:
308                 case SQL_KEYSET_SIZE:
309                 case SQL_MAX_LENGTH:
310                 case SQL_MAX_ROWS:
311                 case SQL_NOSCAN:
312                 case SQL_QUERY_TIMEOUT:
313                 case SQL_RETRIEVE_DATA:
314                 case SQL_ROWSET_SIZE:
315                 case SQL_SIMULATE_CURSOR:
316                 case SQL_USE_BOOKMARKS:
317
318                         /* Affect all current Statements */
319                         for (i = 0; i < conn->num_stmts; i++)
320                         {
321                                 if (conn->stmts[i])
322                                         set_statement_option(NULL, conn->stmts[i], fOption, vParam);
323                         }
324
325                         /*
326                          * Become the default for all future statements on this
327                          * connection
328                          */
329                         retval = set_statement_option(conn, NULL, fOption, vParam);
330
331                         if (retval == SQL_SUCCESS_WITH_INFO)
332                                 changed = TRUE;
333                         else if (retval == SQL_ERROR)
334                                 return SQL_ERROR;
335
336                         break;
337
338                         /*
339                          * Connection Options
340                          */
341
342                 case SQL_ACCESS_MODE:   /* ignored */
343                         break;
344
345                 case SQL_AUTOCOMMIT:
346                         if (CC_is_in_trans(conn))
347                         {
348                                 conn->errormsg = "Cannot switch commit mode while a transaction is in progress";
349                                 conn->errornumber = CONN_TRANSACT_IN_PROGRES;
350                                 CC_log_error(func, "", conn);
351                                 return SQL_ERROR;
352                         }
353
354                         mylog("PGAPI_SetConnectOption: AUTOCOMMIT: transact_status=%d, vparam=%d\n", conn->transact_status, vParam);
355
356                         switch (vParam)
357                         {
358                                 case SQL_AUTOCOMMIT_OFF:
359                                         CC_set_autocommit_off(conn);
360                                         break;
361
362                                 case SQL_AUTOCOMMIT_ON:
363                                         CC_set_autocommit_on(conn);
364                                         break;
365
366                                 default:
367                                         conn->errormsg = "Illegal parameter value for SQL_AUTOCOMMIT";
368                                         conn->errornumber = CONN_INVALID_ARGUMENT_NO;
369                                         CC_log_error(func, "", conn);
370                                         return SQL_ERROR;
371                         }
372                         break;
373
374                 case SQL_CURRENT_QUALIFIER:             /* ignored */
375                         break;
376
377                 case SQL_LOGIN_TIMEOUT:/* ignored */
378                         break;
379
380                 case SQL_PACKET_SIZE:   /* ignored */
381                         break;
382
383                 case SQL_QUIET_MODE:    /* ignored */
384                         break;
385
386                 case SQL_TXN_ISOLATION:/* ignored */
387                         break;
388
389                         /* These options should be handled by driver manager */
390                 case SQL_ODBC_CURSORS:
391                 case SQL_OPT_TRACE:
392                 case SQL_OPT_TRACEFILE:
393                 case SQL_TRANSLATE_DLL:
394                 case SQL_TRANSLATE_OPTION:
395                         CC_log_error(func, "This connect option (Set) is only used by the Driver Manager", conn);
396                         break;
397
398                 default:
399                         {
400                                 char            option[64];
401
402                                 conn->errormsg = "Unknown connect option (Set)";
403                                 conn->errornumber = CONN_UNSUPPORTED_OPTION;
404                                 sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
405                                 if (fOption == 30002 && vParam)
406                                 {
407                                         if (strcmp((char *) vParam, "Microsoft Jet") == 0)
408                                         {
409                                                 conn->errornumber = 0;
410                                                 conn->ms_jet = 1;
411                                                 return SQL_SUCCESS;
412                                         }
413                                 }
414                                 CC_log_error(func, option, conn);
415                                 return SQL_ERROR;
416                         }
417         }
418
419         if (changed)
420         {
421                 conn->errornumber = CONN_OPTION_VALUE_CHANGED;
422                 conn->errormsg = "Requested value changed.";
423                 return SQL_SUCCESS_WITH_INFO;
424         }
425         else
426                 return SQL_SUCCESS;
427 }
428
429
430 /* This function just can tell you whether you are in Autcommit mode or not */
431 RETCODE SQL_API
432 PGAPI_GetConnectOption(
433                                         HDBC hdbc,
434                                         UWORD fOption,
435                                         PTR pvParam)
436 {
437         static char *func = "PGAPI_GetConnectOption";
438         ConnectionClass *conn = (ConnectionClass *) hdbc;
439         ConnInfo *ci = &(conn->connInfo);
440
441         mylog("%s: entering...\n", func);
442
443         if (!conn)
444         {
445                 CC_log_error(func, "", NULL);
446                 return SQL_INVALID_HANDLE;
447         }
448
449         switch (fOption)
450         {
451                 case SQL_ACCESS_MODE:   /* NOT SUPPORTED */
452                         *((UDWORD *) pvParam) = SQL_MODE_READ_WRITE;
453                         break;
454
455                 case SQL_AUTOCOMMIT:
456                         *((UDWORD *) pvParam) = (UDWORD) (CC_is_in_autocommit(conn) ?
457                                                                  SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
458                         break;
459
460                 case SQL_CURRENT_QUALIFIER:             /* don't use qualifiers */
461                         if (pvParam)
462                                 strcpy(pvParam, "");
463
464                         break;
465
466                 case SQL_LOGIN_TIMEOUT:/* NOT SUPPORTED */
467                         *((UDWORD *) pvParam) = 0;
468                         break;
469
470                 case SQL_PACKET_SIZE:   /* NOT SUPPORTED */
471                         *((UDWORD *) pvParam) = ci->drivers.socket_buffersize;
472                         break;
473
474                 case SQL_QUIET_MODE:    /* NOT SUPPORTED */
475                         *((UDWORD *) pvParam) = (UDWORD) NULL;
476                         break;
477
478                 case SQL_TXN_ISOLATION:/* NOT SUPPORTED */
479                         *((UDWORD *) pvParam) = SQL_TXN_SERIALIZABLE;
480                         break;
481
482                         /* These options should be handled by driver manager */
483                 case SQL_ODBC_CURSORS:
484                 case SQL_OPT_TRACE:
485                 case SQL_OPT_TRACEFILE:
486                 case SQL_TRANSLATE_DLL:
487                 case SQL_TRANSLATE_OPTION:
488                         CC_log_error(func, "This connect option (Get) is only used by the Driver Manager", conn);
489                         break;
490
491                 default:
492                         {
493                                 char            option[64];
494
495                                 conn->errormsg = "Unknown connect option (Get)";
496                                 conn->errornumber = CONN_UNSUPPORTED_OPTION;
497                                 sprintf(option, "fOption=%d", fOption);
498                                 CC_log_error(func, option, conn);
499                                 return SQL_ERROR;
500                                 break;
501                         }
502         }
503
504         return SQL_SUCCESS;
505 }
506
507
508 RETCODE SQL_API
509 PGAPI_SetStmtOption(
510                                  HSTMT hstmt,
511                                  UWORD fOption,
512                                  UDWORD vParam)
513 {
514         static char *func = "PGAPI_SetStmtOption";
515         StatementClass *stmt = (StatementClass *) hstmt;
516
517         mylog("%s: entering...\n", func);
518
519         /*
520          * Though we could fake Access out by just returning SQL_SUCCESS all
521          * the time, but it tries to set a huge value for SQL_MAX_LENGTH and
522          * expects the driver to reduce it to the real value.
523          */
524         if (!stmt)
525         {
526                 SC_log_error(func, "", NULL);
527                 return SQL_INVALID_HANDLE;
528         }
529
530         return set_statement_option(NULL, stmt, fOption, vParam);
531 }
532
533
534 RETCODE SQL_API
535 PGAPI_GetStmtOption(
536                                  HSTMT hstmt,
537                                  UWORD fOption,
538                                  PTR pvParam)
539 {
540         static char *func = "PGAPI_GetStmtOption";
541         StatementClass *stmt = (StatementClass *) hstmt;
542         QResultClass *res;
543         ConnInfo *ci = &(SC_get_conn(stmt)->connInfo);
544
545         mylog("%s: entering...\n", func);
546
547         /*
548          * thought we could fake Access out by just returning SQL_SUCCESS all
549          * the time, but it tries to set a huge value for SQL_MAX_LENGTH and
550          * expects the driver to reduce it to the real value
551          */
552         if (!stmt)
553         {
554                 SC_log_error(func, "", NULL);
555                 return SQL_INVALID_HANDLE;
556         }
557
558         switch (fOption)
559         {
560                 case SQL_GET_BOOKMARK:
561                 case SQL_ROW_NUMBER:
562
563                         res = stmt->result;
564
565                         if (stmt->manual_result || !ci->drivers.use_declarefetch)
566                         {
567                                 /* make sure we're positioned on a valid row */
568                                 if ((stmt->currTuple < 0) ||
569                                         (stmt->currTuple >= QR_get_num_tuples(res)))
570                                 {
571                                         stmt->errormsg = "Not positioned on a valid row.";
572                                         stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
573                                         SC_log_error(func, "", stmt);
574                                         return SQL_ERROR;
575                                 }
576                         }
577                         else
578                         {
579                                 if (stmt->currTuple == -1 || !res || !res->tupleField)
580                                 {
581                                         stmt->errormsg = "Not positioned on a valid row.";
582                                         stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
583                                         SC_log_error(func, "", stmt);
584                                         return SQL_ERROR;
585                                 }
586                         }
587
588                         if (fOption == SQL_GET_BOOKMARK && stmt->options.use_bookmarks == SQL_UB_OFF)
589                         {
590                                 stmt->errormsg = "Operation invalid because use bookmarks not enabled.";
591                                 stmt->errornumber = STMT_OPERATION_INVALID;
592                                 SC_log_error(func, "", stmt);
593                                 return SQL_ERROR;
594                         }
595
596                         *((UDWORD *) pvParam) = SC_get_bookmark(stmt);
597
598                         break;
599
600                 case SQL_ASYNC_ENABLE:  /* NOT SUPPORTED */
601                         *((SDWORD *) pvParam) = SQL_ASYNC_ENABLE_OFF;
602                         break;
603
604                 case SQL_BIND_TYPE:
605                         *((SDWORD *) pvParam) = stmt->options.bind_size;
606                         break;
607
608                 case SQL_CONCURRENCY:   /* NOT REALLY SUPPORTED */
609                         mylog("GetStmtOption(): SQL_CONCURRENCY\n");
610                         *((SDWORD *) pvParam) = stmt->options.scroll_concurrency;
611                         break;
612
613                 case SQL_CURSOR_TYPE:   /* PARTIAL SUPPORT */
614                         mylog("GetStmtOption(): SQL_CURSOR_TYPE\n");
615                         *((SDWORD *) pvParam) = stmt->options.cursor_type;
616                         break;
617
618                 case SQL_KEYSET_SIZE:   /* NOT SUPPORTED, but saved */
619                         mylog("GetStmtOption(): SQL_KEYSET_SIZE\n");
620                         *((SDWORD *) pvParam) = stmt->options.keyset_size;
621                         break;
622
623                 case SQL_MAX_LENGTH:    /* NOT SUPPORTED, but saved */
624                         *((SDWORD *) pvParam) = stmt->options.maxLength;
625                         break;
626
627                 case SQL_MAX_ROWS:              /* NOT SUPPORTED, but saved */
628                         *((SDWORD *) pvParam) = stmt->options.maxRows;
629                         mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->options.maxRows);
630                         break;
631
632                 case SQL_NOSCAN:                /* NOT SUPPORTED */
633                         *((SDWORD *) pvParam) = SQL_NOSCAN_ON;
634                         break;
635
636                 case SQL_QUERY_TIMEOUT:/* NOT SUPPORTED */
637                         *((SDWORD *) pvParam) = 0;
638                         break;
639
640                 case SQL_RETRIEVE_DATA:/* NOT SUPPORTED, but saved */
641                         *((SDWORD *) pvParam) = stmt->options.retrieve_data;
642                         break;
643
644                 case SQL_ROWSET_SIZE:
645                         *((SDWORD *) pvParam) = stmt->options.rowset_size;
646                         break;
647
648                 case SQL_SIMULATE_CURSOR:               /* NOT SUPPORTED */
649                         *((SDWORD *) pvParam) = SQL_SC_NON_UNIQUE;
650                         break;
651
652                 case SQL_USE_BOOKMARKS:
653                         *((SDWORD *) pvParam) = stmt->options.use_bookmarks;
654                         break;
655
656                 default:
657                         {
658                                 char            option[64];
659
660                                 stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
661                                 stmt->errormsg = "Unknown statement option (Get)";
662                                 sprintf(option, "fOption=%d", fOption);
663                                 SC_log_error(func, option, stmt);
664                                 return SQL_ERROR;
665                         }
666         }
667
668         return SQL_SUCCESS;
669 }