~ubuntu-branches/ubuntu/lucid/psqlodbc/lucid

« back to all changes in this revision

Viewing changes to results.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2004-05-13 10:47:36 UTC
  • Revision ID: james.westby@ubuntu.com-20040513104736-a530gmn0p3knep89
Tags: upstream-07.03.0200
ImportĀ upstreamĀ versionĀ 07.03.0200

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------
 
2
 * Module:                      results.c
 
3
 *
 
4
 * Description:         This module contains functions related to
 
5
 *                                      retrieving result information through the ODBC API.
 
6
 *
 
7
 * Classes:                     n/a
 
8
 *
 
9
 * API functions:       SQLRowCount, SQLNumResultCols, SQLDescribeCol,
 
10
 *                                      SQLColAttributes, SQLGetData, SQLFetch, SQLExtendedFetch,
 
11
 *                                      SQLMoreResults, SQLSetPos, SQLSetScrollOptions(NI),
 
12
 *                                      SQLSetCursorName, SQLGetCursorName
 
13
 *
 
14
 * Comments:            See "notice.txt" for copyright and license information.
 
15
 *-------
 
16
 */
 
17
 
 
18
#include "psqlodbc.h"
 
19
 
 
20
#include <string.h>
 
21
#include "dlg_specific.h"
 
22
#include "environ.h"
 
23
#include "connection.h"
 
24
#include "statement.h"
 
25
#include "bind.h"
 
26
#include "qresult.h"
 
27
#include "convert.h"
 
28
#include "pgtypes.h"
 
29
 
 
30
#include <stdio.h>
 
31
 
 
32
#include "pgapifunc.h"
 
33
 
 
34
 
 
35
 
 
36
RETCODE         SQL_API
 
37
PGAPI_RowCount(
 
38
                           HSTMT hstmt,
 
39
                           SDWORD FAR * pcrow)
 
40
{
 
41
        CSTR func = "PGAPI_RowCount";
 
42
        StatementClass *stmt = (StatementClass *) hstmt;
 
43
        QResultClass *res;
 
44
        ConnInfo   *ci;
 
45
 
 
46
        mylog("%s: entering...\n", func);
 
47
        if (!stmt)
 
48
        {
 
49
                SC_log_error(func, "", NULL);
 
50
                return SQL_INVALID_HANDLE;
 
51
        }
 
52
        ci = &(SC_get_conn(stmt)->connInfo);
 
53
        if (stmt->manual_result)
 
54
        {
 
55
                if (pcrow)
 
56
                        *pcrow = -1;
 
57
                return SQL_SUCCESS;
 
58
        }
 
59
 
 
60
        res = SC_get_Curres(stmt);
 
61
        if (res && pcrow)
 
62
        {
 
63
                if (stmt->status != STMT_FINISHED)
 
64
                {
 
65
                        SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't get row count while statement is still executing.");
 
66
                        SC_log_error(func, "", stmt);
 
67
                        return  SQL_ERROR;
 
68
                }
 
69
                if (res->recent_processed_row_count >= 0)
 
70
                {
 
71
                        *pcrow = res->recent_processed_row_count;
 
72
                        mylog("**** PGAPI_RowCount(): THE ROWS: *pcrow = %d\n", *pcrow);
 
73
 
 
74
                        return SQL_SUCCESS;
 
75
                }
 
76
                else if (QR_NumResultCols(res) > 0)
 
77
                {
 
78
                        *pcrow = SC_is_fetchcursor(stmt) ? -1 : QR_get_num_total_tuples(res) - res->dl_count;
 
79
                        mylog("RowCount=%d\n", *pcrow);
 
80
                        return SQL_SUCCESS;
 
81
                }
 
82
        }
 
83
 
 
84
        *pcrow = -1;
 
85
        return SQL_SUCCESS;
 
86
        /* SC_set_errornumber(stmt, STMT_SEQUENCE_ERROR);
 
87
        SC_log_error(func, "Bad return value", stmt);
 
88
        return SQL_ERROR; */
 
89
}
 
90
 
 
91
 
 
92
/*
 
93
 *      This returns the number of columns associated with the database
 
94
 *      attached to "hstmt".
 
95
 */
 
96
RETCODE         SQL_API
 
97
PGAPI_NumResultCols(
 
98
                                        HSTMT hstmt,
 
99
                                        SWORD FAR * pccol)
 
100
{
 
101
        CSTR func = "PGAPI_NumResultCols";
 
102
        StatementClass *stmt = (StatementClass *) hstmt;
 
103
        QResultClass *result;
 
104
        char            parse_ok;
 
105
        ConnInfo   *ci;
 
106
 
 
107
        mylog("%s: entering...\n", func);
 
108
        if (!stmt)
 
109
        {
 
110
                SC_log_error(func, "", NULL);
 
111
                return SQL_INVALID_HANDLE;
 
112
        }
 
113
        ci = &(SC_get_conn(stmt)->connInfo);
 
114
 
 
115
        SC_clear_error(stmt);
 
116
 
 
117
        parse_ok = FALSE;
 
118
        if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
 
119
        {
 
120
                if (stmt->parse_status == STMT_PARSE_NONE)
 
121
                {
 
122
                        mylog("PGAPI_NumResultCols: calling parse_statement on stmt=%u\n", stmt);
 
123
                        parse_statement(stmt);
 
124
                }
 
125
 
 
126
                if (stmt->parse_status != STMT_PARSE_FATAL)
 
127
                {
 
128
                        parse_ok = TRUE;
 
129
                        *pccol = SC_get_IRD(stmt)->nfields;
 
130
                        mylog("PARSE: PGAPI_NumResultCols: *pccol = %d\n", *pccol);
 
131
                }
 
132
        }
 
133
 
 
134
        if (!parse_ok)
 
135
        {
 
136
                SC_pre_execute(stmt);
 
137
                result = SC_get_Curres(stmt);
 
138
 
 
139
                mylog("PGAPI_NumResultCols: result = %u, status = %d, numcols = %d\n", result, stmt->status, result != NULL ? QR_NumResultCols(result) : -1);
 
140
                if ((!result) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
 
141
                {
 
142
                        /* no query has been executed on this statement */
 
143
                        SC_set_error(stmt, STMT_SEQUENCE_ERROR, "No query has been executed with that handle");
 
144
                        SC_log_error(func, "", stmt);
 
145
                        return SQL_ERROR;
 
146
                }
 
147
 
 
148
                *pccol = QR_NumPublicResultCols(result);
 
149
        }
 
150
 
 
151
        return SQL_SUCCESS;
 
152
}
 
153
 
 
154
 
 
155
/*
 
156
 *      Return information about the database column the user wants
 
157
 *      information about.
 
158
 */
 
159
RETCODE         SQL_API
 
160
PGAPI_DescribeCol(
 
161
                                  HSTMT hstmt,
 
162
                                  UWORD icol,
 
163
                                  UCHAR FAR * szColName,
 
164
                                  SWORD cbColNameMax,
 
165
                                  SWORD FAR * pcbColName,
 
166
                                  SWORD FAR * pfSqlType,
 
167
                                  UDWORD FAR * pcbColDef,
 
168
                                  SWORD FAR * pibScale,
 
169
                                  SWORD FAR * pfNullable)
 
170
{
 
171
        CSTR func = "PGAPI_DescribeCol";
 
172
 
 
173
        /* gets all the information about a specific column */
 
174
        StatementClass *stmt = (StatementClass *) hstmt;
 
175
        ConnectionClass *conn;
 
176
        IRDFields       *irdflds;
 
177
        QResultClass *res;
 
178
        char       *col_name = NULL;
 
179
        Int4            fieldtype = 0;
 
180
        int                     column_size = 0,
 
181
                                decimal_digits = 0;
 
182
        ConnInfo   *ci;
 
183
        char            parse_ok;
 
184
        char            buf[255];
 
185
        int                     len = 0;
 
186
        RETCODE         result;
 
187
 
 
188
        mylog("%s: entering.%d..\n", func, icol);
 
189
 
 
190
        if (!stmt)
 
191
        {
 
192
                SC_log_error(func, "", NULL);
 
193
                return SQL_INVALID_HANDLE;
 
194
        }
 
195
 
 
196
        conn = SC_get_conn(stmt);
 
197
        ci = &(conn->connInfo);
 
198
 
 
199
        SC_clear_error(stmt);
 
200
 
 
201
        irdflds = SC_get_IRD(stmt);
 
202
#if (ODBCVER >= 0x0300)
 
203
        if (0 == icol) /* bookmark column */
 
204
        {
 
205
                SQLSMALLINT     fType = stmt->options.use_bookmarks == SQL_UB_VARIABLE ? SQL_BINARY : SQL_INTEGER;
 
206
 
 
207
                if (szColName && cbColNameMax > 0)
 
208
                        *szColName = '\0';
 
209
                if (pcbColName)
 
210
                        *pcbColName = 0;
 
211
                if (pfSqlType)
 
212
                        *pfSqlType = fType;
 
213
                if (pcbColDef)
 
214
                        *pcbColDef = 10;
 
215
                if (pibScale)
 
216
                        *pibScale = 0;
 
217
                if (pfNullable)
 
218
                        *pfNullable = SQL_NO_NULLS;
 
219
                return SQL_SUCCESS;
 
220
        }
 
221
#endif /* ODBCVER */
 
222
        /*
 
223
         * Dont check for bookmark column. This is the responsibility of the
 
224
         * driver manager.
 
225
         */
 
226
 
 
227
        icol--;                                         /* use zero based column numbers */
 
228
 
 
229
        parse_ok = FALSE;
 
230
        if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
 
231
        {
 
232
                if (stmt->parse_status == STMT_PARSE_NONE)
 
233
                {
 
234
                        mylog("PGAPI_DescribeCol: calling parse_statement on stmt=%u\n", stmt);
 
235
                        parse_statement(stmt);
 
236
                }
 
237
 
 
238
                mylog("PARSE: DescribeCol: icol=%d, stmt=%u, stmt->nfld=%d, stmt->fi=%u\n", icol, stmt, irdflds->nfields, irdflds->fi);
 
239
 
 
240
                if (stmt->parse_status != STMT_PARSE_FATAL && irdflds->fi && irdflds->fi[icol])
 
241
                {
 
242
                        if (icol >= irdflds->nfields)
 
243
                        {
 
244
                                SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in DescribeCol.");
 
245
                                SC_log_error(func, "", stmt);
 
246
                                return SQL_ERROR;
 
247
                        }
 
248
                        mylog("DescribeCol: getting info for icol=%d\n", icol);
 
249
 
 
250
                        fieldtype = irdflds->fi[icol]->type;
 
251
                        if (irdflds->fi[icol]->alias[0])
 
252
                                col_name = irdflds->fi[icol]->alias;
 
253
                        else
 
254
                                col_name = irdflds->fi[icol]->name;
 
255
                        column_size = irdflds->fi[icol]->column_size;
 
256
                        decimal_digits = irdflds->fi[icol]->decimal_digits;
 
257
 
 
258
                        mylog("PARSE: fieldtype=%d, col_name='%s', column_size=%d\n", fieldtype, col_name, column_size);
 
259
                        if (fieldtype > 0)
 
260
                                parse_ok = TRUE;
 
261
                }
 
262
        }
 
263
 
 
264
        /*
 
265
         * If couldn't parse it OR the field being described was not parsed
 
266
         * (i.e., because it was a function or expression, etc, then do it the
 
267
         * old fashioned way.
 
268
         */
 
269
        if (!parse_ok)
 
270
        {
 
271
                SC_pre_execute(stmt);
 
272
 
 
273
                res = SC_get_Curres(stmt);
 
274
 
 
275
                mylog("**** PGAPI_DescribeCol: res = %u, stmt->status = %d, !finished=%d, !premature=%d\n", res, stmt->status, stmt->status != STMT_FINISHED, stmt->status != STMT_PREMATURE);
 
276
                if ((NULL == res) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
 
277
                {
 
278
                        /* no query has been executed on this statement */
 
279
                        SC_set_error(stmt, STMT_EXEC_ERROR, "No query has been assigned to this statement.");
 
280
                        SC_log_error(func, "", stmt);
 
281
                        return SQL_ERROR;
 
282
                }
 
283
                else if (!QR_command_maybe_successful(res))
 
284
                {
 
285
                        SC_set_errornumber(stmt, STMT_EXEC_ERROR);
 
286
                        SC_log_error(func, "", stmt);
 
287
                        return SQL_ERROR;
 
288
                }
 
289
 
 
290
                if (icol >= QR_NumPublicResultCols(res))
 
291
                {
 
292
                        SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in DescribeCol.");
 
293
                        sprintf(buf, "Col#=%d, #Cols=%d", icol, QR_NumResultCols(res));
 
294
                        SC_log_error(func, buf, stmt);
 
295
                        return SQL_ERROR;
 
296
                }
 
297
 
 
298
                col_name = QR_get_fieldname(res, icol);
 
299
                fieldtype = QR_get_field_type(res, icol);
 
300
 
 
301
                /* atoi(ci->unknown_sizes) */
 
302
                column_size = pgtype_column_size(stmt, fieldtype, icol, ci->drivers.unknown_sizes);
 
303
                decimal_digits = pgtype_decimal_digits(stmt, fieldtype, icol);
 
304
        }
 
305
 
 
306
        mylog("describeCol: col %d fieldname = '%s'\n", icol, col_name);
 
307
        mylog("describeCol: col %d fieldtype = %d\n", icol, fieldtype);
 
308
        mylog("describeCol: col %d column_size = %d\n", icol, column_size);
 
309
 
 
310
        result = SQL_SUCCESS;
 
311
 
 
312
        /*
 
313
         * COLUMN NAME
 
314
         */
 
315
        len = strlen(col_name);
 
316
 
 
317
        if (pcbColName)
 
318
                *pcbColName = len;
 
319
 
 
320
        if (szColName && cbColNameMax > 0)
 
321
        {
 
322
                strncpy_null(szColName, col_name, cbColNameMax);
 
323
 
 
324
                if (len >= cbColNameMax)
 
325
                {
 
326
                        result = SQL_SUCCESS_WITH_INFO;
 
327
                        SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the colName.");
 
328
                }
 
329
        }
 
330
 
 
331
        /*
 
332
         * CONCISE(SQL) TYPE
 
333
         */
 
334
        if (pfSqlType)
 
335
        {
 
336
                *pfSqlType = pgtype_to_concise_type(stmt, fieldtype);
 
337
 
 
338
                mylog("describeCol: col %d *pfSqlType = %d\n", icol, *pfSqlType);
 
339
        }
 
340
 
 
341
        /*
 
342
         * COLUMN SIZE(PRECISION in 2.x)
 
343
         */
 
344
        if (pcbColDef)
 
345
        {
 
346
                if (column_size < 0)
 
347
                        column_size = 0;                /* "I dont know" */
 
348
 
 
349
                *pcbColDef = column_size;
 
350
 
 
351
                mylog("describeCol: col %d  *pcbColDef = %d\n", icol, *pcbColDef);
 
352
        }
 
353
 
 
354
        /*
 
355
         * DECIMAL DIGITS(SCALE in 2.x)
 
356
         */
 
357
        if (pibScale)
 
358
        {
 
359
                if (decimal_digits < 0)
 
360
                        decimal_digits = 0;
 
361
 
 
362
                *pibScale = decimal_digits;
 
363
                mylog("describeCol: col %d  *pibScale = %d\n", icol, *pibScale);
 
364
        }
 
365
 
 
366
        /*
 
367
         * NULLABILITY
 
368
         */
 
369
        if (pfNullable)
 
370
        {
 
371
                *pfNullable = (parse_ok) ? irdflds->fi[icol]->nullable : pgtype_nullable(stmt, fieldtype);
 
372
 
 
373
                mylog("describeCol: col %d  *pfNullable = %d\n", icol, *pfNullable);
 
374
        }
 
375
 
 
376
        return result;
 
377
}
 
378
 
 
379
 
 
380
/*              Returns result column descriptor information for a result set. */
 
381
RETCODE         SQL_API
 
382
PGAPI_ColAttributes(
 
383
                                        HSTMT hstmt,
 
384
                                        UWORD icol,
 
385
                                        UWORD fDescType,
 
386
                                        PTR rgbDesc,
 
387
                                        SWORD cbDescMax,
 
388
                                        SWORD FAR * pcbDesc,
 
389
                                        SDWORD FAR * pfDesc)
 
390
{
 
391
        CSTR func = "PGAPI_ColAttributes";
 
392
        StatementClass *stmt = (StatementClass *) hstmt;
 
393
        IRDFields       *irdflds;
 
394
        Int4            col_idx, field_type = 0;
 
395
        ConnectionClass *conn;
 
396
        ConnInfo        *ci;
 
397
        int                     unknown_sizes;
 
398
        int                     cols = 0;
 
399
        char            parse_ok;
 
400
        RETCODE         result;
 
401
        const char   *p = NULL;
 
402
        int                     len = 0,
 
403
                                value = 0;
 
404
        const   FIELD_INFO      *fi = NULL;
 
405
 
 
406
        mylog("%s: entering..col=%d %d len=%d.\n", func, icol, fDescType,
 
407
                                cbDescMax);
 
408
 
 
409
        if (!stmt)
 
410
        {
 
411
                SC_log_error(func, "", NULL);
 
412
                return SQL_INVALID_HANDLE;
 
413
        }
 
414
 
 
415
        if (pcbDesc)
 
416
                *pcbDesc = 0;
 
417
        irdflds = SC_get_IRD(stmt);
 
418
        conn = SC_get_conn(stmt);
 
419
        ci = &(conn->connInfo);
 
420
 
 
421
        /*
 
422
         * Dont check for bookmark column.      This is the responsibility of the
 
423
         * driver manager.      For certain types of arguments, the column number
 
424
         * is ignored anyway, so it may be 0.
 
425
         */
 
426
 
 
427
#if (ODBCVER >= 0x0300)
 
428
        if (0 == icol && SQL_DESC_COUNT != fDescType) /* bookmark column */
 
429
        {
 
430
                switch (fDescType)
 
431
                {
 
432
                        case SQL_DESC_OCTET_LENGTH:
 
433
                                if (pfDesc)
 
434
                                        *pfDesc = 4;
 
435
                                break;
 
436
                        case SQL_DESC_TYPE:
 
437
                                if (pfDesc)
 
438
                                        *pfDesc = stmt->options.use_bookmarks == SQL_UB_VARIABLE ? SQL_BINARY : SQL_INTEGER;
 
439
                                break;
 
440
                }
 
441
                return SQL_SUCCESS;
 
442
        }
 
443
#endif /* ODBCVER */
 
444
        col_idx = icol - 1;
 
445
 
 
446
        /* atoi(ci->unknown_sizes); */
 
447
        unknown_sizes = ci->drivers.unknown_sizes;
 
448
 
 
449
        /* not appropriate for SQLColAttributes() */
 
450
        if (unknown_sizes == UNKNOWNS_AS_DONTKNOW)
 
451
                unknown_sizes = UNKNOWNS_AS_MAX;
 
452
 
 
453
        parse_ok = FALSE;
 
454
        if (ci->drivers.parse && stmt->statement_type == STMT_TYPE_SELECT)
 
455
        {
 
456
                if (stmt->parse_status == STMT_PARSE_NONE)
 
457
                {
 
458
                        mylog("PGAPI_ColAttributes: calling parse_statement\n");
 
459
                        parse_statement(stmt);
 
460
                }
 
461
 
 
462
                cols = irdflds->nfields;
 
463
 
 
464
                /*
 
465
                 * Column Count is a special case.      The Column number is ignored
 
466
                 * in this case.
 
467
                 */
 
468
#if (ODBCVER >= 0x0300)
 
469
                if (fDescType == SQL_DESC_COUNT)
 
470
#else
 
471
                if (fDescType == SQL_COLUMN_COUNT)
 
472
#endif /* ODBCVER */
 
473
                {
 
474
                        if (pfDesc)
 
475
                                *pfDesc = cols;
 
476
 
 
477
                        return SQL_SUCCESS;
 
478
                }
 
479
 
 
480
                if (stmt->parse_status != STMT_PARSE_FATAL && irdflds->fi)
 
481
                {
 
482
                        if (col_idx >= cols)
 
483
                        {
 
484
                                SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in ColAttributes.");
 
485
                                SC_log_error(func, "", stmt);
 
486
                                return SQL_ERROR;
 
487
                        }
 
488
                        if (irdflds->fi[col_idx])
 
489
                        {
 
490
                                field_type = irdflds->fi[col_idx]->type;
 
491
                                if (field_type > 0)
 
492
                                        parse_ok = TRUE;
 
493
                        }
 
494
                }
 
495
        }
 
496
 
 
497
        if (parse_ok)
 
498
                fi = irdflds->fi[col_idx];
 
499
        else
 
500
        {
 
501
                SC_pre_execute(stmt);
 
502
 
 
503
                mylog("**** PGAPI_ColAtt: result = %u, status = %d, numcols = %d\n", SC_get_Curres(stmt), stmt->status, SC_get_Curres(stmt) != NULL ? QR_NumResultCols(SC_get_Curres(stmt)) : -1);
 
504
 
 
505
                if ((NULL == SC_get_Curres(stmt)) || ((stmt->status != STMT_FINISHED) && (stmt->status != STMT_PREMATURE)))
 
506
                {
 
507
                        SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't get column attributes: no result found.");
 
508
                        SC_log_error(func, "", stmt);
 
509
                        return SQL_ERROR;
 
510
                }
 
511
 
 
512
                cols = QR_NumPublicResultCols(SC_get_Curres(stmt));
 
513
 
 
514
                /*
 
515
                 * Column Count is a special case.      The Column number is ignored
 
516
                 * in this case.
 
517
                 */
 
518
#if (ODBCVER >= 0x0300)
 
519
                if (fDescType == SQL_DESC_COUNT)
 
520
#else
 
521
                if (fDescType == SQL_COLUMN_COUNT)
 
522
#endif /* ODBCVER */
 
523
                {
 
524
                        if (pfDesc)
 
525
                                *pfDesc = cols;
 
526
 
 
527
                        return SQL_SUCCESS;
 
528
                }
 
529
 
 
530
                if (col_idx >= cols)
 
531
                {
 
532
                        SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number in ColAttributes.");
 
533
                        SC_log_error(func, "", stmt);
 
534
                        return SQL_ERROR;
 
535
                }
 
536
 
 
537
                field_type = QR_get_field_type(SC_get_Curres(stmt), col_idx);
 
538
                if (stmt->parse_status != STMT_PARSE_FATAL && irdflds->fi && irdflds->fi[col_idx])
 
539
                        fi = irdflds->fi[col_idx];
 
540
        }
 
541
 
 
542
        mylog("colAttr: col %d field_type = %d\n", col_idx, field_type);
 
543
 
 
544
        switch (fDescType)
 
545
        {
 
546
                case SQL_COLUMN_AUTO_INCREMENT: /* == SQL_DESC_AUTO_UNIQUE_VALUE */
 
547
                        value = pgtype_auto_increment(stmt, field_type);
 
548
                        if (value == -1)        /* non-numeric becomes FALSE (ODBC Doc) */
 
549
                                value = FALSE;
 
550
inolog("AUTO_INCREMENT=%d\n", value);
 
551
 
 
552
                        break;
 
553
 
 
554
                case SQL_COLUMN_CASE_SENSITIVE: /* == SQL_DESC_CASE_SENSITIVE */
 
555
                        value = pgtype_case_sensitive(stmt, field_type);
 
556
                        break;
 
557
 
 
558
                        /*
 
559
                         * This special case is handled above.
 
560
                         *
 
561
                         * case SQL_COLUMN_COUNT:
 
562
                         */
 
563
                case SQL_COLUMN_DISPLAY_SIZE: /* == SQL_DESC_DISPLAY_SIZE */
 
564
                        value = fi ? fi->display_size : pgtype_display_size(stmt, field_type, col_idx, unknown_sizes);
 
565
 
 
566
                        mylog("PGAPI_ColAttributes: col %d, display_size= %d\n", col_idx, value);
 
567
 
 
568
                        break;
 
569
 
 
570
                case SQL_COLUMN_LABEL: /* == SQL_DESC_LABEL */
 
571
                        if (fi && fi->alias[0] != '\0')
 
572
                        {
 
573
                                p = fi->alias;
 
574
 
 
575
                                mylog("PGAPI_ColAttr: COLUMN_LABEL = '%s'\n", p);
 
576
                                break;
 
577
 
 
578
                        }
 
579
                        /* otherwise same as column name -- FALL THROUGH!!! */
 
580
 
 
581
#if (ODBCVER >= 0x0300)
 
582
                case SQL_DESC_NAME:
 
583
#else
 
584
                case SQL_COLUMN_NAME:
 
585
#endif /* ODBCVER */
 
586
                        p = fi ? (fi->alias[0] ? fi->alias : fi->name) : QR_get_fieldname(SC_get_Curres(stmt), col_idx);
 
587
 
 
588
                        mylog("PGAPI_ColAttr: COLUMN_NAME = '%s'\n", p);
 
589
                        break;
 
590
 
 
591
                case SQL_COLUMN_LENGTH:
 
592
                        value = (fi && fi->length > 0) ? fi->length : pgtype_buffer_length(stmt, field_type, col_idx, unknown_sizes);
 
593
                        if (value < 0)
 
594
                                value = 0;
 
595
 
 
596
                        mylog("PGAPI_ColAttributes: col %d, length = %d\n", col_idx, value);
 
597
                        break;
 
598
 
 
599
                case SQL_COLUMN_MONEY: /* == SQL_DESC_FIXED_PREC_SCALE */
 
600
                        value = pgtype_money(stmt, field_type);
 
601
inolog("COLUMN_MONEY=%d\n", value);
 
602
                        break;
 
603
 
 
604
#if (ODBCVER >= 0x0300)
 
605
                case SQL_DESC_NULLABLE:
 
606
#else
 
607
                case SQL_COLUMN_NULLABLE:
 
608
#endif /* ODBCVER */
 
609
                        value = fi ? fi->nullable : pgtype_nullable(stmt, field_type);
 
610
inolog("COLUMN_NULLABLE=%d\n", value);
 
611
                        break;
 
612
 
 
613
                case SQL_COLUMN_OWNER_NAME: /* == SQL_DESC_SCHEMA_NAME */
 
614
                        p = fi && (fi->ti) ? fi->ti->schema : "";
 
615
                        break;
 
616
 
 
617
                case SQL_COLUMN_PRECISION: /* in 2.x */
 
618
                        value = (fi && fi->column_size > 0) ? fi->column_size : pgtype_column_size(stmt, field_type, col_idx, unknown_sizes);
 
619
                        if (value < 0)
 
620
                                value = 0;
 
621
 
 
622
                        mylog("PGAPI_ColAttributes: col %d, column_size = %d\n", col_idx, value);
 
623
                        break;
 
624
 
 
625
                case SQL_COLUMN_QUALIFIER_NAME: /* == SQL_DESC_CATALOG_NAME */
 
626
                        p = "";
 
627
                        break;
 
628
 
 
629
                case SQL_COLUMN_SCALE: /* in 2.x */
 
630
                        value = pgtype_decimal_digits(stmt, field_type, col_idx);
 
631
inolog("COLUMN_SCALE=%d\n", value);
 
632
                        if (value < 0)
 
633
                                value = 0;
 
634
                        break;
 
635
 
 
636
                case SQL_COLUMN_SEARCHABLE: /* SQL_DESC_SEARCHABLE */
 
637
                        value = pgtype_searchable(stmt, field_type);
 
638
                        break;
 
639
 
 
640
                case SQL_COLUMN_TABLE_NAME: /* == SQL_DESC_TABLE_NAME */
 
641
                        p = fi && (fi->ti) ? fi->ti->name : "";
 
642
 
 
643
                        mylog("PGAPI_ColAttr: TABLE_NAME = '%s'\n", p);
 
644
                        break;
 
645
 
 
646
                case SQL_COLUMN_TYPE: /* == SQL_DESC_CONCISE_TYPE */
 
647
                        value = pgtype_to_concise_type(stmt, field_type);
 
648
inolog("COLUMN_TYPE=%d\n", value);
 
649
                        break;
 
650
 
 
651
                case SQL_COLUMN_TYPE_NAME: /* == SQL_DESC_TYPE_NAME */
 
652
                        p = pgtype_to_name(stmt, field_type);
 
653
                        break;
 
654
 
 
655
                case SQL_COLUMN_UNSIGNED: /* == SQL_DESC_UNSINGED */
 
656
                        value = pgtype_unsigned(stmt, field_type);
 
657
                        if (value == -1)        /* non-numeric becomes TRUE (ODBC Doc) */
 
658
                                value = TRUE;
 
659
 
 
660
                        break;
 
661
 
 
662
                case SQL_COLUMN_UPDATABLE: /* == SQL_DESC_UPDATABLE */
 
663
 
 
664
                        /*
 
665
                         * Neither Access or Borland care about this.
 
666
                         *
 
667
                         * if (field_type == PG_TYPE_OID) pfDesc = SQL_ATTR_READONLY;
 
668
                         * else
 
669
                         */
 
670
                        value = fi ? (fi->updatable ? SQL_ATTR_WRITE : SQL_ATTR_READONLY) : SQL_ATTR_READWRITE_UNKNOWN;
 
671
                        if (SQL_ATTR_READONLY != value)
 
672
                        {
 
673
                                const char *name = fi ? fi->name : QR_get_fieldname(SC_get_Curres(stmt), col_idx);
 
674
                                if (stricmp(name, "oid") == 0 ||
 
675
                                    stricmp(name, "ctid") == 0 ||
 
676
                                    stricmp(name, "xmin") == 0)
 
677
                                        value = SQL_ATTR_READONLY;
 
678
                        }
 
679
 
 
680
                        mylog("PGAPI_ColAttr: UPDATEABLE = %d\n", value);
 
681
                        break;
 
682
#if (ODBCVER >= 0x0300)
 
683
                case SQL_DESC_BASE_COLUMN_NAME:
 
684
 
 
685
                        p = fi ? fi->name : QR_get_fieldname(SC_get_Curres(stmt), col_idx);
 
686
 
 
687
                        mylog("PGAPI_ColAttr: BASE_COLUMN_NAME = '%s'\n", p);
 
688
                        break;
 
689
                case SQL_DESC_BASE_TABLE_NAME: /* the same as TABLE_NAME ok ? */
 
690
                        p = (fi && (fi->ti)) ? fi->ti->name : "";
 
691
 
 
692
                        mylog("PGAPI_ColAttr: BASE_TABLE_NAME = '%s'\n", p);
 
693
                        break;
 
694
                case SQL_DESC_LENGTH: /* different from SQL_COLUMN_LENGTH */
 
695
                        value = (fi && fi->length > 0) ? fi->length : pgtype_desclength(stmt, field_type, col_idx, unknown_sizes);
 
696
                        if (value < 0)
 
697
                                value = 0;
 
698
 
 
699
                        mylog("PGAPI_ColAttributes: col %d, length = %d\n", col_idx, value);
 
700
                        break;
 
701
                case SQL_DESC_OCTET_LENGTH:
 
702
                        value = (fi && fi->length > 0) ? fi->length : pgtype_transfer_octet_length(stmt, field_type, col_idx, unknown_sizes);
 
703
                        if (value < 0)
 
704
                                value = 0;
 
705
                        mylog("PGAPI_ColAttributes: col %d, octet_length = %d\n", col_idx, value);
 
706
                        break;
 
707
                case SQL_DESC_PRECISION: /* different from SQL_COLUMN_PRECISION */
 
708
                        if (value = FI_precision(fi), value <= 0)
 
709
                                value = pgtype_precision(stmt, field_type, col_idx, unknown_sizes);
 
710
                        if (value < 0)
 
711
                                value = 0;
 
712
 
 
713
                        mylog("PGAPI_ColAttributes: col %d, desc_precision = %d\n", col_idx, value);
 
714
                        break;
 
715
                case SQL_DESC_SCALE: /* different from SQL_COLUMN_SCALE */
 
716
                        value = pgtype_scale(stmt, field_type, col_idx);
 
717
                        if (value < 0)
 
718
                                value = 0;
 
719
                        break;
 
720
                case SQL_DESC_LOCAL_TYPE_NAME:
 
721
                        p = pgtype_to_name(stmt, field_type);
 
722
                        break;
 
723
                case SQL_DESC_TYPE:
 
724
                        value = pgtype_to_sqldesctype(stmt, field_type);
 
725
                        break;
 
726
                case SQL_DESC_NUM_PREC_RADIX:
 
727
                        value = pgtype_radix(stmt, field_type);
 
728
                        break;
 
729
                case SQL_DESC_LITERAL_PREFIX:
 
730
                        p = pgtype_literal_prefix(stmt, field_type);
 
731
                        break;
 
732
                case SQL_DESC_LITERAL_SUFFIX:
 
733
                        p = pgtype_literal_suffix(stmt, field_type);
 
734
                        break;
 
735
                case SQL_DESC_UNNAMED:
 
736
                        value = (fi && !fi->name[0] && !fi->alias[0]) ? SQL_UNNAMED : SQL_NAMED;
 
737
                        break;
 
738
#endif /* ODBCVER */
 
739
                case 1212:
 
740
                        SC_set_error(stmt, STMT_OPTION_NOT_FOR_THE_DRIVER, "this request may be for MS SQL Server");
 
741
                        return SQL_ERROR;
 
742
                default:
 
743
                        SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "ColAttribute for this type not implemented yet");
 
744
                        SC_log_error(func, "", stmt);
 
745
                        return SQL_ERROR;
 
746
        }
 
747
 
 
748
        result = SQL_SUCCESS;
 
749
 
 
750
        if (p)
 
751
        {                                                       /* char/binary data */
 
752
                len = strlen(p);
 
753
 
 
754
                if (rgbDesc)
 
755
                {
 
756
                        strncpy_null((char *) rgbDesc, p, (size_t) cbDescMax);
 
757
 
 
758
                        if (len >= cbDescMax)
 
759
                        {
 
760
                                result = SQL_SUCCESS_WITH_INFO;
 
761
                                SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the rgbDesc.");
 
762
                        }
 
763
                }
 
764
 
 
765
                if (pcbDesc)
 
766
                        *pcbDesc = len;
 
767
        }
 
768
        else
 
769
        {
 
770
                /* numeric data */
 
771
                if (pfDesc)
 
772
                        *pfDesc = value;
 
773
        }
 
774
 
 
775
        return result;
 
776
}
 
777
 
 
778
 
 
779
/*      Returns result data for a single column in the current row. */
 
780
RETCODE         SQL_API
 
781
PGAPI_GetData(
 
782
                          HSTMT hstmt,
 
783
                          UWORD icol,
 
784
                          SWORD fCType,
 
785
                          PTR rgbValue,
 
786
                          SDWORD cbValueMax,
 
787
                          SDWORD FAR * pcbValue)
 
788
{
 
789
        CSTR func = "PGAPI_GetData";
 
790
        QResultClass *res;
 
791
        StatementClass *stmt = (StatementClass *) hstmt;
 
792
        int                     num_cols,
 
793
                                num_rows;
 
794
        Int4            field_type;
 
795
        void       *value = NULL;
 
796
        int                     result;
 
797
        char            get_bookmark = FALSE;
 
798
        ConnInfo   *ci;
 
799
 
 
800
        mylog("PGAPI_GetData: enter, stmt=%u\n", stmt);
 
801
 
 
802
        if (!stmt)
 
803
        {
 
804
                SC_log_error(func, "", NULL);
 
805
                return SQL_INVALID_HANDLE;
 
806
        }
 
807
        ci = &(SC_get_conn(stmt)->connInfo);
 
808
        res = SC_get_Curres(stmt);
 
809
 
 
810
        if (STMT_EXECUTING == stmt->status)
 
811
        {
 
812
                SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't get data while statement is still executing.");
 
813
                SC_log_error(func, "", stmt);
 
814
                return SQL_ERROR;
 
815
        }
 
816
 
 
817
        if (stmt->status != STMT_FINISHED)
 
818
        {
 
819
                SC_set_error(stmt, STMT_STATUS_ERROR, "GetData can only be called after the successful execution on a SQL statement");
 
820
                SC_log_error(func, "", stmt);
 
821
                return SQL_ERROR;
 
822
        }
 
823
 
 
824
        if (icol == 0)
 
825
        {
 
826
                if (stmt->options.use_bookmarks == SQL_UB_OFF)
 
827
                {
 
828
                        SC_set_error(stmt, STMT_COLNUM_ERROR, "Attempt to retrieve bookmark with bookmark usage disabled");
 
829
                        SC_log_error(func, "", stmt);
 
830
                        return SQL_ERROR;
 
831
                }
 
832
 
 
833
                /* Make sure it is the bookmark data type */
 
834
                switch (fCType)
 
835
                {
 
836
                        case SQL_C_BOOKMARK:
 
837
#if (ODBCVER >= 0x0300)
 
838
                        case SQL_C_VARBOOKMARK:
 
839
#endif /* ODBCVER */
 
840
                                break;
 
841
                        default:
 
842
inolog("GetData Column 0 is type %d not of type SQL_C_BOOKMARK", fCType);
 
843
                                SC_set_error(stmt, STMT_PROGRAM_TYPE_OUT_OF_RANGE, "Column 0 is not of type SQL_C_BOOKMARK");
 
844
                                SC_log_error(func, "", stmt);
 
845
                                return SQL_ERROR;
 
846
                }
 
847
 
 
848
                get_bookmark = TRUE;
 
849
        }
 
850
        else
 
851
        {
 
852
                /* use zero-based column numbers */
 
853
                icol--;
 
854
 
 
855
                /* make sure the column number is valid */
 
856
                num_cols = QR_NumPublicResultCols(res);
 
857
                if (icol >= num_cols)
 
858
                {
 
859
                        SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR, "Invalid column number.");
 
860
                        SC_log_error(func, "", stmt);
 
861
                        return SQL_ERROR;
 
862
                }
 
863
        }
 
864
 
 
865
        if (stmt->manual_result || !SC_is_fetchcursor(stmt))
 
866
        {
 
867
                /* make sure we're positioned on a valid row */
 
868
                num_rows = QR_get_num_total_tuples(res);
 
869
                if ((stmt->currTuple < 0) ||
 
870
                        (stmt->currTuple >= num_rows))
 
871
                {
 
872
                        SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Not positioned on a valid row for GetData.");
 
873
                        SC_log_error(func, "", stmt);
 
874
                        return SQL_ERROR;
 
875
                }
 
876
                mylog("     num_rows = %d\n", num_rows);
 
877
 
 
878
                if (!get_bookmark)
 
879
                {
 
880
                        if (stmt->manual_result)
 
881
                                value = QR_get_value_manual(res, stmt->currTuple, icol);
 
882
                        else
 
883
                        {
 
884
                                Int4    curt = GIdx2ResultIdx(stmt->currTuple, stmt, res);
 
885
                                value = QR_get_value_backend_row(res, curt, icol);
 
886
                        }
 
887
                        mylog("     value = '%s'\n", value);
 
888
                }
 
889
        }
 
890
        else
 
891
        {
 
892
                /* it's a SOCKET result (backend data) */
 
893
                if (stmt->currTuple == -1 || !res || !res->tupleField)
 
894
                {
 
895
                        SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Not positioned on a valid row for GetData.");
 
896
                        SC_log_error(func, "", stmt);
 
897
                        return SQL_ERROR;
 
898
                }
 
899
 
 
900
                if (!get_bookmark)
 
901
                        value = QR_get_value_backend(res, icol);
 
902
 
 
903
                mylog("  socket: value = '%s'\n", value);
 
904
        }
 
905
 
 
906
        if (get_bookmark)
 
907
        {
 
908
                BOOL    contents_get = FALSE;
 
909
 
 
910
                if (rgbValue)
 
911
                {
 
912
                        if (SQL_C_BOOKMARK == fCType || 4 <= cbValueMax)
 
913
                        {
 
914
                                contents_get = TRUE; 
 
915
                                *((UDWORD *) rgbValue) = SC_get_bookmark(stmt);
 
916
                        }
 
917
                }
 
918
                if (pcbValue)
 
919
                        *pcbValue = sizeof(UDWORD);
 
920
 
 
921
                if (contents_get)
 
922
                        return SQL_SUCCESS;
 
923
                else
 
924
                {
 
925
                        SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the GetData.");
 
926
                        return SQL_SUCCESS_WITH_INFO;
 
927
                }
 
928
        }
 
929
 
 
930
        field_type = QR_get_field_type(res, icol);
 
931
 
 
932
        mylog("**** PGAPI_GetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value);
 
933
 
 
934
        stmt->current_col = icol;
 
935
 
 
936
        result = copy_and_convert_field(stmt, field_type, value,
 
937
                                                                 fCType, rgbValue, cbValueMax, pcbValue);
 
938
 
 
939
        stmt->current_col = -1;
 
940
 
 
941
        switch (result)
 
942
        {
 
943
                case COPY_OK:
 
944
                        return SQL_SUCCESS;
 
945
 
 
946
                case COPY_UNSUPPORTED_TYPE:
 
947
                        SC_set_error(stmt, STMT_RESTRICTED_DATA_TYPE_ERROR, "Received an unsupported type from Postgres.");
 
948
                        SC_log_error(func, "", stmt);
 
949
                        return SQL_ERROR;
 
950
 
 
951
                case COPY_UNSUPPORTED_CONVERSION:
 
952
                        SC_set_error(stmt, STMT_RESTRICTED_DATA_TYPE_ERROR, "Couldn't handle the necessary data type conversion.");
 
953
                        SC_log_error(func, "", stmt);
 
954
                        return SQL_ERROR;
 
955
 
 
956
                case COPY_RESULT_TRUNCATED:
 
957
                        SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the GetData.");
 
958
                        return SQL_SUCCESS_WITH_INFO;
 
959
 
 
960
                case COPY_GENERAL_ERROR:                /* error msg already filled in */
 
961
                        SC_log_error(func, "", stmt);
 
962
                        return SQL_ERROR;
 
963
 
 
964
                case COPY_NO_DATA_FOUND:
 
965
                        /* SC_log_error(func, "no data found", stmt); */
 
966
                        return SQL_NO_DATA_FOUND;
 
967
 
 
968
                default:
 
969
                        SC_set_error(stmt, STMT_INTERNAL_ERROR, "Unrecognized return value from copy_and_convert_field.");
 
970
                        SC_log_error(func, "", stmt);
 
971
                        return SQL_ERROR;
 
972
        }
 
973
}
 
974
 
 
975
 
 
976
/*
 
977
 *              Returns data for bound columns in the current row ("hstmt->iCursor"),
 
978
 *              advances the cursor.
 
979
 */
 
980
RETCODE         SQL_API
 
981
PGAPI_Fetch(
 
982
                        HSTMT hstmt)
 
983
{
 
984
        CSTR func = "PGAPI_Fetch";
 
985
        StatementClass *stmt = (StatementClass *) hstmt;
 
986
        ARDFields       *opts;
 
987
        QResultClass *res;
 
988
 
 
989
        mylog("PGAPI_Fetch: stmt = %u, stmt->result= %u\n", stmt, SC_get_Curres(stmt));
 
990
 
 
991
        if (!stmt)
 
992
        {
 
993
                SC_log_error(func, "", NULL);
 
994
                return SQL_INVALID_HANDLE;
 
995
        }
 
996
 
 
997
        SC_clear_error(stmt);
 
998
 
 
999
        if (!(res = SC_get_Curres(stmt)))
 
1000
        {
 
1001
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in PGAPI_Fetch.");
 
1002
                SC_log_error(func, "", stmt);
 
1003
                return SQL_ERROR;
 
1004
        }
 
1005
 
 
1006
        /* Not allowed to bind a bookmark column when using SQLFetch. */
 
1007
        opts = SC_get_ARD(stmt);
 
1008
        if (opts->bookmark->buffer)
 
1009
        {
 
1010
                SC_set_error(stmt, STMT_COLNUM_ERROR, "Not allowed to bind a bookmark column when using PGAPI_Fetch");
 
1011
                SC_log_error(func, "", stmt);
 
1012
                return SQL_ERROR;
 
1013
        }
 
1014
 
 
1015
        if (stmt->status == STMT_EXECUTING)
 
1016
        {
 
1017
                SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't fetch while statement is still executing.");
 
1018
                SC_log_error(func, "", stmt);
 
1019
                return SQL_ERROR;
 
1020
        }
 
1021
 
 
1022
        if (stmt->status != STMT_FINISHED)
 
1023
        {
 
1024
                SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Fetch can only be called after the successful execution on a SQL statement");
 
1025
                SC_log_error(func, "", stmt);
 
1026
                return SQL_ERROR;
 
1027
        }
 
1028
 
 
1029
        if (opts->bindings == NULL)
 
1030
        {
 
1031
                if (stmt->statement_type != STMT_TYPE_SELECT)
 
1032
                        return SQL_NO_DATA_FOUND;
 
1033
                /* just to avoid a crash if the user insists on calling this */
 
1034
                /* function even if SQL_ExecDirect has reported an Error */
 
1035
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Bindings were not allocated properly.");
 
1036
                SC_log_error(func, "", stmt);
 
1037
                return SQL_ERROR;
 
1038
        }
 
1039
 
 
1040
        QR_set_rowset_size(res, 1);
 
1041
        QR_inc_base(res, stmt->last_fetch_count_include_ommitted);
 
1042
 
 
1043
        return SC_fetch(stmt);
 
1044
}
 
1045
 
 
1046
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1047
static RETCODE SQL_API
 
1048
SC_pos_reload_needed(StatementClass *stmt, UDWORD flag);
 
1049
static Int4
 
1050
getNthValid(QResultClass *res, Int4 sta, UWORD orientation, UInt4 nth, Int4 *nearest)
 
1051
{
 
1052
        Int4    i, num_tuples = QR_get_num_total_tuples(res);
 
1053
        UInt4   count;
 
1054
        KeySet  *keyset;
 
1055
 
 
1056
        if (0 == res->dl_count)
 
1057
        {
 
1058
                if (SQL_FETCH_PRIOR == orientation)
 
1059
                {       
 
1060
                        if (sta + 1 >= (Int4) nth)
 
1061
                        {
 
1062
                                *nearest = sta + 1 - nth;
 
1063
                                return nth;
 
1064
                        }
 
1065
                        *nearest = -1;
 
1066
                        return -(Int4)(sta + 1);
 
1067
                }
 
1068
                else
 
1069
                {       
 
1070
                        if ((*nearest = sta + nth - 1) < num_tuples)
 
1071
                                return nth;
 
1072
                        *nearest = num_tuples;
 
1073
                        return -(Int4)(num_tuples - sta);
 
1074
                }
 
1075
        }
 
1076
        count = 0;
 
1077
        if (SQL_FETCH_PRIOR == orientation)
 
1078
        {
 
1079
                for (i = sta, keyset = res->keyset + sta;
 
1080
                        i >= 0; i--, keyset--)
 
1081
                {
 
1082
                        if (0 == (keyset->status & (CURS_SELF_DELETING | CURS_SELF_DELETING | CURS_OTHER_DELETED)))
 
1083
                        {
 
1084
                                *nearest = i;
 
1085
                                if (++count == nth)
 
1086
                                        return count;
 
1087
                        }
 
1088
                }
 
1089
                *nearest = -1; 
 
1090
        }
 
1091
        else
 
1092
        {
 
1093
                for (i = sta, keyset = res->keyset + sta;
 
1094
                        i < num_tuples; i++, keyset++)
 
1095
                {
 
1096
                        if (0 == (keyset->status & (CURS_SELF_DELETING | CURS_SELF_DELETING | CURS_OTHER_DELETED)))
 
1097
                        {
 
1098
                                *nearest = i;
 
1099
                                if (++count == nth)
 
1100
                                        return count;
 
1101
                        }
 
1102
                }
 
1103
                *nearest = num_tuples; 
 
1104
        }
 
1105
        return -(Int4)count;
 
1106
}
 
1107
#endif /* DRIVER_CURSOR_IMPLEMENT */
 
1108
 
 
1109
/*
 
1110
 *      return NO_DATA_FOUND macros
 
1111
 *        save_rowset_start or num_tuples must be defined 
 
1112
 */
 
1113
#define EXTFETCH_RETURN_BOF(stmt, res) \
 
1114
{ \
 
1115
        stmt->rowset_start = -1; \
 
1116
        stmt->currTuple = -1; \
 
1117
        res->base += (stmt->rowset_start - save_rowset_start); \
 
1118
        return SQL_NO_DATA_FOUND; \
 
1119
}
 
1120
#define EXTFETCH_RETURN_EOF(stmt, res) \
 
1121
{ \
 
1122
        stmt->rowset_start = num_tuples; \
 
1123
        stmt->currTuple = -1; \
 
1124
        res->base += (stmt->rowset_start - save_rowset_start); \
 
1125
        return SQL_NO_DATA_FOUND; \
 
1126
}
 
1127
        
 
1128
/*      This fetchs a block of data (rowset). */
 
1129
RETCODE         SQL_API
 
1130
PGAPI_ExtendedFetch(
 
1131
                                        HSTMT hstmt,
 
1132
                                        UWORD fFetchType,
 
1133
                                        SDWORD irow,
 
1134
                                        UDWORD FAR * pcrow,
 
1135
                                        UWORD FAR * rgfRowStatus,
 
1136
                                        SQLINTEGER bookmark_offset,
 
1137
                                        SQLINTEGER rowsetSize)
 
1138
{
 
1139
        CSTR func = "PGAPI_ExtendedFetch";
 
1140
        StatementClass *stmt = (StatementClass *) hstmt;
 
1141
        ARDFields       *opts;
 
1142
        QResultClass *res;
 
1143
        int                     num_tuples,
 
1144
                                i,
 
1145
                                save_rowset_size,
 
1146
                                save_rowset_start,
 
1147
                                progress_size;
 
1148
        RETCODE         result;
 
1149
        char            truncated,
 
1150
                                error;
 
1151
        ConnInfo   *ci;
 
1152
        DWORD           currp;
 
1153
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1154
        UWORD           pstatus;
 
1155
#endif /* DRIVER_CURSOR_IMPLEMENT */
 
1156
 
 
1157
        mylog("PGAPI_ExtendedFetch: stmt=%u\n", stmt);
 
1158
 
 
1159
        if (!stmt)
 
1160
        {
 
1161
                SC_log_error(func, "", NULL);
 
1162
                return SQL_INVALID_HANDLE;
 
1163
        }
 
1164
        ci = &(SC_get_conn(stmt)->connInfo);
 
1165
 
 
1166
        /* if (SC_is_fetchcursor(stmt) && !stmt->manual_result) */
 
1167
        if (SQL_CURSOR_FORWARD_ONLY == stmt->options.cursor_type && !stmt->manual_result)
 
1168
        {
 
1169
                if (fFetchType != SQL_FETCH_NEXT)
 
1170
                {
 
1171
                        SC_set_error(stmt, STMT_FETCH_OUT_OF_RANGE, "The fetch type for PGAPI_ExtendedFetch isn't allowed with ForwardOnly cursor.");
 
1172
                        return SQL_ERROR;
 
1173
                }
 
1174
        }
 
1175
 
 
1176
        SC_clear_error(stmt);
 
1177
 
 
1178
        if (!(res = SC_get_Curres(stmt)))
 
1179
        {
 
1180
                SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Null statement result in PGAPI_ExtendedFetch.");
 
1181
                SC_log_error(func, "", stmt);
 
1182
                return SQL_ERROR;
 
1183
        }
 
1184
 
 
1185
        opts = SC_get_ARD(stmt);
 
1186
        /*
 
1187
         * If a bookmark colunmn is bound but bookmark usage is off, then
 
1188
         * error
 
1189
         */
 
1190
        if (opts->bookmark->buffer && stmt->options.use_bookmarks == SQL_UB_OFF)
 
1191
        {
 
1192
                SC_set_error(stmt, STMT_COLNUM_ERROR, "Attempt to retrieve bookmark with bookmark usage disabled");
 
1193
                SC_log_error(func, "", stmt);
 
1194
                return SQL_ERROR;
 
1195
        }
 
1196
 
 
1197
        if (stmt->status == STMT_EXECUTING)
 
1198
        {
 
1199
                SC_set_error(stmt, STMT_SEQUENCE_ERROR, "Can't fetch while statement is still executing.");
 
1200
                SC_log_error(func, "", stmt);
 
1201
                return SQL_ERROR;
 
1202
        }
 
1203
 
 
1204
        if (stmt->status != STMT_FINISHED)
 
1205
        {
 
1206
                SC_set_error(stmt, STMT_STATUS_ERROR, "ExtendedFetch can only be called after the successful execution on a SQL statement");
 
1207
                SC_log_error(func, "", stmt);
 
1208
                return SQL_ERROR;
 
1209
        }
 
1210
 
 
1211
        if (opts->bindings == NULL)
 
1212
        {
 
1213
                if (stmt->statement_type != STMT_TYPE_SELECT)
 
1214
                        return SQL_NO_DATA_FOUND;
 
1215
                /* just to avoid a crash if the user insists on calling this */
 
1216
                /* function even if SQL_ExecDirect has reported an Error */
 
1217
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Bindings were not allocated properly.");
 
1218
                SC_log_error(func, "", stmt);
 
1219
                return SQL_ERROR;
 
1220
        }
 
1221
 
 
1222
        /* Initialize to no rows fetched */
 
1223
        if (rgfRowStatus)
 
1224
                for (i = 0; i < rowsetSize; i++)
 
1225
                        *(rgfRowStatus + i) = SQL_ROW_NOROW;
 
1226
 
 
1227
        if (pcrow)
 
1228
                *pcrow = 0;
 
1229
 
 
1230
        num_tuples = QR_get_num_total_tuples(res);
 
1231
 
 
1232
        /* Save and discard the saved rowset size */
 
1233
        save_rowset_start = stmt->rowset_start;
 
1234
        save_rowset_size = stmt->save_rowset_size;
 
1235
        stmt->save_rowset_size = -1;
 
1236
 
 
1237
        switch (fFetchType)
 
1238
        {
 
1239
                case SQL_FETCH_NEXT:
 
1240
 
 
1241
                        /*
 
1242
                         * From the odbc spec... If positioned before the start of the
 
1243
                         * RESULT SET, then this should be equivalent to
 
1244
                         * SQL_FETCH_FIRST.
 
1245
                         */
 
1246
 
 
1247
                        progress_size = (save_rowset_size > 0 ? save_rowset_size : rowsetSize);
 
1248
                        if (stmt->rowset_start < 0)
 
1249
                                stmt->rowset_start = 0;
 
1250
 
 
1251
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1252
                        else if (res->keyset)
 
1253
                        {
 
1254
                                if (stmt->last_fetch_count <= progress_size)
 
1255
                                {
 
1256
                                        stmt->rowset_start += stmt->last_fetch_count_include_ommitted;
 
1257
                                        progress_size -= stmt->last_fetch_count;
 
1258
                                }
 
1259
                                if (progress_size > 0 &&
 
1260
                                    getNthValid(res, stmt->rowset_start,
 
1261
                                        SQL_FETCH_NEXT, progress_size + 1,
 
1262
                                        &stmt->rowset_start) <= 0)
 
1263
                                {
 
1264
                                        EXTFETCH_RETURN_EOF(stmt, res)
 
1265
                                }
 
1266
                        }
 
1267
#endif /* DRIVER_CURSOR_IMPLEMENT */
 
1268
                        else
 
1269
                                stmt->rowset_start += progress_size;
 
1270
 
 
1271
                        mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
 
1272
                        break;
 
1273
 
 
1274
                case SQL_FETCH_PRIOR:
 
1275
                        mylog("SQL_FETCH_PRIOR: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
 
1276
 
 
1277
                        /*
 
1278
                         * From the odbc spec... If positioned after the end of the
 
1279
                         * RESULT SET, then this should be equivalent to
 
1280
                         * SQL_FETCH_LAST.
 
1281
                         */
 
1282
                        if (stmt->rowset_start <= 0)
 
1283
                        {
 
1284
                                EXTFETCH_RETURN_BOF(stmt, res)
 
1285
                        }
 
1286
                        if (stmt->rowset_start >= num_tuples)
 
1287
                        {
 
1288
                                if (rowsetSize > num_tuples)
 
1289
                                {
 
1290
                                        SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET, "fetch prior from eof and before the beggining");
 
1291
                                }
 
1292
                                stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - rowsetSize);
 
1293
 
 
1294
                        }
 
1295
                        else
 
1296
                        {
 
1297
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1298
                                if (i = getNthValid(res, stmt->rowset_start - 1, SQL_FETCH_PRIOR, rowsetSize, &stmt->rowset_start), i < -1)
 
1299
                                {
 
1300
                                        SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET, "fetch prior and before the beggining");
 
1301
                                        stmt->rowset_start = 0;
 
1302
                                }
 
1303
                                else if (i <= 0)
 
1304
                                {
 
1305
                                        EXTFETCH_RETURN_BOF(stmt, res)
 
1306
                                }
 
1307
#else
 
1308
                                if (stmt->rowset_start < opts->rowset_size)
 
1309
                                {
 
1310
                                        SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET, "fetch prior and before the beggining");
 
1311
                                        stmt->rowset_start = 0;
 
1312
                                }
 
1313
                                else
 
1314
                                        stmt->rowset_start -= opts->rowset_size;
 
1315
#endif /* DRIVER_CURSOR_IMPLEMENT */
 
1316
                        }
 
1317
                        break;
 
1318
 
 
1319
                case SQL_FETCH_FIRST:
 
1320
                        mylog("SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
 
1321
 
 
1322
                        stmt->rowset_start = 0;
 
1323
                        break;
 
1324
 
 
1325
                case SQL_FETCH_LAST:
 
1326
                        mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
 
1327
 
 
1328
                        stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - rowsetSize);
 
1329
                        break;
 
1330
 
 
1331
                case SQL_FETCH_ABSOLUTE:
 
1332
                        mylog("SQL_FETCH_ABSOLUTE: num_tuples=%d, currtuple=%d, irow=%d\n", num_tuples, stmt->currTuple, irow);
 
1333
 
 
1334
                        /* Position before result set, but dont fetch anything */
 
1335
                        if (irow == 0)
 
1336
                        {
 
1337
                                EXTFETCH_RETURN_BOF(stmt, res)
 
1338
                        }
 
1339
                        /* Position before the desired row */
 
1340
                        else if (irow > 0)
 
1341
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1342
                        {
 
1343
                                if (getNthValid(res, 0, SQL_FETCH_NEXT, irow, &stmt->rowset_start) <= 0)
 
1344
                                {
 
1345
                                        EXTFETCH_RETURN_EOF(stmt, res)
 
1346
                                }
 
1347
                        }
 
1348
#else
 
1349
                                stmt->rowset_start = irow - 1;
 
1350
#endif /* DRIVER_CURSOR_IMPLEMENT */
 
1351
                        /* Position with respect to the end of the result set */
 
1352
                        else
 
1353
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1354
                        {
 
1355
                                if (getNthValid(res, num_tuples - 1, SQL_FETCH_PRIOR, -irow, &stmt->rowset_start) <= 0)
 
1356
                                {
 
1357
                                        EXTFETCH_RETURN_BOF(stmt, res)
 
1358
                                }
 
1359
                        }
 
1360
#else
 
1361
                                stmt->rowset_start = num_tuples + irow;
 
1362
#endif /* DRIVER_CURSOR_IMPLEMENT */
 
1363
                        break;
 
1364
 
 
1365
                case SQL_FETCH_RELATIVE:
 
1366
 
 
1367
                        /*
 
1368
                         * Refresh the current rowset -- not currently implemented,
 
1369
                         * but lie anyway
 
1370
                         */
 
1371
                        if (irow == 0)
 
1372
                                break;
 
1373
 
 
1374
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1375
                        if (irow > 0)
 
1376
                        {
 
1377
                                if (getNthValid(res, stmt->rowset_start + 1, SQL_FETCH_NEXT, irow, &stmt->rowset_start) <= 0)
 
1378
                                {
 
1379
                                        EXTFETCH_RETURN_EOF(stmt, res)
 
1380
                                }
 
1381
                        }
 
1382
                        else
 
1383
                        {
 
1384
                                if (getNthValid(res, stmt->rowset_start - 1, SQL_FETCH_PRIOR, -irow, &stmt->rowset_start) <= 0)
 
1385
                                {
 
1386
                                        EXTFETCH_RETURN_BOF(stmt, res)
 
1387
                                }
 
1388
                        }
 
1389
#else
 
1390
                        stmt->rowset_start += irow;
 
1391
#endif /* DRIVER_CURSOR_IMPLEMENT */
 
1392
                        break;
 
1393
 
 
1394
                case SQL_FETCH_BOOKMARK:
 
1395
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1396
                        if (bookmark_offset > 0)
 
1397
                        {
 
1398
                                if (getNthValid(res, irow - 1, SQL_FETCH_NEXT, bookmark_offset + 1, &stmt->rowset_start) <= 0)
 
1399
                                {
 
1400
                                        EXTFETCH_RETURN_EOF(stmt, res)
 
1401
                                }
 
1402
                        }
 
1403
                        else if (getNthValid(res, irow - 1, SQL_FETCH_PRIOR, 1 - bookmark_offset, &stmt->rowset_start) <= 0)
 
1404
                        {
 
1405
                                stmt->currTuple = -1;
 
1406
                                EXTFETCH_RETURN_BOF(stmt, res)
 
1407
                        }
 
1408
#else
 
1409
                        stmt->rowset_start = irow + bookmark_offset - 1;
 
1410
#endif /* DRIVER_CURSOR_IMPLEMENT */
 
1411
                        break;
 
1412
 
 
1413
                default:
 
1414
                        SC_set_error(stmt, STMT_FETCH_OUT_OF_RANGE, "Unsupported PGAPI_ExtendedFetch Direction");
 
1415
                        SC_log_error(func, "Unsupported PGAPI_ExtendedFetch Direction", stmt);
 
1416
                        return SQL_ERROR;
 
1417
        }
 
1418
 
 
1419
        /*
 
1420
         * CHECK FOR PROPER CURSOR STATE
 
1421
         */
 
1422
 
 
1423
        /*
 
1424
         * Handle Declare Fetch style specially because the end is not really
 
1425
         * the end...
 
1426
         */
 
1427
        if (SC_is_fetchcursor(stmt) && !stmt->manual_result)
 
1428
        {
 
1429
                if (QR_end_tuples(res))
 
1430
                        return SQL_NO_DATA_FOUND;
 
1431
        }
 
1432
        else
 
1433
        {
 
1434
                /* If *new* rowset is after the result_set, return no data found */
 
1435
                if (stmt->rowset_start >= num_tuples)
 
1436
                {
 
1437
                        EXTFETCH_RETURN_EOF(stmt, res)
 
1438
                }
 
1439
        }
 
1440
 
 
1441
        /* If *new* rowset is prior to result_set, return no data found */
 
1442
        if (stmt->rowset_start < 0)
 
1443
        {
 
1444
                if (stmt->rowset_start + rowsetSize <= 0)
 
1445
                {
 
1446
                        EXTFETCH_RETURN_BOF(stmt, res)
 
1447
                }
 
1448
                else
 
1449
                {                                               /* overlap with beginning of result set,
 
1450
                                                                 * so get first rowset */
 
1451
                        stmt->rowset_start = 0;
 
1452
                }
 
1453
        }
 
1454
 
 
1455
        /* currTuple is always 1 row prior to the rowset */
 
1456
        stmt->currTuple = RowIdx2GIdx(-1, stmt);
 
1457
 
 
1458
        /* increment the base row in the tuple cache */
 
1459
        QR_set_rowset_size(res, rowsetSize);
 
1460
        if (SC_is_fetchcursor(stmt))
 
1461
                QR_inc_base(res, stmt->last_fetch_count_include_ommitted);
 
1462
        else
 
1463
                res->base = stmt->rowset_start;
 
1464
 
 
1465
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1466
        if (res->keyset)
 
1467
                SC_pos_reload_needed(stmt, SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type);
 
1468
#endif /* DRIVER_CURSOR_IMPLEMENT */
 
1469
        /* Physical Row advancement occurs for each row fetched below */
 
1470
 
 
1471
        mylog("PGAPI_ExtendedFetch: new currTuple = %d\n", stmt->currTuple);
 
1472
 
 
1473
        truncated = error = FALSE;
 
1474
        for (i = 0, currp = stmt->rowset_start; i < rowsetSize; currp++)
 
1475
        {
 
1476
                stmt->bind_row = i;             /* set the binding location */
 
1477
                result = SC_fetch(stmt);
 
1478
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1479
                if (SQL_SUCCESS_WITH_INFO == result && 0 == stmt->last_fetch_count && res->keyset)
 
1480
                {
 
1481
                        res->keyset[stmt->currTuple].status &= ~CURS_IN_ROWSET;
 
1482
                        continue;
 
1483
                }
 
1484
#endif   /* DRIVER_CURSOR_IMPLEMENT */
 
1485
 
 
1486
                /* Determine Function status */
 
1487
                if (result == SQL_NO_DATA_FOUND)
 
1488
                        break;
 
1489
                else if (result == SQL_SUCCESS_WITH_INFO)
 
1490
                        truncated = TRUE;
 
1491
                else if (result == SQL_ERROR)
 
1492
                        error = TRUE;
 
1493
 
 
1494
                /* Determine Row Status */
 
1495
                if (rgfRowStatus)
 
1496
                {
 
1497
                        if (result == SQL_ERROR)
 
1498
                                *(rgfRowStatus + i) = SQL_ROW_ERROR;
 
1499
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1500
                        else if (res->keyset)
 
1501
                        {
 
1502
                                pstatus = (res->keyset[currp].status & KEYSET_INFO_PUBLIC);
 
1503
                                if (pstatus != 0 && pstatus != SQL_ROW_ADDED)
 
1504
                                {
 
1505
                                        rgfRowStatus[i] = pstatus;
 
1506
                                }
 
1507
                                else
 
1508
                                        rgfRowStatus[i] = SQL_ROW_SUCCESS;
 
1509
                                /* refresh the status */
 
1510
                                /* if (SQL_ROW_DELETED != pstatus) */
 
1511
                                res->keyset[currp].status &= (~KEYSET_INFO_PUBLIC);
 
1512
                        }
 
1513
#endif   /* DRIVER_CURSOR_IMPLEMENT */
 
1514
                        else
 
1515
                                *(rgfRowStatus + i) = SQL_ROW_SUCCESS;
 
1516
                }
 
1517
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1518
                if (SQL_ERROR != result && res->keyset)
 
1519
                        res->keyset[currp].status |= CURS_IN_ROWSET;
 
1520
#endif   /* DRIVER_CURSOR_IMPLEMENT */
 
1521
                i++;
 
1522
        }
 
1523
 
 
1524
        /* Save the fetch count for SQLSetPos */
 
1525
        stmt->last_fetch_count = i;
 
1526
        stmt->last_fetch_count_include_ommitted = GIdx2RowIdx(currp, stmt);
 
1527
 
 
1528
        /* Reset next binding row */
 
1529
        stmt->bind_row = 0;
 
1530
 
 
1531
        /* Move the cursor position to the first row in the result set. */
 
1532
        stmt->currTuple = RowIdx2GIdx(0, stmt);
 
1533
 
 
1534
        /* For declare/fetch, need to reset cursor to beginning of rowset */
 
1535
        if (SC_is_fetchcursor(stmt) && !stmt->manual_result)
 
1536
                QR_set_position(res, 0);
 
1537
 
 
1538
        /* Set the number of rows retrieved */
 
1539
        if (pcrow)
 
1540
                *pcrow = i;
 
1541
 
 
1542
        if (i == 0)
 
1543
                /* Only DeclareFetch should wind up here */
 
1544
                return SQL_NO_DATA_FOUND;
 
1545
        else if (error)
 
1546
                return SQL_ERROR;
 
1547
        else if (truncated)
 
1548
                return SQL_SUCCESS_WITH_INFO;
 
1549
        else if (SC_get_errornumber(stmt) == STMT_POS_BEFORE_RECORDSET)
 
1550
                return SQL_SUCCESS_WITH_INFO;
 
1551
        else
 
1552
                return SQL_SUCCESS;
 
1553
}
 
1554
 
 
1555
 
 
1556
/*
 
1557
 *              This determines whether there are more results sets available for
 
1558
 *              the "hstmt".
 
1559
 */
 
1560
/* CC: return SQL_NO_DATA_FOUND since we do not support multiple result sets */
 
1561
RETCODE         SQL_API
 
1562
PGAPI_MoreResults(
 
1563
                                  HSTMT hstmt)
 
1564
{
 
1565
        CSTR func = "PGAPI_MoreResults";
 
1566
        StatementClass  *stmt = (StatementClass *) hstmt;
 
1567
        QResultClass    *res;
 
1568
 
 
1569
        mylog("%s: entering...\n", func);
 
1570
        if (stmt && (res = SC_get_Curres(stmt)))
 
1571
                SC_set_Curres(stmt, res->next);
 
1572
        if (res = SC_get_Curres(stmt), res)
 
1573
        {
 
1574
                stmt->diag_row_count = res->recent_processed_row_count;
 
1575
                stmt->rowset_start = -1;
 
1576
                stmt->currTuple = -1;
 
1577
                return SQL_SUCCESS;
 
1578
        } 
 
1579
        return SQL_NO_DATA_FOUND;
 
1580
}
 
1581
 
 
1582
 
 
1583
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
1584
/*
 
1585
 *      Stuff for updatable cursors.
 
1586
 */
 
1587
static Int2     getNumResultCols(const QResultClass *res)
 
1588
{
 
1589
        Int2    res_cols = QR_NumPublicResultCols(res);
 
1590
        return res_cols;
 
1591
}
 
1592
static UInt4    getOid(const QResultClass *res, int index)
 
1593
{
 
1594
        return res->keyset[index].oid;
 
1595
}
 
1596
static void getTid(const QResultClass *res, int index, UInt4 *blocknum, UInt2 *offset)
 
1597
{
 
1598
        *blocknum = res->keyset[index].blocknum;
 
1599
        *offset = res->keyset[index].offset;
 
1600
}
 
1601
static void KeySetSet(const TupleField *tuple, int num_fields, KeySet *keyset)
 
1602
{
 
1603
        sscanf(tuple[num_fields - 2].value, "(%u,%hu)",
 
1604
                        &keyset->blocknum, &keyset->offset);
 
1605
        sscanf(tuple[num_fields - 1].value, "%u", &keyset->oid);
 
1606
}
 
1607
 
 
1608
static void DiscardDeleted(QResultClass *res, int index);
 
1609
static void AddRollback(ConnectionClass *conn, QResultClass *res, int index, const KeySet *keyset)
 
1610
{
 
1611
        Rollback *rollback;
 
1612
 
 
1613
        if (!res->rollback)
 
1614
        {
 
1615
                res->rb_count = 0;
 
1616
                res->rb_alloc = 10;
 
1617
                rollback = res->rollback = malloc(sizeof(Rollback) * res->rb_alloc);
 
1618
        }
 
1619
        else
 
1620
        {
 
1621
                if (res->rb_count >= res->rb_alloc)
 
1622
                {
 
1623
                        res->rb_alloc *= 2; 
 
1624
                        if (rollback = realloc(res->rollback, sizeof(Rollback) * res->rb_alloc), !rollback)
 
1625
                        {
 
1626
                                res->rb_alloc = res->rb_count = 0;
 
1627
                                return;
 
1628
                        }
 
1629
                        res->rollback = rollback; 
 
1630
                }
 
1631
                rollback = res->rollback + res->rb_count;
 
1632
        }
 
1633
        rollback->index = index;
 
1634
        if (keyset)
 
1635
        {
 
1636
                rollback->blocknum = keyset[index].blocknum;
 
1637
                rollback->offset = keyset[index].offset;
 
1638
        }
 
1639
        else
 
1640
        {
 
1641
                rollback->offset = 0;
 
1642
                rollback->blocknum = 0;
 
1643
        }
 
1644
 
 
1645
        conn->result_uncommitted = 1;
 
1646
        res->rb_count++;        
 
1647
}
 
1648
 
 
1649
static void DiscardRollback(QResultClass *res)
 
1650
{
 
1651
        int     i, index;
 
1652
        UWORD   status;
 
1653
        Rollback *rollback;
 
1654
        KeySet  *keyset;
 
1655
 
 
1656
        if (0 == res->rb_count || NULL == res->rollback)
 
1657
                return;
 
1658
        rollback = res->rollback;
 
1659
        keyset = res->keyset;
 
1660
        for (i = 0; i < res->rb_count; i++)
 
1661
        {
 
1662
                index = rollback[i].index;
 
1663
                status = keyset[index].status;
 
1664
                if (0 != (status & CURS_SELF_DELETING))
 
1665
                        DiscardDeleted(res, index);
 
1666
                keyset[index].status &= ~(CURS_SELF_DELETING | CURS_SELF_UPDATING | CURS_SELF_ADDING);
 
1667
                keyset[index].status |= ((status & (CURS_SELF_DELETING | CURS_SELF_UPDATING | CURS_SELF_ADDING)) << 3);
 
1668
        }
 
1669
        free(rollback);
 
1670
        res->rollback = NULL;
 
1671
        res->rb_count = res->rb_alloc = 0;
 
1672
}
 
1673
 
 
1674
static void UndoRollback(StatementClass *stmt, QResultClass *res)
 
1675
{
 
1676
        int     i, index, ridx;
 
1677
        UWORD   status;
 
1678
        Rollback *rollback;
 
1679
        KeySet  *keyset;
 
1680
 
 
1681
        if (0 == res->rb_count || NULL == res->rollback)
 
1682
                return;
 
1683
        rollback = res->rollback;
 
1684
        keyset = res->keyset;
 
1685
        for (i = res->rb_count - 1; i >= 0; i--)
 
1686
        {
 
1687
                index = rollback[i].index;
 
1688
                status = keyset[index].status;
 
1689
                if (0 != (status & CURS_SELF_ADDING))
 
1690
                {
 
1691
                        ridx = GIdx2ResultIdx(index, stmt, res);
 
1692
                        if (ridx >=0 && ridx < res->num_backend_rows)
 
1693
                        {
 
1694
                                TupleField *tuple = res->backend_tuples + res->num_fields * ridx;
 
1695
                                int     j;
 
1696
 
 
1697
                                for (j = 0; j < res->num_fields; j++, tuple++)
 
1698
                                {
 
1699
                                        if (tuple->len > 0 && tuple->value)
 
1700
                                        {
 
1701
                                                free(tuple->value);
 
1702
                                                tuple->value = NULL;
 
1703
                                        }
 
1704
                                        tuple->len = 0;
 
1705
                                }
 
1706
                        }
 
1707
                        if (index < res->num_total_rows)
 
1708
                                res->num_total_rows = index;
 
1709
                }
 
1710
                else
 
1711
                {
 
1712
                        if (0 != (status & CURS_SELF_DELETING))
 
1713
                                DiscardDeleted(res, index);
 
1714
                        keyset[index].blocknum = rollback[i].blocknum;
 
1715
                        keyset[index].offset = rollback[i].offset;
 
1716
                        if (0 != (keyset[index].status & CURS_SELF_UPDATING))
 
1717
                                keyset[index].status |= CURS_NEEDS_REREAD;
 
1718
                        keyset[index].status &= ~(CURS_SELF_DELETING | CURS_SELF_UPDATING | CURS_SELF_ADDING | KEYSET_INFO_PUBLIC);
 
1719
                }
 
1720
        }
 
1721
        free(rollback);
 
1722
        res->rollback = NULL;
 
1723
        res->rb_count = res->rb_alloc = 0;
 
1724
}
 
1725
 
 
1726
void    ProcessRollback(ConnectionClass *conn, BOOL undo) 
 
1727
{
 
1728
        int     i;
 
1729
        StatementClass  *stmt;
 
1730
        QResultClass    *res;
 
1731
 
 
1732
        for (i = 0; i < conn->num_stmts; i++)
 
1733
        {
 
1734
                if (stmt = conn->stmts[i], !stmt)
 
1735
                        continue;
 
1736
                for (res = SC_get_Result(stmt); res; res = res->next)
 
1737
                {
 
1738
                        if (undo)
 
1739
                                UndoRollback(stmt, res);
 
1740
                        else
 
1741
                                DiscardRollback(res);
 
1742
                }
 
1743
        }
 
1744
}
 
1745
 
 
1746
 
 
1747
static void AddDeleted(QResultClass *res, int index)
 
1748
{
 
1749
        int     i;
 
1750
        UInt4   *deleted;
 
1751
 
 
1752
        if (!res->deleted)
 
1753
        {
 
1754
                res->dl_count = 0;
 
1755
                res->dl_alloc = 10;
 
1756
                deleted = res->deleted = malloc(sizeof(UInt4) * res->dl_alloc);
 
1757
        }
 
1758
        else
 
1759
        {
 
1760
                if (res->dl_count >= res->dl_alloc)
 
1761
                {
 
1762
                        res->dl_alloc *= 2; 
 
1763
                        if (deleted = realloc(res->deleted, sizeof(UInt4) * res->dl_alloc), !deleted)
 
1764
                        {
 
1765
                                res->dl_alloc = res->dl_count = 0;
 
1766
                                return;
 
1767
                        }
 
1768
                        res->deleted = deleted; 
 
1769
                }
 
1770
                for (i = 0, deleted = res->deleted; i < res->dl_count; i++, deleted++)
 
1771
                {
 
1772
                        if (index < (int) *deleted)
 
1773
                                break;
 
1774
                }
 
1775
                memmove(deleted + 1, deleted, sizeof(UInt4) * (res->dl_count - i)); 
 
1776
        }
 
1777
        *deleted = index;
 
1778
        res->dl_count++;        
 
1779
}
 
1780
static void DiscardDeleted(QResultClass *res, int index)
 
1781
{
 
1782
        int     i;
 
1783
        UInt4   *deleted;
 
1784
 
 
1785
        if (!res->deleted)
 
1786
                return;
 
1787
 
 
1788
        for (i = 0, deleted = res->deleted; i < res->dl_count; i++, deleted++)
 
1789
        {
 
1790
                if (index == (int) *deleted)
 
1791
                        break;
 
1792
        }
 
1793
        if (i >= res->dl_count)
 
1794
                return;
 
1795
        memmove(deleted, deleted + 1, sizeof(UInt4) * (res->dl_count - i - 1)); 
 
1796
        res->dl_count--;        
 
1797
}
 
1798
 
 
1799
#define LATEST_TUPLE_LOAD       1L
 
1800
#define USE_INSERTED_TID        (1L << 1)
 
1801
static QResultClass *
 
1802
positioned_load(StatementClass *stmt, UInt4 flag, UInt4 oid, const char *tidval)
 
1803
{
 
1804
        QResultClass *qres;
 
1805
        char    *selstr;
 
1806
        BOOL    latest = ((flag & LATEST_TUPLE_LOAD) != 0);
 
1807
        UInt4   len;
 
1808
 
 
1809
        len = strlen(stmt->load_statement);
 
1810
        if (tidval)
 
1811
        {
 
1812
                len += 100;
 
1813
                selstr = malloc(len);
 
1814
                if (latest)
 
1815
                {
 
1816
                        if (stmt->ti[0]->schema[0])
 
1817
                                sprintf(selstr, "%s where ctid = currtid2('\"%s\".\"%s\"', '%s') and oid  = %u",
 
1818
                                stmt->load_statement, stmt->ti[0]->schema,
 
1819
                                stmt->ti[0]->name, tidval, oid);
 
1820
                        else
 
1821
                                sprintf(selstr, "%s where ctid = currtid2('%s', '%s') and oid  = %u", stmt->load_statement, stmt->ti[0]->name, tidval, oid);
 
1822
                }
 
1823
                else 
 
1824
                        sprintf(selstr, "%s where ctid = '%s' and oid = %u", stmt->load_statement, tidval, oid); 
 
1825
        }
 
1826
        else if ((flag & USE_INSERTED_TID) != 0)
 
1827
        {
 
1828
                len += 50;
 
1829
                selstr = malloc(len);
 
1830
                sprintf(selstr, "%s where ctid = currtid(0, '(,)') and oid = %u", stmt->load_statement, oid);
 
1831
        } 
 
1832
        else
 
1833
        {
 
1834
                len += 20;
 
1835
                selstr = malloc(len);
 
1836
                sprintf(selstr, "%s where oid = %u", stmt->load_statement, oid);
 
1837
        } 
 
1838
 
 
1839
        mylog("selstr=%s\n", selstr);
 
1840
        qres = CC_send_query(SC_get_conn(stmt), selstr, NULL, CLEAR_RESULT_ON_ABORT);
 
1841
        free(selstr);
 
1842
        return qres;
 
1843
}
 
1844
 
 
1845
RETCODE         SQL_API
 
1846
SC_pos_reload(StatementClass *stmt, UDWORD global_ridx, UWORD *count, BOOL logChanges)
 
1847
{
 
1848
        int                     i,
 
1849
                                res_cols;
 
1850
        UWORD           rcnt, offset;
 
1851
        Int4            res_ridx;
 
1852
        UInt4           oid, blocknum;
 
1853
        QResultClass *res,
 
1854
                           *qres;
 
1855
        IRDFields       *irdflds = SC_get_IRD(stmt);
 
1856
        RETCODE         ret = SQL_ERROR;
 
1857
        char            tidval[32];
 
1858
 
 
1859
        mylog("positioned load fi=%x ti=%x\n", irdflds->fi, stmt->ti);
 
1860
        rcnt = 0;
 
1861
        if (count)
 
1862
                *count = 0;
 
1863
        if (!(res = SC_get_Curres(stmt)))
 
1864
        {
 
1865
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_reload.");
 
1866
                return SQL_ERROR;
 
1867
        }
 
1868
        if (!stmt->ti)
 
1869
                parse_statement(stmt);  /* not preferable */
 
1870
        if (!stmt->updatable)
 
1871
        {
 
1872
                stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
 
1873
                SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only");
 
1874
                return SQL_ERROR;
 
1875
        }
 
1876
        res_ridx = GIdx2ResultIdx(global_ridx, stmt, res);
 
1877
        if (!(oid = getOid(res, global_ridx)))
 
1878
        {
 
1879
                SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the row was already deleted ?");
 
1880
                return SQL_SUCCESS_WITH_INFO;
 
1881
        }
 
1882
        getTid(res, global_ridx, &blocknum, &offset);
 
1883
        sprintf(tidval, "(%u, %u)", blocknum, offset);
 
1884
        res_cols = getNumResultCols(res);
 
1885
        if (qres = positioned_load(stmt, LATEST_TUPLE_LOAD, oid, tidval), qres)
 
1886
        {
 
1887
                TupleField *tupleo, *tuplen;
 
1888
                ConnectionClass *conn = SC_get_conn(stmt);
 
1889
 
 
1890
                rcnt = QR_get_num_backend_tuples(qres);
 
1891
                tupleo = res->backend_tuples + res->num_fields * res_ridx;
 
1892
                if (logChanges && CC_is_in_trans(conn))
 
1893
                        AddRollback(conn, res, global_ridx, res->keyset);
 
1894
                if (rcnt == 1)
 
1895
                {
 
1896
                        int     effective_fields = res_cols;
 
1897
 
 
1898
                        QR_set_position(qres, 0);
 
1899
                        tuplen = qres->tupleField;
 
1900
                        if (res->keyset)
 
1901
                        {
 
1902
                                if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type &&
 
1903
                                        strcmp(tuplen[qres->num_fields - 2].value, tidval))
 
1904
                                        res->keyset[global_ridx].status |= SQL_ROW_UPDATED;
 
1905
                                KeySetSet(tuplen, qres->num_fields, res->keyset + global_ridx);
 
1906
                        }
 
1907
                        for (i = 0; i < effective_fields; i++)
 
1908
                        {
 
1909
                                if (tupleo[i].value)
 
1910
                                        free(tupleo[i].value);
 
1911
                                tupleo[i].len = tuplen[i].len;
 
1912
                                tuplen[i].len = 0;
 
1913
                                tupleo[i].value = tuplen[i].value;
 
1914
                                tuplen[i].value = NULL;
 
1915
                        }
 
1916
                        ret = SQL_SUCCESS;
 
1917
                }
 
1918
                else
 
1919
                {
 
1920
                        SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the content was deleted after last fetch");
 
1921
                        ret = SQL_SUCCESS_WITH_INFO;
 
1922
                        if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
 
1923
                        {
 
1924
                                res->keyset[global_ridx].status |= SQL_ROW_DELETED;
 
1925
                        }
 
1926
                }
 
1927
                QR_Destructor(qres);
 
1928
        }
 
1929
        else if (SC_get_errornumber(stmt) == 0)
 
1930
                SC_set_errornumber(stmt, STMT_ERROR_TAKEN_FROM_BACKEND);
 
1931
        if (count)
 
1932
                *count = rcnt;
 
1933
        return ret;
 
1934
}
 
1935
 
 
1936
static RETCODE  SQL_API
 
1937
SC_pos_reload_needed(StatementClass *stmt, UDWORD flag)
 
1938
{
 
1939
        Int4            i, limitrow;
 
1940
        UWORD           qcount;
 
1941
        QResultClass    *res;
 
1942
        IRDFields       *irdflds = SC_get_IRD(stmt);
 
1943
        RETCODE         ret = SQL_ERROR;
 
1944
        ConnectionClass *conn = SC_get_conn(stmt);
 
1945
        UInt4           oid, blocknum, lodlen;
 
1946
        char            *qval = NULL, *sval;
 
1947
        Int4            rowc;
 
1948
        UWORD           offset;
 
1949
        BOOL            create_from_scratch = (0 != flag);
 
1950
 
 
1951
        mylog("SC_pos_reload_needed\n");
 
1952
        if (!(res = SC_get_Curres(stmt)))
 
1953
        {
 
1954
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_reload_needed.");
 
1955
                return SQL_ERROR;
 
1956
        }
 
1957
        if (!stmt->ti)
 
1958
                parse_statement(stmt);  /* not preferable */
 
1959
        if (!stmt->updatable)
 
1960
        {
 
1961
                stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
 
1962
                SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only");
 
1963
                return SQL_ERROR;
 
1964
        }
 
1965
        limitrow = RowIdx2GIdx(res->rowset_size, stmt);
 
1966
        if (limitrow > res->num_total_rows)
 
1967
                limitrow = res->num_total_rows;
 
1968
        if (create_from_scratch)
 
1969
        {
 
1970
                int     flds_cnt = res->num_backend_rows * res->num_fields,
 
1971
                        brows;
 
1972
 
 
1973
                for (i = 0; i < flds_cnt; i++)
 
1974
                {
 
1975
                        if (res->backend_tuples[i].value)
 
1976
                                free(res->backend_tuples[i].value);
 
1977
                }
 
1978
                brows = GIdx2RowIdx(limitrow, stmt);
 
1979
                if (brows > res->count_backend_allocated)
 
1980
                {
 
1981
                        res->backend_tuples = realloc(res->backend_tuples, sizeof(TupleField) * res->num_fields * brows);
 
1982
                        res->count_backend_allocated = brows;
 
1983
                }
 
1984
                if (brows > 0)
 
1985
                        memset(res->backend_tuples, 0, sizeof(TupleField) * res->num_fields * brows);
 
1986
                res->num_backend_rows = brows;
 
1987
                res->base = 0;
 
1988
                for (i = stmt->rowset_start; i < limitrow; i++)
 
1989
                {
 
1990
                        if (0 == (res->keyset[i].status & (CURS_SELF_DELETING | CURS_SELF_DELETED | CURS_OTHER_DELETED)))
 
1991
                                res->keyset[i].status |= CURS_NEEDS_REREAD;
 
1992
                }
 
1993
        }
 
1994
 
 
1995
        for (i = stmt->rowset_start, rowc = 0;; i++)
 
1996
        {
 
1997
                if (i >= limitrow)
 
1998
                {
 
1999
                        if (!rowc)
 
2000
                                break;
 
2001
                        rowc = -1; /* end of loop */
 
2002
                }
 
2003
                if (rowc < 0 || rowc >= 10)
 
2004
                {
 
2005
                        QResultClass    *qres;
 
2006
 
 
2007
                        strcpy(sval, ")");
 
2008
                        qres = CC_send_query(conn, qval, NULL, CLEAR_RESULT_ON_ABORT | CREATE_KEYSET);
 
2009
                        if (qres)
 
2010
                        {
 
2011
                                int             j, k, l, m;
 
2012
                                TupleField      *tuple, *tuplew;
 
2013
 
 
2014
                                for (j = 0; j < qres->num_total_rows; j++)
 
2015
                                {
 
2016
                                        oid = getOid(qres, j); 
 
2017
                                        getTid(qres, j, &blocknum, &offset);
 
2018
                                        for (k = stmt->rowset_start; k < limitrow; k++)
 
2019
                                        {
 
2020
                                                if (oid == getOid(res, k))
 
2021
                                                {
 
2022
                                                        l = GIdx2ResultIdx(k, stmt, res);
 
2023
                                                        tuple = res->backend_tuples + res->num_fields * l;
 
2024
                                                        tuplew = qres->backend_tuples + qres->num_fields * j;
 
2025
                                                        for (m = 0; m < res->num_fields; m++, tuple++, tuplew++)
 
2026
                                                        {
 
2027
                                                                if (tuple->len > 0 && tuple->value)
 
2028
                                                                        free(tuple->value);
 
2029
                                                                tuple->value = tuplew->value;
 
2030
                                                                tuple->len = tuplew->len;
 
2031
                                                                tuplew->value = NULL;
 
2032
                                                                tuplew->len = 0;
 
2033
                                                        }
 
2034
                                                        res->keyset[k].status &= ~CURS_NEEDS_REREAD;
 
2035
                                                        break;
 
2036
                                                }
 
2037
                                        }
 
2038
                                }
 
2039
                                QR_Destructor(qres);
 
2040
                        }
 
2041
                        if (rowc < 0)
 
2042
                                break;
 
2043
                        rowc = 0;
 
2044
                }
 
2045
                if (!rowc)
 
2046
                {
 
2047
                        if (!qval)
 
2048
                        {
 
2049
                                UInt4   allen;
 
2050
 
 
2051
                                lodlen = strlen(stmt->load_statement);
 
2052
                                allen = lodlen + 20 + 23 * 10;
 
2053
                                qval = malloc(allen);
 
2054
                        }
 
2055
                        memcpy(qval, stmt->load_statement, lodlen);
 
2056
                        sval = qval + lodlen;
 
2057
                        sval[0]= '\0';
 
2058
                        strcpy(sval, " where ctid in (");
 
2059
                        sval = strchr(sval, '\0');
 
2060
                }
 
2061
                if (0 != (res->keyset[i].status & CURS_NEEDS_REREAD))
 
2062
                {
 
2063
                        getTid(res, i, &blocknum, &offset);
 
2064
                        if (rowc)
 
2065
                                sprintf(sval, ", '(%u, %u)'", blocknum, offset);
 
2066
                        else
 
2067
                                sprintf(sval, "'(%u, %u)'", blocknum, offset);
 
2068
                        sval = strchr(sval, '\0');
 
2069
                        rowc++;
 
2070
                }
 
2071
        }
 
2072
        if (qval)
 
2073
                free(qval);
 
2074
        else
 
2075
                return SQL_SUCCESS;
 
2076
 
 
2077
        for (i = stmt->rowset_start; i < limitrow; i++)
 
2078
        {
 
2079
                if (0 != (res->keyset[i].status & CURS_NEEDS_REREAD))
 
2080
                {
 
2081
                        ret = SC_pos_reload(stmt, i, &qcount, FALSE);
 
2082
                        if (SQL_ERROR == ret)
 
2083
                        {
 
2084
                                break;
 
2085
                        }
 
2086
                        if (SQL_ROW_DELETED == (res->keyset[i].status & KEYSET_INFO_PUBLIC))
 
2087
                        {
 
2088
                                res->keyset[i].status |= CURS_OTHER_DELETED;
 
2089
                        }
 
2090
                        res->keyset[i].status &= ~CURS_NEEDS_REREAD;
 
2091
                }
 
2092
        }
 
2093
        return ret;
 
2094
}
 
2095
 
 
2096
RETCODE         SQL_API
 
2097
SC_pos_newload(StatementClass *stmt, UInt4 oid, BOOL tidRef)
 
2098
{
 
2099
        int                     i;
 
2100
        QResultClass *res, *qres;
 
2101
        RETCODE         ret = SQL_ERROR;
 
2102
 
 
2103
        mylog("positioned new ti=%x\n", stmt->ti);
 
2104
        if (!(res = SC_get_Curres(stmt)))
 
2105
        {
 
2106
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_newload.");
 
2107
                return SQL_ERROR;
 
2108
        }
 
2109
        if (!stmt->ti)
 
2110
                parse_statement(stmt);  /* not preferable */
 
2111
        if (!stmt->updatable)
 
2112
        {
 
2113
                stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
 
2114
                SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only");
 
2115
                return SQL_ERROR;
 
2116
        }
 
2117
        if (qres = positioned_load(stmt, tidRef ? USE_INSERTED_TID : 0, oid, NULL), qres)
 
2118
        {
 
2119
                TupleField *tupleo, *tuplen;
 
2120
                int             count = QR_get_num_backend_tuples(qres);
 
2121
 
 
2122
                QR_set_position(qres, 0);
 
2123
                if (count == 1)
 
2124
                {
 
2125
                        int     effective_fields = res->num_fields;
 
2126
                        int     tuple_size;
 
2127
 
 
2128
                        tuplen = qres->tupleField;
 
2129
                        if (res->haskeyset &&
 
2130
                            res->num_total_rows >= res->count_keyset_allocated)
 
2131
                        {
 
2132
 
 
2133
                                if (!res->count_keyset_allocated)
 
2134
                                        tuple_size = TUPLE_MALLOC_INC;
 
2135
                                else
 
2136
                                        tuple_size = res->count_keyset_allocated * 2;
 
2137
                                res->keyset = (KeySet *) realloc(res->keyset, sizeof(KeySet) * tuple_size);     
 
2138
                                res->count_keyset_allocated = tuple_size;
 
2139
                        }
 
2140
                        KeySetSet(tuplen, qres->num_fields, res->keyset + res->num_total_rows);
 
2141
 
 
2142
                        if (res->num_total_rows == ResultIdx2GIdx(res->num_backend_rows, stmt, res))
 
2143
                        {
 
2144
                                if (res->num_backend_rows >= res->count_backend_allocated)
 
2145
                                {
 
2146
                                        if (!res->count_backend_allocated)
 
2147
                                                tuple_size = TUPLE_MALLOC_INC;
 
2148
                                        else
 
2149
                                                tuple_size = res->count_backend_allocated * 2;
 
2150
                                        res->backend_tuples = (TupleField *) realloc(
 
2151
                                                res->backend_tuples,
 
2152
                                                res->num_fields * sizeof(TupleField) * tuple_size);
 
2153
                                        if (!res->backend_tuples)
 
2154
                                        {
 
2155
                                                SC_set_error(stmt, res->status = PGRES_FATAL_ERROR, "Out of memory while reading tuples.");
 
2156
                                                QR_Destructor(qres);
 
2157
                                                return SQL_ERROR;
 
2158
                                        }
 
2159
                                        res->count_backend_allocated = tuple_size;
 
2160
                                }
 
2161
                                tupleo = res->backend_tuples + res->num_fields * res->num_backend_rows;
 
2162
                                for (i = 0; i < effective_fields; i++)
 
2163
                                {
 
2164
                                        tupleo[i].len = tuplen[i].len;
 
2165
                                        tuplen[i].len = 0;
 
2166
                                        tupleo[i].value = tuplen[i].value;
 
2167
                                        tuplen[i].value = NULL;
 
2168
                                }
 
2169
                                for (; i < res->num_fields; i++)
 
2170
                                {
 
2171
                                        tupleo[i].len = 0;
 
2172
                                        tupleo[i].value = NULL;
 
2173
                                }
 
2174
                                res->num_backend_rows++;
 
2175
                        }
 
2176
                        res->num_total_rows++;
 
2177
                        ret = SQL_SUCCESS;
 
2178
                }
 
2179
                else if (0 == count)
 
2180
                        ret = SQL_NO_DATA_FOUND;
 
2181
                else
 
2182
                {
 
2183
                        SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the driver cound't identify inserted rows");
 
2184
                        ret = SQL_ERROR;
 
2185
                }
 
2186
                QR_Destructor(qres);
 
2187
                /* stmt->currTuple = stmt->rowset_start + ridx; */
 
2188
        }
 
2189
        return ret;
 
2190
}
 
2191
 
 
2192
static RETCODE SQL_API
 
2193
irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt, UWORD irow, UDWORD global_ridx)
 
2194
{
 
2195
        if (ret != SQL_ERROR)
 
2196
        {
 
2197
                int                     updcnt;
 
2198
                const char *cmdstr = QR_get_command(SC_get_Curres(ustmt));
 
2199
 
 
2200
                if (cmdstr &&
 
2201
                        sscanf(cmdstr, "UPDATE %d", &updcnt) == 1)
 
2202
                {
 
2203
                        if (updcnt == 1)
 
2204
                                ret = SC_pos_reload(stmt, global_ridx, (UWORD *) 0, TRUE);
 
2205
                        else if (updcnt == 0)
 
2206
                        {
 
2207
                                SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the content was changed before updation");
 
2208
                                ret = SQL_ERROR;
 
2209
                                if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
 
2210
                                        SC_pos_reload(stmt, global_ridx, (UWORD *) 0, FALSE);
 
2211
                        }
 
2212
                        else
 
2213
                                ret = SQL_ERROR;
 
2214
                }
 
2215
                else
 
2216
                        ret = SQL_ERROR;
 
2217
                if (ret == SQL_ERROR && SC_get_errornumber(stmt) == 0)
 
2218
                {
 
2219
                        SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND, "SetPos update return error");
 
2220
                }
 
2221
        }
 
2222
        return ret;
 
2223
}
 
2224
RETCODE
 
2225
SC_pos_update(StatementClass *stmt,
 
2226
                          UWORD irow, UDWORD global_ridx)
 
2227
{
 
2228
        int                     i,
 
2229
                                num_cols,
 
2230
                                upd_cols;
 
2231
        QResultClass *res;
 
2232
        ConnectionClass *conn = SC_get_conn(stmt);
 
2233
        ARDFields       *opts = SC_get_ARD(stmt);
 
2234
        IRDFields       *irdflds = SC_get_IRD(stmt);
 
2235
        BindInfoClass *bindings = opts->bindings;
 
2236
        FIELD_INFO      **fi = SC_get_IRD(stmt)->fi;
 
2237
        char            updstr[4096];
 
2238
        RETCODE         ret;
 
2239
        UInt4   oid, offset, blocknum;
 
2240
        UInt2   pgoffset;
 
2241
        Int4    *used, bind_size = opts->bind_size;
 
2242
 
 
2243
        if (!(res = SC_get_Curres(stmt)))
 
2244
        {
 
2245
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_update.");
 
2246
                return SQL_ERROR;
 
2247
        }
 
2248
        mylog("POS UPDATE %d+%d fi=%x ti=%x\n", irow, res->base, fi, stmt->ti);
 
2249
        if (!stmt->ti)
 
2250
                parse_statement(stmt);  /* not preferable */
 
2251
        if (!stmt->updatable)
 
2252
        {
 
2253
                stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
 
2254
                SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only");
 
2255
                return SQL_ERROR;
 
2256
        }
 
2257
        if (!(oid = getOid(res, global_ridx)))
 
2258
        {
 
2259
                SC_set_error(stmt, STMT_INVALID_CURSOR_POSITION, "The row is already deleted ?");
 
2260
                return SQL_ERROR;
 
2261
        }
 
2262
        getTid(res, global_ridx, &blocknum, &pgoffset);
 
2263
 
 
2264
        if (stmt->ti[0]->schema[0])
 
2265
                sprintf(updstr, "update \"%s\".\"%s\" set", stmt->ti[0]->schema, stmt->ti[0]->name);
 
2266
        else
 
2267
                sprintf(updstr, "update \"%s\" set", stmt->ti[0]->name);
 
2268
        num_cols = irdflds->nfields;
 
2269
        offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
 
2270
        for (i = upd_cols = 0; i < num_cols; i++)
 
2271
        {
 
2272
                if (used = bindings[i].used, used != NULL)
 
2273
                {
 
2274
                        used += (offset >> 2);
 
2275
                        if (bind_size > 0)
 
2276
                                used += (bind_size * irow / 4);
 
2277
                        else    
 
2278
                                used += irow; 
 
2279
                        mylog("%d used=%d,%x\n", i, *used, used);
 
2280
                        if (*used != SQL_IGNORE && fi[i]->updatable)
 
2281
                        {
 
2282
                                if (upd_cols)
 
2283
                                        sprintf(updstr, "%s, \"%s\" = ?", updstr, fi[i]->name);
 
2284
                                else
 
2285
                                        sprintf(updstr, "%s \"%s\" = ?", updstr, fi[i]->name);
 
2286
                                upd_cols++;
 
2287
                        }
 
2288
                }
 
2289
                else
 
2290
                        mylog("%d null bind\n", i);
 
2291
        }
 
2292
        if (upd_cols > 0)
 
2293
        {
 
2294
                HSTMT           hstmt;
 
2295
                int                     j;
 
2296
                int                     res_cols = QR_NumResultCols(res);
 
2297
                ConnInfo        *ci = &(conn->connInfo);
 
2298
                StatementClass *qstmt;
 
2299
                APDFields       *apdopts;
 
2300
                Int4            fieldtype = 0;
 
2301
 
 
2302
                /*sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr,
 
2303
                                tidval, oidval);*/
 
2304
                sprintf(updstr, "%s where ctid = '(%u, %u)' and oid = %u", updstr,
 
2305
                                blocknum, pgoffset, oid);
 
2306
                mylog("updstr=%s\n", updstr);
 
2307
                if (PGAPI_AllocStmt(conn, &hstmt) != SQL_SUCCESS)
 
2308
                {
 
2309
                        SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "internal AllocStmt error");
 
2310
                        return SQL_ERROR;
 
2311
                }
 
2312
                qstmt = (StatementClass *) hstmt;
 
2313
                apdopts = SC_get_APD(qstmt);
 
2314
                apdopts->param_bind_type = opts->bind_size;
 
2315
                apdopts->param_offset_ptr = opts->row_offset_ptr;
 
2316
                for (i = j = 0; i < num_cols; i++)
 
2317
                {
 
2318
                        if (used = bindings[i].used, used != NULL)
 
2319
                        {
 
2320
                                used += (offset >> 2);
 
2321
                                if (bind_size > 0)
 
2322
                                        used += (bind_size * irow / 4);
 
2323
                                else
 
2324
                                        used += irow;
 
2325
                                mylog("%d used=%d\n", i, *used);
 
2326
                                if (*used != SQL_IGNORE && fi[i]->updatable)
 
2327
                                {
 
2328
                                        fieldtype = QR_get_field_type(res, i);
 
2329
                                        PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++j,
 
2330
                                                                 SQL_PARAM_INPUT, bindings[i].returntype,
 
2331
                                          pgtype_to_concise_type(stmt, fieldtype),
 
2332
                                                                                                                        fi[i]->column_size > 0 ? fi[i]->column_size : pgtype_column_size(stmt, fieldtype, i, ci->drivers.unknown_sizes),
 
2333
                                                                        (SQLSMALLINT) fi[i]->decimal_digits,
 
2334
                                                                                bindings[i].buffer,
 
2335
                                                                                bindings[i].buflen,
 
2336
                                                                                bindings[i].used);
 
2337
                                }
 
2338
                        }
 
2339
                }
 
2340
                qstmt->exec_start_row = qstmt->exec_end_row = irow; 
 
2341
                ret = PGAPI_ExecDirect(hstmt, updstr, strlen(updstr));
 
2342
                if (ret == SQL_ERROR)
 
2343
                {
 
2344
                        SC_error_copy(stmt, qstmt);
 
2345
                }
 
2346
                else if (ret == SQL_NEED_DATA)  /* must be fixed */
 
2347
                {
 
2348
                        stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
 
2349
                        SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "SetPos with data_at_exec not yet supported");
 
2350
                        ret = SQL_ERROR;
 
2351
                }
 
2352
                ret = irow_update(ret, stmt, qstmt, irow, global_ridx);
 
2353
                PGAPI_FreeStmt(hstmt, SQL_DROP);
 
2354
        }
 
2355
        else
 
2356
        {
 
2357
                ret = SQL_SUCCESS_WITH_INFO;
 
2358
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "update list null");
 
2359
        }
 
2360
        if (SQL_SUCCESS == ret && res->keyset)
 
2361
        {
 
2362
                if (CC_is_in_trans(conn))
 
2363
                {
 
2364
                        res->keyset[global_ridx].status |= (SQL_ROW_UPDATED  | CURS_SELF_UPDATING);
 
2365
                }
 
2366
                else
 
2367
                        res->keyset[global_ridx].status |= (SQL_ROW_UPDATED  | CURS_SELF_UPDATED);
 
2368
        }
 
2369
#if (ODBCVER >= 0x0300)
 
2370
        if (irdflds->rowStatusArray)
 
2371
        {
 
2372
                switch (ret)
 
2373
                {
 
2374
                        case SQL_SUCCESS:
 
2375
                                irdflds->rowStatusArray[irow] = SQL_ROW_UPDATED;
 
2376
                                break;
 
2377
                        default:
 
2378
                                irdflds->rowStatusArray[irow] = ret;
 
2379
                }
 
2380
        }
 
2381
#endif /* ODBCVER */
 
2382
 
 
2383
        return ret;
 
2384
}
 
2385
RETCODE
 
2386
SC_pos_delete(StatementClass *stmt,
 
2387
                          UWORD irow, UDWORD global_ridx)
 
2388
{
 
2389
        UWORD           offset;
 
2390
        QResultClass *res, *qres;
 
2391
        ConnectionClass *conn = SC_get_conn(stmt);
 
2392
        ARDFields       *opts = SC_get_ARD(stmt);
 
2393
        IRDFields       *irdflds = SC_get_IRD(stmt);
 
2394
        BindInfoClass *bindings = opts->bindings;
 
2395
        char            dltstr[4096];
 
2396
        RETCODE         ret;
 
2397
        UInt4           oid, blocknum, qflag;
 
2398
 
 
2399
        mylog("POS DELETE ti=%x\n", stmt->ti);
 
2400
        if (!(res = SC_get_Curres(stmt)))
 
2401
        {
 
2402
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_delete.");
 
2403
                return SQL_ERROR;
 
2404
        }
 
2405
        if (!stmt->ti)
 
2406
                parse_statement(stmt);  /* not preferable */
 
2407
        if (!stmt->updatable)
 
2408
        {
 
2409
                stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
 
2410
                SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only");
 
2411
                return SQL_ERROR;
 
2412
        }
 
2413
        if (!(oid = getOid(res, global_ridx)))
 
2414
        {
 
2415
                SC_set_error(stmt, STMT_INVALID_CURSOR_POSITION, "The row is already deleted ?");
 
2416
                return SQL_ERROR;
 
2417
        }
 
2418
        getTid(res, global_ridx, &blocknum, &offset);
 
2419
        /*sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s",*/
 
2420
        if (stmt->ti[0]->schema[0])
 
2421
                sprintf(dltstr, "delete from \"%s\".\"%s\" where ctid = '(%u, %u)' and oid = %u",
 
2422
                stmt->ti[0]->schema, stmt->ti[0]->name, blocknum, offset, oid);
 
2423
        else
 
2424
                sprintf(dltstr, "delete from \"%s\" where ctid = '(%u, %u)' and oid = %u",
 
2425
                        stmt->ti[0]->name, blocknum, offset, oid);
 
2426
 
 
2427
        mylog("dltstr=%s\n", dltstr);
 
2428
        qflag = CLEAR_RESULT_ON_ABORT;
 
2429
        if (!stmt->internal && !CC_is_in_trans(conn) &&
 
2430
                 (!CC_is_in_autocommit(conn)))
 
2431
                qflag |= GO_INTO_TRANSACTION;
 
2432
        qres = CC_send_query(conn, dltstr, NULL, qflag);
 
2433
        ret = SQL_SUCCESS;
 
2434
        if (qres && QR_command_maybe_successful(qres))
 
2435
        {
 
2436
                int                     dltcnt;
 
2437
                const char *cmdstr = QR_get_command(qres);
 
2438
 
 
2439
                if (cmdstr &&
 
2440
                        sscanf(cmdstr, "DELETE %d", &dltcnt) == 1)
 
2441
                {
 
2442
                        if (dltcnt == 1)
 
2443
                                SC_pos_reload(stmt, global_ridx, (UWORD *) 0, TRUE);
 
2444
                        else if (dltcnt == 0)
 
2445
                        {
 
2446
                                SC_set_error(stmt, STMT_ROW_VERSION_CHANGED, "the content was changed before deletion");
 
2447
                                ret = SQL_ERROR;
 
2448
                                if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
 
2449
                                        SC_pos_reload(stmt, global_ridx, (UWORD *) 0, FALSE);
 
2450
                        }
 
2451
                        else
 
2452
                                ret = SQL_ERROR;
 
2453
                }
 
2454
                else
 
2455
                        ret = SQL_ERROR;
 
2456
        }
 
2457
        else
 
2458
                ret = SQL_ERROR;
 
2459
        if (ret == SQL_ERROR && SC_get_errornumber(stmt) == 0)
 
2460
        {
 
2461
                SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND, "SetPos delete return error");
 
2462
        }
 
2463
        if (qres)
 
2464
                QR_Destructor(qres);
 
2465
        if (SQL_SUCCESS == ret && res->keyset)
 
2466
        {
 
2467
                AddDeleted(res, global_ridx);
 
2468
                if (CC_is_in_trans(conn))
 
2469
                {
 
2470
                        res->keyset[global_ridx].status |= (SQL_ROW_DELETED | CURS_SELF_DELETING);
 
2471
                }
 
2472
                else
 
2473
                        res->keyset[global_ridx].status |= (SQL_ROW_DELETED | CURS_SELF_DELETED);
 
2474
        }
 
2475
#if (ODBCVER >= 0x0300)
 
2476
        if (irdflds->rowStatusArray)
 
2477
        {
 
2478
                switch (ret)
 
2479
                {
 
2480
                        case SQL_SUCCESS:
 
2481
                                irdflds->rowStatusArray[irow] = SQL_ROW_DELETED;
 
2482
                                break;
 
2483
                        default:
 
2484
                                irdflds->rowStatusArray[irow] = ret;
 
2485
                }
 
2486
        }
 
2487
#endif /* ODBCVER */
 
2488
        return ret;
 
2489
}
 
2490
 
 
2491
static RETCODE SQL_API
 
2492
irow_insert(RETCODE ret, StatementClass *stmt, StatementClass *istmt, int addpos)
 
2493
{
 
2494
        if (ret != SQL_ERROR)
 
2495
        {
 
2496
                int             addcnt;
 
2497
                UInt4           oid;
 
2498
                ARDFields       *opts = SC_get_ARD(stmt);
 
2499
                QResultClass    *ires = SC_get_Curres(istmt);
 
2500
                const char *cmdstr;
 
2501
                BindInfoClass   *bookmark;
 
2502
 
 
2503
                cmdstr = QR_get_command((ires->next ? ires->next : ires));
 
2504
                if (cmdstr &&
 
2505
                        sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 &&
 
2506
                        addcnt == 1)
 
2507
                {
 
2508
                        ConnectionClass *conn = SC_get_conn(stmt);
 
2509
                        RETCODE qret;
 
2510
 
 
2511
                        qret = SQL_NO_DATA_FOUND;
 
2512
                        if (PG_VERSION_GE(conn, 7.2))
 
2513
                        {
 
2514
                                qret = SC_pos_newload(stmt, oid, TRUE);
 
2515
                                if (SQL_ERROR == qret)
 
2516
                                        return qret;
 
2517
                        }
 
2518
                        if (SQL_NO_DATA_FOUND == qret)
 
2519
                        {
 
2520
                                qret = SC_pos_newload(stmt, oid, FALSE);
 
2521
                                if (SQL_ERROR == qret)
 
2522
                                        return qret;
 
2523
                        }
 
2524
                        bookmark = opts->bookmark;
 
2525
                        if (bookmark->buffer)
 
2526
                        {
 
2527
                                char    buf[32];
 
2528
                                UInt4   offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0;
 
2529
 
 
2530
                                sprintf(buf, "%lu", addpos + 1);
 
2531
                                copy_and_convert_field(stmt,
 
2532
                                        PG_TYPE_INT4,
 
2533
                                        buf,
 
2534
                                        bookmark->returntype,
 
2535
                                        bookmark->buffer + offset,
 
2536
                                        bookmark->buflen,
 
2537
                                        bookmark->used ? bookmark->used
 
2538
                                        + (offset >> 2) : NULL);
 
2539
                        }
 
2540
                }
 
2541
                else
 
2542
                {
 
2543
                        SC_set_error(stmt, STMT_ERROR_TAKEN_FROM_BACKEND, "SetPos insert return error");
 
2544
                }
 
2545
        }
 
2546
        return ret;
 
2547
}
 
2548
RETCODE
 
2549
SC_pos_add(StatementClass *stmt,
 
2550
                   UWORD irow)
 
2551
{
 
2552
        int                     num_cols,
 
2553
                                add_cols,
 
2554
                                i;
 
2555
        HSTMT           hstmt;
 
2556
        StatementClass *qstmt;
 
2557
        ConnectionClass *conn;
 
2558
        ConnInfo        *ci;
 
2559
        QResultClass *res;
 
2560
        ARDFields       *opts = SC_get_ARD(stmt);
 
2561
        IRDFields       *irdflds = SC_get_IRD(stmt);
 
2562
        APDFields       *apdopts;
 
2563
        BindInfoClass *bindings = opts->bindings;
 
2564
        FIELD_INFO      **fi = SC_get_IRD(stmt)->fi;
 
2565
        char            addstr[4096];
 
2566
        RETCODE         ret;
 
2567
        UInt4           offset;
 
2568
        Int4            *used, bind_size = opts->bind_size;
 
2569
        Int4            fieldtype;
 
2570
 
 
2571
        mylog("POS ADD fi=%x ti=%x\n", fi, stmt->ti);
 
2572
        if (!(res = SC_get_Curres(stmt)))
 
2573
        {
 
2574
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in SC_pos_add.");
 
2575
                return SQL_ERROR;
 
2576
        }
 
2577
        if (!stmt->ti)
 
2578
                parse_statement(stmt);  /* not preferable */
 
2579
        if (!stmt->updatable)
 
2580
        {
 
2581
                stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
 
2582
                SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER, "the statement is read-only");
 
2583
                return SQL_ERROR;
 
2584
        }
 
2585
        num_cols = irdflds->nfields;
 
2586
        conn = SC_get_conn(stmt);
 
2587
        if (stmt->ti[0]->schema[0])
 
2588
                sprintf(addstr, "insert into \"%s\".\"%s\" (", stmt->ti[0]->schema, stmt->ti[0]->name);
 
2589
        else
 
2590
                sprintf(addstr, "insert into \"%s\" (", stmt->ti[0]->name);
 
2591
        if (PGAPI_AllocStmt(conn, &hstmt) != SQL_SUCCESS)
 
2592
        {
 
2593
                SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "internal AllocStmt error");
 
2594
                return SQL_ERROR;
 
2595
        }
 
2596
        if (opts->row_offset_ptr)
 
2597
                offset = *opts->row_offset_ptr;
 
2598
        else
 
2599
                offset = 0;
 
2600
        qstmt = (StatementClass *) hstmt;
 
2601
        apdopts = SC_get_APD(qstmt);
 
2602
        apdopts->param_bind_type = opts->bind_size;
 
2603
        apdopts->param_offset_ptr = opts->row_offset_ptr;
 
2604
        ci = &(conn->connInfo);
 
2605
        for (i = add_cols = 0; i < num_cols; i++)
 
2606
        {
 
2607
                if (used = bindings[i].used, used != NULL)
 
2608
                {
 
2609
                        used += (offset >> 2);
 
2610
                        if (bind_size > 0)
 
2611
                                used += (bind_size * irow / 4);
 
2612
                        else
 
2613
                                used += irow;
 
2614
                        mylog("%d used=%d\n", i, *used);
 
2615
                        if (*used != SQL_IGNORE && fi[i]->updatable)
 
2616
                        {
 
2617
                                fieldtype = QR_get_field_type(res, i);
 
2618
                                if (add_cols)
 
2619
                                        sprintf(addstr, "%s, \"%s\"", addstr, fi[i]->name);
 
2620
                                else
 
2621
                                        sprintf(addstr, "%s\"%s\"", addstr, fi[i]->name);
 
2622
                                PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++add_cols,
 
2623
                                                                 SQL_PARAM_INPUT, bindings[i].returntype,
 
2624
                                          pgtype_to_concise_type(stmt, fieldtype),
 
2625
                                                                                                                        fi[i]->column_size > 0 ? fi[i]->column_size : pgtype_column_size(stmt, fieldtype, i, ci->drivers.unknown_sizes),
 
2626
                                                                        (SQLSMALLINT) fi[i]->decimal_digits,
 
2627
                                                                        bindings[i].buffer,
 
2628
                                                                        bindings[i].buflen,
 
2629
                                                                        bindings[i].used);
 
2630
                        }
 
2631
                }
 
2632
                else
 
2633
                        mylog("%d null bind\n", i);
 
2634
        }
 
2635
        if (add_cols > 0)
 
2636
        {
 
2637
                int     brow_save;
 
2638
 
 
2639
                sprintf(addstr, "%s) values (", addstr);
 
2640
                for (i = 0; i < add_cols; i++)
 
2641
                {
 
2642
                        if (i)
 
2643
                                strcat(addstr, ", ?");
 
2644
                        else
 
2645
                                strcat(addstr, "?");
 
2646
                }
 
2647
                strcat(addstr, ")");
 
2648
                mylog("addstr=%s\n", addstr);
 
2649
                qstmt->exec_start_row = qstmt->exec_end_row = irow; 
 
2650
                ret = PGAPI_ExecDirect(hstmt, addstr, strlen(addstr));
 
2651
                if (ret == SQL_ERROR)
 
2652
                {
 
2653
                        SC_error_copy(stmt, qstmt);
 
2654
                }
 
2655
                else if (ret == SQL_NEED_DATA)          /* must be fixed */
 
2656
                {
 
2657
                        stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
 
2658
                        SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "SetPos with data_at_exec not yet supported");
 
2659
                        ret = SQL_ERROR;
 
2660
                }
 
2661
                brow_save = stmt->bind_row; 
 
2662
                stmt->bind_row = irow; 
 
2663
                ret = irow_insert(ret, stmt, qstmt, res->num_total_rows);
 
2664
                stmt->bind_row = brow_save; 
 
2665
        }
 
2666
        else
 
2667
        {
 
2668
                ret = SQL_SUCCESS_WITH_INFO;
 
2669
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "insert list null");
 
2670
        }
 
2671
        PGAPI_FreeStmt(hstmt, SQL_DROP);
 
2672
        if (SQL_SUCCESS == ret && res->keyset)
 
2673
        {
 
2674
                int     global_ridx = res->num_total_rows - 1;
 
2675
                if (CC_is_in_trans(conn))
 
2676
                {
 
2677
 
 
2678
                        AddRollback(conn, res, global_ridx, NULL);
 
2679
                        res->keyset[global_ridx].status |= (SQL_ROW_ADDED | CURS_SELF_ADDING);
 
2680
                }
 
2681
                else
 
2682
                        res->keyset[global_ridx].status |= (SQL_ROW_ADDED | CURS_SELF_ADDED);
 
2683
        }
 
2684
#if (ODBCVER >= 0x0300)
 
2685
        if (irdflds->rowStatusArray)
 
2686
        {
 
2687
                switch (ret)
 
2688
                {
 
2689
                        case SQL_SUCCESS:
 
2690
                                irdflds->rowStatusArray[irow] = SQL_ROW_ADDED;
 
2691
                                break;
 
2692
                        default:
 
2693
                                irdflds->rowStatusArray[irow] = ret;
 
2694
                }
 
2695
        }
 
2696
#endif /* ODBCVER */
 
2697
 
 
2698
        return ret;
 
2699
}
 
2700
 
 
2701
/*
 
2702
 *      Stuff for updatable cursors end.
 
2703
 */
 
2704
#endif   /* DRIVER_CURSOR_IMPLEMENT */
 
2705
 
 
2706
RETCODE
 
2707
SC_pos_refresh(StatementClass *stmt, UWORD irow , UDWORD global_ridx)
 
2708
{
 
2709
        RETCODE ret;
 
2710
#if (ODBCVER >= 0x0300)
 
2711
        IRDFields       *irdflds = SC_get_IRD(stmt);
 
2712
#endif /* ODBCVER */
 
2713
        /* save the last_fetch_count */
 
2714
        int             last_fetch = stmt->last_fetch_count;
 
2715
        int             last_fetch2 = stmt->last_fetch_count_include_ommitted;
 
2716
        int             bind_save = stmt->bind_row;
 
2717
 
 
2718
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
2719
        if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN)
 
2720
                SC_pos_reload(stmt, global_ridx, (UWORD *) 0, FALSE);
 
2721
#endif   /* DRIVER_CURSOR_IMPLEMENT */
 
2722
        stmt->bind_row = irow;
 
2723
        ret = SC_fetch(stmt);
 
2724
        /* restore the last_fetch_count */
 
2725
        stmt->last_fetch_count = last_fetch;
 
2726
        stmt->last_fetch_count_include_ommitted = last_fetch2;
 
2727
        stmt->bind_row = bind_save;
 
2728
#if (ODBCVER >= 0x0300)
 
2729
        if (irdflds->rowStatusArray)
 
2730
        {
 
2731
                switch (ret)
 
2732
                {
 
2733
                        case SQL_ERROR:
 
2734
                                irdflds->rowStatusArray[irow] = SQL_ROW_ERROR;
 
2735
                                break;
 
2736
                        case SQL_SUCCESS:
 
2737
                                irdflds->rowStatusArray[irow] = SQL_ROW_SUCCESS;
 
2738
                                break;
 
2739
                        case SQL_SUCCESS_WITH_INFO:
 
2740
                        default:
 
2741
                                irdflds->rowStatusArray[irow] = ret;
 
2742
                                break;
 
2743
                }
 
2744
        }
 
2745
#endif /* ODBCVER */
 
2746
 
 
2747
        return SQL_SUCCESS;
 
2748
}
 
2749
 
 
2750
/*
 
2751
 *      This positions the cursor within a rowset, that was positioned using SQLExtendedFetch.
 
2752
 *      This will be useful (so far) only when using SQLGetData after SQLExtendedFetch.
 
2753
 */
 
2754
RETCODE         SQL_API
 
2755
PGAPI_SetPos(
 
2756
                         HSTMT hstmt,
 
2757
                         UWORD irow,
 
2758
                         UWORD fOption,
 
2759
                         UWORD fLock)
 
2760
{
 
2761
        CSTR func = "PGAPI_SetPos";
 
2762
        RETCODE ret;
 
2763
        StatementClass *stmt = (StatementClass *) hstmt;
 
2764
        ConnectionClass *conn = SC_get_conn(stmt);
 
2765
        QResultClass *res;
 
2766
        int             num_cols, i, start_row, end_row, processed, ridx;
 
2767
        UWORD           nrow;
 
2768
        ARDFields       *opts;
 
2769
        BindInfoClass *bindings;
 
2770
        UDWORD          global_ridx, rowsetSize;
 
2771
        BOOL            auto_commit_needed = FALSE;
 
2772
 
 
2773
        if (!stmt)
 
2774
        {
 
2775
                SC_log_error(func, "", NULL);
 
2776
                return SQL_INVALID_HANDLE;
 
2777
        }
 
2778
 
 
2779
        opts = SC_get_ARD(stmt);
 
2780
        bindings = opts->bindings;
 
2781
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
2782
        mylog("%s fOption=%d irow=%d lock=%d currt=%d\n", func, fOption, irow, fLock, stmt->currTuple);
 
2783
        if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
 
2784
                ;
 
2785
        else
 
2786
#endif   /* DRIVER_CURSOR_IMPLEMENT */
 
2787
        if (fOption != SQL_POSITION && fOption != SQL_REFRESH)
 
2788
        {
 
2789
                SC_set_error(stmt, STMT_NOT_IMPLEMENTED_ERROR, "Only SQL_POSITION/REFRESH is supported for PGAPI_SetPos");
 
2790
                SC_log_error(func, "", stmt);
 
2791
                return SQL_ERROR;
 
2792
        }
 
2793
 
 
2794
        if (!(res = SC_get_Curres(stmt)))
 
2795
        {
 
2796
                SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR, "Null statement result in PGAPI_SetPos.");
 
2797
                SC_log_error(func, "", stmt);
 
2798
                return SQL_ERROR;
 
2799
        }
 
2800
 
 
2801
#if (ODBCVER >= 0x0300)
 
2802
        rowsetSize = (stmt->transition_status == 7 ? opts->size_of_rowset_odbc2 : opts->size_of_rowset);
 
2803
#else
 
2804
        rowsetSize = opts->size_of_rowset_odbc2;
 
2805
#endif /* ODBCVER */
 
2806
        if (irow == 0) /* bulk operation */
 
2807
        {
 
2808
                if (SQL_POSITION == fOption)
 
2809
                {
 
2810
                        SC_set_error(stmt, STMT_INVALID_CURSOR_POSITION, "Bulk Position operations not allowed.");
 
2811
                        SC_log_error(func, "", stmt);
 
2812
                        return SQL_ERROR;
 
2813
                }
 
2814
                start_row = 0;
 
2815
                end_row = rowsetSize - 1;
 
2816
        }
 
2817
        else
 
2818
        {
 
2819
                if (SQL_ADD != fOption && irow > stmt->last_fetch_count)
 
2820
                {
 
2821
                        SC_set_error(stmt, STMT_ROW_OUT_OF_RANGE, "Row value out of range");
 
2822
                        SC_log_error(func, "", stmt);
 
2823
                        return SQL_ERROR;
 
2824
                }
 
2825
                start_row = end_row = irow - 1;
 
2826
        }
 
2827
 
 
2828
        num_cols = QR_NumResultCols(res);
 
2829
        /* Reset for SQLGetData */
 
2830
        if (bindings)
 
2831
                for (i = 0; i < num_cols; i++)
 
2832
                        bindings[i].data_left = -1;
 
2833
        ret = SQL_SUCCESS;
 
2834
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
2835
        switch (fOption)
 
2836
        {
 
2837
                case SQL_UPDATE:
 
2838
                case SQL_DELETE:
 
2839
                case SQL_ADD:
 
2840
                        if (auto_commit_needed = CC_is_in_autocommit(conn), auto_commit_needed)
 
2841
                                PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
 
2842
                        break;
 
2843
        }
 
2844
#endif   /* DRIVER_CURSOR_IMPLEMENT */
 
2845
        ridx = -1;
 
2846
        for (i = nrow = 0, processed = 0; nrow <= end_row; i++)
 
2847
        {
 
2848
                global_ridx = RowIdx2GIdx(i, stmt);
 
2849
                if (SQL_ADD != fOption)
 
2850
                {
 
2851
                        if ((int) global_ridx >= res->num_total_rows)
 
2852
                                break;
 
2853
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
2854
                        if (res->keyset) /* the row may be deleted and not in the rowset */
 
2855
                        {
 
2856
                                if (0 == (res->keyset[global_ridx].status & CURS_IN_ROWSET))
 
2857
                                        continue;
 
2858
                        }
 
2859
#endif   /* DRIVER_CURSOR_IMPLEMENT */
 
2860
                }
 
2861
                if (nrow < start_row)
 
2862
                {
 
2863
                        nrow++;
 
2864
                        continue;
 
2865
                }
 
2866
                ridx = nrow;
 
2867
#if (ODBCVER >= 0x0300)
 
2868
                if (0 != irow || !opts->row_operation_ptr || opts->row_operation_ptr[nrow] == SQL_ROW_PROCEED)
 
2869
                {
 
2870
#endif /* ODBCVER */
 
2871
                        switch (fOption)
 
2872
                        {
 
2873
#ifdef  DRIVER_CURSOR_IMPLEMENT
 
2874
                                case SQL_UPDATE:
 
2875
                                        ret = SC_pos_update(stmt, nrow, global_ridx);
 
2876
                                        break;
 
2877
                                case SQL_DELETE:
 
2878
                                        ret = SC_pos_delete(stmt, nrow, global_ridx);
 
2879
                                        break;
 
2880
                                case SQL_ADD:
 
2881
                                        ret = SC_pos_add(stmt, nrow);
 
2882
                                        break;
 
2883
#endif   /* DRIVER_CURSOR_IMPLEMENT */
 
2884
                                case SQL_REFRESH:
 
2885
                                        ret = SC_pos_refresh(stmt, nrow, global_ridx);
 
2886
                                        break;
 
2887
                        }
 
2888
                        processed++;
 
2889
                        if (SQL_ERROR == ret)
 
2890
                                break;
 
2891
#if (ODBCVER >= 0x0300)
 
2892
                }
 
2893
#endif /* ODBCVER */
 
2894
                nrow++;
 
2895
        }
 
2896
        if (SQL_ERROR == ret)
 
2897
                CC_abort(conn);
 
2898
        if (auto_commit_needed)
 
2899
                PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON);
 
2900
        if (irow > 0)
 
2901
        {
 
2902
                if (SQL_ADD != fOption && ridx >= 0) /* for SQLGetData */
 
2903
                { 
 
2904
                        stmt->currTuple = RowIdx2GIdx(ridx, stmt);
 
2905
                        QR_set_position(res, ridx);
 
2906
                }
 
2907
        }
 
2908
        else if (SC_get_IRD(stmt)->rowsFetched)
 
2909
                *(SC_get_IRD(stmt)->rowsFetched) = processed;
 
2910
        res->recent_processed_row_count = stmt->diag_row_count = processed;
 
2911
        return ret; 
 
2912
}
 
2913
 
 
2914
 
 
2915
/*              Sets options that control the behavior of cursors. */
 
2916
RETCODE         SQL_API
 
2917
PGAPI_SetScrollOptions( HSTMT hstmt,
 
2918
                                UWORD fConcurrency,
 
2919
                                SDWORD crowKeyset,
 
2920
                                UWORD crowRowset)
 
2921
{
 
2922
        CSTR func = "PGAPI_SetScrollOptions";
 
2923
        StatementClass *stmt = (StatementClass *) hstmt;
 
2924
 
 
2925
        mylog("PGAPI_SetScrollOptions fConcurrency=%d crowKeyset=%d crowRowset=%d\n",
 
2926
                  fConcurrency, crowKeyset, crowRowset);
 
2927
        SC_set_error(stmt, STMT_NOT_IMPLEMENTED_ERROR, "SetScroll option not implemeted");
 
2928
 
 
2929
        SC_log_error(func, "Function not implemented", hstmt);
 
2930
        return SQL_ERROR;
 
2931
}
 
2932
 
 
2933
 
 
2934
/*      Set the cursor name on a statement handle */
 
2935
RETCODE         SQL_API
 
2936
PGAPI_SetCursorName(
 
2937
                                        HSTMT hstmt,
 
2938
                                        UCHAR FAR * szCursor,
 
2939
                                        SWORD cbCursor)
 
2940
{
 
2941
        CSTR func = "PGAPI_SetCursorName";
 
2942
        StatementClass *stmt = (StatementClass *) hstmt;
 
2943
        int                     len;
 
2944
 
 
2945
        mylog("PGAPI_SetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d\n", hstmt, szCursor, cbCursor);
 
2946
 
 
2947
        if (!stmt)
 
2948
        {
 
2949
                SC_log_error(func, "", NULL);
 
2950
                return SQL_INVALID_HANDLE;
 
2951
        }
 
2952
 
 
2953
        len = (cbCursor == SQL_NTS) ? strlen(szCursor) : cbCursor;
 
2954
 
 
2955
        if (len <= 0 || len > sizeof(stmt->cursor_name) - 1)
 
2956
        {
 
2957
                SC_set_error(stmt, STMT_INVALID_CURSOR_NAME, "Invalid Cursor Name");
 
2958
                SC_log_error(func, "", stmt);
 
2959
                return SQL_ERROR;
 
2960
        }
 
2961
 
 
2962
        strncpy_null(stmt->cursor_name, szCursor, len + 1);
 
2963
        return SQL_SUCCESS;
 
2964
}
 
2965
 
 
2966
 
 
2967
/*      Return the cursor name for a statement handle */
 
2968
RETCODE         SQL_API
 
2969
PGAPI_GetCursorName(
 
2970
                                        HSTMT hstmt,
 
2971
                                        UCHAR FAR * szCursor,
 
2972
                                        SWORD cbCursorMax,
 
2973
                                        SWORD FAR * pcbCursor)
 
2974
{
 
2975
        CSTR func = "PGAPI_GetCursorName";
 
2976
        StatementClass *stmt = (StatementClass *) hstmt;
 
2977
        int                     len = 0;
 
2978
        RETCODE         result;
 
2979
 
 
2980
        mylog("PGAPI_GetCursorName: hstmt=%u, szCursor=%u, cbCursorMax=%d, pcbCursor=%u\n", hstmt, szCursor, cbCursorMax, pcbCursor);
 
2981
 
 
2982
        if (!stmt)
 
2983
        {
 
2984
                SC_log_error(func, "", NULL);
 
2985
                return SQL_INVALID_HANDLE;
 
2986
        }
 
2987
 
 
2988
        if (stmt->cursor_name[0] == '\0')
 
2989
        {
 
2990
                SC_set_error(stmt, STMT_NO_CURSOR_NAME, "No Cursor name available");
 
2991
                SC_log_error(func, "", stmt);
 
2992
                return SQL_ERROR;
 
2993
        }
 
2994
 
 
2995
        result = SQL_SUCCESS;
 
2996
        len = strlen(stmt->cursor_name);
 
2997
 
 
2998
        if (szCursor)
 
2999
        {
 
3000
                strncpy_null(szCursor, stmt->cursor_name, cbCursorMax);
 
3001
 
 
3002
                if (len >= cbCursorMax)
 
3003
                {
 
3004
                        result = SQL_SUCCESS_WITH_INFO;
 
3005
                        SC_set_error(stmt, STMT_TRUNCATED, "The buffer was too small for the GetCursorName.");
 
3006
                }
 
3007
        }
 
3008
 
 
3009
        if (pcbCursor)
 
3010
                *pcbCursor = len;
 
3011
 
 
3012
        return result;
 
3013
}