results.c 45.7 KB
Newer Older
1 2
/*-------
 * Module:			results.c
3
 *
4 5
 * Description:		This module contains functions related to
 *					retrieving result information through the ODBC API.
6
 *
7
 * Classes:			n/a
8
 *
9 10
 * API functions:	SQLRowCount, SQLNumResultCols, SQLDescribeCol,
 *					SQLColAttributes, SQLGetData, SQLFetch, SQLExtendedFetch,
11 12
 *					SQLMoreResults(NI), SQLSetPos, SQLSetScrollOptions(NI),
 *					SQLSetCursorName, SQLGetCursorName
13
 *
14
 * Comments:		See "notice.txt" for copyright and license information.
15
 *-------
16
 */
17

18
#include "psqlodbc.h"
Byron Nikolaidis's avatar
Byron Nikolaidis committed
19

20
#include <string.h>
21
#include "dlg_specific.h"
22 23 24 25 26 27
#include "environ.h"
#include "connection.h"
#include "statement.h"
#include "bind.h"
#include "qresult.h"
#include "convert.h"
28
#include "pgtypes.h"
29 30

#include <stdio.h>
Byron Nikolaidis's avatar
Byron Nikolaidis committed
31

32
#include "pgapifunc.h"
33 34


Byron Nikolaidis's avatar
Byron Nikolaidis committed
35

36
RETCODE SQL_API
37
PGAPI_RowCount(
38 39
			HSTMT hstmt,
			SDWORD FAR *pcrow)
40
{
41
	static char *func = "PGAPI_RowCount";
42 43 44 45
	StatementClass *stmt = (StatementClass *) hstmt;
	QResultClass *res;
	char	   *msg,
			   *ptr;
46
	ConnInfo *ci;
47 48 49

	if (!stmt)
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
50 51 52
		SC_log_error(func, "", NULL);
		return SQL_INVALID_HANDLE;
	}
53
	ci = &(SC_get_conn(stmt)->connInfo);
54 55
	if (stmt->manual_result)
	{
56 57 58 59
		if (pcrow)
			*pcrow = -1;
		return SQL_SUCCESS;
	}
60

61 62 63 64
	if (stmt->statement_type == STMT_TYPE_SELECT)
	{
		if (stmt->status == STMT_FINISHED)
		{
65 66
			res = SC_get_Result(stmt);

67 68
			if (res && pcrow)
			{
69
				*pcrow = SC_is_fetchcursor(stmt) ? -1 : QR_get_num_tuples(res);
70 71 72
				return SQL_SUCCESS;
			}
		}
73 74 75
	}
	else
	{
76
		res = SC_get_Result(stmt);
77 78
		if (res && pcrow)
		{
79 80
			msg = QR_get_command(res);
			mylog("*** msg = '%s'\n", msg);
81
			trim(msg);			/* get rid of trailing spaces */
82
			ptr = strrchr(msg, ' ');
83 84 85
			if (ptr)
			{
				*pcrow = atoi(ptr + 1);
86
				mylog("**** PGAPI_RowCount(): THE ROWS: *pcrow = %d\n", *pcrow);
87
			}
88 89
			else
			{
90
				*pcrow = -1;
91
				mylog("**** PGAPI_RowCount(): NO ROWS: *pcrow = %d\n", *pcrow);
92 93
			}

94
			return SQL_SUCCESS;
95 96 97
		}
	}

Byron Nikolaidis's avatar
Byron Nikolaidis committed
98
	SC_log_error(func, "Bad return value", stmt);
99
	return SQL_ERROR;
100 101 102
}


103 104 105 106
/*
 *	This returns the number of columns associated with the database
 *	attached to "hstmt".
 */
107
RETCODE SQL_API
108
PGAPI_NumResultCols(
109 110 111
				 HSTMT hstmt,
				 SWORD FAR *pccol)
{
112
	static char *func = "PGAPI_NumResultCols";
113 114 115
	StatementClass *stmt = (StatementClass *) hstmt;
	QResultClass *result;
	char		parse_ok;
116
	ConnInfo *ci;
117

118 119
	if (!stmt)
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
120
		SC_log_error(func, "", NULL);
121
		return SQL_INVALID_HANDLE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
122
	}
123
	ci = &(SC_get_conn(stmt)->connInfo);
124

125
	SC_clear_error(stmt);
126

Byron Nikolaidis's avatar
Byron Nikolaidis committed
127
	parse_ok = FALSE;
128
	if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
129 130 131
	{
		if (stmt->parse_status == STMT_PARSE_NONE)
		{
132
			mylog("PGAPI_NumResultCols: calling parse_statement on stmt=%u\n", stmt);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
133 134
			parse_statement(stmt);
		}
135

136 137
		if (stmt->parse_status != STMT_PARSE_FATAL)
		{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
138 139
			parse_ok = TRUE;
			*pccol = stmt->nfld;
140
			mylog("PARSE: PGAPI_NumResultCols: *pccol = %d\n", *pccol);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
141
		}
142 143
	}

144 145 146
	if (!parse_ok)
	{
		SC_pre_execute(stmt);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
147 148
		result = SC_get_Result(stmt);

149
		mylog("PGAPI_NumResultCols: result = %u, status = %d, numcols = %d\n", result, stmt->status, result != NULL ? QR_NumResultCols(result) : -1);
150 151
		if ((!result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
		{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
152 153 154 155 156 157 158 159
			/* no query has been executed on this statement */
			stmt->errornumber = STMT_SEQUENCE_ERROR;
			stmt->errormsg = "No query has been executed with that handle";
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
		}

		*pccol = QR_NumResultCols(result);
160 161 162
		/* updatable cursors */
		if (ci->updatable_cursors &&
		    stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
163 164 165
		{
			*pccol -= 2;
		}
Byron Nikolaidis's avatar
Byron Nikolaidis committed
166
	}
167 168 169 170

	return SQL_SUCCESS;
}

171

172 173 174 175
/*
 *	Return information about the database column the user wants
 *	information about.
 */
176
RETCODE SQL_API
177
PGAPI_DescribeCol(
178 179 180 181 182 183 184 185 186
			   HSTMT hstmt,
			   UWORD icol,
			   UCHAR FAR *szColName,
			   SWORD cbColNameMax,
			   SWORD FAR *pcbColName,
			   SWORD FAR *pfSqlType,
			   UDWORD FAR *pcbColDef,
			   SWORD FAR *pibScale,
			   SWORD FAR *pfNullable)
187
{
188
	static char *func = "PGAPI_DescribeCol";
189 190 191 192 193 194

	/* gets all the information about a specific column */
	StatementClass *stmt = (StatementClass *) hstmt;
	QResultClass *res;
	char	   *col_name = NULL;
	Int4		fieldtype = 0;
195
	int		precision = 0, scale = 0;
196 197 198 199 200
	ConnInfo   *ci;
	char		parse_ok;
	char		buf[255];
	int			len = 0;
	RETCODE		result;
201

202 203
	mylog("%s: entering...\n", func);

204 205
	if (!stmt)
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
206
		SC_log_error(func, "", NULL);
207
		return SQL_INVALID_HANDLE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
208
	}
209

210
	ci = &(SC_get_conn(stmt)->connInfo);
211

212
	SC_clear_error(stmt);
213

214
	/*
215 216
	 * Dont check for bookmark column. This is the responsibility of the
	 * driver manager.
217
	 */
218

219
	icol--;						/* use zero based column numbers */
220

Byron Nikolaidis's avatar
Byron Nikolaidis committed
221
	parse_ok = FALSE;
222
	if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
223 224 225
	{
		if (stmt->parse_status == STMT_PARSE_NONE)
		{
226
			mylog("PGAPI_DescribeCol: calling parse_statement on stmt=%u\n", stmt);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
227 228 229 230 231
			parse_statement(stmt);
		}

		mylog("PARSE: DescribeCol: icol=%d, stmt=%u, stmt->nfld=%d, stmt->fi=%u\n", icol, stmt, stmt->nfld, stmt->fi);

232 233 234 235
		if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[icol])
		{
			if (icol >= stmt->nfld)
			{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
236 237 238 239 240 241 242 243 244 245
				stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
				stmt->errormsg = "Invalid column number in DescribeCol.";
				SC_log_error(func, "", stmt);
				return SQL_ERROR;
			}
			mylog("DescribeCol: getting info for icol=%d\n", icol);

			fieldtype = stmt->fi[icol]->type;
			col_name = stmt->fi[icol]->name;
			precision = stmt->fi[icol]->precision;
246
			scale = stmt->fi[icol]->scale;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
247 248 249 250 251 252 253

			mylog("PARSE: fieldtype=%d, col_name='%s', precision=%d\n", fieldtype, col_name, precision);
			if (fieldtype > 0)
				parse_ok = TRUE;
		}
	}

254
	/*
255 256 257
	 * If couldn't parse it OR the field being described was not parsed
	 * (i.e., because it was a function or expression, etc, then do it the
	 * old fashioned way.
258 259 260
	 */
	if (!parse_ok)
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
261
		SC_pre_execute(stmt);
262

263
		res = SC_get_Result(stmt);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
264

265
		mylog("**** PGAPI_DescribeCol: res = %u, stmt->status = %d, !finished=%d, !premature=%d\n", res, stmt->status, stmt->status != STMT_FINISHED, stmt->status != STMT_PREMATURE);
266 267
		if ((NULL == res) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
		{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
268 269 270 271 272 273 274
			/* no query has been executed on this statement */
			stmt->errornumber = STMT_SEQUENCE_ERROR;
			stmt->errormsg = "No query has been assigned to this statement.";
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
		}

275 276
		if (icol >= QR_NumResultCols(res))
		{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
277 278
			stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
			stmt->errormsg = "Invalid column number in DescribeCol.";
279
			sprintf(buf, "Col#=%d, #Cols=%d", icol, QR_NumResultCols(res));
280
			SC_log_error(func, buf, stmt);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
281 282 283
			return SQL_ERROR;
		}

284
		col_name = QR_get_fieldname(res, icol);
285
		fieldtype = QR_get_field_type(res, icol);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
286

287
		/* atoi(ci->unknown_sizes) */
288
		precision = pgtype_precision(stmt, fieldtype, icol, ci->drivers.unknown_sizes);
289
		scale = pgtype_scale(stmt, fieldtype, icol);
290
	}
Byron Nikolaidis's avatar
Byron Nikolaidis committed
291 292 293 294 295

	mylog("describeCol: col %d fieldname = '%s'\n", icol, col_name);
	mylog("describeCol: col %d fieldtype = %d\n", icol, fieldtype);
	mylog("describeCol: col %d precision = %d\n", icol, precision);

296 297
	result = SQL_SUCCESS;

298 299 300
	/*
	 * COLUMN NAME
	 */
301 302 303 304 305
	len = strlen(col_name);

	if (pcbColName)
		*pcbColName = len;

306 307
	if (szColName)
	{
308 309
		strncpy_null(szColName, col_name, cbColNameMax);

310 311
		if (len >= cbColNameMax)
		{
312 313
			result = SQL_SUCCESS_WITH_INFO;
			stmt->errornumber = STMT_TRUNCATED;
314
			stmt->errormsg = "The buffer was too small for the colName.";
315
		}
316
	}
317

318 319 320
	/*
	 * SQL TYPE
	 */
321 322 323
	if (pfSqlType)
	{
		*pfSqlType = pgtype_to_sqltype(stmt, fieldtype);
324 325

		mylog("describeCol: col %d *pfSqlType = %d\n", icol, *pfSqlType);
326 327
	}

328 329 330
	/*
	 * PRECISION
	 */
331 332 333
	if (pcbColDef)
	{
		if (precision < 0)
334
			precision = 0;		/* "I dont know" */
335

Byron Nikolaidis's avatar
Byron Nikolaidis committed
336
		*pcbColDef = precision;
337 338 339

		mylog("describeCol: col %d  *pcbColDef = %d\n", icol, *pcbColDef);
	}
340

341 342 343
	/*
	 * SCALE
	 */
344 345
	if (pibScale)
	{
346
		if (scale < 0)
347 348 349
			scale = 0;

		*pibScale = scale;
350
		mylog("describeCol: col %d  *pibScale = %d\n", icol, *pibScale);
351
	}
352

353 354 355
	/*
	 * NULLABILITY
	 */
356 357
	if (pfNullable)
	{
358
		*pfNullable = (parse_ok) ? stmt->fi[icol]->nullable : pgtype_nullable(stmt, fieldtype);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
359

360
		mylog("describeCol: col %d  *pfNullable = %d\n", icol, *pfNullable);
361
	}
362

363
	return result;
364 365
}

366

367
/*		Returns result column descriptor information for a result set. */
368
RETCODE SQL_API
369
PGAPI_ColAttributes(
370 371 372 373 374 375 376
				 HSTMT hstmt,
				 UWORD icol,
				 UWORD fDescType,
				 PTR rgbDesc,
				 SWORD cbDescMax,
				 SWORD FAR *pcbDesc,
				 SDWORD FAR *pfDesc)
377
{
378
	static char *func = "PGAPI_ColAttributes";
379 380 381 382 383 384 385 386 387 388
	StatementClass *stmt = (StatementClass *) hstmt;
	Int4		field_type = 0;
	ConnInfo   *ci;
	int			unknown_sizes;
	int			cols = 0;
	char		parse_ok;
	RETCODE		result;
	char	   *p = NULL;
	int			len = 0,
				value = 0;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
389

390
	mylog("%s: entering...\n", func);
391

392 393
	if (!stmt)
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
394
		SC_log_error(func, "", NULL);
395
		return SQL_INVALID_HANDLE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
396
	}
397

398
	ci = &(SC_get_conn(stmt)->connInfo);
399

400 401 402 403 404
	/*
	 * Dont check for bookmark column.	This is the responsibility of the
	 * driver manager.	For certain types of arguments, the column number
	 * is ignored anyway, so it may be 0.
	 */
405

Byron Nikolaidis's avatar
Byron Nikolaidis committed
406
	icol--;
407

408
	/* atoi(ci->unknown_sizes); */
409
	unknown_sizes = ci->drivers.unknown_sizes;
410 411

	/* not appropriate for SQLColAttributes() */
412
	if (unknown_sizes == UNKNOWNS_AS_DONTKNOW)
413 414
		unknown_sizes = UNKNOWNS_AS_MAX;

Byron Nikolaidis's avatar
Byron Nikolaidis committed
415
	parse_ok = FALSE;
416
	if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
417 418 419
	{
		if (stmt->parse_status == STMT_PARSE_NONE)
		{
420
			mylog("PGAPI_ColAttributes: calling parse_statement\n");
Byron Nikolaidis's avatar
Byron Nikolaidis committed
421 422 423 424 425
			parse_statement(stmt);
		}

		cols = stmt->nfld;

426 427 428 429 430 431
		/*
		 * Column Count is a special case.	The Column number is ignored
		 * in this case.
		 */
		if (fDescType == SQL_COLUMN_COUNT)
		{
432 433 434 435 436 437
			if (pfDesc)
				*pfDesc = cols;

			return SQL_SUCCESS;
		}

438 439 440 441
		if (stmt->parse_status != STMT_PARSE_FATAL && stmt->fi && stmt->fi[icol])
		{
			if (icol >= cols)
			{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
442
				stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
443
				stmt->errormsg = "Invalid column number in ColAttributes.";
Byron Nikolaidis's avatar
Byron Nikolaidis committed
444 445 446 447 448 449 450 451 452
				SC_log_error(func, "", stmt);
				return SQL_ERROR;
			}
			field_type = stmt->fi[icol]->type;
			if (field_type > 0)
				parse_ok = TRUE;
		}
	}

453 454 455
	if (!parse_ok)
	{
		SC_pre_execute(stmt);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
456

457
		mylog("**** PGAPI_ColAtt: result = %u, status = %d, numcols = %d\n", stmt->result, stmt->status, stmt->result != NULL ? QR_NumResultCols(stmt->result) : -1);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
458

459 460
		if ((NULL == stmt->result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
		{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
461 462 463 464 465 466 467
			stmt->errormsg = "Can't get column attributes: no result found.";
			stmt->errornumber = STMT_SEQUENCE_ERROR;
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
		}

		cols = QR_NumResultCols(stmt->result);
468

469 470 471 472 473 474
		/*
		 * Column Count is a special case.	The Column number is ignored
		 * in this case.
		 */
		if (fDescType == SQL_COLUMN_COUNT)
		{
475 476 477 478 479 480
			if (pfDesc)
				*pfDesc = cols;

			return SQL_SUCCESS;
		}

481 482
		if (icol >= cols)
		{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
483
			stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
484
			stmt->errormsg = "Invalid column number in ColAttributes.";
Byron Nikolaidis's avatar
Byron Nikolaidis committed
485 486 487 488 489 490 491 492 493
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
		}

		field_type = QR_get_field_type(stmt->result, icol);
	}

	mylog("colAttr: col %d field_type = %d\n", icol, field_type);

494 495 496 497 498 499
	switch (fDescType)
	{
		case SQL_COLUMN_AUTO_INCREMENT:
			value = pgtype_auto_increment(stmt, field_type);
			if (value == -1)	/* non-numeric becomes FALSE (ODBC Doc) */
				value = FALSE;
500

501
			break;
502

503 504 505
		case SQL_COLUMN_CASE_SENSITIVE:
			value = pgtype_case_sensitive(stmt, field_type);
			break;
506

507 508 509 510 511
			/*
			 * This special case is handled above.
			 *
			 * case SQL_COLUMN_COUNT:
			 */
512 513
		case SQL_COLUMN_DISPLAY_SIZE:
			value = (parse_ok) ? stmt->fi[icol]->display_size : pgtype_display_size(stmt, field_type, icol, unknown_sizes);
514

515
			mylog("PGAPI_ColAttributes: col %d, display_size= %d\n", icol, value);
516

517
			break;
518

519 520 521 522
		case SQL_COLUMN_LABEL:
			if (parse_ok && stmt->fi[icol]->alias[0] != '\0')
			{
				p = stmt->fi[icol]->alias;
523

524
				mylog("PGAPI_ColAttr: COLUMN_LABEL = '%s'\n", p);
525
				break;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
526

527 528
			}
			/* otherwise same as column name -- FALL THROUGH!!! */
Byron Nikolaidis's avatar
Byron Nikolaidis committed
529

530 531
		case SQL_COLUMN_NAME:
			p = (parse_ok) ? stmt->fi[icol]->name : QR_get_fieldname(stmt->result, icol);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
532

533
			mylog("PGAPI_ColAttr: COLUMN_NAME = '%s'\n", p);
534
			break;
535

536 537
		case SQL_COLUMN_LENGTH:
			value = (parse_ok) ? stmt->fi[icol]->length : pgtype_length(stmt, field_type, icol, unknown_sizes);
538

539
			mylog("PGAPI_ColAttributes: col %d, length = %d\n", icol, value);
540
			break;
541

542 543 544
		case SQL_COLUMN_MONEY:
			value = pgtype_money(stmt, field_type);
			break;
545

546 547 548
		case SQL_COLUMN_NULLABLE:
			value = (parse_ok) ? stmt->fi[icol]->nullable : pgtype_nullable(stmt, field_type);
			break;
549

550 551 552
		case SQL_COLUMN_OWNER_NAME:
			p = "";
			break;
553

554 555
		case SQL_COLUMN_PRECISION:
			value = (parse_ok) ? stmt->fi[icol]->precision : pgtype_precision(stmt, field_type, icol, unknown_sizes);
556

557
			mylog("PGAPI_ColAttributes: col %d, precision = %d\n", icol, value);
558
			break;
559

560 561 562
		case SQL_COLUMN_QUALIFIER_NAME:
			p = "";
			break;
563

564 565 566
		case SQL_COLUMN_SCALE:
			value = pgtype_scale(stmt, field_type, icol);
			break;
567

568 569 570
		case SQL_COLUMN_SEARCHABLE:
			value = pgtype_searchable(stmt, field_type);
			break;
571

572 573
		case SQL_COLUMN_TABLE_NAME:
			p = (parse_ok && stmt->fi[icol]->ti) ? stmt->fi[icol]->ti->name : "";
574

575
			mylog("PGAPI_ColAttr: TABLE_NAME = '%s'\n", p);
576
			break;
577

578 579 580
		case SQL_COLUMN_TYPE:
			value = pgtype_to_sqltype(stmt, field_type);
			break;
581

582 583 584
		case SQL_COLUMN_TYPE_NAME:
			p = pgtype_to_name(stmt, field_type);
			break;
585

586 587 588 589
		case SQL_COLUMN_UNSIGNED:
			value = pgtype_unsigned(stmt, field_type);
			if (value == -1)	/* non-numeric becomes TRUE (ODBC Doc) */
				value = TRUE;
590

591
			break;
592

593
		case SQL_COLUMN_UPDATABLE:
594

595 596 597 598 599 600 601
			/*
			 * Neither Access or Borland care about this.
			 *
			 * if (field_type == PG_TYPE_OID) pfDesc = SQL_ATTR_READONLY;
			 * else
			 */
			value = SQL_ATTR_WRITE;
602

603
			mylog("PGAPI_ColAttr: UPDATEABLE = %d\n", value);
604 605
			break;
	}
606

607 608
	result = SQL_SUCCESS;

609 610
	if (p)
	{							/* char/binary data */
611 612
		len = strlen(p);

613 614 615
		if (rgbDesc)
		{
			strncpy_null((char *) rgbDesc, p, (size_t) cbDescMax);
616

617 618
			if (len >= cbDescMax)
			{
619 620
				result = SQL_SUCCESS_WITH_INFO;
				stmt->errornumber = STMT_TRUNCATED;
621
				stmt->errormsg = "The buffer was too small for the rgbDesc.";
622 623 624
			}
		}

625
		if (pcbDesc)
626 627
			*pcbDesc = len;
	}
628
	else
629 630
	{
		/* numeric data */
631 632 633 634
		if (pfDesc)
			*pfDesc = value;
	}

635
	return result;
636 637 638
}


639
/*	Returns result data for a single column in the current row. */
640
RETCODE SQL_API
641
PGAPI_GetData(
642 643 644 645 646 647
		   HSTMT hstmt,
		   UWORD icol,
		   SWORD fCType,
		   PTR rgbValue,
		   SDWORD cbValueMax,
		   SDWORD FAR *pcbValue)
648
{
649
	static char *func = "PGAPI_GetData";
650 651 652 653 654 655 656 657
	QResultClass *res;
	StatementClass *stmt = (StatementClass *) hstmt;
	int			num_cols,
				num_rows;
	Int4		field_type;
	void	   *value = NULL;
	int			result;
	char		get_bookmark = FALSE;
658
	ConnInfo *ci;
659

660
	mylog("PGAPI_GetData: enter, stmt=%u\n", stmt);
661 662 663

	if (!stmt)
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
664
		SC_log_error(func, "", NULL);
665 666
		return SQL_INVALID_HANDLE;
	}
667
	ci = &(SC_get_conn(stmt)->connInfo);
668 669
	res = stmt->result;

670 671 672 673
	if (STMT_EXECUTING == stmt->status)
	{
		stmt->errormsg = "Can't get data while statement is still executing.";
		stmt->errornumber = STMT_SEQUENCE_ERROR;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
674
		SC_log_error(func, "", stmt);
675 676
		return SQL_ERROR;
	}
677

678 679 680 681
	if (stmt->status != STMT_FINISHED)
	{
		stmt->errornumber = STMT_STATUS_ERROR;
		stmt->errormsg = "GetData can only be called after the successful execution on a SQL statement";
Byron Nikolaidis's avatar
Byron Nikolaidis committed
682
		SC_log_error(func, "", stmt);
683 684
		return SQL_ERROR;
	}
685

686 687 688 689
	if (icol == 0)
	{
		if (stmt->options.use_bookmarks == SQL_UB_OFF)
		{
690 691 692 693 694 695
			stmt->errornumber = STMT_COLNUM_ERROR;
			stmt->errormsg = "Attempt to retrieve bookmark with bookmark usage disabled";
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
		}

696 697 698
		/* Make sure it is the bookmark data type */
		if (fCType != SQL_C_BOOKMARK)
		{
699 700 701 702 703
			stmt->errormsg = "Column 0 is not of type SQL_C_BOOKMARK";
			stmt->errornumber = STMT_PROGRAM_TYPE_OUT_OF_RANGE;
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
		}
704

705
		get_bookmark = TRUE;
706 707 708
	}
	else
	{
709
		/* use zero-based column numbers */
710 711
		icol--;

712
		/* make sure the column number is valid */
713
		num_cols = QR_NumResultCols(res);
714 715
		if (icol >= num_cols)
		{
716 717 718 719 720 721 722
			stmt->errormsg = "Invalid column number.";
			stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
		}
	}

723
	if (stmt->manual_result || !SC_is_fetchcursor(stmt))
724
	{
725
		/* make sure we're positioned on a valid row */
726
		num_rows = QR_get_num_tuples(res);
727 728 729
		if ((stmt->currTuple < 0) ||
			(stmt->currTuple >= num_rows))
		{
730 731
			stmt->errormsg = "Not positioned on a valid row for GetData.";
			stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
732
			SC_log_error(func, "", stmt);
733 734
			return SQL_ERROR;
		}
735
		mylog("     num_rows = %d\n", num_rows);
736

737 738 739
		if (!get_bookmark)
		{
			if (stmt->manual_result)
740
				value = QR_get_value_manual(res, stmt->currTuple, icol);
741
			else
742 743
				value = QR_get_value_backend_row(res, stmt->currTuple, icol);
			mylog("     value = '%s'\n", value);
744
		}
745
	}
746
	else
747 748
	{
		/* it's a SOCKET result (backend data) */
749 750
		if (stmt->currTuple == -1 || !res || !res->tupleField)
		{
751 752
			stmt->errormsg = "Not positioned on a valid row for GetData.";
			stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
753
			SC_log_error(func, "", stmt);
754 755 756
			return SQL_ERROR;
		}

757
		if (!get_bookmark)
758
			value = QR_get_value_backend(res, icol);
759

760
		mylog("  socket: value = '%s'\n", value);
761 762
	}

763 764
	if (get_bookmark)
	{
765 766 767 768 769 770 771 772
		*((UDWORD *) rgbValue) = SC_get_bookmark(stmt);

		if (pcbValue)
			*pcbValue = 4;

		return SQL_SUCCESS;
	}

773 774
	field_type = QR_get_field_type(res, icol);

775
	mylog("**** PGAPI_GetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value);
776

777
	stmt->current_col = icol;
778

779 780
	result = copy_and_convert_field(stmt, field_type, value,
								 fCType, rgbValue, cbValueMax, pcbValue);
781

782
	stmt->current_col = -1;
783

784 785 786 787
	switch (result)
	{
		case COPY_OK:
			return SQL_SUCCESS;
788

789 790 791 792 793
		case COPY_UNSUPPORTED_TYPE:
			stmt->errormsg = "Received an unsupported type from Postgres.";
			stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
794

795 796 797 798 799
		case COPY_UNSUPPORTED_CONVERSION:
			stmt->errormsg = "Couldn't handle the necessary data type conversion.";
			stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
800

801 802
		case COPY_RESULT_TRUNCATED:
			stmt->errornumber = STMT_TRUNCATED;
803
			stmt->errormsg = "The buffer was too small for the GetData.";
804
			return SQL_SUCCESS_WITH_INFO;
805

806 807 808
		case COPY_GENERAL_ERROR:		/* error msg already filled in */
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
809

810 811 812
		case COPY_NO_DATA_FOUND:
			/* SC_log_error(func, "no data found", stmt); */
			return SQL_NO_DATA_FOUND;
813

814 815 816 817 818 819
		default:
			stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
			stmt->errornumber = STMT_INTERNAL_ERROR;
			SC_log_error(func, "", stmt);
			return SQL_ERROR;
	}
820 821
}

822

823 824 825 826
/*
 *		Returns data for bound columns in the current row ("hstmt->iCursor"),
 *		advances the cursor.
 */
827
RETCODE SQL_API
828
PGAPI_Fetch(
829
		 HSTMT hstmt)
830
{
831
	static char *func = "PGAPI_Fetch";
832 833
	StatementClass *stmt = (StatementClass *) hstmt;
	QResultClass *res;
834

835
	mylog("PGAPI_Fetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
836

837 838
	if (!stmt)
	{
839 840 841 842 843 844
		SC_log_error(func, "", NULL);
		return SQL_INVALID_HANDLE;
	}

	SC_clear_error(stmt);

845 846
	if (!(res = stmt->result))
	{
847
		stmt->errormsg = "Null statement result in PGAPI_Fetch.";
848 849 850 851 852
		stmt->errornumber = STMT_SEQUENCE_ERROR;
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

853 854 855
	/* Not allowed to bind a bookmark column when using SQLFetch. */
	if (stmt->bookmark.buffer)
	{
856
		stmt->errornumber = STMT_COLNUM_ERROR;
857
		stmt->errormsg = "Not allowed to bind a bookmark column when using PGAPI_Fetch";
858 859 860 861
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

862 863
	if (stmt->status == STMT_EXECUTING)
	{
864 865 866 867 868 869
		stmt->errormsg = "Can't fetch while statement is still executing.";
		stmt->errornumber = STMT_SEQUENCE_ERROR;
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

870 871
	if (stmt->status != STMT_FINISHED)
	{
872 873 874 875 876 877
		stmt->errornumber = STMT_STATUS_ERROR;
		stmt->errormsg = "Fetch can only be called after the successful execution on a SQL statement";
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

878 879
	if (stmt->bindings == NULL)
	{
880 881
		/* just to avoid a crash if the user insists on calling this */
		/* function even if SQL_ExecDirect has reported an Error */
882 883 884 885 886 887 888
		stmt->errormsg = "Bindings were not allocated properly.";
		stmt->errornumber = STMT_SEQUENCE_ERROR;
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

	QR_set_rowset_size(res, 1);
889
	QR_inc_base(res, stmt->last_fetch_count);
890 891

	return SC_fetch(stmt);
892 893 894
}


895
/*	This fetchs a block of data (rowset). */
896
RETCODE SQL_API
897
PGAPI_ExtendedFetch(
898 899 900 901 902
				 HSTMT hstmt,
				 UWORD fFetchType,
				 SDWORD irow,
				 UDWORD FAR *pcrow,
				 UWORD FAR *rgfRowStatus)
903
{
904
	static char *func = "PGAPI_ExtendedFetch";
905 906 907 908 909 910 911 912
	StatementClass *stmt = (StatementClass *) hstmt;
	QResultClass *res;
	int			num_tuples,
				i,
				save_rowset_size;
	RETCODE		result;
	char		truncated,
				error;
913
	ConnInfo *ci;
914

915
	mylog("PGAPI_ExtendedFetch: stmt=%u\n", stmt);
916 917 918

	if (!stmt)
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
919
		SC_log_error(func, "", NULL);
920
		return SQL_INVALID_HANDLE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
921
	}
922
	ci = &(SC_get_conn(stmt)->connInfo);
923

924
	if (SC_is_fetchcursor(stmt) && !stmt->manual_result)
925 926 927
	{
		if (fFetchType != SQL_FETCH_NEXT)
		{
928
			stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
929
			stmt->errormsg = "Unsupported fetch type for PGAPI_ExtendedFetch with UseDeclareFetch option.";
930 931 932 933 934 935
			return SQL_ERROR;
		}
	}

	SC_clear_error(stmt);

936 937
	if (!(res = stmt->result))
	{
938
		stmt->errormsg = "Null statement result in PGAPI_ExtendedFetch.";
939 940 941 942 943
		stmt->errornumber = STMT_SEQUENCE_ERROR;
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

944 945 946 947 948 949
	/*
	 * If a bookmark colunmn is bound but bookmark usage is off, then
	 * error
	 */
	if (stmt->bookmark.buffer && stmt->options.use_bookmarks == SQL_UB_OFF)
	{
950 951 952 953 954 955
		stmt->errornumber = STMT_COLNUM_ERROR;
		stmt->errormsg = "Attempt to retrieve bookmark with bookmark usage disabled";
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

956 957
	if (stmt->status == STMT_EXECUTING)
	{
958 959 960 961 962 963
		stmt->errormsg = "Can't fetch while statement is still executing.";
		stmt->errornumber = STMT_SEQUENCE_ERROR;
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

964 965
	if (stmt->status != STMT_FINISHED)
	{
966 967 968 969 970 971
		stmt->errornumber = STMT_STATUS_ERROR;
		stmt->errormsg = "ExtendedFetch can only be called after the successful execution on a SQL statement";
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

972 973
	if (stmt->bindings == NULL)
	{
974 975
		/* just to avoid a crash if the user insists on calling this */
		/* function even if SQL_ExecDirect has reported an Error */
976 977 978
		stmt->errormsg = "Bindings were not allocated properly.";
		stmt->errornumber = STMT_SEQUENCE_ERROR;
		SC_log_error(func, "", stmt);
979
		return SQL_ERROR;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
980
	}
981

982
	/* Initialize to no rows fetched */
983
	if (rgfRowStatus)
984 985 986
		for (i = 0; i < stmt->options.rowset_size; i++)
			*(rgfRowStatus + i) = SQL_ROW_NOROW;

987 988 989
	if (pcrow)
		*pcrow = 0;

990 991
	num_tuples = QR_get_num_tuples(res);

992
	/* Save and discard the saved rowset size */
993 994
	save_rowset_size = stmt->save_rowset_size;
	stmt->save_rowset_size = -1;
995

996 997 998
	switch (fFetchType)
	{
		case SQL_FETCH_NEXT:
999

1000 1001 1002 1003 1004
			/*
			 * From the odbc spec... If positioned before the start of the
			 * RESULT SET, then this should be equivalent to
			 * SQL_FETCH_FIRST.
			 */
1005

1006 1007
			if (stmt->rowset_start < 0)
				stmt->rowset_start = 0;
1008

1009 1010
			else
				stmt->rowset_start += (save_rowset_size > 0 ? save_rowset_size : stmt->options.rowset_size);
1011

1012 1013
			mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
			break;
1014

1015 1016
		case SQL_FETCH_PRIOR:
			mylog("SQL_FETCH_PRIOR: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
1017

1018 1019 1020 1021 1022 1023 1024
			/*
			 * From the odbc spec... If positioned after the end of the
			 * RESULT SET, then this should be equivalent to
			 * SQL_FETCH_LAST.
			 */
			if (stmt->rowset_start >= num_tuples)
			{
1025 1026 1027 1028 1029
				if (stmt->options.rowset_size > num_tuples)
				{
					stmt->errornumber = STMT_POS_BEFORE_RECORDSET;
					stmt->errormsg = "fetch prior from eof and before the beggining";
				}
1030
				stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size);
1031

1032 1033
			}
			else
1034 1035 1036 1037 1038 1039
			{
				if (stmt->rowset_start < stmt->options.rowset_size)
				{
					stmt->errormsg = "fetch prior and before the beggining";
					stmt->errornumber = STMT_POS_BEFORE_RECORDSET;
				}
1040
				stmt->rowset_start -= stmt->options.rowset_size;
1041
			}
1042
			break;
1043

1044 1045
		case SQL_FETCH_FIRST:
			mylog("SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
1046

1047 1048
			stmt->rowset_start = 0;
			break;
1049

1050 1051
		case SQL_FETCH_LAST:
			mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
1052

1053 1054
			stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size);
			break;
1055

1056 1057
		case SQL_FETCH_ABSOLUTE:
			mylog("SQL_FETCH_ABSOLUTE: num_tuples=%d, currtuple=%d, irow=%d\n", num_tuples, stmt->currTuple, irow);
1058

1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
			/* Position before result set, but dont fetch anything */
			if (irow == 0)
			{
				stmt->rowset_start = -1;
				stmt->currTuple = -1;
				return SQL_NO_DATA_FOUND;
			}
			/* Position before the desired row */
			else if (irow > 0)
				stmt->rowset_start = irow - 1;
			/* Position with respect to the end of the result set */
			else
				stmt->rowset_start = num_tuples + irow;
1072
			break;
1073

1074
		case SQL_FETCH_RELATIVE:
1075

1076 1077 1078 1079 1080 1081
			/*
			 * Refresh the current rowset -- not currently implemented,
			 * but lie anyway
			 */
			if (irow == 0)
				break;
1082

1083 1084
			stmt->rowset_start += irow;
			break;
1085

1086 1087 1088 1089 1090
		case SQL_FETCH_BOOKMARK:
			stmt->rowset_start = irow - 1;
			break;

		default:
1091
			SC_log_error(func, "Unsupported PGAPI_ExtendedFetch Direction", stmt);
1092 1093
			return SQL_ERROR;
	}
1094

1095 1096 1097
	/*
	 * CHECK FOR PROPER CURSOR STATE
	 */
1098 1099 1100 1101 1102

	/*
	 * Handle Declare Fetch style specially because the end is not really
	 * the end...
	 */
1103
	if (SC_is_fetchcursor(stmt) && !stmt->manual_result)
1104 1105
	{
		if (QR_end_tuples(res))
1106 1107
			return SQL_NO_DATA_FOUND;
	}
1108 1109 1110 1111 1112
	else
	{
		/* If *new* rowset is after the result_set, return no data found */
		if (stmt->rowset_start >= num_tuples)
		{
1113 1114 1115 1116 1117
			stmt->rowset_start = num_tuples;
			return SQL_NO_DATA_FOUND;
		}
	}

1118 1119 1120 1121 1122
	/* If *new* rowset is prior to result_set, return no data found */
	if (stmt->rowset_start < 0)
	{
		if (stmt->rowset_start + stmt->options.rowset_size <= 0)
		{
1123 1124 1125
			stmt->rowset_start = -1;
			return SQL_NO_DATA_FOUND;
		}
1126 1127 1128
		else
		{						/* overlap with beginning of result set,
								 * so get first rowset */
1129 1130 1131 1132
			stmt->rowset_start = 0;
		}
	}

1133
	/* currTuple is always 1 row prior to the rowset */
1134 1135
	stmt->currTuple = stmt->rowset_start - 1;

1136
	/* increment the base row in the tuple cache */
1137
	QR_set_rowset_size(res, stmt->options.rowset_size);
1138 1139 1140
	/* QR_inc_base(res, stmt->last_fetch_count); */
	/* Is inc_base right ? */
	res->base = stmt->rowset_start;
1141 1142

	/* Physical Row advancement occurs for each row fetched below */
1143

1144
	mylog("PGAPI_ExtendedFetch: new currTuple = %d\n", stmt->currTuple);
1145

1146
	truncated = error = FALSE;
1147 1148
	for (i = 0; i < stmt->options.rowset_size; i++)
	{
1149
		stmt->bind_row = i;		/* set the binding location */
1150 1151
		result = SC_fetch(stmt);

1152
		/* Determine Function status */
1153 1154 1155 1156 1157 1158 1159
		if (result == SQL_NO_DATA_FOUND)
			break;
		else if (result == SQL_SUCCESS_WITH_INFO)
			truncated = TRUE;
		else if (result == SQL_ERROR)
			error = TRUE;

1160 1161 1162 1163
		/* Determine Row Status */
		if (rgfRowStatus)
		{
			if (result == SQL_ERROR)
1164
				*(rgfRowStatus + i) = SQL_ROW_ERROR;
1165 1166 1167 1168 1169
#ifdef	DRIVER_CURSOR_IMPLEMENT
			/* this should be refined */
			else if (result > 10 && result < 20)
				*(rgfRowStatus + i) = result - 10;
#endif /* DRIVER_CURSOR_IMPLEMENT */
1170
			else
1171
				*(rgfRowStatus + i) = SQL_ROW_SUCCESS;
1172
		}
1173 1174
	}

1175 1176
	/* Save the fetch count for SQLSetPos */
	stmt->last_fetch_count = i;
1177

1178
	/* Reset next binding row */
1179 1180
	stmt->bind_row = 0;

1181
	/* Move the cursor position to the first row in the result set. */
1182 1183
	stmt->currTuple = stmt->rowset_start;

1184
	/* For declare/fetch, need to reset cursor to beginning of rowset */
1185
	if (SC_is_fetchcursor(stmt) && !stmt->manual_result)
1186 1187
		QR_set_position(res, 0);

1188
	/* Set the number of rows retrieved */
1189 1190 1191 1192
	if (pcrow)
		*pcrow = i;

	if (i == 0)
1193 1194
		/* Only DeclareFetch should wind up here */
		return SQL_NO_DATA_FOUND;
1195 1196 1197 1198
	else if (error)
		return SQL_ERROR;
	else if (truncated)
		return SQL_SUCCESS_WITH_INFO;
1199 1200
	else if (stmt->errornumber == STMT_POS_BEFORE_RECORDSET)
		return SQL_SUCCESS_WITH_INFO;
1201 1202
	else
		return SQL_SUCCESS;
1203 1204
}

1205

1206 1207 1208 1209
/*
 *		This determines whether there are more results sets available for
 *		the "hstmt".
 */
1210
/* CC: return SQL_NO_DATA_FOUND since we do not support multiple result sets */
1211
RETCODE SQL_API
1212
PGAPI_MoreResults(
1213
			   HSTMT hstmt)
1214
{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
1215
	return SQL_NO_DATA_FOUND;
1216 1217
}

1218

1219
#ifdef	DRIVER_CURSOR_IMPLEMENT
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
/*
 *	Stuff for updatable cursors. 
 */
static QResultClass *
positioned_load(StatementClass *stmt, BOOL latest, int res_cols, UInt4 oid, const char *tidval)
{
	int	i;
	QResultClass	*qres;
	char	selstr[4096];

	sprintf(selstr, "select");
	for (i = 0; i < res_cols; i++)
		sprintf(selstr, "%s \"%s\",", selstr, stmt->fi[i]->name);
	sprintf(selstr, "%s CTID, OID from \"%s\" where", selstr, stmt->ti[0]->name);
	if (tidval)
	{
		if (latest)
			sprintf(selstr, "%s ctid = currtid2('%s', '%s') and",
				selstr, stmt->ti[0]->name, tidval);
		else
			sprintf(selstr, "%s ctid = '%s' and", selstr, tidval);
	}
	sprintf(selstr, "%s oid = %u", selstr, oid), 
	mylog("selstr=%s\n", selstr);
	qres = CC_send_query(SC_get_conn(stmt), selstr, NULL);
	if (qres && QR_aborted(qres))
	{
		QR_Destructor(qres);
		qres = (QResultClass *) 0;
	}
	return qres;
}

1253 1254 1255 1256 1257
RETCODE SQL_API
SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count)
{
	int	i, res_cols;
	UWORD	rcnt, global_ridx;
1258
	UInt4	oid;
1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280
	QResultClass	*res, *qres;
	RETCODE	ret = SQL_ERROR;
	char	*tidval, *oidval;

	mylog("positioned load fi=%x ti=%x\n", stmt->fi, stmt->ti);
	rcnt = 0;
	if (count)
		*count = 0;
	if (!(res = stmt->result))
		return SQL_ERROR;
	if (!stmt->ti)
		parse_statement(stmt); /* not preferable */
	if (!stmt->ti || stmt->ntab != 1)
	{
		stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
		return SQL_ERROR;
	}
	global_ridx = irow + stmt->rowset_start;
	res_cols = QR_NumResultCols(res);
	if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
	{
		return SQL_SUCCESS_WITH_INFO;
1281 1282
	}
	sscanf(oidval, "%u", &oid); 
1283 1284
	tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2);
	res_cols -= 2;
1285
	if (qres = positioned_load(stmt, TRUE, res_cols, oid, tidval), qres)
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
	{
		TupleField	*tupleo, *tuplen;

		rcnt = QR_get_num_tuples(qres);
		tupleo = res->backend_tuples + res->num_fields * global_ridx;
		if (rcnt == 1)
		{
			QR_set_position(qres, 0);
			tuplen = res->tupleField;
			for (i = 0; i < res->num_fields; i++)
			{
				if (tupleo[i].value)
					free(tupleo[i].value);
				tupleo[i].len = tuplen[i].len;
				tuplen[i].len = 0;
				tupleo[i].value = tuplen[i].value;
				tuplen[i].value = NULL;
			}
			ret = SQL_SUCCESS;
		}
		else
		{
			stmt->errornumber = STMT_ROW_VERSION_CHANGED;
			stmt->errormsg = "the content was deleted after last fetch";
			ret = SQL_SUCCESS_WITH_INFO;
			if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
			{
				if (tupleo[res_cols + 1].value)
					free(tupleo[res_cols + 1].value);
				tupleo[res_cols + 1].value = NULL;
				tupleo[res_cols + 1].len = 0;
			}
		}
1319
		QR_Destructor(qres);
1320 1321 1322 1323 1324 1325 1326 1327 1328
	}
	else if (stmt->errornumber == 0)
		stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
	if (count)
		*count = rcnt;
	return ret;
}

RETCODE SQL_API
1329
SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval)
1330
{
1331
	int	i;
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344
	QResultClass	*res, *qres;
	RETCODE	ret = SQL_ERROR;

	mylog("positioned new fi=%x ti=%x\n", stmt->fi, stmt->ti);
	if (!(res = stmt->result))
		return SQL_ERROR;
	if (!stmt->ti)
		parse_statement(stmt); /* not preferable */
	if (!stmt->ti || stmt->ntab != 1)
	{
		stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
		return SQL_ERROR;
	}
1345 1346
	if (qres = positioned_load(stmt, TRUE, QR_NumResultCols(res) - 2, oid, tidval), qres)
	{ 
1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
		TupleField	*tupleo, *tuplen;
		int	count = QR_get_num_tuples(qres);
		QR_set_position(qres, 0);
		if (count == 1)
		{
			tuplen = qres->tupleField;
			if (res->fcount >= res->count_allocated)
			{
				int	tuple_size;
				if (!res->count_allocated)
					tuple_size = TUPLE_MALLOC_INC;
				else
					tuple_size = res->count_allocated * 2;
				res->backend_tuples = (TupleField *) realloc(
				res->backend_tuples,
				res->num_fields * sizeof(TupleField) * tuple_size);
				if (!res->backend_tuples)
				{
					stmt->errornumber = res->status = PGRES_FATAL_ERROR;
					stmt->errormsg = "Out of memory while reading tuples.";
					QR_Destructor(qres);
					return SQL_ERROR;
				}
				res->count_allocated = tuple_size;
			}
			tupleo = res->backend_tuples + res->num_fields * res->fcount; 
			for (i = 0; i < res->num_fields; i++)
			{
				tupleo[i].len = tuplen[i].len;
				tuplen[i].len = 0;
				tupleo[i].value = tuplen[i].value;
				tuplen[i].value = NULL;
			}
			res->fcount++;
			ret = SQL_SUCCESS;
		}
		else
		{
			stmt->errornumber = STMT_ROW_VERSION_CHANGED;
			stmt->errormsg = "the content was changed before updation";
			ret = SQL_SUCCESS_WITH_INFO;
		}
1389
		QR_Destructor(qres);
1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673
		/*stmt->currTuple = stmt->rowset_start + irow;*/
	}
	return ret;
}

RETCODE SQL_API
SC_pos_update(StatementClass *stmt,
		UWORD irow)
{
	int	i, res_cols, num_cols, upd_cols;
	UWORD	global_ridx;
	QResultClass	*res;
	BindInfoClass *bindings = stmt->bindings;
	char	updstr[4096];
	RETCODE	ret;
	char	*tidval, *oidval;

	mylog("POS UPDATE %d+%d fi=%x ti=%x\n", irow, stmt->result->base, stmt->fi, stmt->ti);
	if (!(res = stmt->result))
		return SQL_ERROR;
	if (!stmt->ti)
		parse_statement(stmt); /* not preferable */
	if (!stmt->ti || stmt->ntab != 1)
	{
		stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
		return SQL_ERROR;
	}
	global_ridx = irow + stmt->rowset_start;
	res_cols = QR_NumResultCols(res);
	if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
	{
		stmt->errormsg = "The row is already deleted";
		return SQL_ERROR;
	} 
	tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2);

	sprintf(updstr, "update \"%s\" set", stmt->ti[0]->name);
	num_cols = stmt->nfld;
	for (i = upd_cols = 0; i < num_cols; i++)
	{
		if (bindings[i].used)
		{
			mylog("%d used=%d\n", i, *bindings[i].used);
			if (*bindings[i].used != SQL_IGNORE)
			{
				if (upd_cols)
					sprintf(updstr, "%s, \"%s\" = ?", updstr, stmt->fi[i]->name);
				else
					sprintf(updstr, "%s \"%s\" = ?", updstr, stmt->fi[i]->name);
				upd_cols++;
			}
		}
		else
			mylog("%d null bind\n", i);
	}
	if (upd_cols > 0)
	{
		HSTMT	hstmt;
		int	j;
		int	res_cols = QR_NumResultCols(res);
		StatementClass	*qstmt;

		sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr,
			tidval, oidval); 
		mylog("updstr=%s\n", updstr);
		if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS)
			return SQL_ERROR;
		qstmt = (StatementClass *) hstmt;
		for (i = j = 0; i < num_cols; i++)
		{
			if (bindings[i].used)
			{
				mylog("%d used=%d\n", i, *bindings[i].used);
				if (*bindings[i].used != SQL_IGNORE)
				{
					PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++j,
					SQL_PARAM_INPUT, bindings[i].returntype,
					pgtype_to_sqltype(stmt, QR_get_field_type(res, i)),
					QR_get_fieldsize(res, i),
					(SQLSMALLINT) stmt->fi[i]->precision,
					bindings[i].buffer,
					bindings[i].buflen,
					bindings[i].used);
				}
			}
		}
		ret = PGAPI_ExecDirect(hstmt, updstr, strlen(updstr));
		if (ret == SQL_ERROR)
		{
			stmt->errornumber = qstmt->errornumber;
			stmt->errormsg = qstmt->errormsg;
		}
		else if (ret == SQL_NEED_DATA) /* must be fixed */
		{
			stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
			stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
			stmt->errormsg = "SetPos with data_at_exec not yet supported";
			ret = SQL_ERROR;
		}
		if (ret != SQL_ERROR)
		{
			int	updcnt;
			const char *cmdstr = QR_get_command(qstmt->result);
			if (cmdstr &&
			    sscanf(cmdstr, "UPDATE %d", &updcnt) == 1)
			{ 
				if (updcnt == 1)
					SC_pos_reload(stmt, irow, (UWORD *) 0);
				else if (updcnt == 0)
				{
					stmt->errornumber = STMT_ROW_VERSION_CHANGED;
					stmt->errormsg = "the content was changed before updation";
					ret = SQL_ERROR;
					if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
						SC_pos_reload(stmt, irow, (UWORD *) 0);
				}
				else
					ret = SQL_ERROR;
				stmt->currTuple = stmt->rowset_start + irow;
			}	
			else
				ret = SQL_ERROR;
			if (ret == SQL_ERROR && stmt->errornumber == 0)
			{
				stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
				stmt->errormsg = "SetPos update return error";
			}
		}
		PGAPI_FreeStmt(hstmt, SQL_DROP);
	}
	else
		ret = SQL_SUCCESS_WITH_INFO;
	return ret;
}
RETCODE SQL_API
SC_pos_delete(StatementClass *stmt,
		UWORD irow)
{
	int	res_cols;
	UWORD	global_ridx;
	QResultClass	*res, *qres;
	BindInfoClass *bindings = stmt->bindings;
	char	dltstr[4096];
	RETCODE	ret;
	char	*oidval;

	mylog("POS DELETE fi=%x ti=%x\n", stmt->fi, stmt->ti);
	if (!(res = stmt->result))
		return SQL_ERROR;
	if (!stmt->ti)
		parse_statement(stmt); /* not preferable */
	if (!stmt->ti || stmt->ntab != 1)
	{
		stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
		return SQL_ERROR;
	}
	res_cols = QR_NumResultCols(res);
	global_ridx = irow + stmt->rowset_start;
	if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
	{
		stmt->errormsg = "The row is already deleted";
		return SQL_ERROR;
	} 
	sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s",
			stmt->ti[0]->name,
			QR_get_value_backend_row(stmt->result, global_ridx, res_cols - 2), 
			oidval); 

	mylog("dltstr=%s\n", dltstr);
	qres = CC_send_query(SC_get_conn(stmt), dltstr, NULL);
	if (qres && QR_command_successful(qres))
	{
		int	dltcnt;
		const char *cmdstr = QR_get_command(qres);
		if (cmdstr &&
		    sscanf(cmdstr, "DELETE %d", &dltcnt) == 1)
		{
			if (dltcnt == 1)
				SC_pos_reload(stmt, irow, (UWORD *) 0);
			else if (dltcnt == 0)
			{
				stmt->errornumber = STMT_ROW_VERSION_CHANGED;
				stmt->errormsg = "the content was changed before deletion";
				ret = SQL_ERROR;
				if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
					SC_pos_reload(stmt, irow, (UWORD *) 0);
			}
			else
				ret = SQL_ERROR;
			stmt->currTuple = stmt->rowset_start + irow;
		}
		else
			ret = SQL_ERROR;
	}
	else
		ret = SQL_ERROR;
	if (ret == SQL_ERROR && stmt->errornumber == 0)
	{
		stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
		stmt->errormsg = "SetPos delete return error";
	}
	if (qres)
		QR_Destructor(qres);
	return ret;
}
RETCODE SQL_API
SC_pos_add(StatementClass *stmt,
		UWORD irow)
{
	int	num_cols, add_cols, i;
	HSTMT	hstmt;
	QResultClass	*res;
	BindInfoClass *bindings = stmt->bindings;
	char	addstr[4096];
	RETCODE	ret;

	mylog("POS ADD fi=%x ti=%x\n", stmt->fi, stmt->ti);
	if (!(res = stmt->result))
		return SQL_ERROR;
	if (!stmt->ti)
		parse_statement(stmt); /* not preferable */
	if (!stmt->ti || stmt->ntab != 1)
	{
		stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
		return SQL_ERROR;
	}
	num_cols = stmt->nfld;
	sprintf(addstr, "insert into \"%s\" (", stmt->ti[0]->name);
	if (PGAPI_AllocStmt(SC_get_conn(stmt), &hstmt) != SQL_SUCCESS)
		return SQL_ERROR;
	for (i = add_cols = 0; i < num_cols; i++)
	{
		if (bindings[i].used)
		{
			mylog("%d used=%d\n", i, *bindings[i].used);
			if (*bindings[i].used != SQL_IGNORE)
			{
				if (add_cols)
					sprintf(addstr, "%s, \"%s\"", addstr, stmt->fi[i]->name);
				else
					sprintf(addstr, "%s\"%s\"", addstr, stmt->fi[i]->name);
				PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++add_cols,
					SQL_PARAM_INPUT, bindings[i].returntype,
					pgtype_to_sqltype(stmt, QR_get_field_type(res, i)),
					QR_get_fieldsize(res, i),
					(SQLSMALLINT) stmt->fi[i]->precision,
					bindings[i].buffer,
					bindings[i].buflen,
					bindings[i].used);
			}
		}
		else
			mylog("%d null bind\n", i);
	}
	if (add_cols > 0)
	{
		StatementClass	*qstmt = (StatementClass *) hstmt;

		sprintf(addstr, "%s) values (", addstr);
		for (i = 0; i < add_cols; i++)
		{
			if (i)
				strcat(addstr, ", ?");
			else
				strcat(addstr, "?");
		}
		strcat(addstr, ")");
		mylog("addstr=%s\n", addstr);
		ret = PGAPI_ExecDirect(hstmt, addstr, strlen(addstr));
		if (ret == SQL_NEED_DATA) /* must be fixed */
		{
			stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
			stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
			stmt->errormsg = "SetPos with data_at_exec not yet supported";
			ret = SQL_ERROR;
		}
		if (ret == SQL_ERROR)
		{
			stmt->errornumber = qstmt->errornumber; 
			stmt->errormsg = qstmt->errormsg; 
		}
		else
		{
			int	addcnt;
1674
			UInt4	oid;
1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703
			const char *cmdstr = QR_get_command(qstmt->result);
			if (cmdstr &&
			    sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 &&
			    addcnt == 1)
			{
				SC_pos_newload(stmt, oid, NULL);
        			if (stmt->bookmark.buffer)
        			{
                			char            buf[32];

                			sprintf(buf, "%ld", res->fcount);
                			copy_and_convert_field(stmt, 0, buf,
                         			SQL_C_ULONG, stmt->bookmark.buffer,
						 0, stmt->bookmark.used);
        			} 
			}
			else
			{
				stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
				stmt->errormsg = "SetPos insert return error";
				ret = SQL_ERROR;
			}
		}
	}
	else
		ret = SQL_SUCCESS_WITH_INFO;
	PGAPI_FreeStmt(hstmt, SQL_DROP);
	return ret;
}
1704 1705 1706
/*
 *	Stuff for updatable cursors end. 
 */
1707 1708
#endif /* DRIVER_CURSOR_IMPLEMENT */

1709 1710 1711 1712
/*
 *	This positions the cursor within a rowset, that was positioned using SQLExtendedFetch.
 *	This will be useful (so far) only when using SQLGetData after SQLExtendedFetch.
 */
1713
RETCODE SQL_API
1714
PGAPI_SetPos(
1715 1716 1717 1718
		  HSTMT hstmt,
		  UWORD irow,
		  UWORD fOption,
		  UWORD fLock)
1719
{
1720
	static char *func = "PGAPI_SetPos";
1721 1722 1723 1724 1725 1726 1727 1728
	StatementClass *stmt = (StatementClass *) hstmt;
	QResultClass *res;
	int			num_cols,
				i;
	BindInfoClass *bindings = stmt->bindings;

	if (!stmt)
	{
1729 1730 1731 1732
		SC_log_error(func, "", NULL);
		return SQL_INVALID_HANDLE;
	}

1733 1734 1735 1736 1737 1738
#ifdef	DRIVER_CURSOR_IMPLEMENT
	mylog("SetPos fOption=%d irow=%d lock=%d currt=%d\n", fOption, irow, fLock, stmt->currTuple);
	if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
		;
	else
#endif /* DRIVER_CURSOR_IMPLEMENT */
1739 1740
	if (fOption != SQL_POSITION && fOption != SQL_REFRESH)
	{
1741
		stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
1742
		stmt->errormsg = "Only SQL_POSITION/REFRESH is supported for PGAPI_SetPos";
1743 1744 1745 1746
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

1747 1748
	if (!(res = stmt->result))
	{
1749
		stmt->errormsg = "Null statement result in PGAPI_SetPos.";
1750 1751 1752 1753 1754 1755
		stmt->errornumber = STMT_SEQUENCE_ERROR;
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}
	num_cols = QR_NumResultCols(res);

1756 1757
	if (irow == 0)
	{
1758 1759 1760 1761 1762 1763
		stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
		stmt->errormsg = "Driver does not support Bulk operations.";
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

1764 1765
	if (irow > stmt->last_fetch_count)
	{
1766 1767 1768 1769 1770 1771 1772 1773
		stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
		stmt->errormsg = "Row value out of range";
		SC_log_error(func, "", stmt);
		return SQL_ERROR;
	}

	irow--;

1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784
#ifdef	DRIVER_CURSOR_IMPLEMENT
	switch (fOption)
	{
		case SQL_UPDATE:
			return SC_pos_update(stmt, irow);
		case SQL_DELETE:
			return SC_pos_delete(stmt, irow);
		case SQL_ADD:
			return SC_pos_add(stmt, irow);
	}
#endif /* DRIVER_CURSOR_IMPLEMENT */
1785
	/* Reset for SQLGetData */
1786 1787 1788
	for (i = 0; i < num_cols; i++)
		bindings[i].data_left = -1;

1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807
	if (fOption == SQL_REFRESH)
	{
		/* save the last_fetch_count */
		int	last_fetch = stmt->last_fetch_count;
		int	bind_save = stmt->bind_row;

#ifdef	DRIVER_CURSOR_IMPLEMENT
		if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
			SC_pos_reload(stmt, irow, (UWORD *) 0); 
#endif /* DRIVER_CURSOR_IMPLEMENT */
		stmt->currTuple = stmt->rowset_start + irow - 1;
		stmt->bind_row = irow;
		SC_fetch(stmt);
		/* restore the last_fetch_count */
		stmt->last_fetch_count = last_fetch;
		stmt->bind_row = bind_save;
	}
	else
		stmt->currTuple = stmt->rowset_start + irow;
1808 1809 1810
	QR_set_position(res, irow);

	return SQL_SUCCESS;
1811 1812 1813
}


1814
/*		Sets options that control the behavior of cursors. */
1815
RETCODE SQL_API
1816
PGAPI_SetScrollOptions(
1817 1818 1819 1820
					HSTMT hstmt,
					UWORD fConcurrency,
					SDWORD crowKeyset,
					UWORD crowRowset)
1821
{
1822 1823 1824 1825 1826 1827
	static char *func = "PGAPI_SetScrollOptions";
	StatementClass *stmt = (StatementClass *) hstmt;
	mylog("PGAPI_SetScrollOptions fConcurrency=%d crowKeyset=%d crowRowset=%d\n",
		fConcurrency, crowKeyset, crowRowset);
	stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
	stmt->errormsg = "SetScroll option not implemeted";
Byron Nikolaidis's avatar
Byron Nikolaidis committed
1828

1829
	SC_log_error(func, "Function not implemented", hstmt);
Byron Nikolaidis's avatar
Byron Nikolaidis committed
1830
	return SQL_ERROR;
1831 1832 1833
}


1834
/*	Set the cursor name on a statement handle */
1835
RETCODE SQL_API
1836
PGAPI_SetCursorName(
1837 1838 1839
				 HSTMT hstmt,
				 UCHAR FAR *szCursor,
				 SWORD cbCursor)
1840
{
1841
	static char *func = "PGAPI_SetCursorName";
1842 1843
	StatementClass *stmt = (StatementClass *) hstmt;
	int			len;
1844

1845
	mylog("PGAPI_SetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d\n", hstmt, szCursor, cbCursor);
1846

1847 1848
	if (!stmt)
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
1849
		SC_log_error(func, "", NULL);
1850
		return SQL_INVALID_HANDLE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
1851
	}
1852 1853

	len = (cbCursor == SQL_NTS) ? strlen(szCursor) : cbCursor;
1854

1855 1856
	if (len <= 0 || len > sizeof(stmt->cursor_name) - 1)
	{
1857 1858
		stmt->errornumber = STMT_INVALID_CURSOR_NAME;
		stmt->errormsg = "Invalid Cursor Name";
Byron Nikolaidis's avatar
Byron Nikolaidis committed
1859
		SC_log_error(func, "", stmt);
1860 1861
		return SQL_ERROR;
	}
1862

1863
	strncpy_null(stmt->cursor_name, szCursor, len + 1);
1864
	return SQL_SUCCESS;
1865 1866 1867
}


1868
/*	Return the cursor name for a statement handle */
1869
RETCODE SQL_API
1870
PGAPI_GetCursorName(
1871 1872 1873 1874
				 HSTMT hstmt,
				 UCHAR FAR *szCursor,
				 SWORD cbCursorMax,
				 SWORD FAR *pcbCursor)
1875
{
1876
	static char *func = "PGAPI_GetCursorName";
1877 1878 1879
	StatementClass *stmt = (StatementClass *) hstmt;
	int			len = 0;
	RETCODE		result;
1880

1881
	mylog("PGAPI_GetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d, pcbCursor=%u\n", hstmt, szCursor, cbCursorMax, pcbCursor);
1882

1883 1884
	if (!stmt)
	{
Byron Nikolaidis's avatar
Byron Nikolaidis committed
1885
		SC_log_error(func, "", NULL);
1886
		return SQL_INVALID_HANDLE;
Byron Nikolaidis's avatar
Byron Nikolaidis committed
1887
	}
1888

1889 1890
	if (stmt->cursor_name[0] == '\0')
	{
1891 1892
		stmt->errornumber = STMT_NO_CURSOR_NAME;
		stmt->errormsg = "No Cursor name available";
Byron Nikolaidis's avatar
Byron Nikolaidis committed
1893
		SC_log_error(func, "", stmt);
1894 1895 1896
		return SQL_ERROR;
	}

1897 1898 1899
	result = SQL_SUCCESS;
	len = strlen(stmt->cursor_name);

1900 1901
	if (szCursor)
	{
1902 1903
		strncpy_null(szCursor, stmt->cursor_name, cbCursorMax);

1904 1905
		if (len >= cbCursorMax)
		{
1906 1907
			result = SQL_SUCCESS_WITH_INFO;
			stmt->errornumber = STMT_TRUNCATED;
1908
			stmt->errormsg = "The buffer was too small for the GetCursorName.";
1909 1910
		}
	}
1911 1912

	if (pcbCursor)
1913
		*pcbCursor = len;
1914

1915
	return result;
1916
}