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 "readme.txt" for copyright and license information.
13  *--------
14  */
15 
16 #include "psqlodbc.h"
17 #include "unicode_support.h"
18 #include <string.h>
19 
20 #include "misc.h"
21 #include "environ.h"
22 #include "connection.h"
23 #include "statement.h"
24 #include "qresult.h"
25 #include "pgapifunc.h"
26 
27 static RETCODE
set_statement_option(ConnectionClass * conn,StatementClass * stmt,SQLUSMALLINT fOption,SQLULEN vParam)28 set_statement_option(ConnectionClass *conn,
29 					 StatementClass *stmt,
30 					 SQLUSMALLINT fOption,
31 					 SQLULEN vParam)
32 {
33 	CSTR func = "set_statement_option";
34 	char		changed = FALSE;
35 	ConnInfo   *ci = NULL;
36 	SQLULEN		setval;
37 
38 	if (conn)
39 		ci = &(conn->connInfo);
40 	else
41 		ci = &(SC_get_conn(stmt)->connInfo);
42 	switch (fOption)
43 	{
44 		case SQL_ASYNC_ENABLE:	/* ignored */
45 			break;
46 
47 		case SQL_BIND_TYPE:
48 			/* now support multi-column and multi-row binding */
49 			if (conn)
50 				conn->ardOptions.bind_size = (SQLUINTEGER) vParam;
51 			if (stmt)
52 				SC_get_ARDF(stmt)->bind_size = (SQLUINTEGER) vParam;
53 			break;
54 
55 		case SQL_CONCURRENCY:
56 			/*
57 			 * positioned update isn't supported so cursor concurrency is
58 			 * read-only
59 			 */
60 			MYLOG(0, "SQL_CONCURRENCY = " FORMAT_LEN " ", vParam);
61 			setval = SQL_CONCUR_READ_ONLY;
62 			if (SQL_CONCUR_READ_ONLY == vParam)
63 				;
64 			else if (ci->drivers.lie)
65 				setval = vParam;
66 			else if (0 != ci->updatable_cursors)
67 				setval = SQL_CONCUR_ROWVER;
68 			if (conn)
69 				conn->stmtOptions.scroll_concurrency = (SQLUINTEGER) setval;
70 			else if (stmt)
71 			{
72 				if (SC_get_Result(stmt))
73 				{
74 					SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "The attr can't be changed because the cursor is open.", func);
75 					return SQL_ERROR;
76 				}
77 				stmt->options.scroll_concurrency =
78 				stmt->options_orig.scroll_concurrency = (SQLUINTEGER) setval;
79 			}
80 			if (setval != vParam)
81 				changed = TRUE;
82 			MYPRINTF(0, "-> " FORMAT_LEN "\n", setval);
83 			break;
84 
85 		case SQL_CURSOR_TYPE:
86 			/*
87 			 * if declare/fetch, then type can only be forward. otherwise,
88 			 * it can only be forward or static.
89 			 */
90 			MYLOG(0, "SQL_CURSOR_TYPE = " FORMAT_LEN " ", vParam);
91 			setval = SQL_CURSOR_FORWARD_ONLY;
92 			if (ci->drivers.lie)
93 				setval = vParam;
94 			else if (SQL_CURSOR_STATIC == vParam)
95 				setval = vParam;
96 			else if (SQL_CURSOR_KEYSET_DRIVEN == vParam)
97 			{
98 				if (0 != (ci->updatable_cursors & ALLOW_KEYSET_DRIVEN_CURSORS))
99 					setval = vParam;
100 				else
101 					setval = SQL_CURSOR_STATIC; /* at least scrollable */
102 			}
103 			else if (SQL_CURSOR_DYNAMIC == vParam)
104 			{
105 				if (0 != (ci->updatable_cursors & ALLOW_DYNAMIC_CURSORS))
106 					setval = vParam;
107 				else if (0 != (ci->updatable_cursors & ALLOW_KEYSET_DRIVEN_CURSORS))
108 					setval = SQL_CURSOR_KEYSET_DRIVEN;
109 				else
110 					setval = SQL_CURSOR_STATIC; /* at least scrollable */
111 			}
112 			if (conn)
113 				conn->stmtOptions.cursor_type = (SQLUINTEGER) setval;
114 			else if (stmt)
115 			{
116 				if (SC_get_Result(stmt))
117 				{
118 					SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "The attr can't be changed because the cursor is open.", func);
119 					return SQL_ERROR;
120 				}
121 				stmt->options_orig.cursor_type =
122 				stmt->options.cursor_type = (SQLUINTEGER) setval;
123 			}
124 			if (setval != vParam)
125 				changed = TRUE;
126 			MYPRINTF(0, "-> " FORMAT_LEN "\n", setval);
127 			break;
128 
129 		case SQL_KEYSET_SIZE:	/* ignored, but saved and returned	*/
130 			MYLOG(0, "SQL_KEYSET_SIZE, vParam = " FORMAT_LEN "\n", vParam);
131 
132 			if (conn)
133 				conn->stmtOptions.keyset_size = vParam;
134 			if (stmt)
135 			{
136 				stmt->options_orig.keyset_size = vParam;
137 				if (!SC_get_Result(stmt))
138 					stmt->options.keyset_size = vParam;
139 				if (stmt->options.keyset_size != (SQLLEN) vParam)
140 					changed = TRUE;
141 			}
142 
143 			break;
144 
145 		case SQL_MAX_LENGTH:	/* ignored, but saved */
146 			MYLOG(0, "SQL_MAX_LENGTH, vParam = " FORMAT_LEN "\n", vParam);
147 			if (conn)
148 				conn->stmtOptions.maxLength = vParam;
149 			if (stmt)
150 			{
151 				stmt->options_orig.maxLength = vParam;
152 				if (!SC_get_Result(stmt))
153 					stmt->options.maxLength = vParam;
154 				if (stmt->options.maxLength != (SQLLEN) vParam)
155 					changed = TRUE;
156 			}
157 			break;
158 
159 		case SQL_MAX_ROWS:		/* ignored, but saved */
160 			MYLOG(0, "SQL_MAX_ROWS, vParam = " FORMAT_LEN "\n", vParam);
161 			if (conn)
162 				conn->stmtOptions.maxRows = vParam;
163 			if (stmt)
164 			{
165 				stmt->options_orig.maxRows = vParam;
166 				if (!SC_get_Result(stmt))
167 					stmt->options.maxRows = vParam;
168 				if (stmt->options.maxRows != (SQLLEN)vParam)
169 					changed = TRUE;
170 			}
171 			break;
172 
173 		case SQL_NOSCAN:		/* ignored */
174 			MYLOG(0, "SQL_NOSCAN, vParam = " FORMAT_LEN "\n", vParam);
175 			break;
176 
177 		case SQL_QUERY_TIMEOUT:	/* ignored */
178 			MYLOG(0, "SQL_QUERY_TIMEOUT, vParam = " FORMAT_LEN "\n", vParam);
179 			if (conn)
180 				conn->stmtOptions.stmt_timeout = (SQLULEN) vParam;
181 			if (stmt)
182 				stmt->options.stmt_timeout = (SQLULEN) vParam;
183 			break;
184 
185 		case SQL_RETRIEVE_DATA:
186 			MYLOG(0, "SQL_RETRIEVE_DATA, vParam = " FORMAT_LEN "\n", vParam);
187 			if (conn)
188 				conn->stmtOptions.retrieve_data = (SQLUINTEGER) vParam;
189 			if (stmt)
190 				stmt->options.retrieve_data = (SQLUINTEGER) vParam;
191 			break;
192 
193 		case SQL_ROWSET_SIZE:
194 			MYLOG(0, "SQL_ROWSET_SIZE, vParam = " FORMAT_LEN "\n", vParam);
195 
196 			if (vParam < 1)
197 			{
198 				vParam = 1;
199 				changed = TRUE;
200 			}
201 
202 			if (conn)
203 				conn->ardOptions.size_of_rowset_odbc2 = vParam;
204 			if (stmt)
205 				SC_get_ARDF(stmt)->size_of_rowset_odbc2 = vParam;
206 			break;
207 
208 		case SQL_SIMULATE_CURSOR:		/* NOT SUPPORTED */
209 			if (stmt)
210 			{
211 				SC_set_error(stmt, STMT_NOT_IMPLEMENTED_ERROR, "Simulated positioned update/delete not supported.  Use the cursor library.", func);
212 			}
213 			if (conn)
214 			{
215 				CC_set_error(conn, CONN_NOT_IMPLEMENTED_ERROR, "Simulated positioned update/delete not supported.  Use the cursor library.", func);
216 			}
217 			return SQL_ERROR;
218 
219 		case SQL_USE_BOOKMARKS:
220 			if (stmt)
221 			{
222 				MYLOG(0, "USE_BOOKMARKS %s\n", (vParam == SQL_UB_OFF) ? "off" : ((vParam == SQL_UB_VARIABLE) ? "variable" : "fixed"));
223 				setval = vParam;
224 				stmt->options.use_bookmarks = (SQLUINTEGER) setval;
225 			}
226 			if (conn)
227 				conn->stmtOptions.use_bookmarks = (SQLUINTEGER) vParam;
228 			break;
229 
230 		case 1204: /* SQL_COPT_SS_PRESERVE_CURSORS ? */
231 			if (stmt)
232 			{
233 				SC_set_error(stmt, STMT_OPTION_NOT_FOR_THE_DRIVER, "The option may be for MS SQL Server(Set)", func);
234 			}
235 			else if (conn)
236 			{
237 				CC_set_error(conn, CONN_OPTION_NOT_FOR_THE_DRIVER, "The option may be for MS SQL Server(Set)", func);
238 			}
239 			return SQL_ERROR;
240 		case 1227: /* SQL_SOPT_SS_HIDDEN_COLUMNS ? */
241 		case 1228: /* SQL_SOPT_SS_NOBROWSETABLE ? */
242 			if (stmt)
243 			{
244 #ifndef	NOT_USED
245 				if (0 != vParam)
246 					changed = TRUE;
247 				break;
248 #else
249 				SC_set_error(stmt, STMT_OPTION_NOT_FOR_THE_DRIVER, "The option may be for MS SQL Server(Set)", func);
250 #endif /* NOT_USED */
251 			}
252 			else if (conn)
253 			{
254 				CC_set_error(conn, CONN_OPTION_NOT_FOR_THE_DRIVER, "The option may be for MS SQL Server(Set)", func);
255 			}
256 			return SQL_ERROR;
257 		default:
258 			{
259 				char		option[64];
260 
261 				if (stmt)
262 				{
263 					SC_set_error(stmt, STMT_NOT_IMPLEMENTED_ERROR, "Unknown statement option (Set)", func);
264 					SPRINTF_FIXED(option, "fOption=%d, vParam=" FORMAT_ULEN, fOption, vParam);
265 					SC_log_error(func, option, stmt);
266 				}
267 				if (conn)
268 				{
269 					CC_set_error(conn, CONN_NOT_IMPLEMENTED_ERROR, "Unknown statement option (Set)", func);
270 					SPRINTF_FIXED(option, "fOption=%d, vParam=" FORMAT_ULEN, fOption, vParam);
271 					CC_log_error(func, option, conn);
272 				}
273 
274 				return SQL_ERROR;
275 			}
276 	}
277 
278 	if (changed)
279 	{
280 		if (stmt)
281 		{
282 			SC_set_error(stmt, STMT_OPTION_VALUE_CHANGED, "Requested value changed.", func);
283 		}
284 		if (conn)
285 		{
286 			CC_set_error(conn, CONN_OPTION_VALUE_CHANGED, "Requested value changed.", func);
287 		}
288 		return SQL_SUCCESS_WITH_INFO;
289 	}
290 	else
291 		return SQL_SUCCESS;
292 }
293 
294 
295 /* Implements only SQL_AUTOCOMMIT */
296 RETCODE		SQL_API
PGAPI_SetConnectOption(HDBC hdbc,SQLUSMALLINT fOption,SQLULEN vParam)297 PGAPI_SetConnectOption(HDBC hdbc,
298 					   SQLUSMALLINT fOption,
299 					   SQLULEN vParam)
300 {
301 	CSTR func = "PGAPI_SetConnectOption";
302 	ConnectionClass *conn = (ConnectionClass *) hdbc;
303 	char		changed = FALSE;
304 	RETCODE		retval;
305 	BOOL		autocomm_on;
306 
307 	MYLOG(0, "entering fOption = %d vParam = " FORMAT_LEN "\n", fOption, vParam);
308 	if (!conn)
309 	{
310 		CC_log_error(func, "", NULL);
311 		return SQL_INVALID_HANDLE;
312 	}
313 
314 	switch (fOption)
315 	{
316 		/*
317 		 * Statement Options (apply to all stmts on the connection and
318 		 * become defaults for new stmts)
319 		 */
320 		case SQL_ASYNC_ENABLE:
321 		case SQL_BIND_TYPE:
322 		case SQL_CONCURRENCY:
323 		case SQL_CURSOR_TYPE:
324 		case SQL_KEYSET_SIZE:
325 		case SQL_MAX_LENGTH:
326 		case SQL_MAX_ROWS:
327 		case SQL_NOSCAN:
328 		case SQL_QUERY_TIMEOUT:
329 		case SQL_RETRIEVE_DATA:
330 		case SQL_ROWSET_SIZE:
331 		case SQL_SIMULATE_CURSOR:
332 		case SQL_USE_BOOKMARKS:
333 			/*
334 			 * Become the default for all future statements on this
335 			 * connection
336 			 */
337 			retval = set_statement_option(conn, NULL, fOption, vParam);
338 
339 			if (retval == SQL_SUCCESS_WITH_INFO)
340 				changed = TRUE;
341 			else if (retval == SQL_ERROR)
342 				return SQL_ERROR;
343 
344 			break;
345 
346 		/*
347 		 * Connection Options
348 		 */
349 
350 		case SQL_ACCESS_MODE:	/* ignored */
351 			break;
352 
353 		case SQL_AUTOCOMMIT:
354 			switch (vParam)
355 			{
356 				case SQL_AUTOCOMMIT_ON:
357 					autocomm_on = TRUE;
358 					break;
359 				case SQL_AUTOCOMMIT_OFF:
360 					autocomm_on = FALSE;
361 					break;
362 				default:
363 					CC_set_error(conn, CONN_INVALID_ARGUMENT_NO, "Illegal parameter value for SQL_AUTOCOMMIT", func);
364 					return SQL_ERROR;
365 			}
366 			if (autocomm_on && SQL_AUTOCOMMIT_OFF != conn->autocommit_public)
367 				break;
368 			else if (!autocomm_on && SQL_AUTOCOMMIT_OFF == conn->autocommit_public)
369 				break;
370 			conn->autocommit_public = (autocomm_on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
371 			MYLOG(0, "AUTOCOMMIT: transact_status=%d, vparam=" FORMAT_LEN "\n", conn->transact_status, vParam);
372 
373 #ifdef	_HANDLE_ENLIST_IN_DTC_
374 			if (CC_is_in_global_trans(conn))
375 			{
376 				CC_set_error(conn, CONN_ILLEGAL_TRANSACT_STATE, "Don't change AUTOCOMMIT mode in a distributed transaction", func);
377 				return SQL_ERROR;
378 			}
379 #endif	/* _HANDLE_ENLIST_IN_DTC_ */
380 			CC_set_autocommit(conn, autocomm_on);
381 			break;
382 
383 		case SQL_CURRENT_QUALIFIER:		/* ignored */
384 			break;
385 
386 		case SQL_LOGIN_TIMEOUT:
387 			conn->login_timeout = (SQLUINTEGER) vParam;
388 			break;
389 
390 		case SQL_PACKET_SIZE:	/* ignored */
391 			break;
392 
393 		case SQL_QUIET_MODE:	/* ignored */
394 			break;
395 
396 		case SQL_TXN_ISOLATION:
397 			if (conn->isolation == vParam)
398 				break;
399 			/*
400 			 * If the connection is not established, just record the setting to
401 			 * reflect it upon connection.
402 			 */
403 			if (CC_not_connected(conn))
404 			{
405 				conn->isolation = (UInt4) vParam;
406 				break;
407 			}
408 
409 			/* The ODBC spec prohibits changing the isolation level while in manual transaction. */
410 			if (CC_is_in_trans(conn))
411 			{
412 				if (CC_does_autocommit(conn) && !CC_is_in_error_trans(conn))
413 					CC_commit(conn);
414 				else
415 				{
416 					CC_set_error(conn, CONN_TRANSACT_IN_PROGRES, "Cannot switch isolation level while a transaction is in progress", func);
417 					return SQL_ERROR;
418 				}
419 			}
420 
421 			if (!CC_set_transact(conn, (UInt4) vParam))
422 				return SQL_ERROR;
423 
424 			conn->isolation = (UInt4) vParam;
425 			break;
426 
427 		/* These options should be handled by driver manager */
428 		case SQL_ODBC_CURSORS:
429 		case SQL_OPT_TRACE:
430 		case SQL_OPT_TRACEFILE:
431 		case SQL_TRANSLATE_DLL:
432 		case SQL_TRANSLATE_OPTION:
433 			CC_log_error(func, "This connect option (Set) is only used by the Driver Manager", conn);
434 			break;
435 
436 		default:
437 			{
438 				char		option[64];
439 
440 				CC_set_error(conn, CONN_UNSUPPORTED_OPTION, "Unknown connect option (Set)", func);
441 				SPRINTF_FIXED(option, "fOption=%d, vParam=" FORMAT_LEN, fOption, vParam);
442 #ifdef	WIN32
443 				if (fOption == 30002 && vParam)
444 				{
445 					int	cmp;
446 #ifdef	UNICODE_SUPPORT
447 					if (CC_is_in_unicode_driver(conn))
448 					{
449 						char *asPara = ucs2_to_utf8((SQLWCHAR *) vParam, SQL_NTS, NULL, FALSE);
450 						cmp = strcmp(asPara, "Microsoft Jet");
451 						free(asPara);
452 					}
453 					else
454 #endif /* UNICODE_SUPPORT */
455 					cmp = strncmp((char *) vParam, "Microsoft Jet", 13);
456 					if (0 == cmp)
457 					{
458 						MYLOG(0, "Microsoft Jet !!!!\n");
459 						CC_set_errornumber(conn, 0);
460 						conn->ms_jet = 1;
461 						return SQL_SUCCESS;
462 					}
463 				}
464 #endif /* WIN32 */
465 				CC_log_error(func, option, conn);
466 				return SQL_ERROR;
467 			}
468 	}
469 
470 	if (changed)
471 	{
472 		CC_set_error(conn, CONN_OPTION_VALUE_CHANGED, "Requested value changed.", func);
473 		return SQL_SUCCESS_WITH_INFO;
474 	}
475 	else
476 		return SQL_SUCCESS;
477 }
478 
479 
480 /* This function just can tell you whether you are in Autcommit mode or not */
481 RETCODE		SQL_API
PGAPI_GetConnectOption(HDBC hdbc,SQLUSMALLINT fOption,PTR pvParam,SQLINTEGER * StringLength,SQLINTEGER BufferLength)482 PGAPI_GetConnectOption(HDBC hdbc,
483 					   SQLUSMALLINT fOption,
484 					   PTR pvParam,
485 					   SQLINTEGER *StringLength,
486 					   SQLINTEGER BufferLength)
487 {
488 	CSTR func = "PGAPI_GetConnectOption";
489 	ConnectionClass *conn = (ConnectionClass *) hdbc;
490 	const char	*p = NULL;
491 	SQLLEN		len = sizeof(SQLINTEGER);
492 	SQLRETURN	result = SQL_SUCCESS;
493 
494 	MYLOG(0, "entering...\n");
495 
496 	if (!conn)
497 	{
498 		CC_log_error(func, "", NULL);
499 		return SQL_INVALID_HANDLE;
500 	}
501 
502 	switch (fOption)
503 	{
504 		case SQL_ACCESS_MODE:	/* NOT SUPPORTED */
505 			*((SQLUINTEGER *) pvParam) = SQL_MODE_READ_WRITE;
506 			break;
507 
508 		case SQL_AUTOCOMMIT:
509 			*((SQLUINTEGER *) pvParam) = conn->autocommit_public;
510 			break;
511 
512 		case SQL_CURRENT_QUALIFIER:		/* don't use qualifiers */
513 			len = 0;
514 			p = CurrCatString(conn);
515 			break;
516 
517 		case SQL_LOGIN_TIMEOUT:
518 			*((SQLUINTEGER *) pvParam) = conn->login_timeout;
519 			break;
520 
521 		case SQL_PACKET_SIZE:	/* NOT SUPPORTED */
522 			*((SQLUINTEGER *) pvParam) = 4096;
523 			break;
524 
525 		case SQL_QUERY_TIMEOUT:
526 			*((SQLULEN *) pvParam) = conn->stmtOptions.stmt_timeout;
527 			break;
528 
529 		case SQL_QUIET_MODE:	/* NOT SUPPORTED */
530 			*((SQLULEN *) pvParam) = 0;
531 			break;
532 
533 		case SQL_TXN_ISOLATION:
534 			if (conn->isolation == 0)
535 			{
536 				if (CC_not_connected(conn))
537 					return SQL_NO_DATA;
538 				conn->isolation = CC_get_isolation(conn);
539 			}
540 			*((SQLUINTEGER *) pvParam) = conn->isolation;
541 			break;
542 
543 #ifdef	SQL_ATTR_CONNECTION_DEAD
544 		case SQL_ATTR_CONNECTION_DEAD:
545 #else
546 		case 1209:
547 #endif /* SQL_ATTR_CONNECTION_DEAD */
548 			MYLOG(0, "CONNECTION_DEAD status=%d", conn->status);
549 			*((SQLUINTEGER *) pvParam) = CC_not_connected(conn);
550 			MYPRINTF(0, " val=" FORMAT_UINTEGER "\n", *((SQLUINTEGER *) pvParam));
551                         break;
552 
553 		case SQL_ATTR_ANSI_APP:
554 			*((SQLUINTEGER *) pvParam) = CC_is_in_ansi_app(conn);
555 			MYLOG(0, "ANSI_APP val=" FORMAT_UINTEGER "\n", *((SQLUINTEGER *) pvParam));
556                         break;
557 
558 			/* These options should be handled by driver manager */
559 		case SQL_ODBC_CURSORS:
560 		case SQL_OPT_TRACE:
561 		case SQL_OPT_TRACEFILE:
562 		case SQL_TRANSLATE_DLL:
563 		case SQL_TRANSLATE_OPTION:
564 			CC_log_error(func, "This connect option (Get) is only used by the Driver Manager", conn);
565 			break;
566 
567 		default:
568 			{
569 				char		option[64];
570 
571 				CC_set_error(conn, CONN_UNSUPPORTED_OPTION, "Unknown connect option (Get)", func);
572 				SPRINTF_FIXED(option, "fOption=%d", fOption);
573 				CC_log_error(func, option, conn);
574 				return SQL_ERROR;
575 				break;
576 			}
577 	}
578 
579 	if (NULL != p && 0 == len)
580 	{
581 		/* char/binary data */
582 		len = strlen(p);
583 
584 		if (pvParam)
585 		{
586 #ifdef  UNICODE_SUPPORT
587 			if (CC_is_in_unicode_driver(conn))
588 			{
589 				len = utf8_to_ucs2(p, len, (SQLWCHAR *) pvParam , BufferLength / WCLEN);
590 				len *= WCLEN;
591 			}
592 			else
593 #endif /* UNICODE_SUPPORT */
594 			strncpy_null((char *) pvParam, p, (size_t) BufferLength);
595 
596 			if (len >= BufferLength)
597 			{
598 				result = SQL_SUCCESS_WITH_INFO;
599 				CC_set_error(conn, CONN_TRUNCATED, "The buffer was too small for the pvParam.", func);
600 			}
601 		}
602 	}
603 	if (StringLength)
604 		*StringLength = (SQLINTEGER) len;
605 	return result;
606 }
607 
608 
609 RETCODE		SQL_API
PGAPI_SetStmtOption(HSTMT hstmt,SQLUSMALLINT fOption,SQLULEN vParam)610 PGAPI_SetStmtOption(HSTMT hstmt,
611 					SQLUSMALLINT fOption,
612 					SQLULEN vParam)
613 {
614 	CSTR func = "PGAPI_SetStmtOption";
615 	StatementClass *stmt = (StatementClass *) hstmt;
616 	RETCODE	retval;
617 
618 	MYLOG(0, " entering...\n");
619 
620 	/*
621 	 * Though we could fake Access out by just returning SQL_SUCCESS all
622 	 * the time, but it tries to set a huge value for SQL_MAX_LENGTH and
623 	 * expects the driver to reduce it to the real value.
624 	 */
625 	if (!stmt)
626 	{
627 		SC_log_error(func, "", NULL);
628 		return SQL_INVALID_HANDLE;
629 	}
630 
631 	/* StartRollbackState(stmt); */
632 	retval = set_statement_option(NULL, stmt, fOption, vParam);
633 	return retval;
634 }
635 
636 
637 RETCODE		SQL_API
PGAPI_GetStmtOption(HSTMT hstmt,SQLUSMALLINT fOption,PTR pvParam,SQLINTEGER * StringLength,SQLINTEGER BufferLength)638 PGAPI_GetStmtOption(HSTMT hstmt,
639 					SQLUSMALLINT fOption,
640 					PTR pvParam,
641 					SQLINTEGER *StringLength,
642 					SQLINTEGER BufferLength)
643 {
644 	CSTR func = "PGAPI_GetStmtOption";
645 	StatementClass *stmt = (StatementClass *) hstmt;
646 	QResultClass *res;
647 	SQLLEN		ridx;
648 	SQLINTEGER	len = sizeof(SQLINTEGER);
649 	Int4		bookmark;
650 
651 	MYLOG(0, "entering...\n");
652 
653 	/*
654 	 * thought we could fake Access out by just returning SQL_SUCCESS all
655 	 * the time, but it tries to set a huge value for SQL_MAX_LENGTH and
656 	 * expects the driver to reduce it to the real value
657 	 */
658 	if (!stmt)
659 	{
660 		SC_log_error(func, "", NULL);
661 		return SQL_INVALID_HANDLE;
662 	}
663 
664 	switch (fOption)
665 	{
666 		case SQL_GET_BOOKMARK:
667 		case SQL_ROW_NUMBER:
668 
669 			res = SC_get_Curres(stmt);
670 			if (!res)
671 			{
672 				SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "The cursor has no result.", func);
673 				return SQL_ERROR;
674 			}
675 
676 			ridx = GIdx2CacheIdx(stmt->currTuple, stmt, res);
677 			if (!SC_is_fetchcursor(stmt))
678 			{
679 				/* make sure we're positioned on a valid row */
680 				if ((ridx < 0) ||
681 					(((SQLULEN) ridx) >= QR_get_num_cached_tuples(res)))
682 				{
683 					SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Not positioned on a valid row.", func);
684 					return SQL_ERROR;
685 				}
686 			}
687 			else
688 			{
689 				if (stmt->currTuple < 0 || !res->tupleField)
690 				{
691 					SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Not positioned on a valid row.", func);
692 					return SQL_ERROR;
693 				}
694 			}
695 
696 			if (fOption == SQL_GET_BOOKMARK && stmt->options.use_bookmarks == SQL_UB_OFF)
697 			{
698 				SC_set_error(stmt, STMT_OPERATION_INVALID, "Operation invalid because use bookmarks not enabled.", func);
699 				return SQL_ERROR;
700 			}
701 
702 			bookmark = SC_make_int4_bookmark(stmt->currTuple);
703 			memcpy(pvParam, &bookmark, sizeof(UInt4));
704 
705 			break;
706 
707 		case SQL_ASYNC_ENABLE:	/* NOT SUPPORTED */
708 			*((SQLINTEGER *) pvParam) = SQL_ASYNC_ENABLE_OFF;
709 			break;
710 
711 		case SQL_BIND_TYPE:
712 			*((SQLINTEGER *) pvParam) = SC_get_ARDF(stmt)->bind_size;
713 			break;
714 
715 		case SQL_CONCURRENCY:	/* NOT REALLY SUPPORTED */
716 			MYLOG(0, "SQL_CONCURRENCY " FORMAT_INTEGER "\n", stmt->options.scroll_concurrency);
717 			*((SQLINTEGER *) pvParam) = stmt->options.scroll_concurrency;
718 			break;
719 
720 		case SQL_CURSOR_TYPE:	/* PARTIAL SUPPORT */
721 			MYLOG(0, "SQL_CURSOR_TYPE " FORMAT_INTEGER "\n", stmt->options.cursor_type);
722 			*((SQLINTEGER *) pvParam) = stmt->options.cursor_type;
723 			break;
724 
725 		case SQL_KEYSET_SIZE:	/* NOT SUPPORTED, but saved */
726 			MYLOG(0, "SQL_KEYSET_SIZE\n");
727 			*((SQLLEN *) pvParam) = stmt->options.keyset_size;
728 			break;
729 
730 		case SQL_MAX_LENGTH:	/* NOT SUPPORTED, but saved */
731 			*((SQLLEN *) pvParam) = stmt->options.maxLength;
732 			break;
733 
734 		case SQL_MAX_ROWS:		/* NOT SUPPORTED, but saved */
735 			*((SQLLEN *) pvParam) = stmt->options.maxRows;
736 			MYLOG(0, "MAX_ROWS, returning " FORMAT_LEN "\n", stmt->options.maxRows);
737 			break;
738 
739 		case SQL_NOSCAN:		/* NOT SUPPORTED */
740 			*((SQLINTEGER *) pvParam) = SQL_NOSCAN_ON;
741 			break;
742 
743 		case SQL_QUERY_TIMEOUT:	/* NOT SUPPORTED */
744 			*((SQLULEN *) pvParam) = stmt->options.stmt_timeout;
745 			break;
746 
747 		case SQL_RETRIEVE_DATA:
748 			*((SQLINTEGER *) pvParam) = stmt->options.retrieve_data;
749 			break;
750 
751 		case SQL_ROWSET_SIZE:
752 			*((SQLLEN *) pvParam) = SC_get_ARDF(stmt)->size_of_rowset_odbc2;
753 			break;
754 
755 		case SQL_SIMULATE_CURSOR:		/* NOT SUPPORTED */
756 			*((SQLINTEGER *) pvParam) = SQL_SC_NON_UNIQUE;
757 			break;
758 
759 		case SQL_USE_BOOKMARKS:
760 			*((SQLINTEGER *) pvParam) = stmt->options.use_bookmarks;
761 			break;
762 		case 1227: /* SQL_SOPT_SS_HIDDEN_COLUMNS ? */
763 		case 1228: /* SQL_SOPT_SS_NOBROWSETABLE ? */
764 			*((SQLINTEGER *) pvParam) = 0;
765 			break;
766 
767 		default:
768 			{
769 				char		option[64];
770 
771 				SC_set_error(stmt, STMT_NOT_IMPLEMENTED_ERROR, "Unknown statement option (Get)", func);
772 				SPRINTF_FIXED(option, "fOption=%d", fOption);
773 				SC_log_error(func, option, stmt);
774 				return SQL_ERROR;
775 			}
776 	}
777 	if (StringLength)
778 		*StringLength = len;
779 
780 	return SQL_SUCCESS;
781 }
782