From 79420840ee4f56b8399d3f526bb45ce788198e2e Mon Sep 17 00:00:00 2001 From: Hiroshi Inoue Date: Wed, 22 May 2002 05:51:03 +0000 Subject: [PATCH] 1) Support Keyset Driven driver cursors. 2) Supprt ARD precision/scale and SQL_C_NUEMRIC. 3) Minimal implementation of SQLGetDiagField(). 4) SQLRowCount() reports the result of SQLSetPos and SQLBulkOperation. 5) int8 -> SQL_NUMERIC for Microsoft Jet. 6) Support isolation level change. 7) ODBC3.0 SQLSTATE code. 8) Append mode log files. --- src/interfaces/odbc/bind.c | 28 ++ src/interfaces/odbc/bind.h | 6 +- src/interfaces/odbc/connection.c | 65 ++- src/interfaces/odbc/connection.h | 18 +- src/interfaces/odbc/convert.c | 281 ++++++++++- src/interfaces/odbc/descriptor.h | 5 +- src/interfaces/odbc/dlg_specific.c | 22 +- src/interfaces/odbc/environ.c | 114 +++-- src/interfaces/odbc/execute.c | 30 +- src/interfaces/odbc/info.c | 92 +++- src/interfaces/odbc/info30.c | 13 +- src/interfaces/odbc/misc.c | 13 +- src/interfaces/odbc/misc.h | 5 +- src/interfaces/odbc/odbcapi.c | 49 +- src/interfaces/odbc/odbcapi30.c | 92 +--- src/interfaces/odbc/odbcapi30w.c | 5 + src/interfaces/odbc/options.c | 70 ++- src/interfaces/odbc/parse.c | 233 ++++++--- src/interfaces/odbc/pgapi30.c | 355 ++++++++++++- src/interfaces/odbc/pgapifunc.h | 5 +- src/interfaces/odbc/pgtypes.c | 11 +- src/interfaces/odbc/psqlodbc.h | 6 +- src/interfaces/odbc/psqlodbc.rc | 9 +- src/interfaces/odbc/qresult.c | 136 +++-- src/interfaces/odbc/qresult.h | 13 +- src/interfaces/odbc/results.c | 779 +++++++++++++++++++++++------ src/interfaces/odbc/statement.c | 35 +- src/interfaces/odbc/statement.h | 12 +- src/interfaces/odbc/tuple.h | 2 + 29 files changed, 1958 insertions(+), 546 deletions(-) diff --git a/src/interfaces/odbc/bind.c b/src/interfaces/odbc/bind.c index e931b8b8d5..e6deedaa8b 100644 --- a/src/interfaces/odbc/bind.c +++ b/src/interfaces/odbc/bind.c @@ -69,6 +69,23 @@ PGAPI_BindParameter( opts->parameters[ipar].SQLType = fSqlType; opts->parameters[ipar].column_size = cbColDef; opts->parameters[ipar].decimal_digits = ibScale; + opts->parameters[ipar].precision = 0; + opts->parameters[ipar].scale = 0; +#if (ODBCVER >= 0x0300) + switch (fCType) + { + case SQL_C_NUMERIC: + if (cbColDef > 0) + opts->parameters[ipar].precision = (UInt2) cbColDef; + if (ibScale > 0) + opts->parameters[ipar].scale = ibScale; + break; + case SQL_C_TYPE_TIMESTAMP: + if (ibScale > 0) + opts->parameters[ipar].precision = ibScale; + break; + } +#endif /* ODBCVER */ /* * If rebinding a parameter that had data-at-exec stuff in it, then @@ -210,6 +227,8 @@ inolog("Column 0 is type %d not of type SQL_C_BOOKMARK", fCType); free(opts->bindings[icol].ttlbuf); opts->bindings[icol].ttlbuf = NULL; opts->bindings[icol].ttlbuflen = 0; + opts->bindings[icol].precision = 0; + opts->bindings[icol].scale = 0; } else { @@ -218,6 +237,13 @@ inolog("Column 0 is type %d not of type SQL_C_BOOKMARK", fCType); opts->bindings[icol].buffer = rgbValue; opts->bindings[icol].used = pcbValue; opts->bindings[icol].returntype = fCType; +#if (ODBCVER >= 0x0300) + if (SQL_C_NUMERIC == fCType) + opts->bindings[icol].precision = 32; + else +#endif /* ODBCVER */ + opts->bindings[icol].precision = 0; + opts->bindings[icol].scale = 0; mylog(" bound buffer[%d] = %u\n", icol, opts->bindings[icol].buffer); } @@ -460,6 +486,8 @@ reset_a_parameter_binding(APDFields *self, int ipar) self->parameters[ipar].SQLType = 0; self->parameters[ipar].column_size = 0; self->parameters[ipar].decimal_digits = 0; + self->parameters[ipar].precision = 0; + self->parameters[ipar].scale = 0; self->parameters[ipar].data_at_exec = FALSE; self->parameters[ipar].lobj_oid = 0; } diff --git a/src/interfaces/odbc/bind.h b/src/interfaces/odbc/bind.h index 7eadea92e4..f2467c7713 100644 --- a/src/interfaces/odbc/bind.h +++ b/src/interfaces/odbc/bind.h @@ -27,6 +27,8 @@ struct BindInfoClass_ Int2 returntype; /* kind of conversion to be applied when * returning (SQL_C_DEFAULT, * SQL_C_CHAR...) */ + Int2 precision; /* the precision for numeric or timestamp type */ + Int2 scale; /* the scale for numeric type */ }; /* @@ -40,12 +42,14 @@ struct ParameterInfoClass_ Int2 paramType; Int2 CType; Int2 SQLType; - UInt4 column_size; Int2 decimal_digits; + UInt4 column_size; Oid lobj_oid; Int4 *EXEC_used; /* amount of data OR the oid of the large * object */ char *EXEC_buffer; /* the data or the FD of the large object */ + Int2 precision; /* the precision for numeric or timestamp type */ + Int2 scale; /* the scale for numeric type */ char data_at_exec; }; diff --git a/src/interfaces/odbc/connection.c b/src/interfaces/odbc/connection.c index 2b0d67a648..81ed7d5ba7 100644 --- a/src/interfaces/odbc/connection.c +++ b/src/interfaces/odbc/connection.c @@ -237,7 +237,7 @@ CC_conninfo_init(ConnInfo *conninfo) { memset(conninfo, 0, sizeof(ConnInfo)); conninfo->disallow_premature = -1; - conninfo->updatable_cursors = -1; + conninfo->allow_keyset = -1; conninfo->lf_conversion = -1; conninfo->true_is_minus1 = -1; memcpy(&(conninfo->drivers), &globals, sizeof(globals)); @@ -293,6 +293,7 @@ CC_Constructor() rv->unicode = 0; rv->result_uncommitted = 0; rv->schema_support = 0; + rv->isolation = SQL_TXN_READ_COMMITTED; #ifdef MULTIBYTE rv->client_encoding = NULL; rv->server_encoding = NULL; @@ -996,6 +997,12 @@ another_version_retry: } #endif /* UNICODE_SUPPORT */ #endif /* MULTIBYTE */ + ci->updatable_cursors = 0; +#ifdef DRIVER_CURSOR_IMPLEMENT + if (!ci->drivers.use_declarefetch && + PG_VERSION_GE(self, 7.0)) /* Tid scan since 7.0 */ + ci->updatable_cursors = ci->allow_keyset; +#endif /* DRIVER_CURSOR_IMPLEMENT */ CC_clear_error(self); /* clear any initial command errors */ self->status = CONN_CONNECTED; @@ -1130,7 +1137,7 @@ void CC_on_commit(ConnectionClass *conn) } conn->result_uncommitted = 0; } -void CC_on_abort(ConnectionClass *conn, BOOL set_no_trans) +void CC_on_abort(ConnectionClass *conn, UDWORD opt) { if (CC_is_in_trans(conn)) { @@ -1138,9 +1145,11 @@ void CC_on_abort(ConnectionClass *conn, BOOL set_no_trans) if (conn->result_uncommitted) ProcessRollback(conn, TRUE); #endif /* DRIVER_CURSOR_IMPLEMENT */ - if (set_no_trans) + if (0 != (opt & NO_TRANS)) CC_set_no_trans(conn); } + if (0 != (opt & CONN_DEAD)) + conn->status = CONN_DOWN; conn->result_uncommitted = 0; } @@ -1162,8 +1171,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) BOOL clear_result_on_abort = ((flag & CLEAR_RESULT_ON_ABORT) != 0), create_keyset = ((flag & CREATE_KEYSET) != 0), issue_begin = ((flag & GO_INTO_TRANSACTION) != 0 && !CC_is_in_trans(self)); - char swallow, - *wq; + char swallow, *wq, *ptr; int id; SocketClass *sock = self->sock; int maxlen, @@ -1173,8 +1181,8 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) query_completed = FALSE, before_64 = PG_VERSION_LT(self, 6.4), aborted = FALSE, - used_passed_result_object = FALSE, - set_no_trans; + used_passed_result_object = FALSE; + UDWORD abort_opt; /* ERROR_MSG_LENGTH is suffcient */ static char msgbuffer[ERROR_MSG_LENGTH + 1]; @@ -1201,7 +1209,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) { self->errornumber = CONNECTION_COULD_NOT_SEND; self->errormsg = "Could not send Query to backend"; - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS | CONN_DEAD); return NULL; } @@ -1210,7 +1218,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) { self->errornumber = CONNECTION_COULD_NOT_SEND; self->errormsg = "Could not send Query to backend"; - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS | CONN_DEAD); return NULL; } @@ -1223,7 +1231,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) { self->errornumber = CONNECTION_COULD_NOT_SEND; self->errormsg = "Could not send Query to backend"; - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS | CONN_DEAD); return NULL; } @@ -1260,7 +1268,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) self->errormsg = "No response from the backend"; mylog("send_query: 'id' - %s\n", self->errormsg); - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS | CONN_DEAD); ReadyToReturn = TRUE; retres = NULL; break; @@ -1284,7 +1292,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) self->errornumber = CONNECTION_NO_RESPONSE; self->errormsg = "No response from backend while receiving a portal query command"; mylog("send_query: 'C' - %s\n", self->errormsg); - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS | CONN_DEAD); ReadyToReturn = TRUE; retres = NULL; } @@ -1312,11 +1320,20 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) else if (strnicmp(cmdbuffer, "COMMIT", 6) == 0) CC_on_commit(self); else if (strnicmp(cmdbuffer, "ROLLBACK", 8) == 0) - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS); else if (strnicmp(cmdbuffer, "END", 3) == 0) CC_on_commit(self); else if (strnicmp(cmdbuffer, "ABORT", 5) == 0) - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS); + else + { + trim(cmdbuffer); /* get rid of trailing space */ + ptr = strrchr(cmdbuffer, ' '); + if (ptr) + res->recent_processed_row_count = atoi(ptr + 1); + else + res->recent_processed_row_count = -1; + } if (QR_command_successful(res)) QR_set_status(res, PGRES_COMMAND_OK); @@ -1400,15 +1417,15 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) qlog("ERROR from backend during send_query: '%s'\n", msgbuffer); /* We should report that an error occured. Zoltan */ - set_no_trans = FALSE; + abort_opt = 0; if (!strncmp(msgbuffer, "FATAL", 5)) { self->errornumber = CONNECTION_SERVER_REPORTED_ERROR; - set_no_trans = TRUE; + abort_opt = NO_TRANS | CONN_DEAD; } else self->errornumber = CONNECTION_SERVER_REPORTED_WARNING; - CC_on_abort(self, set_no_trans); + CC_on_abort(self, abort_opt); QR_set_status(res, PGRES_FATAL_ERROR); QR_set_message(res, msgbuffer); QR_set_aborted(res, TRUE); @@ -1497,7 +1514,7 @@ CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) default: self->errornumber = CONNECTION_BACKEND_CRAZY; self->errormsg = "Unexpected protocol character from backend (send_query)"; - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS | CONN_DEAD); mylog("send_query: error - %s\n", self->errormsg); ReadyToReturn = TRUE; @@ -1585,7 +1602,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ { self->errornumber = CONNECTION_COULD_NOT_SEND; self->errormsg = "Could not send function to backend"; - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS | CONN_DEAD); return FALSE; } @@ -1594,7 +1611,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ { self->errornumber = CONNECTION_COULD_NOT_SEND; self->errormsg = "Could not send function to backend"; - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS | CONN_DEAD); return FALSE; } @@ -1643,7 +1660,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ case 'E': SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); self->errormsg = msgbuffer; - CC_on_abort(self, FALSE); + CC_on_abort(self, 0); mylog("send_function(V): 'E' - %s\n", self->errormsg); qlog("ERROR from backend during send_function: '%s'\n", self->errormsg); @@ -1656,7 +1673,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ default: self->errornumber = CONNECTION_BACKEND_CRAZY; self->errormsg = "Unexpected protocol character from backend (send_function, args)"; - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS | CONN_DEAD); mylog("send_function: error - %s\n", self->errormsg); return FALSE; @@ -1690,7 +1707,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ case 'E': SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); self->errormsg = msgbuffer; - CC_on_abort(self, FALSE); + CC_on_abort(self, 0); mylog("send_function(G): 'E' - %s\n", self->errormsg); qlog("ERROR from backend during send_function: '%s'\n", self->errormsg); @@ -1711,7 +1728,7 @@ CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_ default: self->errornumber = CONNECTION_BACKEND_CRAZY; self->errormsg = "Unexpected protocol character from backend (send_function, result)"; - CC_on_abort(self, TRUE); + CC_on_abort(self, NO_TRANS | CONN_DEAD); mylog("send_function: error - %s\n", self->errormsg); return FALSE; diff --git a/src/interfaces/odbc/connection.h b/src/interfaces/odbc/connection.h index 849350d14d..3bd8632845 100644 --- a/src/interfaces/odbc/connection.h +++ b/src/interfaces/odbc/connection.h @@ -166,6 +166,7 @@ typedef struct char translation_option[SMALL_REGISTRY_LEN]; char focus_password; char disallow_premature; + char allow_keyset; char updatable_cursors; char lf_conversion; char true_is_minus1; @@ -290,12 +291,13 @@ struct ConnectionClass_ char result_uncommitted; char schema_support; #ifdef MULTIBYTE - char *client_encoding; - char *server_encoding; + char *client_encoding; + char *server_encoding; #endif /* MULTIBYTE */ - int ccsc; + int ccsc; int be_pid; /* pid returned by backend */ int be_key; /* auth code needed to send cancel */ + UInt4 isolation; }; @@ -339,11 +341,15 @@ void CC_log_error(const char *func, const char *desc, const ConnectionClass *se int CC_get_max_query_len(const ConnectionClass *self); int CC_send_cancel_request(const ConnectionClass *conn); void CC_on_commit(ConnectionClass *conn); -void CC_on_abort(ConnectionClass *conn, BOOL set_no_trans); +void CC_on_abort(ConnectionClass *conn, UDWORD opt); void ProcessRollback(ConnectionClass *conn, BOOL undo); -/* CC_send_query_options */ +/* CC_send_query options */ #define CLEAR_RESULT_ON_ABORT 1L #define CREATE_KEYSET (1L << 1) /* create keyset for updatable curosrs */ #define GO_INTO_TRANSACTION (1L << 2) /* issue begin in advance */ -#endif +/* CC_on_abort options */ +#define NO_TRANS 1L +#define CONN_DEAD (1L << 1) /* connection is no longer valid */ + +#endif /* __CONNECTION_H__ */ diff --git a/src/interfaces/odbc/convert.c b/src/interfaces/odbc/convert.c index 433f4b3fe1..d33f390e48 100644 --- a/src/interfaces/odbc/convert.c +++ b/src/interfaces/odbc/convert.c @@ -371,7 +371,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 pbic = &opts->bindings[stmt->current_col]; if (pbic->data_left == -2) pbic->data_left = (cbValueMax > 0) ? 0 : -1; /* This seems to be * - * needed for ADO ? */ + * needed by ADO ? */ if (pbic->data_left == 0) { if (pbic->ttlbuf != NULL) @@ -984,6 +984,90 @@ inolog("2stime fr=%d\n", st.fr); #endif /* HAVE_LOCALE_H */ break; +#if (ODBCVER >= 0x0300) + case SQL_C_NUMERIC: +#ifdef HAVE_LOCALE_H + /* strcpy(saved_locale, setlocale(LC_ALL, NULL)); + setlocale(LC_ALL, "C"); not needed currently */ +#endif /* HAVE_LOCALE_H */ + { + SQL_NUMERIC_STRUCT *ns; + int i, nlen, bit, hval, tv, dig, sta, olen; + char calv[SQL_MAX_NUMERIC_LEN * 3], *wv; + BOOL dot_exist; + + len = sizeof(SQL_NUMERIC_STRUCT); + if (bind_size > 0) + ns = (SQL_NUMERIC_STRUCT *) ((char *) rgbValue + (bind_row * bind_size)); + else + ns = (SQL_NUMERIC_STRUCT *) rgbValue + bind_row; + for (wv = neut_str; *wv && isspace(*wv); wv++) + ; + ns->sign = 1; + if (*wv == '-') + { + ns->sign = 0; + wv++; + } + else if (*wv == '+') + wv++; + while (*wv == '0') wv++; + ns->precision = 0; + ns->scale = 0; + for (nlen = 0, dot_exist = FALSE;; wv++) + { + if (*wv == '.') + { + if (dot_exist) + break; + dot_exist = TRUE; + } + else if (!isdigit(*wv)) + break; + else + { + if (dot_exist) + ns->scale++; + else + ns->precision++; + calv[nlen++] = *wv; + } + } + memset(ns->val, 0, sizeof(ns->val)); + for (hval = 0, bit = 1L, sta = 0, olen = 0; sta < nlen;) + { + for (dig = 0, i = sta; i < nlen; i++) + { + tv = dig * 10 + calv[i] - '0'; + dig = tv % 2; + calv[i] = tv / 2 + '0'; + if (i == sta && tv < 2) + sta++; + } + if (dig > 0) + hval |= bit; + bit <<= 1; + if (bit >= (1L << 8)) + { + ns->val[olen++] = hval; + hval = 0; + bit = 1L; + if (olen >= SQL_MAX_NUMERIC_LEN - 1) + { + ns->scale = sta - ns->precision; + break; + } + } + } + if (hval && olen < SQL_MAX_NUMERIC_LEN - 1) + ns->val[olen++] = hval; + } +#ifdef HAVE_LOCALE_H + /* setlocale(LC_ALL, saved_locale); */ +#endif /* HAVE_LOCALE_H */ + break; +#endif /* ODBCVER */ + case SQL_C_SSHORT: case SQL_C_SHORT: len = 2; @@ -1179,6 +1263,8 @@ QP_initialize(QueryParse *q, const StatementClass *stmt) #define FLGB_PRE_EXECUTING 1L #define FLGB_INACCURATE_RESULT (1L << 1) +#define FLGB_CREATE_KEYSET (1L << 2) +#define FLGB_KEYSET_DRIVEN (1L << 3) typedef struct _QueryBuild { char *query_statement; UInt4 str_size_limit; @@ -1589,10 +1675,16 @@ copy_statement_with_parameters(StatementClass *stmt) { if (stmt->parse_status == STMT_PARSE_NONE) parse_statement(stmt); - /*if (stmt->parse_status != STMT_PARSE_COMPLETE) + if (stmt->parse_status == STMT_PARSE_FATAL) + { stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; - else*/ if (!stmt->updatable) + return SQL_ERROR; + } + else if (!stmt->updatable) + { stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; + stmt->options.cursor_type = SQL_CURSOR_STATIC; + } else { qp->from_pos = stmt->from_pos; @@ -1602,7 +1694,7 @@ copy_statement_with_parameters(StatementClass *stmt) #else stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) - stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY; + stmt->options.cursor_type = SQL_CURSOR_STATIC; #endif /* DRIVER_CURSOR_IMPLEMENT */ /* If the application hasn't set a cursor name, then generate one */ @@ -1641,6 +1733,12 @@ copy_statement_with_parameters(StatementClass *stmt) qp->flags |= FLGP_CURSOR_CHECK_OK; qp->declare_pos = qb->npos; } + else if (SQL_CONCUR_READ_ONLY != stmt->options.scroll_concurrency) + { + qb->flags |= FLGB_CREATE_KEYSET; + if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type) + qb->flags |= FLGB_KEYSET_DRIVEN; + } } for (qp->opos = 0; qp->opos < qp->stmt_len; qp->opos++) @@ -1693,13 +1791,29 @@ copy_statement_with_parameters(StatementClass *stmt) UInt4 npos = qb->load_stmt_len; if (0 == npos) + { npos = qb->npos; + for (; npos > 0; npos--) + { + if (isspace(new_statement[npos - 1])) + continue; + if (';' != new_statement[npos - 1]) + break; + } + if (0 != (qb->flags & FLGB_KEYSET_DRIVEN)) + { + qb->npos = npos; + /* ---------- + * 1st query is for field information + * 2nd query is keyset gathering + */ + CVT_APPEND_STR(qb, " where ctid = '(,)';select ctid, oid from "); + CVT_APPEND_DATA(qb, qp->statement + qp->from_pos + 5, npos - qp->from_pos - 5); + } + } stmt->load_statement = malloc(npos + 1); - memcpy(stmt->load_statement, new_statement, npos); - if (stmt->load_statement[npos - 1] == ';') - stmt->load_statement[npos - 1] = '\0'; - else - stmt->load_statement[npos] = '\0'; + memcpy(stmt->load_statement, qb->query_statement, npos); + stmt->load_statement[npos] = '\0'; } #endif /* DRIVER_CURSOR_IMPLEMENT */ if (prepare_dummy_cursor && SC_is_pre_executable(stmt)) @@ -1732,7 +1846,14 @@ inner_process_tokens(QueryParse *qp, QueryBuild *qb) CVT_APPEND_STR(qb, ", CTID, OID "); } else if (qp->where_pos == (Int4) qp->opos) + { qb->load_stmt_len = qb->npos; + if (0 != (qb->flags & FLGB_KEYSET_DRIVEN)) + { + CVT_APPEND_STR(qb, "where ctid = '(,)';select CTID, OID from "); + CVT_APPEND_DATA(qb, qp->statement + qp->from_pos + 5, qp->where_pos - qp->from_pos - 5); + } + } #ifdef MULTIBYTE oldchar = encoded_byte_check(&qp->encstr, qp->opos); if (ENCODE_STATUS(qp->encstr) != 0) @@ -1836,6 +1957,7 @@ inner_process_tokens(QueryParse *qp, QueryBuild *qb) { qp->flags |= FLGP_SELECT_INTO; qp->flags &= ~FLGP_CURSOR_CHECK_OK; + qb->flags &= ~FLGB_KEYSET_DRIVEN; qp->statement_type = STMT_TYPE_CREATE; memmove(qb->query_statement, qb->query_statement + qp->declare_pos, qb->npos - qp->declare_pos); qb->npos -= qp->declare_pos; @@ -1887,6 +2009,130 @@ inner_process_tokens(QueryParse *qp, QueryBuild *qb) return SQL_SUCCESS; } +#if (ODBCVER >= 0x0300) +static BOOL +ResolveNumericParam(const SQL_NUMERIC_STRUCT *ns, char *chrform) +{ + static int prec[] = {1, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 29, 32, 34, 37, 39}; + Int4 i, j, k, ival, vlen, len, newlen; + unsigned char calv[40]; + const unsigned char *val = (const unsigned char *) ns->val; + BOOL next_figure; + + if (0 == ns->precision) + { + strcpy(chrform, "0"); + return TRUE; + } + else if (ns->precision < prec[sizeof(Int4)]) + { + for (i = 0, ival = 0; i < sizeof(Int4) && prec[i] <= ns->precision; i++) + { + ival += (val[i] << (8 * i)); /* ns->val is little endian */ + } + if (0 == ns->scale) + { + if (0 == ns->sign) + ival *= -1; + sprintf(chrform, "%d", ival); + } + else if (ns->scale > 0) + { + Int4 i, div, o1val, o2val; + + for (i = 0, div = 1; i < ns->scale; i++) + div *= 10; + o1val = ival / div; + o2val = ival % div; + if (0 == ns->sign) + o1val *= -1; + sprintf(chrform, "%d.%0.*d", o1val, ns->scale, o2val); + } + return TRUE; + } + + for (i = 0; i < SQL_MAX_NUMERIC_LEN && prec[i] <= ns->precision; i++) + ; + vlen = i; + len = 0; + memset(calv, 0, sizeof(calv)); + for (i = vlen - 1; i >= 0; i--) + { + for (j = len - 1; j >= 0; j--) + { + if (!calv[j]) + continue; + ival = (((Int4)calv[j]) << 8); + calv[j] = (ival % 10); + ival /= 10; + calv[j + 1] += (ival % 10); + ival /= 10; + calv[j + 2] += (ival % 10); + ival /= 10; + calv[j + 3] += ival; + for (k = j;; k++) + { + next_figure = FALSE; + if (calv[k] > 0) + { + if (k >= len) + len = k + 1; + while (calv[k] > 9) + { + calv[k + 1]++; + calv[k] -= 10; + next_figure = TRUE; + } + } + if (k >= j + 3 && !next_figure) + break; + } + } + ival = val[i]; + if (!ival) + continue; + calv[0] += (ival % 10); + ival /= 10; + calv[1] += (ival % 10); + ival /= 10; + calv[2] += ival; + for (j = 0;; j++) + { + next_figure = FALSE; + if (calv[j] > 0) + { + if (j >= len) + len = j + 1; + while (calv[j] > 9) + { + calv[j + 1]++; + calv[j] -= 10; + next_figure = TRUE; + } + } + if (j >= 2 && !next_figure) + break; + } + } + newlen = 0; + if (0 == ns->sign) + chrform[newlen++] = '-'; + for (i = len - 1; i >= ns->scale; i--) + chrform[newlen++] = calv[i] + '0'; + if (ns->scale > 0) + { + chrform[newlen++] = '.'; + for (; i >= 0; i--) + chrform[newlen++] = calv[i] + '0'; + } + chrform[newlen] = '\0'; + return TRUE; +} +#endif /* ODBCVER */ + +/* + * + */ static int ResolveOneParam(QueryBuild *qb) { @@ -2138,6 +2384,11 @@ ResolveOneParam(QueryBuild *qb) break; } +#if (ODBCVER >= 0x0300) + case SQL_C_NUMERIC: + if (ResolveNumericParam((SQL_NUMERIC_STRUCT *) buffer, param_string)) + break; +#endif default: /* error */ qb->errormsg = "Unrecognized C_parameter type in copy_statement_with_parameters"; @@ -2336,16 +2587,16 @@ ResolveOneParam(QueryBuild *qb) if (buf) { cbuf[0] = '\''; - my_strcpy(cbuf + 1, sizeof(cbuf) - 12, buf, used); /* 12 = 1('\'') + - * strlen("'::numeric") + my_strcpy(cbuf + 1, sizeof(cbuf) - 3, buf, used); /* 3 = 1('\'') + + * strlen("'") * + 1('\0') */ - strcat(cbuf, "'::numeric"); + strcat(cbuf, "'"); } else - sprintf(cbuf, "'%s'::numeric", param_string); + sprintf(cbuf, "'%s'", param_string); CVT_APPEND_STR(qb, cbuf); break; - default: /* a numeric type or SQL_BIT */ + default: /* a numeric type or SQL_BIT */ if (param_sqltype == SQL_BIT) CVT_APPEND_CHAR(qb, '\''); /* Open Quote */ @@ -2938,7 +3189,7 @@ conv_from_octal(const unsigned char *s) y = 0; for (i = 1; i <= 3; i++) - y += (s[i] - '0') << (3 * (3 - i)); + y += (s[i] - '0') << (3 * (3 - i)); return y; diff --git a/src/interfaces/odbc/descriptor.h b/src/interfaces/odbc/descriptor.h index 59e4212ed6..182142bf2e 100644 --- a/src/interfaces/odbc/descriptor.h +++ b/src/interfaces/odbc/descriptor.h @@ -5,7 +5,7 @@ * * Comments: See "notice.txt" for copyright and license information. * - * $Id: descriptor.h,v 1.4 2002/04/10 08:18:54 inoue Exp $ + * $Id: descriptor.h,v 1.5 2002/05/22 05:51:03 inoue Exp $ * */ @@ -20,6 +20,7 @@ typedef struct char schema[MAX_SCHEMA_LEN + 1]; char name[MAX_TABLE_LEN + 1]; char alias[MAX_TABLE_LEN + 1]; + char updatable; } TABLE_INFO; typedef struct @@ -41,6 +42,8 @@ typedef struct char name[MAX_COLUMN_LEN + 1]; char alias[MAX_COLUMN_LEN + 1]; } FIELD_INFO; +Int4 FI_precision(const FIELD_INFO *); +Int4 FI_scale(const FIELD_INFO *); struct ARDFields_ { diff --git a/src/interfaces/odbc/dlg_specific.c b/src/interfaces/odbc/dlg_specific.c index a4423e43b4..259c7b3692 100644 --- a/src/interfaces/odbc/dlg_specific.c +++ b/src/interfaces/odbc/dlg_specific.c @@ -340,7 +340,7 @@ ds_optionsProc(HWND hdlg, CheckDlgButton(hdlg, DS_DISALLOWPREMATURE, ci->disallow_premature); CheckDlgButton(hdlg, DS_LFCONVERSION, ci->lf_conversion); CheckDlgButton(hdlg, DS_TRUEISMINUS1, ci->true_is_minus1); - CheckDlgButton(hdlg, DS_UPDATABLECURSORS, ci->updatable_cursors); + CheckDlgButton(hdlg, DS_UPDATABLECURSORS, ci->allow_keyset); #ifndef DRIVER_CURSOR_IMPLEMENT EnableWindow(GetDlgItem(hdlg, DS_UPDATABLECURSORS), FALSE); #endif /* DRIVER_CURSOR_IMPLEMENT */ @@ -382,7 +382,7 @@ ds_optionsProc(HWND hdlg, ci->lf_conversion = IsDlgButtonChecked(hdlg, DS_LFCONVERSION); ci->true_is_minus1 = IsDlgButtonChecked(hdlg, DS_TRUEISMINUS1); #ifdef DRIVER_CURSOR_IMPLEMENT - ci->updatable_cursors = IsDlgButtonChecked(hdlg, DS_UPDATABLECURSORS); + ci->allow_keyset = IsDlgButtonChecked(hdlg, DS_UPDATABLECURSORS); #endif /* DRIVER_CURSOR_IMPLEMENT */ /* OID Options */ @@ -590,7 +590,7 @@ makeConnectString(char *connect_string, const ConnInfo *ci, UWORD len) INI_LFCONVERSION, ci->lf_conversion, INI_UPDATABLECURSORS, - ci->updatable_cursors, + ci->allow_keyset, INI_DISALLOWPREMATURE, ci->disallow_premature, INI_TRUEISMINUS1, @@ -601,7 +601,7 @@ makeConnectString(char *connect_string, const ConnInfo *ci, UWORD len) unsigned long flag = 0; if (ci->disallow_premature) flag |= BIT_DISALLOWPREMATURE; - if (ci->updatable_cursors) + if (ci->allow_keyset) flag |= BIT_UPDATABLECURSORS; if (ci->lf_conversion) flag |= BIT_LFCONVERSION; @@ -686,7 +686,7 @@ unfoldCXAttribute(ConnInfo *ci, const char *value) sscanf(value + 2, "%lx", &flag); } ci->disallow_premature = (char)((flag & BIT_DISALLOWPREMATURE) != 0); - ci->updatable_cursors = (char)((flag & BIT_UPDATABLECURSORS) != 0); + ci->allow_keyset = (char)((flag & BIT_UPDATABLECURSORS) != 0); ci->lf_conversion = (char)((flag & BIT_LFCONVERSION) != 0); if (count < 4) return; @@ -770,7 +770,7 @@ copyAttributes(ConnInfo *ci, const char *attribute, const char *value) else if (stricmp(attribute, INI_DISALLOWPREMATURE) == 0 || stricmp(attribute, "C3") == 0) ci->disallow_premature = atoi(value); else if (stricmp(attribute, INI_UPDATABLECURSORS) == 0 || stricmp(attribute, "C4") == 0) - ci->updatable_cursors = atoi(value); + ci->allow_keyset = atoi(value); else if (stricmp(attribute, INI_LFCONVERSION) == 0) ci->lf_conversion = atoi(value); else if (stricmp(attribute, INI_TRUEISMINUS1) == 0) @@ -870,8 +870,8 @@ getDSNdefaults(ConnInfo *ci) if (ci->disallow_premature < 0) ci->disallow_premature = DEFAULT_DISALLOWPREMATURE; - if (ci->updatable_cursors < 0) - ci->updatable_cursors = DEFAULT_UPDATABLECURSORS; + if (ci->allow_keyset < 0) + ci->allow_keyset = DEFAULT_UPDATABLECURSORS; if (ci->lf_conversion < 0) ci->lf_conversion = DEFAULT_LFCONVERSION; if (ci->true_is_minus1 < 0) @@ -960,11 +960,11 @@ getDSNinfo(ConnInfo *ci, char overwrite) ci->disallow_premature = atoi(temp); } - if (ci->updatable_cursors < 0 || overwrite) + if (ci->allow_keyset < 0 || overwrite) { SQLGetPrivateProfileString(DSN, INI_UPDATABLECURSORS, "", temp, sizeof(temp), ODBC_INI); if (temp[0]) - ci->updatable_cursors = atoi(temp); + ci->allow_keyset = atoi(temp); } if (ci->lf_conversion < 0 || overwrite) @@ -1094,7 +1094,7 @@ writeDSNinfo(const ConnInfo *ci) INI_DISALLOWPREMATURE, temp, ODBC_INI); - sprintf(temp, "%d", ci->updatable_cursors); + sprintf(temp, "%d", ci->allow_keyset); SQLWritePrivateProfileString(DSN, INI_UPDATABLECURSORS, temp, diff --git a/src/interfaces/odbc/environ.c b/src/interfaces/odbc/environ.c index 7c8f80dd8f..9ac8f8eb6c 100644 --- a/src/interfaces/odbc/environ.c +++ b/src/interfaces/odbc/environ.c @@ -78,6 +78,12 @@ PGAPI_FreeEnv(HENV henv) } +static void +pg_sqlstate_set(const EnvironmentClass *env, UCHAR *szSqlState, const UCHAR *ver3str, const UCHAR *ver2str) +{ + strcpy(szSqlState, EN_is_odbc3(env) ? ver3str : ver2str); +} + #define DRVMNGRDIV 511 /* Returns the next SQL error information. */ RETCODE SQL_API @@ -92,6 +98,7 @@ PGAPI_StmtError( HSTMT hstmt, { /* CC: return an error of a hstmt */ StatementClass *stmt = (StatementClass *) hstmt; + EnvironmentClass *env = (EnvironmentClass *) SC_get_conn(stmt)->henv; char *msg; int status; BOOL partial_ok = ((flag & PODBC_ALLOW_PARTIAL_EXTRACT) != 0), @@ -173,120 +180,124 @@ PGAPI_StmtError( HSTMT hstmt, { /* now determine the SQLSTATE to be returned */ case STMT_ROW_VERSION_CHANGED: - strcpy(szSqlState, "01001"); + pg_sqlstate_set(env, szSqlState, "01001", "01001"); /* data truncated */ break; case STMT_TRUNCATED: - strcpy(szSqlState, "01004"); + pg_sqlstate_set(env, szSqlState, "01004", "01004"); /* data truncated */ break; case STMT_INFO_ONLY: - strcpy(szSqlState, "00000"); + pg_sqlstate_set(env, szSqlState, "00000", "0000"); /* just information that is returned, no error */ break; case STMT_BAD_ERROR: - strcpy(szSqlState, "08S01"); + pg_sqlstate_set(env, szSqlState, "08S01", "08S01"); /* communication link failure */ break; case STMT_CREATE_TABLE_ERROR: - strcpy(szSqlState, "S0001"); + pg_sqlstate_set(env, szSqlState, "42S01", "S0001"); /* table already exists */ break; case STMT_STATUS_ERROR: case STMT_SEQUENCE_ERROR: - strcpy(szSqlState, "S1010"); + pg_sqlstate_set(env, szSqlState, "HY010", "S1010"); /* Function sequence error */ break; case STMT_NO_MEMORY_ERROR: - strcpy(szSqlState, "S1001"); + pg_sqlstate_set(env, szSqlState, "HY001", "S1001"); /* memory allocation failure */ break; case STMT_COLNUM_ERROR: - strcpy(szSqlState, "S1002"); + pg_sqlstate_set(env, szSqlState, "07009", "S1002"); /* invalid column number */ break; case STMT_NO_STMTSTRING: - strcpy(szSqlState, "S1001"); + pg_sqlstate_set(env, szSqlState, "HY001", "S1001"); /* having no stmtstring is also a malloc problem */ break; case STMT_ERROR_TAKEN_FROM_BACKEND: - strcpy(szSqlState, "S1000"); + pg_sqlstate_set(env, szSqlState, "HY000", "S1000"); /* general error */ break; case STMT_INTERNAL_ERROR: - strcpy(szSqlState, "S1000"); + pg_sqlstate_set(env, szSqlState, "HY000", "S1000"); /* general error */ break; + case STMT_FETCH_OUT_OF_RANGE: + pg_sqlstate_set(env, szSqlState, "HY106", "S1106"); + break; + case STMT_ROW_OUT_OF_RANGE: - strcpy(szSqlState, "S1107"); + pg_sqlstate_set(env, szSqlState, "HY107", "S1107"); break; case STMT_OPERATION_CANCELLED: - strcpy(szSqlState, "S1008"); + pg_sqlstate_set(env, szSqlState, "HY008", "S1008"); break; case STMT_NOT_IMPLEMENTED_ERROR: - strcpy(szSqlState, "S1C00"); /* == 'driver not + pg_sqlstate_set(env, szSqlState, "HYC00", "S1C00"); /* == 'driver not * capable' */ break; case STMT_OPTION_OUT_OF_RANGE_ERROR: - strcpy(szSqlState, "S1092"); + pg_sqlstate_set(env, szSqlState, "HY092", "S1092"); break; case STMT_BAD_PARAMETER_NUMBER_ERROR: - strcpy(szSqlState, "S1093"); + pg_sqlstate_set(env, szSqlState, "07009", "S1093"); break; case STMT_INVALID_COLUMN_NUMBER_ERROR: - strcpy(szSqlState, "S1002"); + pg_sqlstate_set(env, szSqlState, "07009", "S1002"); break; case STMT_RESTRICTED_DATA_TYPE_ERROR: - strcpy(szSqlState, "07006"); + pg_sqlstate_set(env, szSqlState, "07006", "07006"); break; case STMT_INVALID_CURSOR_STATE_ERROR: - strcpy(szSqlState, "24000"); + pg_sqlstate_set(env, szSqlState, "07005", "24000"); break; case STMT_ERROR_IN_ROW: - strcpy(szSqlState, "01S01"); + pg_sqlstate_set(env, szSqlState, "01S01", "01S01"); break; case STMT_OPTION_VALUE_CHANGED: - strcpy(szSqlState, "01S02"); + pg_sqlstate_set(env, szSqlState, "01S02", "01S02"); break; case STMT_POS_BEFORE_RECORDSET: - strcpy(szSqlState, "01S06"); + pg_sqlstate_set(env, szSqlState, "01S06", "01S06"); break; case STMT_INVALID_CURSOR_NAME: - strcpy(szSqlState, "34000"); + pg_sqlstate_set(env, szSqlState, "34000", "34000"); break; case STMT_NO_CURSOR_NAME: - strcpy(szSqlState, "S1015"); + pg_sqlstate_set(env, szSqlState, "S1015", "S1015"); break; case STMT_INVALID_ARGUMENT_NO: - strcpy(szSqlState, "S1009"); + pg_sqlstate_set(env, szSqlState, "HY024", "S1009"); /* invalid argument value */ break; case STMT_INVALID_CURSOR_POSITION: - strcpy(szSqlState, "S1109"); + pg_sqlstate_set(env, szSqlState, "HY109", "S1109"); break; case STMT_RETURN_NULL_WITHOUT_INDICATOR: - strcpy(szSqlState, "22002"); + pg_sqlstate_set(env, szSqlState, "22002", "22002"); break; case STMT_VALUE_OUT_OF_RANGE: - strcpy(szSqlState, "22003"); + pg_sqlstate_set(env, szSqlState, "HY019", "22003"); break; case STMT_OPERATION_INVALID: - strcpy(szSqlState, "S1011"); + pg_sqlstate_set(env, szSqlState, "HY011", "S1011"); break; case STMT_INVALID_DESCRIPTOR_IDENTIFIER: - strcpy(szSqlState, "HY091"); + pg_sqlstate_set(env, szSqlState, "HY091", "HY091"); break; case STMT_INVALID_OPTION_IDENTIFIER: - strcpy(szSqlState, "HY092"); + pg_sqlstate_set(env, szSqlState, "HY092", "HY092"); break; case STMT_OPTION_NOT_FOR_THE_DRIVER: - strcpy(szSqlState, "HYC00"); + pg_sqlstate_set(env, szSqlState, "HYC00", "HYC00"); break; case STMT_EXEC_ERROR: default: - strcpy(szSqlState, "S1000"); + pg_sqlstate_set(env, szSqlState, "HY000", "S1000"); /* also a general error */ break; } @@ -314,6 +325,7 @@ PGAPI_ConnectError( HDBC hdbc, UWORD flag) { ConnectionClass *conn = (ConnectionClass *) hdbc; + EnvironmentClass *env = (EnvironmentClass *) conn->henv; char *msg; int status; BOOL once_again = FALSE; @@ -357,43 +369,43 @@ PGAPI_ConnectError( HDBC hdbc, { case STMT_OPTION_VALUE_CHANGED: case CONN_OPTION_VALUE_CHANGED: - strcpy(szSqlState, "01S02"); + pg_sqlstate_set(env, szSqlState, "01S02", "01S02"); break; case STMT_TRUNCATED: case CONN_TRUNCATED: - strcpy(szSqlState, "01004"); + pg_sqlstate_set(env, szSqlState, "01004", "01004"); /* data truncated */ break; case CONN_INIREAD_ERROR: - strcpy(szSqlState, "IM002"); + pg_sqlstate_set(env, szSqlState, "IM002", "IM002"); /* data source not found */ break; case CONNECTION_SERVER_NOT_REACHED: case CONN_OPENDB_ERROR: - strcpy(szSqlState, "08001"); + pg_sqlstate_set(env, szSqlState, "08001", "08001"); /* unable to connect to data source */ break; case CONN_INVALID_AUTHENTICATION: case CONN_AUTH_TYPE_UNSUPPORTED: - strcpy(szSqlState, "28000"); + pg_sqlstate_set(env, szSqlState, "28000", "28000"); break; case CONN_STMT_ALLOC_ERROR: - strcpy(szSqlState, "S1001"); + pg_sqlstate_set(env, szSqlState, "HY001", "S1001"); /* memory allocation failure */ break; case CONN_IN_USE: - strcpy(szSqlState, "S1000"); + pg_sqlstate_set(env, szSqlState, "HY000", "S1000"); /* general error */ break; case CONN_UNSUPPORTED_OPTION: - strcpy(szSqlState, "IM001"); + pg_sqlstate_set(env, szSqlState, "IM001", "IM001"); /* driver does not support this function */ case CONN_INVALID_ARGUMENT_NO: - strcpy(szSqlState, "S1009"); + pg_sqlstate_set(env, szSqlState, "HY009", "S1009"); /* invalid argument value */ break; case CONN_TRANSACT_IN_PROGRES: - strcpy(szSqlState, "S1010"); + pg_sqlstate_set(env, szSqlState, "HY010", "S1010"); /* * when the user tries to switch commit mode in a @@ -402,21 +414,21 @@ PGAPI_ConnectError( HDBC hdbc, /* -> function sequence error */ break; case CONN_NO_MEMORY_ERROR: - strcpy(szSqlState, "S1001"); + pg_sqlstate_set(env, szSqlState, "HY001", "S1001"); break; case CONN_NOT_IMPLEMENTED_ERROR: case STMT_NOT_IMPLEMENTED_ERROR: - strcpy(szSqlState, "S1C00"); + pg_sqlstate_set(env, szSqlState, "HYC00", "S1C00"); break; case STMT_RETURN_NULL_WITHOUT_INDICATOR: - strcpy(szSqlState, "22002"); + pg_sqlstate_set(env, szSqlState, "22002", "22002"); break; case CONN_VALUE_OUT_OF_RANGE: case STMT_VALUE_OUT_OF_RANGE: - strcpy(szSqlState, "22003"); + pg_sqlstate_set(env, szSqlState, "HY019", "22003"); break; default: - strcpy(szSqlState, "S1000"); + pg_sqlstate_set(env, szSqlState, "HY000", "S1000"); /* general error */ break; } @@ -455,7 +467,7 @@ PGAPI_EnvError( HENV henv, mylog("EN_get_error: status = %d, msg = #%s#\n", status, msg); if (NULL != szSqlState) - strcpy(szSqlState, "00000"); + pg_sqlstate_set(env, szSqlState, "00000", "00000"); if (NULL != pcbErrorMsg) *pcbErrorMsg = 0; if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0)) @@ -478,10 +490,10 @@ PGAPI_EnvError( HENV henv, { case ENV_ALLOC_ERROR: /* memory allocation failure */ - strcpy(szSqlState, "S1001"); + pg_sqlstate_set(env, szSqlState, "HY001", "S1001"); break; default: - strcpy(szSqlState, "S1000"); + pg_sqlstate_set(env, szSqlState, "HY000", "S1000"); /* general error */ break; } diff --git a/src/interfaces/odbc/execute.c b/src/interfaces/odbc/execute.c index f82a327f99..164debbf4d 100644 --- a/src/interfaces/odbc/execute.c +++ b/src/interfaces/odbc/execute.c @@ -212,6 +212,7 @@ PGAPI_Execute( int i, retval, start_row, end_row; int cursor_type, scroll_concurrency; + QResultClass *res; mylog("%s: entering...\n", func); @@ -403,6 +404,23 @@ next_param_row: { if (ipdopts->param_processed_ptr) (*ipdopts->param_processed_ptr)++; + /* special handling of result for keyset driven cursors */ + if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type && + SQL_CONCUR_READ_ONLY != stmt->options.scroll_concurrency) + { + QResultClass *kres; + + res = SC_get_Result(stmt); + if (kres = res->next, kres) + { + kres->fields = res->fields; + res->fields = NULL; + kres->num_fields = res->num_fields; + res->next = NULL; + QR_Destructor(res); + SC_set_Result(stmt, kres); + } + } } #if (ODBCVER >= 0x0300) if (ipdopts->param_status_ptr) @@ -440,7 +458,7 @@ next_param_row: BOOL in_trans = CC_is_in_trans(conn); BOOL issued_begin = FALSE, begin_included = FALSE; - QResultClass *res, *curres; + QResultClass *curres; if (strnicmp(stmt->stmt_with_params, "BEGIN;", 6) == 0) begin_included = TRUE; @@ -474,8 +492,10 @@ next_param_row: stmt->status = STMT_FINISHED; return SQL_SUCCESS; } - else if (stmt->options.cursor_type != cursor_type || - stmt->options.scroll_concurrency != scroll_concurrency) + if (res = SC_get_Curres(stmt), res) + stmt->diag_row_count = res->recent_processed_row_count; + if (stmt->options.cursor_type != cursor_type || + stmt->options.scroll_concurrency != scroll_concurrency) { stmt->errornumber = STMT_OPTION_VALUE_CHANGED; stmt->errormsg = "cursor updatability changed"; @@ -548,7 +568,7 @@ PGAPI_Transact( if (!res) { /* error msg will be in the connection */ - CC_on_abort(conn, TRUE); + CC_on_abort(conn, NO_TRANS); CC_log_error(func, "", conn); return SQL_ERROR; } @@ -558,7 +578,7 @@ PGAPI_Transact( if (!ok) { - CC_on_abort(conn, TRUE); + CC_on_abort(conn, NO_TRANS); CC_log_error(func, "", conn); return SQL_ERROR; } diff --git a/src/interfaces/odbc/info.c b/src/interfaces/odbc/info.c index 75deea4c7b..9b9844e396 100644 --- a/src/interfaces/odbc/info.c +++ b/src/interfaces/odbc/info.c @@ -210,7 +210,10 @@ PGAPI_GetInfo( case SQL_DEFAULT_TXN_ISOLATION: /* ODBC 1.0 */ len = 4; - value = SQL_TXN_READ_COMMITTED; /* SQL_TXN_SERIALIZABLE; */ + if (PG_VERSION_LT(conn, 6.5)) + value = SQL_TXN_SERIALIZABLE; + else + value = SQL_TXN_READ_COMMITTED; break; case SQL_DRIVER_NAME: /* ODBC 1.0 */ @@ -505,7 +508,7 @@ PGAPI_GetInfo( case SQL_POS_OPERATIONS: /* ODBC 2.0 */ len = 4; - value = ci->drivers.lie ? (SQL_POS_POSITION | SQL_POS_REFRESH | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD) : (SQL_POS_POSITION | SQL_POS_REFRESH); + value = (SQL_POS_POSITION | SQL_POS_REFRESH); #ifdef DRIVER_CURSOR_IMPLEMENT if (ci->updatable_cursors) value |= (SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD); @@ -557,32 +560,29 @@ PGAPI_GetInfo( * Driver doesn't support keyset-driven or mixed cursors, so * not much point in saying row updates are supported */ - p = (ci->drivers.lie || ci->updatable_cursors) ? "Y" : "N"; + p = (ci->updatable_cursors) ? "Y" : "N"; break; case SQL_SCROLL_CONCURRENCY: /* ODBC 1.0 */ len = 4; - value = ci->drivers.lie ? (SQL_SCCO_READ_ONLY | - SQL_SCCO_LOCK | - SQL_SCCO_OPT_ROWVER | - SQL_SCCO_OPT_VALUES) : - (SQL_SCCO_READ_ONLY); + value = SQL_SCCO_READ_ONLY; #ifdef DRIVER_CURSOR_IMPLEMENT if (ci->updatable_cursors) value |= SQL_SCCO_OPT_ROWVER; #endif /* DRIVER_CURSOR_IMPLEMENT */ + if (ci->drivers.lie) + value |= (SQL_SCCO_LOCK | SQL_SCCO_OPT_VALUES); break; case SQL_SCROLL_OPTIONS: /* ODBC 1.0 */ len = 4; - value = ci->drivers.lie ? (SQL_SO_FORWARD_ONLY | - SQL_SO_STATIC | - SQL_SO_KEYSET_DRIVEN | - SQL_SO_DYNAMIC | - SQL_SO_MIXED) - : (ci->drivers.use_declarefetch ? SQL_SO_FORWARD_ONLY : (SQL_SO_FORWARD_ONLY | SQL_SO_STATIC)); + value = SQL_SO_FORWARD_ONLY; + if (!ci->drivers.use_declarefetch) + value |= SQL_SO_STATIC; if (ci->updatable_cursors) - value |= 0; /* SQL_SO_KEYSET_DRIVEN in the furure */ + value |= SQL_SO_KEYSET_DRIVEN; + if (ci->drivers.lie) + value |= (SQL_SO_DYNAMIC | SQL_SO_MIXED); break; case SQL_SEARCH_PATTERN_ESCAPE: /* ODBC 1.0 */ @@ -602,7 +602,7 @@ PGAPI_GetInfo( case SQL_STATIC_SENSITIVITY: /* ODBC 2.0 */ len = 4; - value = ci->drivers.lie ? (SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES) : 0; + value = 0; #ifdef DRIVER_CURSOR_IMPLEMENT if (ci->updatable_cursors) value |= (SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES); @@ -666,7 +666,12 @@ PGAPI_GetInfo( case SQL_TXN_ISOLATION_OPTION: /* ODBC 1.0 */ len = 4; - value = SQL_TXN_READ_COMMITTED; /* SQL_TXN_SERIALIZABLE; */ + if (PG_VERSION_LT(conn, 6.5)) + value = SQL_TXN_SERIALIZABLE; + else if (PG_VERSION_GE(conn, 7.1)) + value = SQL_TXN_READ_COMMITTED | SQL_TXN_SERIALIZABLE; + else + value = SQL_TXN_READ_COMMITTED; break; case SQL_UNION: /* ODBC 2.0 */ @@ -2097,7 +2102,7 @@ PGAPI_SpecialColumns( RETCODE result; char relhasrules[MAX_INFO_STRING]; - mylog("%s: entering...stmt=%u scnm=%x len=%d\n", func, stmt, szTableOwner, cbTableOwner); + mylog("%s: entering...stmt=%u scnm=%x len=%d colType=%d\n", func, stmt, szTableOwner, cbTableOwner, fColType); if (!stmt) { @@ -2221,6 +2226,43 @@ PGAPI_SpecialColumns( } } } + else + { + /* use the oid value for the rowid */ + if (fColType == SQL_BEST_ROWID) + { + row = (TupleNode *) malloc(sizeof(TupleNode) + (8 - 1) *sizeof(TupleField)); + + set_tuplefield_int2(&row->tuple[0], SQL_SCOPE_SESSION); + set_tuplefield_string(&row->tuple[1], "oid"); + set_tuplefield_int2(&row->tuple[2], pgtype_to_concise_type(stmt, PG_TYPE_OID)); + set_tuplefield_string(&row->tuple[3], "OID"); + set_tuplefield_int4(&row->tuple[4], pgtype_column_size(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[5], pgtype_buffer_length(stmt, PG_TYPE_OID, PG_STATIC, PG_STATIC)); + set_tuplefield_int2(&row->tuple[6], pgtype_decimal_digits(stmt, PG_TYPE_OID, PG_STATIC)); + set_tuplefield_int2(&row->tuple[7], SQL_PC_NOT_PSEUDO); + + QR_add_tuple(res, row); + + } + else if (fColType == SQL_ROWVER) + { + Int2 the_type = PG_TYPE_TID; + + row = (TupleNode *) malloc(sizeof(TupleNode) + (8 - 1) *sizeof(TupleField)); + + set_tuplefield_null(&row->tuple[0]); + set_tuplefield_string(&row->tuple[1], "ctid"); + set_tuplefield_int2(&row->tuple[2], pgtype_to_concise_type(stmt, the_type)); + set_tuplefield_string(&row->tuple[3], pgtype_to_name(stmt, the_type)); + set_tuplefield_int4(&row->tuple[4], pgtype_column_size(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int4(&row->tuple[5], pgtype_buffer_length(stmt, the_type, PG_STATIC, PG_STATIC)); + set_tuplefield_int2(&row->tuple[6], pgtype_decimal_digits(stmt, the_type, PG_STATIC)); + set_tuplefield_int2(&row->tuple[7], SQL_PC_NOT_PSEUDO); + + QR_add_tuple(res, row); + } + } stmt->status = STMT_FINISHED; stmt->currTuple = -1; @@ -3124,7 +3166,7 @@ getClientColumnName(ConnectionClass *conn, UInt4 relid, char *serverColumnName, { if (res = CC_send_query(conn, "select getdatabaseencoding()", NULL, CLEAR_RESULT_ON_ABORT), res) { - if (QR_get_num_tuples(res) > 0) + if (QR_get_num_backend_tuples(res) > 0) conn->server_encoding = strdup(QR_get_value_backend_row(res, 0, 0)); QR_Destructor(res); } @@ -3140,7 +3182,7 @@ getClientColumnName(ConnectionClass *conn, UInt4 relid, char *serverColumnName, relid, serverColumnName); if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res) { - if (QR_get_num_tuples(res) > 0) + if (QR_get_num_backend_tuples(res) > 0) { strcpy(saveattnum, QR_get_value_backend_row(res, 0, 0)); } @@ -3165,7 +3207,7 @@ getClientColumnName(ConnectionClass *conn, UInt4 relid, char *serverColumnName, sprintf(query, "select attname from pg_attribute where attrelid = %u and attnum = %s", relid, saveattnum); if (res = CC_send_query(conn, query, NULL, CLEAR_RESULT_ON_ABORT), res) { - if (QR_get_num_tuples(res) > 0) + if (QR_get_num_backend_tuples(res) > 0) { ret = strdup(QR_get_value_backend_row(res, 0, 0)); *nameAlloced = TRUE; @@ -4135,7 +4177,7 @@ PGAPI_Procedures( * The following seems the simplest implementation */ if (conn->schema_support) - strcpy(proc_query, "select '' as " "PROCEDURE_CAT" ", case when nspname = 'PUBLIC' then ''::text else nspname end as " "PROCEDURE_SCHEM" "," + strcpy(proc_query, "select '' as " "PROCEDURE_CAT" ", nspname as " "PROCEDURE_SCHEM" "," " proname as " "PROCEDURE_NAME" ", '' as " "NUM_INPUT_PARAMS" "," " '' as " "NUM_OUTPUT_PARAMS" ", '' as " "NUM_RESULT_SETS" "," " '' as " "REMARKS" "," @@ -4204,7 +4246,7 @@ usracl_auth(char *usracl, const char *auth) static void useracl_upd(char (*useracl)[ACLMAX], QResultClass *allures, const char *user, const char *auth) { - int usercount = QR_get_num_tuples(allures), i, addcnt = 0; + int usercount = QR_get_num_backend_tuples(allures), i, addcnt = 0; mylog("user=%s auth=%s\n", user, auth); if (user[0]) @@ -4315,7 +4357,7 @@ PGAPI_TablePrivileges( return SQL_ERROR; } strncpy_null(proc_query, "select usename, usesysid, usesuper from pg_user", sizeof(proc_query)); - tablecount = QR_get_num_tuples(res); + tablecount = QR_get_num_backend_tuples(res); if (allures = CC_send_query(conn, proc_query, NULL, CLEAR_RESULT_ON_ABORT), !allures) { QR_Destructor(res); @@ -4323,7 +4365,7 @@ PGAPI_TablePrivileges( stmt->errormsg = "PGAPI_TablePrivileges query error"; return SQL_ERROR; } - usercount = QR_get_num_tuples(allures); + usercount = QR_get_num_backend_tuples(allures); useracl = (char (*)[ACLMAX]) malloc(usercount * sizeof(char [ACLMAX])); for (i = 0; i < tablecount; i++) { diff --git a/src/interfaces/odbc/info30.c b/src/interfaces/odbc/info30.c index f92c2ab6ff..5024534307 100644 --- a/src/interfaces/odbc/info30.c +++ b/src/interfaces/odbc/info30.c @@ -38,12 +38,11 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1: len = 4; - value = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | - SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK; + value = SQL_CA1_NEXT; /* others aren't allowed in ODBC spec */ break; case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2: len = 4; - value = 0; + value = SQL_CA2_READ_ONLY_CONCURRENCY; break; case SQL_KEYSET_CURSOR_ATTRIBUTES1: len = 4; @@ -71,6 +70,8 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, value = 0; if (ci->updatable_cursors || ci->drivers.lie) value |= (SQL_CA2_OPT_ROWVER_CONCURRENCY + /*| SQL_CA2_CRC_APPROXIMATE*/ + | SQL_CA2_CRC_EXACT | SQL_CA2_SENSITIVITY_DELETIONS | SQL_CA2_SENSITIVITY_UPDATES /* | SQL_CA2_SENSITIVITY_ADDITIONS */ @@ -85,8 +86,6 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, | SQL_CA2_MAX_ROWS_UPDATE | SQL_CA2_MAX_ROWS_CATALOG | SQL_CA2_MAX_ROWS_AFFECTS_ALL - | SQL_CA2_CRC_EXACT - | SQL_CA2_CRC_APPROXIMATE | SQL_CA2_SIMULATE_NON_UNIQUE | SQL_CA2_SIMULATE_TRY_UNIQUE | SQL_CA2_SIMULATE_UNIQUE @@ -101,6 +100,7 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, | SQL_CA1_POS_REFRESH; if (ci->updatable_cursors) value |= (SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE + | SQL_CA1_BULK_ADD ); break; case SQL_STATIC_CURSOR_ATTRIBUTES2: @@ -108,6 +108,7 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, value = SQL_CA2_READ_ONLY_CONCURRENCY; if (ci->updatable_cursors) value |= (SQL_CA2_OPT_ROWVER_CONCURRENCY + | SQL_CA2_CRC_EXACT /* | SQL_CA2_SENSITIVITY_ADDITIONS | SQL_CA2_SENSITIVITY_DELETIONS | SQL_CA2_SENSITIVITY_UPDATES */ @@ -117,6 +118,8 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, case SQL_ODBC_INTERFACE_CONFORMANCE: len = 4; value = SQL_OIC_CORE; + if (ci->drivers.lie) + value = SQL_OIC_LEVEL2; break; case SQL_ACTIVE_ENVIRONMENTS: len = 2; diff --git a/src/interfaces/odbc/misc.c b/src/interfaces/odbc/misc.c index df99994175..e4828455f5 100644 --- a/src/interfaces/odbc/misc.c +++ b/src/interfaces/odbc/misc.c @@ -105,7 +105,7 @@ mylog(char *fmt,...) if (!LOGFP) { generate_filename(MYLOGDIR, MYLOGFILE, filebuf); - LOGFP = fopen(filebuf, PG_BINARY_W); + LOGFP = fopen(filebuf, PG_BINARY_A); setbuf(LOGFP, NULL); } @@ -138,7 +138,7 @@ qlog(char *fmt,...) if (!LOGFP) { generate_filename(QLOGDIR, QLOGFILE, filebuf); - LOGFP = fopen(filebuf, PG_BINARY_W); + LOGFP = fopen(filebuf, PG_BINARY_A); setbuf(LOGFP, NULL); } @@ -284,8 +284,13 @@ schema_strcat(char *buf, const char *fmt, const char *s, int len, const char *tb { if (!s || 0 == len) { - if (tbname && (tbnmlen > 0 || tbnmlen == SQL_NTS)) - return my_strcat(buf, fmt, "public", 6); + /* + * I can find no appropriate way to find + * the CURRENT SCHEMA. If you are lucky + * you can get expected result. + */ + /***** if (tbname && (tbnmlen > 0 || tbnmlen == SQL_NTS)) + return my_strcat(buf, fmt, "public", 6); *****/ return NULL; } return my_strcat(buf, fmt, s, len); diff --git a/src/interfaces/odbc/misc.h b/src/interfaces/odbc/misc.h index fe946eebfa..161920f9b2 100644 --- a/src/interfaces/odbc/misc.h +++ b/src/interfaces/odbc/misc.h @@ -77,10 +77,12 @@ extern void qlog(char *fmt,...); #define PG_BINARY O_BINARY #define PG_BINARY_R "rb" #define PG_BINARY_W "wb" +#define PG_BINARY_A "ab" #else #define PG_BINARY 0 #define PG_BINARY_R "r" #define PG_BINARY_W "w" +#define PG_BINARY_A "a" #endif @@ -91,7 +93,8 @@ char *make_string(const char *s, int len, char *buf); char *my_strcat(char *buf, const char *fmt, const char *s, int len); char *schema_strcat(char *buf, const char *fmt, const char *s, int len, const char *, int); -#define GET_SCHEMA_NAME(nspname) (stricmp(nspname, "public") ? nspname : "") +/* #define GET_SCHEMA_NAME(nspname) (stricmp(nspname, "public") ? nspname : "") */ +#define GET_SCHEMA_NAME(nspname) (nspname) /* defines for return value of my_strcpy */ #define STRCPY_SUCCESS 1 diff --git a/src/interfaces/odbc/odbcapi.c b/src/interfaces/odbc/odbcapi.c index d116d00006..6a4a618b38 100644 --- a/src/interfaces/odbc/odbcapi.c +++ b/src/interfaces/odbc/odbcapi.c @@ -54,6 +54,7 @@ SQLAllocStmt(HDBC ConnectionHandle, HSTMT *StatementHandle) { mylog("[SQLAllocStmt]"); + CC_clear_error((ConnectionClass *) ConnectionHandle); return PGAPI_AllocStmt(ConnectionHandle, StatementHandle); } @@ -64,6 +65,7 @@ SQLBindCol(HSTMT StatementHandle, SQLINTEGER *StrLen_or_Ind) { mylog("[SQLBindCol]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_BindCol(StatementHandle, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); } @@ -72,6 +74,7 @@ RETCODE SQL_API SQLCancel(HSTMT StatementHandle) { mylog("[SQLCancel]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_Cancel(StatementHandle); } @@ -83,6 +86,7 @@ SQLColumns(HSTMT StatementHandle, SQLCHAR *ColumnName, SQLSMALLINT NameLength4) { mylog("[SQLColumns]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_Columns(StatementHandle, CatalogName, NameLength1, SchemaName, NameLength2, TableName, NameLength3, ColumnName, NameLength4, 0); @@ -96,6 +100,7 @@ SQLConnect(HDBC ConnectionHandle, SQLCHAR *Authentication, SQLSMALLINT NameLength3) { mylog("[SQLConnect]"); + CC_clear_error((ConnectionClass *) ConnectionHandle); return PGAPI_Connect(ConnectionHandle, ServerName, NameLength1, UserName, NameLength2, Authentication, NameLength3); } @@ -111,6 +116,7 @@ SQLDriverConnect(HDBC hdbc, UWORD fDriverCompletion) { mylog("[SQLDriverConnect]"); + CC_clear_error((ConnectionClass *) hdbc); return PGAPI_DriverConnect(hdbc, hwnd, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion); } @@ -124,6 +130,7 @@ SQLBrowseConnect( SQLSMALLINT *pcbConnStrOut) { mylog("[SQLBrowseConnect]"); + CC_clear_error((ConnectionClass *) hdbc); return PGAPI_BrowseConnect(hdbc, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut); } @@ -153,6 +160,7 @@ SQLDescribeCol(HSTMT StatementHandle, SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable) { mylog("[SQLDescribeCol]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_DescribeCol(StatementHandle, ColumnNumber, ColumnName, BufferLength, NameLength, DataType, ColumnSize, DecimalDigits, Nullable); @@ -162,6 +170,7 @@ RETCODE SQL_API SQLDisconnect(HDBC ConnectionHandle) { mylog("[SQLDisconnect]"); + CC_clear_error((ConnectionClass *) ConnectionHandle); return PGAPI_Disconnect(ConnectionHandle); } @@ -183,6 +192,7 @@ SQLExecDirect(HSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) { mylog("[SQLExecDirect]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_ExecDirect(StatementHandle, StatementText, TextLength); } @@ -190,6 +200,7 @@ RETCODE SQL_API SQLExecute(HSTMT StatementHandle) { mylog("[SQLExecute]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_Execute(StatementHandle); } @@ -202,6 +213,7 @@ SQLFetch(HSTMT StatementHandle) StatementClass *stmt = (StatementClass *) StatementHandle; ConnectionClass *conn = SC_get_conn(stmt); + SC_clear_error(stmt); if (conn->driver_version >= 0x0300) { IRDFields *irdopts = SC_get_IRD(stmt); @@ -210,7 +222,7 @@ SQLFetch(HSTMT StatementHandle) mylog("[[%s]]", func); return PGAPI_ExtendedFetch(StatementHandle, SQL_FETCH_NEXT, 0, - pcRow, rowStatusArray); + pcRow, rowStatusArray, 0); } #endif mylog("[%s]", func); @@ -244,6 +256,7 @@ SQLGetConnectOption(HDBC ConnectionHandle, SQLUSMALLINT Option, PTR Value) { mylog("[SQLGetConnectOption]"); + CC_clear_error((ConnectionClass *) ConnectionHandle); return PGAPI_GetConnectOption(ConnectionHandle, Option, Value); } RETCODE SQL_API @@ -252,6 +265,7 @@ SQLGetCursorName(HSTMT StatementHandle, SQLSMALLINT *NameLength) { mylog("[SQLGetCursorName]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_GetCursorName(StatementHandle, CursorName, BufferLength, NameLength); } @@ -263,6 +277,7 @@ SQLGetData(HSTMT StatementHandle, SQLINTEGER *StrLen_or_Ind) { mylog("[SQLGetData]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_GetData(StatementHandle, ColumnNumber, TargetType, TargetValue, BufferLength, StrLen_or_Ind); } @@ -272,6 +287,7 @@ SQLGetFunctions(HDBC ConnectionHandle, SQLUSMALLINT FunctionId, SQLUSMALLINT *Supported) { mylog("[SQLGetFunctions]"); + CC_clear_error((ConnectionClass *) ConnectionHandle); #if (ODBCVER >= 0x0300) if (FunctionId == SQL_API_ODBC3_ALL_FUNCTIONS) return PGAPI_GetFunctions30(ConnectionHandle, FunctionId, Supported); @@ -315,6 +331,7 @@ SQLGetStmtOption(HSTMT StatementHandle, SQLUSMALLINT Option, PTR Value) { mylog("[SQLGetStmtOption]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_GetStmtOption(StatementHandle, Option, Value); } @@ -323,6 +340,7 @@ SQLGetTypeInfo(HSTMT StatementHandle, SQLSMALLINT DataType) { mylog("[SQLGetTypeInfo]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_GetTypeInfo(StatementHandle, DataType); } @@ -331,6 +349,7 @@ SQLNumResultCols(HSTMT StatementHandle, SQLSMALLINT *ColumnCount) { mylog("[SQLNumResultCols]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_NumResultCols(StatementHandle, ColumnCount); } @@ -339,6 +358,7 @@ SQLParamData(HSTMT StatementHandle, PTR *Value) { mylog("[SQLParamData]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_ParamData(StatementHandle, Value); } @@ -347,6 +367,7 @@ SQLPrepare(HSTMT StatementHandle, SQLCHAR *StatementText, SQLINTEGER TextLength) { mylog("[SQLPrepare]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_Prepare(StatementHandle, StatementText, TextLength); } @@ -355,6 +376,7 @@ SQLPutData(HSTMT StatementHandle, PTR Data, SQLINTEGER StrLen_or_Ind) { mylog("[SQLPutData]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_PutData(StatementHandle, Data, StrLen_or_Ind); } @@ -363,6 +385,7 @@ SQLRowCount(HSTMT StatementHandle, SQLINTEGER *RowCount) { mylog("[SQLRowCount]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_RowCount(StatementHandle, RowCount); } @@ -371,6 +394,7 @@ SQLSetConnectOption(HDBC ConnectionHandle, SQLUSMALLINT Option, SQLUINTEGER Value) { mylog("[SQLSetConnectionOption]"); + CC_clear_error((ConnectionClass *) ConnectionHandle); return PGAPI_SetConnectOption(ConnectionHandle, Option, Value); } @@ -379,6 +403,7 @@ SQLSetCursorName(HSTMT StatementHandle, SQLCHAR *CursorName, SQLSMALLINT NameLength) { mylog("[SQLSetCursorName]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_SetCursorName(StatementHandle, CursorName, NameLength); } @@ -390,6 +415,7 @@ SQLSetParam(HSTMT StatementHandle, SQLINTEGER *StrLen_or_Ind) { mylog("[SQLSetParam]"); + SC_clear_error((StatementClass *) StatementHandle); /* * return PGAPI_SetParam(StatementHandle, ParameterNumber, ValueType, @@ -404,6 +430,7 @@ SQLSetStmtOption(HSTMT StatementHandle, SQLUSMALLINT Option, SQLUINTEGER Value) { mylog("[SQLSetStmtOption]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_SetStmtOption(StatementHandle, Option, Value); } @@ -416,6 +443,7 @@ SQLSpecialColumns(HSTMT StatementHandle, SQLUSMALLINT Nullable) { mylog("[SQLSpecialColumns]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_SpecialColumns(StatementHandle, IdentifierType, CatalogName, NameLength1, SchemaName, NameLength2, TableName, NameLength3, Scope, Nullable); @@ -429,6 +457,7 @@ SQLStatistics(HSTMT StatementHandle, SQLUSMALLINT Unique, SQLUSMALLINT Reserved) { mylog("[SQLStatistics]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_Statistics(StatementHandle, CatalogName, NameLength1, SchemaName, NameLength2, TableName, NameLength3, Unique, Reserved); @@ -442,6 +471,7 @@ SQLTables(HSTMT StatementHandle, SQLCHAR *TableType, SQLSMALLINT NameLength4) { mylog("[SQLTables]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_Tables(StatementHandle, CatalogName, NameLength1, SchemaName, NameLength2, TableName, NameLength3, TableType, NameLength4); @@ -466,6 +496,7 @@ SQLColAttributes( SQLINTEGER *pfDesc) { mylog("[SQLColAttributes]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_ColAttributes(hstmt, icol, fDescType, rgbDesc, cbDescMax, pcbDesc, pfDesc); } @@ -483,6 +514,7 @@ SQLColumnPrivileges( SQLSMALLINT cbColumnName) { mylog("[SQLColumnPrivileges]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_ColumnPrivileges(hstmt, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szTableName, cbTableName, szColumnName, cbColumnName); @@ -498,6 +530,7 @@ SQLDescribeParam( SQLSMALLINT *pfNullable) { mylog("[SQLDescribeParam]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_DescribeParam(hstmt, ipar, pfSqlType, pcbParamDef, pibScale, pfNullable); } @@ -511,7 +544,8 @@ SQLExtendedFetch( SQLUSMALLINT *rgfRowStatus) { mylog("[SQLExtendedFetch]"); - return PGAPI_ExtendedFetch(hstmt, fFetchType, irow, pcrow, rgfRowStatus); + SC_clear_error((StatementClass *) hstmt); + return PGAPI_ExtendedFetch(hstmt, fFetchType, irow, pcrow, rgfRowStatus, 0); } RETCODE SQL_API @@ -531,6 +565,7 @@ SQLForeignKeys( SQLSMALLINT cbFkTableName) { mylog("[SQLForeignKeys]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_ForeignKeys(hstmt, szPkCatalogName, cbPkCatalogName, szPkSchemaName, cbPkSchemaName, szPkTableName, cbPkTableName, szFkCatalogName, cbFkCatalogName, @@ -541,6 +576,7 @@ RETCODE SQL_API SQLMoreResults(HSTMT hstmt) { mylog("[SQLMoreResults]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_MoreResults(hstmt); } @@ -554,6 +590,7 @@ SQLNativeSql( SQLINTEGER *pcbSqlStr) { mylog("[SQLNativeSql]"); + CC_clear_error((ConnectionClass *) hdbc); return PGAPI_NativeSql(hdbc, szSqlStrIn, cbSqlStrIn, szSqlStr, cbSqlStrMax, pcbSqlStr); } @@ -564,6 +601,7 @@ SQLNumParams( SQLSMALLINT *pcpar) { mylog("[SQLNumParams]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_NumParams(hstmt, pcpar); } @@ -574,6 +612,7 @@ SQLParamOptions( SQLUINTEGER *pirow) { mylog("[SQLParamOptions]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_ParamOptions(hstmt, crow, pirow); } @@ -588,6 +627,7 @@ SQLPrimaryKeys( SQLSMALLINT cbTableName) { mylog("[SQLPrimaryKeys]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_PrimaryKeys(hstmt, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szTableName, cbTableName); } @@ -605,6 +645,7 @@ SQLProcedureColumns( SQLSMALLINT cbColumnName) { mylog("[SQLProcedureColumns]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_ProcedureColumns(hstmt, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szProcName, cbProcName, szColumnName, cbColumnName); @@ -621,6 +662,7 @@ SQLProcedures( SQLSMALLINT cbProcName) { mylog("[SQLProcedures]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_Procedures(hstmt, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szProcName, cbProcName); } @@ -633,6 +675,7 @@ SQLSetPos( SQLUSMALLINT fLock) { mylog("[SQLSetPos]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_SetPos(hstmt, irow, fOption, fLock); } @@ -647,6 +690,7 @@ SQLTablePrivileges( SQLSMALLINT cbTableName) { mylog("[SQLTablePrivileges]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_TablePrivileges(hstmt, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szTableName, cbTableName, 0); } @@ -665,6 +709,7 @@ SQLBindParameter( SQLINTEGER *pcbValue) { mylog("[SQLBindParameter]"); + SC_clear_error((StatementClass *) hstmt); return PGAPI_BindParameter(hstmt, ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, cbValueMax, pcbValue); diff --git a/src/interfaces/odbc/odbcapi30.c b/src/interfaces/odbc/odbcapi30.c index 0c03007701..cf73924065 100644 --- a/src/interfaces/odbc/odbcapi30.c +++ b/src/interfaces/odbc/odbcapi30.c @@ -61,6 +61,7 @@ SQLBindParam(HSTMT StatementHandle, int BufferLength = 512; /* Is it OK ? */ mylog("[[SQLBindParam]]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_BindParameter(StatementHandle, ParameterNumber, SQL_PARAM_INPUT, ValueType, ParameterType, LengthPrecision, ParameterScale, ParameterValue, BufferLength, StrLen_or_Ind); } @@ -69,6 +70,7 @@ RETCODE SQL_API SQLCloseCursor(HSTMT StatementHandle) { mylog("[[SQLCloseCursor]]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_FreeStmt(StatementHandle, SQL_CLOSE); } @@ -80,6 +82,7 @@ SQLColAttribute(HSTMT StatementHandle, SQLSMALLINT *StringLength, PTR NumericAttribute) { mylog("[[SQLColAttribute]]"); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_ColAttributes(StatementHandle, ColumnNumber, FieldIdentifier, CharacterAttribute, BufferLength, StringLength, NumericAttribute); @@ -140,6 +143,7 @@ SQLEndTran(SQLSMALLINT HandleType, SQLHANDLE Handle, case SQL_HANDLE_ENV: return PGAPI_Transact(Handle, SQL_NULL_HDBC, CompletionType); case SQL_HANDLE_DBC: + CC_clear_error((ConnectionClass *) Handle); return PGAPI_Transact(SQL_NULL_HENV, Handle, CompletionType); default: break; @@ -157,16 +161,18 @@ SQLFetchScroll(HSTMT StatementHandle, RETCODE ret; IRDFields *irdopts = SC_get_IRD(stmt); SQLUSMALLINT *rowStatusArray = irdopts->rowStatusArray; - SQLINTEGER *pcRow = irdopts->rowsFetched; + SQLINTEGER *pcRow = irdopts->rowsFetched, bkmarkoff = 0; mylog("[[%s]] %d,%d\n", func, FetchOrientation, FetchOffset); + SC_clear_error(stmt); if (FetchOrientation == SQL_FETCH_BOOKMARK) { if (stmt->options.bookmark_ptr) -{ - FetchOffset += *((Int4 *) stmt->options.bookmark_ptr); -mylog("real FetchOffset = %d\n", FetchOffset); -} + { + bkmarkoff = FetchOffset; + FetchOffset = *((Int4 *) stmt->options.bookmark_ptr); +mylog("bookmark=%u FetchOffset = %d\n", FetchOffset, bkmarkoff); + } else { stmt->errornumber = STMT_SEQUENCE_ERROR; @@ -176,7 +182,7 @@ mylog("real FetchOffset = %d\n", FetchOffset); } } ret = PGAPI_ExtendedFetch(StatementHandle, FetchOrientation, FetchOffset, - pcRow, rowStatusArray); + pcRow, rowStatusArray, bkmarkoff); if (ret != SQL_SUCCESS) mylog("%s return = %d\n", func, ret); return ret; @@ -288,6 +294,7 @@ SQLGetConnectAttr(HDBC ConnectionHandle, SQLINTEGER BufferLength, SQLINTEGER *StringLength) { mylog("[[SQLGetConnectAttr]] %d\n", Attribute); + CC_clear_error((ConnectionClass *) ConnectionHandle); return PGAPI_GetConnectAttr(ConnectionHandle, Attribute,Value, BufferLength, StringLength); } @@ -301,6 +308,7 @@ SQLGetStmtAttr(HSTMT StatementHandle, static char *func = "SQLGetStmtAttr"; mylog("[[%s]] Handle=%u %d\n", func, StatementHandle, Attribute); + SC_clear_error((StatementClass *) StatementHandle); return PGAPI_GetStmtAttr(StatementHandle, Attribute, Value, BufferLength, StringLength); } @@ -314,6 +322,7 @@ SQLSetConnectAttr(HDBC ConnectionHandle, ConnectionClass *conn = (ConnectionClass *) ConnectionHandle; mylog("[[SQLSetConnectAttr]] %d\n", Attribute); + CC_clear_error(conn); return PGAPI_SetConnectAttr(ConnectionHandle, Attribute, Value, StringLength); } @@ -396,6 +405,7 @@ SQLSetStmtAttr(HSTMT StatementHandle, StatementClass *stmt = (StatementClass *) StatementHandle; mylog("[[%s]] Handle=%u %d,%u\n", func, StatementHandle, Attribute, Value); + SC_clear_error(stmt); return PGAPI_SetStmtAttr(StatementHandle, Attribute, Value, StringLength); } @@ -409,6 +419,7 @@ PGAPI_GetFunctions30(HDBC hdbc, UWORD fFunction, UWORD FAR * pfExists) ConnectionClass *conn = (ConnectionClass *) hdbc; ConnInfo *ci = &(conn->connInfo); + CC_clear_error(conn); if (fFunction != SQL_API_ODBC3_ALL_FUNCTIONS) return SQL_ERROR; memset(pfExists, 0, sizeof(UWORD) * SQL_API_ODBC3_ALL_FUNCTIONS_SIZE); @@ -497,12 +508,12 @@ PGAPI_GetFunctions30(HDBC hdbc, UWORD fFunction, UWORD FAR * pfExists) SQL_FUNC_ESET(pfExists, SQL_API_SQLENDTRAN); /* 1005 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLFREEHANDLE); /* 1006 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETCONNECTATTR); /* 1007 */ + SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDESCFIELD); /* 1008 */ if (ci->drivers.lie) { - SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDESCFIELD); /* 1008 not implemented yet */ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDESCREC); /* 1009 not implemented yet */ - SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDIAGFIELD); /* 1010 not implemented yet */ } + SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDIAGFIELD); /* 1010 minimal implementation */ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETDIAGREC); /* 1011 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETENVATTR); /* 1012 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLGETSTMTATTR); /* 1014 */ @@ -525,72 +536,15 @@ RETCODE SQL_API SQLBulkOperations(HSTMT hstmt, SQLSMALLINT operation) { static char *func = "SQLBulkOperations"; - StatementClass *stmt = (StatementClass *) hstmt; #ifndef DRIVER_CURSOR_IMPLEMENT + StatementClass *stmt = (StatementClass *) hstmt; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errormsg = "driver must be compiled with the DRIVER_CURSOR_IMPLEMENT option"; SC_log_error(func, "", stmt); return SQL_ERROR; #else - ARDFields *opts = SC_get_ARD(stmt); - RETCODE ret; - UInt4 offset, bind_size = opts->bind_size, *bmark; - int i, processed; - ConnectionClass *conn; - BOOL auto_commit_needed = FALSE; - - mylog("[[%s]] operation = %d\n", func, operation); - offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0; - switch (operation) - { - case SQL_ADD: - ret = PGAPI_SetPos(hstmt, 0, operation, SQL_LOCK_NO_CHANGE); - break; - default: - if (SQL_FETCH_BY_BOOKMARK != operation) - { - conn = SC_get_conn(stmt); - if (auto_commit_needed = CC_is_in_autocommit(conn), auto_commit_needed) - PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, -SQL_AUTOCOMMIT_OFF); - } - if (bmark = (UInt4 *) opts->bookmark->buffer, !bmark) - { - stmt->errormsg = "bookmark isn't specified"; - return SQL_ERROR; - } - bmark += (offset >> 4); - for (i = 0, processed = 0; i < opts->rowset_size; i++) - { - if (!opts->row_operation_ptr || SQL_ROW_PROCEED == opts->row_operation_ptr[i]) - { - switch (operation) - { - case SQL_UPDATE_BY_BOOKMARK: - ret = SC_pos_update(stmt, (UWORD) i, *bmark); - break; - case SQL_DELETE_BY_BOOKMARK: - ret = SC_pos_delete(stmt, (UWORD) i, *bmark); - break; - case SQL_FETCH_BY_BOOKMARK: - ret = SC_pos_refresh(stmt, (UWORD) i, *bmark); - break; - } - processed++; - if (SQL_ERROR == ret) - break; - if (bind_size > 0) - bmark += (bind_size >> 2); - else - bmark++; - } - } - if (auto_commit_needed) - PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON); - if (SC_get_IRD(stmt)->rowsFetched) - *SC_get_IRD(stmt)->rowsFetched = processed; - break; - } - return ret; + mylog("[[%s]] Handle=%u %d\n", func, hstmt, operation); + SC_clear_error((StatementClass *) hstmt); + return PGAPI_BulkOperations(hstmt, operation); #endif /* DRIVER_CURSOR_IMPLEMENT */ } diff --git a/src/interfaces/odbc/odbcapi30w.c b/src/interfaces/odbc/odbcapi30w.c index ac0f1d9931..8992353b31 100644 --- a/src/interfaces/odbc/odbcapi30w.c +++ b/src/interfaces/odbc/odbcapi30w.c @@ -30,6 +30,7 @@ RETCODE SQL_API SQLGetStmtAttrW(SQLHSTMT hstmt, RETCODE ret; mylog("[SQLGetStmtAttrW]"); + SC_clear_error((StatementClass *) hstmt); ret = PGAPI_GetStmtAttr(hstmt, fAttribute, rgbValue, cbValueMax, pcbValue); return ret; @@ -43,6 +44,7 @@ RETCODE SQL_API SQLSetStmtAttrW(SQLHSTMT hstmt, RETCODE ret; mylog("[SQLSetStmtAttrW]"); + SC_clear_error((StatementClass *) hstmt); ret = PGAPI_SetStmtAttr(hstmt, fAttribute, rgbValue, cbValueMax); return ret; @@ -57,6 +59,7 @@ RETCODE SQL_API SQLGetConnectAttrW(HDBC hdbc, RETCODE ret; mylog("[SQLGetConnectAttrW]"); + CC_clear_error((ConnectionClass *) hdbc); ret = PGAPI_GetConnectAttr(hdbc, fAttribute, rgbValue, cbValueMax, pcbValue); return ret; @@ -70,6 +73,7 @@ RETCODE SQL_API SQLSetConnectAttrW(HDBC hdbc, RETCODE ret; mylog("[SQLSetConnectAttrW]"); + CC_clear_error((ConnectionClass *) hdbc); ret = PGAPI_SetConnectAttr(hdbc, fAttribute, rgbValue, cbValue); return ret; @@ -229,6 +233,7 @@ RETCODE SQL_API SQLColAttributeW( char *rgbD = NULL; mylog("[SQLColAttributeW]"); + SC_clear_error((StatementClass *) hstmt); switch (fDescType) { case SQL_DESC_BASE_COLUMN_NAME: diff --git a/src/interfaces/odbc/options.c b/src/interfaces/odbc/options.c index ab974c3cd0..f4a510a587 100644 --- a/src/interfaces/odbc/options.c +++ b/src/interfaces/odbc/options.c @@ -95,9 +95,13 @@ set_statement_option(ConnectionClass *conn, ; else if (SQL_CURSOR_STATIC == vParam) setval = vParam; - /** else if (SQL_CURSOR_KEYSET_DRIVEN == vParam && ci->updatable) - setval = vParam; **/ - + else if (SQL_CURSOR_KEYSET_DRIVEN == vParam) + { + if (ci->updatable_cursors) + setval = vParam; + else + setval = SQL_CURSOR_STATIC; /* at least scrollable */ + } if (conn) conn->stmtOptions.cursor_type = setval; else if (stmt) @@ -372,6 +376,60 @@ PGAPI_SetConnectOption( break; case SQL_TXN_ISOLATION: /* ignored */ + retval = SQL_SUCCESS; + if (CC_is_in_trans(conn)) + { + conn->errormsg = "Cannot switch isolation level while a transaction is in progress"; + conn->errornumber = CONN_TRANSACT_IN_PROGRES; + CC_log_error(func, "", conn); + return SQL_ERROR; + } + if (conn->isolation == vParam) + break; + switch (vParam) + { + case SQL_TXN_SERIALIZABLE: + if (PG_VERSION_GE(conn, 6.5) && + PG_VERSION_LE(conn, 7.0)) + retval = SQL_ERROR; + break; + case SQL_TXN_READ_COMMITTED: + if (PG_VERSION_LT(conn, 6.5)) + retval = SQL_ERROR; + break; + default: + retval = SQL_ERROR; + } + if (SQL_ERROR == retval) + { + conn->errornumber = CONN_INVALID_ARGUMENT_NO; + conn->errormsg = "Illegal parameter value for SQL_TXN_ISOLATION"; + CC_log_error(func, "", conn); + return SQL_ERROR; + } + else + { + char *query; + QResultClass *res; + + if (vParam == SQL_TXN_SERIALIZABLE) + query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE"; + else + query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED"; + res = CC_send_query(conn, query, NULL, 0); + if (!res || !QR_command_maybe_successful(res)) + retval = SQL_ERROR; + else + conn->isolation = vParam; + if (res) + QR_Destructor(res); + if (SQL_ERROR == retval) + { + conn->errornumber = STMT_EXEC_ERROR; + conn->errormsg = "ISOLATION change request to the server error"; + return SQL_ERROR; + } + } break; /* These options should be handled by driver manager */ @@ -476,8 +534,8 @@ PGAPI_GetConnectOption( *((UDWORD *) pvParam) = (UDWORD) NULL; break; - case SQL_TXN_ISOLATION: /* NOT SUPPORTED */ - *((UDWORD *) pvParam) = SQL_TXN_READ_COMMITTED; + case SQL_TXN_ISOLATION: + *((UDWORD *) pvParam) = conn->isolation; break; /* These options should be handled by driver manager */ @@ -567,7 +625,7 @@ PGAPI_GetStmtOption( { /* make sure we're positioned on a valid row */ if ((stmt->currTuple < 0) || - (stmt->currTuple >= QR_get_num_tuples(res))) + (stmt->currTuple >= QR_get_num_backend_tuples(res))) { stmt->errormsg = "Not positioned on a valid row."; stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR; diff --git a/src/interfaces/odbc/parse.c b/src/interfaces/odbc/parse.c index cb77fddd83..2ca0ef00f9 100644 --- a/src/interfaces/odbc/parse.c +++ b/src/interfaces/odbc/parse.c @@ -50,6 +50,29 @@ char *getNextToken(char *s, char *token, int smax, char *delim, char *quote, void getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k); char searchColInfo(COL_INFO *col_info, FIELD_INFO *fi); +Int4 FI_precision(const FIELD_INFO *fi) +{ + if (!fi) return -1; + switch (fi->type) + { + case PG_TYPE_NUMERIC: + return fi->column_size; + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP_NO_TMZONE: + return fi->decimal_digits; + } + return 0; +} +Int4 FI_scale(const FIELD_INFO *fi) +{ + if (!fi) return -1; + switch (fi->type) + { + case PG_TYPE_NUMERIC: + return fi->decimal_digits; + } + return 0; +} char * getNextToken( @@ -265,7 +288,7 @@ searchColInfo(COL_INFO *col_info, FIELD_INFO *fi) cmp; char *col; - for (k = 0; k < QR_get_num_tuples(col_info->result); k++) + for (k = 0; k < QR_get_num_backend_tuples(col_info->result); k++) { col = QR_get_value_manual(col_info->result, k, 3); if (fi->dquote) @@ -291,7 +314,7 @@ char parse_statement(StatementClass *stmt) { static char *func = "parse_statement"; - char token[256]; + char token[256], stoken[256]; char delim, quote, dquote, @@ -315,7 +338,7 @@ parse_statement(StatementClass *stmt) i, k = 0, n, - blevel = 0; + blevel = 0, old_blevel, subqlevel = 0; FIELD_INFO **fi; TABLE_INFO **ti; char parse; @@ -347,42 +370,43 @@ parse_statement(StatementClass *stmt) mylog("unquoted=%d, quote=%d, dquote=%d, numeric=%d, delim='%c', token='%s', ptr='%s'\n", unquoted, quote, dquote, numeric, delim, token, ptr); - if (in_select && unquoted && blevel == 0) + old_blevel = blevel; + if (unquoted && blevel == 0) { - if (!stricmp(token, "distinct")) + if (in_select) { - in_distinct = TRUE; - updatable = FALSE; + if (!stricmp(token, "distinct")) + { + in_distinct = TRUE; + updatable = FALSE; - mylog("DISTINCT\n"); - continue; - } - if (!stricmp(token, "into")) - { - in_select = FALSE; - mylog("INTO\n"); - stmt->statement_type = STMT_TYPE_CREATE; - stmt->parse_status = STMT_PARSE_FATAL; - return FALSE; - } - if (!stricmp(token, "from")) - { - in_select = FALSE; - in_from = TRUE; - if (stmt->from_pos < 0 && - (!strnicmp(pptr, "from", 4))) + mylog("DISTINCT\n"); + continue; + } + else if (!stricmp(token, "into")) { - mylog("First "); - stmt->from_pos = pptr - stmt->statement; + in_select = FALSE; + mylog("INTO\n"); + stmt->statement_type = STMT_TYPE_CREATE; + stmt->parse_status = STMT_PARSE_FATAL; + return FALSE; } + else if (!stricmp(token, "from")) + { + in_select = FALSE; + in_from = TRUE; + if (stmt->from_pos < 0 && + (!strnicmp(pptr, "from", 4))) + { + mylog("First "); + stmt->from_pos = pptr - stmt->statement; + } - mylog("FROM\n"); - continue; - } - } /* in_select && unquoted && blevel == 0 */ - if (unquoted && blevel == 0) - { - if ((!stricmp(token, "where") || + mylog("FROM\n"); + continue; + } + } /* in_select && unquoted && blevel == 0 */ + else if ((!stricmp(token, "where") || !stricmp(token, "union") || !stricmp(token, "intersect") || !stricmp(token, "except") || @@ -390,7 +414,6 @@ parse_statement(StatementClass *stmt) !stricmp(token, "group") || !stricmp(token, "having"))) { - in_select = FALSE; in_from = FALSE; in_where = TRUE; @@ -406,54 +429,82 @@ parse_statement(StatementClass *stmt) continue; } } /* unquoted && blevel == 0 */ - if (in_select && (in_expr || in_func)) + /* check the change of blevel etc */ + if (unquoted) { - /* just eat the expression */ - mylog("in_expr=%d or func=%d\n", in_expr, in_func); - - if (unquoted) + if (!stricmp(token, "select")) { - if (token[0] == '(') + stoken[0] = '\0'; + if (0 == blevel) { - blevel++; - mylog("blevel++ = %d\n", blevel); + in_select = TRUE; + mylog("SELECT\n"); + continue; } - else if (token[0] == ')') + else { - blevel--; - mylog("blevel-- = %d\n", blevel); + mylog("SUBSELECT\n"); + if (0 == subqlevel) + subqlevel = blevel; } } - if (blevel == 0) + else if (token[0] == '(') { - if (delim == ',') + blevel++; + mylog("blevel++ = %d\n", blevel); + /* aggregate function ? */ + if (stoken[0] && updatable && 0 == subqlevel) { - mylog("**** Got comma in_expr/func\n"); - in_func = FALSE; - in_expr = FALSE; - in_field = FALSE; - } - else if (unquoted && !stricmp(token, "as")) - { - mylog("got AS in_expr\n"); - in_func = FALSE; - in_expr = FALSE; - in_as = TRUE; - in_field = TRUE; + if (stricmp(stoken, "count") == 0 || + stricmp(stoken, "sum") == 0 || + stricmp(stoken, "avg") == 0 || + stricmp(stoken, "max") == 0 || + stricmp(stoken, "min") == 0 || + stricmp(stoken, "variance") == 0 || + stricmp(stoken, "stddev") == 0) + updatable = FALSE; } } - continue; - } /* in_select && (in_expr || in_func) */ - - if (unquoted && !stricmp(token, "select")) - { - in_select = TRUE; - - mylog("SELECT\n"); - continue; + else if (token[0] == ')') + { + blevel--; + mylog("blevel-- = %d\n", blevel); + if (blevel < subqlevel) + subqlevel = 0; + } + if (blevel >= old_blevel && ',' != delim) + strcpy(stoken, token); + else + stoken[0] = '\0'; } if (in_select) { + if (in_expr || in_func) + { + /* just eat the expression */ + mylog("in_expr=%d or func=%d\n", in_expr, in_func); + + if (blevel == 0) + { + if (delim == ',') + { + mylog("**** Got comma in_expr/func\n"); + in_func = FALSE; + in_expr = FALSE; + in_field = FALSE; + } + else if (unquoted && !stricmp(token, "as")) + { + mylog("got AS in_expr\n"); + in_func = FALSE; + in_expr = FALSE; + in_as = TRUE; + in_field = TRUE; + } + } + continue; + } /* (in_expr || in_func) && in_select */ + if (in_distinct) { mylog("in distinct\n"); @@ -515,12 +566,11 @@ parse_statement(StatementClass *stmt) mylog("**** got numeric: nfld = %d\n", irdflds->nfields); fi[irdflds->nfields]->numeric = TRUE; } - else if (token[0] == '(') + else if (0 == old_blevel && blevel > 0) { /* expression */ mylog("got EXPRESSION\n"); fi[irdflds->nfields++]->expr = TRUE; in_expr = TRUE; - blevel = 1; continue; } else @@ -579,11 +629,10 @@ parse_statement(StatementClass *stmt) } /* Function */ - if (token[0] == '(') + if (0 == old_blevel && blevel > 0) { in_dot = FALSE; in_func = TRUE; - blevel = 1; fi[irdflds->nfields - 1]->func = TRUE; /* @@ -654,6 +703,7 @@ parse_statement(StatementClass *stmt) ti[stmt->ntab]->schema[0] = '\0'; ti[stmt->ntab]->alias[0] = '\0'; + ti[stmt->ntab]->updatable = 1; strcpy(ti[stmt->ntab]->name, token); if (!dquote) @@ -845,6 +895,37 @@ parse_statement(StatementClass *stmt) col_stmt = (StatementClass *) hcol_stmt; col_stmt->internal = TRUE; + if (!ti[i]->schema[0] && conn->schema_support) + { + QResultClass *res; + BOOL tblFound = FALSE; + + /* Unfortunately CURRENT_SCHEMA doesn't exist + * in PostgreSQL and we have to check as follows. + */ + sprintf(token, "select nspname from pg_namespace n, pg_class c" + " where c.relnamespace=n.oid and c.oid='%s'::regclass", ti[i]->name); + res = CC_send_query(conn, token, NULL, CLEAR_RESULT_ON_ABORT); + if (res) + { + if (QR_get_num_total_tuples(res) == 1) + { + tblFound = TRUE; + strcpy(ti[i]->schema, QR_get_value_backend_row(res, 0, 0)); + } + QR_Destructor(res); + } + else + CC_abort(conn); + if (!tblFound) + { + stmt->parse_status = STMT_PARSE_FATAL; + stmt->errornumber = STMT_EXEC_ERROR; + stmt->errormsg = "Table not found"; + stmt->updatable = FALSE; + return FALSE; + } + } result = PGAPI_Columns(hcol_stmt, "", 0, ti[i]->schema, SQL_NTS, ti[i]->name, SQL_NTS, "", 0, PODBC_NOT_SEARCH_PATTERN); @@ -907,6 +988,8 @@ parse_statement(StatementClass *stmt) /* * Now resolve the fields to point to column info */ + if (updatable && 1 == stmt->ntab) + updatable = stmt->ti[0]->updatable; for (i = 0; i < (int) irdflds->nfields;) { fi[i]->updatable = updatable; @@ -934,14 +1017,14 @@ parse_statement(StatementClass *stmt) if (fi[i]->ti) /* The star represents only the qualified * table */ - total_cols = QR_get_num_tuples(fi[i]->ti->col_info->result); + total_cols = QR_get_num_backend_tuples(fi[i]->ti->col_info->result); else { /* The star represents all tables */ /* Calculate the total number of columns after expansion */ for (k = 0; k < stmt->ntab; k++) - total_cols += QR_get_num_tuples(ti[k]->col_info->result); + total_cols += QR_get_num_backend_tuples(ti[k]->col_info->result); } increased_cols = total_cols - 1; @@ -988,7 +1071,7 @@ parse_statement(StatementClass *stmt) { TABLE_INFO *the_ti = do_all_tables ? ti[k] : fi[i]->ti; - cols = QR_get_num_tuples(the_ti->col_info->result); + cols = QR_get_num_backend_tuples(the_ti->col_info->result); for (n = 0; n < cols; n++) { diff --git a/src/interfaces/odbc/pgapi30.c b/src/interfaces/odbc/pgapi30.c index e737739315..7fd6c9ac1c 100644 --- a/src/interfaces/odbc/pgapi30.c +++ b/src/interfaces/odbc/pgapi30.c @@ -25,6 +25,7 @@ #include "connection.h" #include "statement.h" #include "descriptor.h" +#include "qresult.h" #include "pgapifunc.h" static HSTMT statementHandleFromDescHandle(SQLHDESC, SQLINTEGER *descType); @@ -69,13 +70,22 @@ PGAPI_GetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, return ret; } +/* + * Minimal implementation. + * + */ RETCODE SQL_API PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, PTR DiagInfoPtr, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr) { - RETCODE ret = SQL_ERROR; + RETCODE ret = SQL_ERROR, rtn; + ConnectionClass *conn; + SQLHANDLE stmtHandle; + StatementClass *stmt; + SDWORD rc; + SWORD pcbErrm; static const char *func = "PGAPI_GetDiagField"; mylog("%s entering rec=%d", func, RecNumber); @@ -87,43 +97,243 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, case SQL_DIAG_CLASS_ORIGIN: case SQL_DIAG_SUBCLASS_ORIGIN: case SQL_DIAG_CONNECTION_NAME: + case SQL_DIAG_SERVER_NAME: + strcpy((char *) DiagInfoPtr, ""); + if (StringLengthPtr) + *StringLengthPtr = 0; + ret = SQL_SUCCESS; + break; case SQL_DIAG_MESSAGE_TEXT: + ret = PGAPI_EnvError(Handle, RecNumber, + NULL, NULL, DiagInfoPtr, + BufferLength, StringLengthPtr, 0); + break; case SQL_DIAG_NATIVE: + ret = PGAPI_EnvError(Handle, RecNumber, + NULL, DiagInfoPtr, NULL, + 0, NULL, 0); + if (StringLengthPtr) + *StringLengthPtr = sizeof(SQLINTEGER); + if (SQL_SUCCESS_WITH_INFO == ret) + ret = SQL_SUCCESS; + break; case SQL_DIAG_NUMBER: - case SQL_DIAG_RETURNCODE: - case SQL_DIAG_SERVER_NAME: + ret = PGAPI_EnvError(Handle, RecNumber, + NULL, NULL, NULL, + 0, NULL, 0); + if (SQL_SUCCESS == ret || + SQL_SUCCESS_WITH_INFO == ret) + { + *((SQLINTEGER *) DiagInfoPtr) = 1; + if (StringLengthPtr) + *StringLengthPtr = sizeof(SQLINTEGER); + ret = SQL_SUCCESS; + } + break; case SQL_DIAG_SQLSTATE: + ret = PGAPI_EnvError(Handle, RecNumber, + DiagInfoPtr, NULL, NULL, + 0, NULL, 0); + if (StringLengthPtr) + *StringLengthPtr = 5; + if (SQL_SUCCESS_WITH_INFO == ret) + ret = SQL_SUCCESS; + break; + case SQL_DIAG_RETURNCODE: /* driver manager returns */ + break; + case SQL_DIAG_CURSOR_ROW_COUNT: + case SQL_DIAG_ROW_COUNT: + case SQL_DIAG_DYNAMIC_FUNCTION: + case SQL_DIAG_DYNAMIC_FUNCTION_CODE: + /* options for statement type only */ break; } break; case SQL_HANDLE_DBC: + conn = (ConnectionClass *) Handle; switch (DiagIdentifier) { case SQL_DIAG_CLASS_ORIGIN: case SQL_DIAG_SUBCLASS_ORIGIN: case SQL_DIAG_CONNECTION_NAME: + strcpy((char *) DiagInfoPtr, ""); + if (StringLengthPtr) + *StringLengthPtr = 0; + ret = SQL_SUCCESS; + break; + case SQL_DIAG_SERVER_NAME: + strcpy((SQLCHAR *) DiagInfoPtr, CC_get_DSN(conn)); + if (StringLengthPtr) + *StringLengthPtr = strlen(CC_get_DSN(conn)); + ret = SQL_SUCCESS; + break; case SQL_DIAG_MESSAGE_TEXT: + ret = PGAPI_ConnectError(Handle, RecNumber, + NULL, NULL, DiagInfoPtr, + BufferLength, StringLengthPtr, 0); + break; case SQL_DIAG_NATIVE: + ret = PGAPI_ConnectError(Handle, RecNumber, + NULL, DiagInfoPtr, NULL, + 0, NULL, 0); + if (StringLengthPtr) + *StringLengthPtr = sizeof(SQLINTEGER); + if (SQL_SUCCESS_WITH_INFO == ret) + ret = SQL_SUCCESS; + break; case SQL_DIAG_NUMBER: - case SQL_DIAG_RETURNCODE: - case SQL_DIAG_SERVER_NAME: + ret = PGAPI_ConnectError(Handle, RecNumber, + NULL, NULL, NULL, + 0, NULL, 0); + if (SQL_SUCCESS == ret || + SQL_SUCCESS_WITH_INFO == ret) + { + *((SQLINTEGER *) DiagInfoPtr) = 1; + if (StringLengthPtr) + *StringLengthPtr = sizeof(SQLINTEGER); + ret = SQL_SUCCESS; + } + break; case SQL_DIAG_SQLSTATE: + ret = PGAPI_ConnectError(Handle, RecNumber, + DiagInfoPtr, NULL, NULL, + 0, NULL, 0); + if (StringLengthPtr) + *StringLengthPtr = 5; + if (SQL_SUCCESS_WITH_INFO == ret) + ret = SQL_SUCCESS; + break; + case SQL_DIAG_RETURNCODE: /* driver manager returns */ + break; + case SQL_DIAG_CURSOR_ROW_COUNT: + case SQL_DIAG_ROW_COUNT: + case SQL_DIAG_DYNAMIC_FUNCTION: + case SQL_DIAG_DYNAMIC_FUNCTION_CODE: + /* options for statement type only */ break; } break; case SQL_HANDLE_STMT: + conn = (ConnectionClass *) SC_get_conn(((StatementClass *) Handle)); switch (DiagIdentifier) { case SQL_DIAG_CLASS_ORIGIN: case SQL_DIAG_SUBCLASS_ORIGIN: case SQL_DIAG_CONNECTION_NAME: + strcpy((char *) DiagInfoPtr, ""); + if (StringLengthPtr) + *StringLengthPtr = 0; + ret = SQL_SUCCESS; + break; + case SQL_DIAG_SERVER_NAME: + strcpy((SQLCHAR *) DiagInfoPtr, CC_get_DSN(conn)); + if (StringLengthPtr) + *StringLengthPtr = strlen(CC_get_DSN(conn)); + ret = SQL_SUCCESS; + break; case SQL_DIAG_MESSAGE_TEXT: + ret = PGAPI_StmtError(Handle, RecNumber, + NULL, NULL, DiagInfoPtr, + BufferLength, StringLengthPtr, 0); + break; case SQL_DIAG_NATIVE: + ret = PGAPI_StmtError(Handle, RecNumber, + NULL, DiagInfoPtr, NULL, + 0, NULL, 0); + if (StringLengthPtr) + *StringLengthPtr = sizeof(SQLINTEGER); + if (SQL_SUCCESS_WITH_INFO == ret) + ret = SQL_SUCCESS; + break; case SQL_DIAG_NUMBER: - case SQL_DIAG_RETURNCODE: + *((SQLINTEGER *) DiagInfoPtr) = 0; + ret = SQL_NO_DATA_FOUND; + stmt = (StatementClass *) Handle; + do + { + rtn = PGAPI_StmtError(Handle, RecNumber, + NULL, NULL, NULL, + 0, &pcbErrm, 0); + if (SQL_SUCCESS == rtn || + SQL_SUCCESS_WITH_INFO == rtn) + { + *((SQLINTEGER *) DiagInfoPtr)++; + ret = SQL_SUCCESS; + } + } while (pcbErrm >= stmt->error_recsize); + if (StringLengthPtr) + *StringLengthPtr = sizeof(SQLINTEGER); + break; + case SQL_DIAG_SQLSTATE: + ret = PGAPI_StmtError(Handle, RecNumber, + DiagInfoPtr, NULL, NULL, + 0, NULL, 0); + if (StringLengthPtr) + *StringLengthPtr = 5; + if (SQL_SUCCESS_WITH_INFO == ret) + ret = SQL_SUCCESS; + break; + case SQL_DIAG_CURSOR_ROW_COUNT: + stmt = (StatementClass *) Handle; + rc = -1; + if (stmt->status == STMT_FINISHED) + { + QResultClass *res = SC_get_Curres(stmt); + + if (res && QR_NumResultCols(res) > 0 && !SC_is_fetchcursor(stmt)) + rc = QR_get_num_total_tuples(res) - res->dl_count; + } + *((SQLINTEGER *) DiagInfoPtr) = rc; + if (StringLengthPtr) + *StringLengthPtr = sizeof(SQLINTEGER); + ret = SQL_SUCCESS; + break; + case SQL_DIAG_ROW_COUNT: + stmt = (StatementClass *) Handle; + *((SQLINTEGER *) DiagInfoPtr) = stmt->diag_row_count; + if (StringLengthPtr) + *StringLengthPtr = sizeof(SQLINTEGER); + ret = SQL_SUCCESS; + break; + case SQL_DIAG_RETURNCODE: /* driver manager returns */ + break; + } + break; + case SQL_HANDLE_DESC: + stmtHandle = statementHandleFromDescHandle(Handle, NULL); + conn = (ConnectionClass *) SC_get_conn(((StatementClass *) stmtHandle)); + switch (DiagIdentifier) + { + case SQL_DIAG_CLASS_ORIGIN: + case SQL_DIAG_SUBCLASS_ORIGIN: + case SQL_DIAG_CONNECTION_NAME: + strcpy((char *) DiagInfoPtr, ""); + if (StringLengthPtr) + *StringLengthPtr = 0; + ret = SQL_SUCCESS; + break; case SQL_DIAG_SERVER_NAME: + strcpy((SQLCHAR *) DiagInfoPtr, CC_get_DSN(conn)); + if (StringLengthPtr) + *StringLengthPtr = strlen(CC_get_DSN(conn)); + ret = SQL_SUCCESS; break; + case SQL_DIAG_MESSAGE_TEXT: + case SQL_DIAG_NATIVE: + case SQL_DIAG_NUMBER: case SQL_DIAG_SQLSTATE: + ret = PGAPI_GetDiagField(SQL_HANDLE_STMT, + stmtHandle, RecNumber, + DiagIdentifier, DiagInfoPtr, + BufferLength, StringLengthPtr); + break; + case SQL_DIAG_RETURNCODE: /* driver manager returns */ + break; + case SQL_DIAG_CURSOR_ROW_COUNT: + case SQL_DIAG_ROW_COUNT: + case SQL_DIAG_DYNAMIC_FUNCTION: + case SQL_DIAG_DYNAMIC_FUNCTION_CODE: + /* options for statement type only */ break; } break; @@ -155,12 +365,7 @@ PGAPI_GetConnectAttr(HDBC ConnectionHandle, *((SQLUINTEGER *) Value) = SQL_FALSE; break; case SQL_ATTR_CONNECTION_DEAD: - if (CC_is_in_trans(conn)) - *((SQLUINTEGER *) Value) = SQL_CD_FALSE; - else if (conn->num_stmts > 0) - *((SQLUINTEGER *) Value) = SQL_CD_FALSE; - else - *((SQLUINTEGER *) Value) = SQL_CD_FALSE; + *((SQLUINTEGER *) Value) = (conn->status == CONN_NOT_CONNECTED || conn->status == CONN_DOWN); break; case SQL_ATTR_CONNECTION_TIMEOUT: *((SQLUINTEGER *) Value) = 0; @@ -348,12 +553,24 @@ ARDSetField(StatementClass *stmt, SQLSMALLINT RecNumber, opts->bindings[RecNumber - 1].buflen = (Int4) Value; } break; + case SQL_DESC_PRECISION: + if (RecNumber) + { + column_bindings_set(opts, RecNumber, TRUE); + opts->bindings[RecNumber - 1].precision = (Int2) Value; + } + break; + case SQL_DESC_SCALE: + if (RecNumber) + { + column_bindings_set(opts, RecNumber, TRUE); + opts->bindings[RecNumber - 1].scale = (Int4) Value; + } + break; case SQL_DESC_ALLOC_TYPE: /* read-only */ case SQL_DESC_DATETIME_INTERVAL_PRECISION: case SQL_DESC_LENGTH: case SQL_DESC_NUM_PREC_RADIX: - case SQL_DESC_PRECISION: - case SQL_DESC_SCALE: default:ret = SQL_ERROR; stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; } @@ -460,12 +677,18 @@ APDSetField(StatementClass *stmt, SQLSMALLINT RecNumber, case SQL_DESC_COUNT: parameter_bindings_set(opts, (SQLUINTEGER) Value, FALSE); break; + case SQL_DESC_PRECISION: + parameter_bindings_set(opts, RecNumber, TRUE); + opts->parameters[RecNumber - 1].precision = (Int2) Value; + break; + case SQL_DESC_SCALE: + parameter_bindings_set(opts, RecNumber, TRUE); + opts->parameters[RecNumber - 1].scale = (Int2) Value; + break; case SQL_DESC_ALLOC_TYPE: /* read-only */ case SQL_DESC_DATETIME_INTERVAL_PRECISION: case SQL_DESC_LENGTH: case SQL_DESC_NUM_PREC_RADIX: - case SQL_DESC_PRECISION: - case SQL_DESC_SCALE: default:ret = SQL_ERROR; stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; } @@ -710,11 +933,23 @@ ARDGetField(StatementClass *stmt, SQLSMALLINT RecNumber, case SQL_DESC_ALLOC_TYPE: /* read-only */ ival = SQL_DESC_ALLOC_AUTO; break; - case SQL_DESC_DATETIME_INTERVAL_PRECISION: - case SQL_DESC_LENGTH: - case SQL_DESC_NUM_PREC_RADIX: case SQL_DESC_PRECISION: + if (RecNumber) + { + ival = opts->bindings[RecNumber - 1].precision; + } + break; case SQL_DESC_SCALE: + if (RecNumber) + { + ival = opts->bindings[RecNumber - 1].scale; + } + break; + case SQL_DESC_NUM_PREC_RADIX: + ival = 10; + break; + case SQL_DESC_DATETIME_INTERVAL_PRECISION: + case SQL_DESC_LENGTH: default:ret = SQL_ERROR; stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; } @@ -818,11 +1053,17 @@ APDGetField(StatementClass *stmt, SQLSMALLINT RecNumber, case SQL_DESC_ALLOC_TYPE: /* read-only */ ival = SQL_DESC_ALLOC_AUTO; break; + case SQL_DESC_NUM_PREC_RADIX: + ival = 10; + break; case SQL_DESC_PRECISION: + ival = opts->parameters[RecNumber - 1].precision; + break; case SQL_DESC_SCALE: + ival = opts->parameters[RecNumber - 1].scale; + break; case SQL_DESC_DATETIME_INTERVAL_PRECISION: case SQL_DESC_LENGTH: - case SQL_DESC_NUM_PREC_RADIX: default:ret = SQL_ERROR; stmt->errornumber = STMT_INVALID_DESCRIPTOR_IDENTIFIER; } @@ -1339,3 +1580,77 @@ PGAPI_SetStmtAttr(HSTMT StatementHandle, } return SQL_SUCCESS; } + +#ifdef DRIVER_CURSOR_IMPLEMENT +RETCODE SQL_API +PGAPI_BulkOperations(HSTMT hstmt, SQLSMALLINT operation) +{ + static char *func = "PGAPI_BulkOperations"; + StatementClass *stmt = (StatementClass *) hstmt; + ARDFields *opts = SC_get_ARD(stmt); + RETCODE ret; + UInt4 offset, bind_size = opts->bind_size, *bmark = NULL; + int i, processed; + ConnectionClass *conn; + BOOL auto_commit_needed = FALSE; + QResultClass *res; + + mylog("%s operation = %d\n", func, operation); + SC_clear_error(stmt); + offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0; + + if (SQL_FETCH_BY_BOOKMARK != operation) + { + conn = SC_get_conn(stmt); + if (auto_commit_needed = CC_is_in_autocommit(conn), auto_commit_needed) + PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF); + } + if (SQL_ADD != operation) + { + if (bmark = (UInt4 *) opts->bookmark->buffer, !bmark) + { + stmt->errornumber = STMT_INVALID_OPTION_IDENTIFIER; + stmt->errormsg = "bookmark isn't specified"; + return SQL_ERROR; + } + bmark += (offset >> 4); + } + for (i = 0, processed = 0; i < opts->rowset_size; i++) + { + /* Note opts->row_operation_ptr is ignored */ + switch (operation) + { + case SQL_ADD: + ret = SC_pos_add(stmt, (UWORD) i); + break; + case SQL_UPDATE_BY_BOOKMARK: + ret = SC_pos_update(stmt, (UWORD) i, *bmark); + break; + case SQL_DELETE_BY_BOOKMARK: + ret = SC_pos_delete(stmt, (UWORD) i, *bmark); + break; + case SQL_FETCH_BY_BOOKMARK: + ret = SC_pos_refresh(stmt, (UWORD) i, *bmark); + break; + } + processed++; + if (SQL_ERROR == ret) + break; + if (SQL_ADD != operation) + { + if (bind_size > 0) + bmark += (bind_size >> 2); + else + bmark++; + } + } + if (auto_commit_needed) + PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON); + if (SC_get_IRD(stmt)->rowsFetched) + *(SC_get_IRD(stmt)->rowsFetched) = processed; + + if (res = SC_get_Curres(stmt), res) + res->recent_processed_row_count = stmt->diag_row_count = processed; + return ret; +} +#endif /* DRIVER_CURSOR_IMPLEMENT */ diff --git a/src/interfaces/odbc/pgapifunc.h b/src/interfaces/odbc/pgapifunc.h index f4c740bca8..756a49d945 100644 --- a/src/interfaces/odbc/pgapifunc.h +++ b/src/interfaces/odbc/pgapifunc.h @@ -172,7 +172,8 @@ RETCODE SQL_API PGAPI_ExtendedFetch( SQLUSMALLINT fFetchType, SQLINTEGER irow, SQLUINTEGER *pcrow, - SQLUSMALLINT *rgfRowStatus); + SQLUSMALLINT *rgfRowStatus, + SQLINTEGER FetchOffset); RETCODE SQL_API PGAPI_ForeignKeys( HSTMT hstmt, SQLCHAR *szPkCatalogName, @@ -281,6 +282,8 @@ RETCODE SQL_API PGAPI_SetConnectAttr(HDBC ConnectionHandle, RETCODE SQL_API PGAPI_SetStmtAttr(HSTMT StatementHandle, SQLINTEGER Attribute, PTR Value, SQLINTEGER StringLength); +RETCODE SQL_API PGAPI_BulkOperations(HSTMT StatementHandle, + SQLSMALLINT operation); RETCODE SQL_API PGAPI_SetDescField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength); diff --git a/src/interfaces/odbc/pgtypes.c b/src/interfaces/odbc/pgtypes.c index 74df2a79e9..fe0d5b86de 100644 --- a/src/interfaces/odbc/pgtypes.c +++ b/src/interfaces/odbc/pgtypes.c @@ -101,7 +101,8 @@ Int2 sqlTypes[] = { }; #if (ODBCVER >= 0x0300) && defined(OBDCINT64) -#define ALLOWED_C_BIGINT SQL_C_SBIGINT +/* #define ALLOWED_C_BIGINT SQL_C_SBIGINT */ +#define ALLOWED_C_BIGINT SQL_C_CHAR /* Delphi should be either ? */ #else #define ALLOWED_C_BIGINT SQL_C_CHAR #endif @@ -286,11 +287,13 @@ pgtype_to_concise_type(StatementClass *stmt, Int4 type) /* Change this to SQL_BIGINT for ODBC v3 bjm 2001-01-23 */ case PG_TYPE_INT8: + if (conn->ms_jet) + return SQL_NUMERIC; /* maybe a little better than SQL_VARCHAR */ #if (ODBCVER >= 0x0300) - if (!conn->ms_jet) - return SQL_BIGINT; -#endif /* ODBCVER */ + return SQL_BIGINT; +#else return SQL_VARCHAR; +#endif /* ODBCVER */ case PG_TYPE_NUMERIC: return SQL_NUMERIC; diff --git a/src/interfaces/odbc/psqlodbc.h b/src/interfaces/odbc/psqlodbc.h index 3a2d8c9cb0..6d39e63737 100644 --- a/src/interfaces/odbc/psqlodbc.h +++ b/src/interfaces/odbc/psqlodbc.h @@ -5,7 +5,7 @@ * * Comments: See "notice.txt" for copyright and license information. * - * $Id: psqlodbc.h,v 1.65 2002/04/15 02:46:00 inoue Exp $ + * $Id: psqlodbc.h,v 1.66 2002/05/22 05:51:03 inoue Exp $ * */ @@ -20,6 +20,8 @@ #include /* for FILE* pointers: see GLOBAL_VALUES */ +#include "version.h" + /* Must come before sql.h */ #ifndef ODBCVER #define ODBCVER 0x0250 @@ -87,8 +89,6 @@ typedef UInt4 Oid; #define DBMS_NAME "PostgreSQL" #endif /* ODBCVER */ -#define POSTGRESDRIVERVERSION "07.02.0001" - #ifdef WIN32 #if (ODBCVER >= 0x0300) #ifdef UNICODE_SUPPORT diff --git a/src/interfaces/odbc/psqlodbc.rc b/src/interfaces/odbc/psqlodbc.rc index 050b45d941..9cbb44b608 100644 --- a/src/interfaces/odbc/psqlodbc.rc +++ b/src/interfaces/odbc/psqlodbc.rc @@ -1,6 +1,7 @@ //Microsoft Developer Studio generated resource script. // #include "resource.h" +#include "version.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// @@ -366,8 +367,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 7,2,0,01 - PRODUCTVERSION 7,2,0,01 + FILEVERSION PG_DRVFILE_VERSION + PRODUCTVERSION PG_DRVFILE_VERSION FILEFLAGSMASK 0x3L #ifdef _DEBUG FILEFLAGS 0x1L @@ -389,14 +390,14 @@ BEGIN VALUE "CompanyName", "Insight Distribution Systems\0" #endif VALUE "FileDescription", "PostgreSQL Driver\0" - VALUE "FileVersion", " 07.02.0001\0" + VALUE "FileVersion", POSTGRES_RESOURCE_VERSION VALUE "InternalName", "psqlodbc\0" VALUE "LegalCopyright", "\0" VALUE "LegalTrademarks", "ODBC(TM) is a trademark of Microsoft Corporation. Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation.\0" VALUE "OriginalFilename", "psqlodbc.dll\0" VALUE "PrivateBuild", "\0" VALUE "ProductName", "Microsoft Open Database Connectivity\0" - VALUE "ProductVersion", " 07.02.0001\0" + VALUE "ProductVersion", POSTGRES_RESOURCE_VERSION VALUE "SpecialBuild", "\0" END END diff --git a/src/interfaces/odbc/qresult.c b/src/interfaces/odbc/qresult.c index 2817a7f316..b10408207f 100644 --- a/src/interfaces/odbc/qresult.c +++ b/src/interfaces/odbc/qresult.c @@ -110,9 +110,13 @@ QR_Constructor() rv->conn = NULL; rv->next = NULL; rv->inTuples = FALSE; - rv->fcount = 0; + rv->count_backend_allocated = 0; + rv->count_keyset_allocated = 0; + rv->num_total_rows = 0; + rv->num_backend_rows = 0; rv->fetch_count = 0; rv->base = 0; + rv->recent_processed_row_count = -1; rv->currTuple = -1; rv->num_fields = 0; rv->tupleField = NULL; @@ -126,6 +130,9 @@ QR_Constructor() rv->rb_alloc = 0; rv->rb_count = 0; rv->rollback = NULL; + rv->dl_alloc = 0; + rv->dl_count = 0; + rv->deleted = NULL; } mylog("exit QR_Constructor\n"); @@ -202,14 +209,14 @@ QR_free_memory(QResultClass *self) register int lf, row; register TupleField *tuple = self->backend_tuples; - int fcount = self->fcount; + int num_backend_rows = self->num_backend_rows; int num_fields = self->num_fields; - mylog("QResult: free memory in, fcount=%d\n", fcount); + mylog("QResult: free memory in, fcount=%d\n", num_backend_rows); if (self->backend_tuples) { - for (row = 0; row < fcount; row++) + for (row = 0; row < num_backend_rows; row++) { mylog("row = %d, num_fields = %d\n", row, num_fields); for (lf = 0; lf < num_fields; lf++) @@ -224,12 +231,14 @@ QR_free_memory(QResultClass *self) } free(self->backend_tuples); + self->count_backend_allocated = 0; self->backend_tuples = NULL; } if (self->keyset) { free(self->keyset); self->keyset = NULL; + self->count_keyset_allocated = 0; } if (self->rollback) { @@ -238,8 +247,16 @@ QR_free_memory(QResultClass *self) self->rb_count = 0; self->rollback = NULL; } + if (self->deleted) + { + free(self->deleted); + self->dl_alloc = 0; + self->dl_count = 0; + self->deleted = NULL; + } - self->fcount = 0; + self->num_total_rows = 0; + self->num_backend_rows = 0; mylog("QResult: free memory out\n"); } @@ -313,7 +330,7 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor) /* allocate memory for the tuple cache */ mylog("MALLOC: tuple_size = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size); - self->count_allocated = 0; + self->count_backend_allocated = self->count_keyset_allocated = 0; if (self->num_fields > 0) { self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * tuple_size); @@ -323,15 +340,24 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor) QR_set_message(self, "Could not get memory for tuple cache."); return FALSE; } + self->count_backend_allocated = tuple_size; } if (self->haskeyset) - self->keyset = (KeySet *) calloc(sizeof(KeySet), tuple_size); - self->count_allocated = tuple_size; + { + if (self->keyset = (KeySet *) calloc(sizeof(KeySet), tuple_size), !self->keyset) + { + self->status = PGRES_FATAL_ERROR; + QR_set_message(self, "Could not get memory for tuple cache."); + return FALSE; + } + self->count_keyset_allocated = tuple_size; + } self->inTuples = TRUE; /* Force a read to occur in next_tuple */ - self->fcount = tuple_size + 1; + self->num_total_rows = tuple_size + 1; + self->num_backend_rows = tuple_size + 1; self->fetch_count = tuple_size + 1; self->base = 0; @@ -415,7 +441,7 @@ QR_next_tuple(QResultClass *self) /* Speed up access */ int fetch_count = self->fetch_count; - int fcount = self->fcount; + int num_backend_rows = self->num_backend_rows; int fetch_size, offset = 0; int end_tuple = self->rowset_size + self->base; @@ -430,21 +456,21 @@ QR_next_tuple(QResultClass *self) char fetch[128]; QueryInfo qi; ConnInfo *ci = NULL; - BOOL set_no_trans; + UDWORD abort_opt; - if (fetch_count < fcount) + if (fetch_count < num_backend_rows) { /* return a row from cache */ - mylog("next_tuple: fetch_count < fcount: returning tuple %d, fcount = %d\n", fetch_count, fcount); + mylog("next_tuple: fetch_count < fcount: returning tuple %d, fcount = %d\n", fetch_count, num_backend_rows); self->tupleField = the_tuples + (fetch_count * self->num_fields); /* next row */ self->fetch_count++; return TRUE; } - else if (self->fcount < self->cache_size) + else if (self->num_backend_rows < self->cache_size) { /* last row from cache */ /* We are done because we didn't even get CACHE_SIZE tuples */ - mylog("next_tuple: fcount < CACHE_SIZE: fcount = %d, fetch_count = %d\n", fcount, fetch_count); + mylog("next_tuple: fcount < CACHE_SIZE: fcount = %d, fetch_count = %d\n", num_backend_rows, fetch_count); self->tupleField = NULL; self->status = PGRES_END_TUPLES; /* end of tuples */ @@ -464,13 +490,13 @@ QR_next_tuple(QResultClass *self) ci = &(self->conn->connInfo); if (!self->cursor || !ci->drivers.use_declarefetch) { - mylog("next_tuple: ALL_ROWS: done, fcount = %d, fetch_count = %d\n", fcount, fetch_count); + mylog("next_tuple: ALL_ROWS: done, fcount = %d, fetch_count = %d\n", self->num_total_rows, fetch_count); self->tupleField = NULL; self->status = PGRES_END_TUPLES; return -1; /* end of tuples */ } - if (self->base == fcount) + if (self->base == num_backend_rows) { /* not a correction */ /* Determine the optimum cache size. */ @@ -489,7 +515,7 @@ QR_next_tuple(QResultClass *self) /* need to correct */ corrected = TRUE; - fetch_size = end_tuple - fcount; + fetch_size = end_tuple - num_backend_rows; self->cache_size += fetch_size; @@ -497,9 +523,9 @@ QR_next_tuple(QResultClass *self) self->fetch_count++; } - if (!self->backend_tuples || self->cache_size > self->count_allocated) + if (!self->backend_tuples || self->cache_size > self->count_backend_allocated) { - self->count_allocated = 0; + self->count_backend_allocated = 0; if (self->num_fields > 0) { self->backend_tuples = (TupleField *) realloc(self->backend_tuples, @@ -510,10 +536,14 @@ QR_next_tuple(QResultClass *self) QR_set_message(self, "Out of memory while reading tuples."); return FALSE; } + self->count_backend_allocated = self->cache_size; } - if (self->haskeyset) - self->keyset = (KeySet *) realloc(self->keyset, sizeof(KeySet) * self->cache_size); - self->count_allocated = self->cache_size; + } + if (self->haskeyset && (!self->keyset || self->cache_size > self->count_keyset_allocated)) + { + self->count_keyset_allocated = 0; + self->keyset = (KeySet *) realloc(self->keyset, sizeof(KeySet) * self->cache_size); + self->count_keyset_allocated = self->cache_size; } sprintf(fetch, "fetch %d in %s", fetch_size, self->cursor); @@ -534,7 +564,7 @@ QR_next_tuple(QResultClass *self) } else { - mylog("next_tuple: inTuples = true, falling through: fcount = %d, fetch_count = %d\n", self->fcount, self->fetch_count); + mylog("next_tuple: inTuples = true, falling through: fcount = %d, fetch_count = %d\n", self->num_backend_rows, self->fetch_count); /* * This is a pre-fetch (fetching rows right after query but @@ -548,7 +578,8 @@ QR_next_tuple(QResultClass *self) if (!corrected) { self->base = 0; - self->fcount = 0; + self->num_total_rows = 0; /* right ? */ + self->num_backend_rows = 0; } sock = CC_get_socket(self->conn); @@ -569,14 +600,15 @@ QR_next_tuple(QResultClass *self) case 'B': /* Tuples in binary format */ case 'D': /* Tuples in ASCII format */ - if ((!self->cursor || !ci->drivers.use_declarefetch) && self->fcount >= self->count_allocated) - { - int tuple_size = self->count_allocated; - - mylog("REALLOC: old_count = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size); - tuple_size *= 2; - if (self->num_fields > 0) + if (!self->cursor || !ci->drivers.use_declarefetch) + { + if (self->num_fields > 0 && + self->num_total_rows >= self->count_backend_allocated) { + int tuple_size = self->count_backend_allocated; + + mylog("REALLOC: old_count = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size); + tuple_size *= 2; self->backend_tuples = (TupleField *) realloc(self->backend_tuples, tuple_size * self->num_fields * sizeof(TupleField)); if (!self->backend_tuples) @@ -585,10 +617,16 @@ QR_next_tuple(QResultClass *self) QR_set_message(self, "Out of memory while reading tuples."); return FALSE; } + self->count_backend_allocated = tuple_size; } - if (self->haskeyset) + if (self->haskeyset && + self->num_total_rows >= self->count_keyset_allocated) + { + int tuple_size = self->count_keyset_allocated; + tuple_size *= 2; self->keyset = (KeySet *) realloc(self->keyset, sizeof(KeySet) * tuple_size); - self->count_allocated = tuple_size; + self->count_keyset_allocated = tuple_size; + } } if (!QR_read_tuple(self, (char) (id == 0))) @@ -597,7 +635,9 @@ QR_next_tuple(QResultClass *self) QR_set_message(self, "Error reading the tuple"); return FALSE; } - self->fcount++; + self->num_total_rows++; + if (self->num_fields > 0) + self->num_backend_rows++; break; /* continue reading */ case 'C': /* End of tuple list */ @@ -607,10 +647,10 @@ QR_next_tuple(QResultClass *self) mylog("end of tuple list -- setting inUse to false: this = %u\n", self); self->inTuples = FALSE; - if (self->fcount > 0) + if (self->num_total_rows > 0) { - qlog(" [ fetched %d rows ]\n", self->fcount); - mylog("_next_tuple: 'C' fetch_max && fcount = %d\n", self->fcount); + qlog(" [ fetched %d rows ]\n", self->num_total_rows); + mylog("_next_tuple: 'C' fetch_max && fcount = %d\n", self->num_total_rows); /* set to first row */ self->tupleField = self->backend_tuples + (offset * self->num_fields); @@ -629,11 +669,15 @@ QR_next_tuple(QResultClass *self) QR_set_message(self, msgbuffer); self->status = PGRES_FATAL_ERROR; - set_no_trans = FALSE; + abort_opt = 0; if (!strncmp(msgbuffer, "FATAL", 5)) - set_no_trans = TRUE; - CC_on_abort(self->conn, set_no_trans); + abort_opt = NO_TRANS | CONN_DEAD; + CC_on_abort(self->conn, abort_opt); + QR_set_status(self, PGRES_FATAL_ERROR); + QR_set_message(self, msgbuffer); + QR_set_aborted(self, TRUE); + mylog("ERROR from backend in next_tuple: '%s'\n", msgbuffer); qlog("ERROR from backend in next_tuple: '%s'\n", msgbuffer); return FALSE; @@ -651,7 +695,7 @@ QR_next_tuple(QResultClass *self) qlog("QR_next_tuple: Unexpected result from backend: id = '%c' (%d)\n", id, id); QR_set_message(self, "Unexpected result from backend. It probably crashed"); self->status = PGRES_FATAL_ERROR; - CC_on_abort(self->conn, TRUE); + CC_on_abort(self->conn, NO_TRANS | CONN_DEAD); return FALSE; } } @@ -681,16 +725,16 @@ QR_read_tuple(QResultClass *self, char binary) /* set the current row to read the fields into */ effective_cols = ci_num_fields; - this_tuplefield = self->backend_tuples + (self->fcount * num_fields); + this_tuplefield = self->backend_tuples + (self->num_backend_rows * num_fields); if (self->haskeyset) { - this_keyset = self->keyset + self->fcount; + this_keyset = self->keyset + self->num_total_rows; this_keyset->status = 0; effective_cols -= 2; } - bitmaplen = (Int2) num_fields / BYTELEN; - if ((num_fields % BYTELEN) > 0) + bitmaplen = (Int2) ci_num_fields / BYTELEN; + if ((ci_num_fields % BYTELEN) > 0) bitmaplen++; /* diff --git a/src/interfaces/odbc/qresult.h b/src/interfaces/odbc/qresult.h index b313cbc467..03c4ed35fb 100644 --- a/src/interfaces/odbc/qresult.h +++ b/src/interfaces/odbc/qresult.h @@ -48,15 +48,18 @@ struct QResultClass_ QResultClass *next; /* the following result class */ /* Stuff for declare/fetch tuples */ - int count_allocated; /* m(re)alloced count */ + int num_total_rows; /* total count of rows read in */ + int count_backend_allocated;/* m(re)alloced count */ + int count_keyset_allocated; /* m(re)alloced count */ + int num_backend_rows; /* count of tuples kept in backend_tuples member */ int fetch_count; /* logical rows read so far */ - int fcount; /* actual rows read in the fetch */ int currTuple; int base; int num_fields; /* number of fields in the result */ int cache_size; int rowset_size; + Int4 recent_processed_row_count; QueryResultCode status; @@ -77,6 +80,9 @@ struct QResultClass_ UInt2 rb_alloc; /* count of allocated rollback info */ UInt2 rb_count; /* count of rollback info */ Rollback *rollback; + UInt2 dl_alloc; /* count of allocated deleted info */ + UInt2 dl_count; /* count of deleted info */ + UInt4 *deleted; }; #define QR_get_fields(self) (self->fields) @@ -96,7 +102,8 @@ struct QResultClass_ #define QR_get_field_type(self, fieldno_) (CI_get_oid(self->fields, fieldno_)) /* These functions are used only for manual result sets */ -#define QR_get_num_tuples(self) (self->manual_tuples ? TL_get_num_tuples(self->manual_tuples) : self->fcount) +#define QR_get_num_total_tuples(self) (self->manual_tuples ? TL_get_num_tuples(self->manual_tuples) : self->num_total_rows) +#define QR_get_num_backend_tuples(self) (self->manual_tuples ? TL_get_num_tuples(self->manual_tuples) : self->num_backend_rows) #define QR_add_tuple(self, new_tuple) (TL_add_tuple(self->manual_tuples, new_tuple)) #define QR_set_field_info(self, field_num, name, adtid, adtsize) (CI_set_field_info(self->fields, field_num, name, adtid, adtsize, -1)) diff --git a/src/interfaces/odbc/results.c b/src/interfaces/odbc/results.c index 937e275e05..0739167fdf 100644 --- a/src/interfaces/odbc/results.c +++ b/src/interfaces/odbc/results.c @@ -41,8 +41,6 @@ PGAPI_RowCount( static char *func = "PGAPI_RowCount"; StatementClass *stmt = (StatementClass *) hstmt; QResultClass *res; - char *msg, - *ptr; ConnInfo *ci; mylog("%s: entering...\n", func); @@ -59,43 +57,32 @@ PGAPI_RowCount( return SQL_SUCCESS; } - if (stmt->statement_type == STMT_TYPE_SELECT) + res = SC_get_Curres(stmt); + if (res && pcrow) { - if (stmt->status == STMT_FINISHED) + if (stmt->status != STMT_FINISHED) { - res = SC_get_Curres(stmt); - - if (res && pcrow) - { - *pcrow = SC_is_fetchcursor(stmt) ? -1 : QR_get_num_tuples(res); - return SQL_SUCCESS; - } + stmt->errornumber = STMT_SEQUENCE_ERROR; + stmt->errormsg = "Can't get row count while statement is still executing."; + SC_log_error(func, "", stmt); + return SQL_ERROR; } - } - else - { - res = SC_get_Curres(stmt); - if (res && pcrow) + if (res->recent_processed_row_count >= 0) { - msg = QR_get_command(res); - mylog("*** msg = '%s'\n", msg); - trim(msg); /* get rid of trailing spaces */ - ptr = strrchr(msg, ' '); - if (ptr) - { - *pcrow = atoi(ptr + 1); - mylog("**** PGAPI_RowCount(): THE ROWS: *pcrow = %d\n", *pcrow); - } - else - { - *pcrow = -1; - mylog("**** PGAPI_RowCount(): NO ROWS: *pcrow = %d\n", *pcrow); - } + *pcrow = res->recent_processed_row_count; + mylog("**** PGAPI_RowCount(): THE ROWS: *pcrow = %d\n", *pcrow); return SQL_SUCCESS; } + else if (QR_NumResultCols(res) > 0) + { + *pcrow = SC_is_fetchcursor(stmt) ? -1 : QR_get_num_total_tuples(res) - res->dl_count; + mylog("RowCount=%d\n", *pcrow); + return SQL_SUCCESS; + } } + stmt->errornumber = STMT_SEQUENCE_ERROR; SC_log_error(func, "Bad return value", stmt); return SQL_ERROR; } @@ -685,6 +672,14 @@ inolog("COLUMN_TYPE=%d\n", value); * else */ value = fi ? (fi->updatable ? SQL_ATTR_WRITE : SQL_ATTR_READONLY) : SQL_ATTR_READWRITE_UNKNOWN; + if (SQL_ATTR_READONLY != value) + { + const char *name = fi ? fi->name : QR_get_fieldname(SC_get_Curres(stmt), col_idx); + if (stricmp(name, "oid") == 0 || + stricmp(name, "ctid") == 0 || + stricmp(name, "xmin") == 0) + value = SQL_ATTR_READONLY; + } mylog("PGAPI_ColAttr: UPDATEABLE = %d\n", value); break; @@ -714,7 +709,8 @@ inolog("COLUMN_TYPE=%d\n", value); mylog("PGAPI_ColAttributes: col %d, octet_length = %d\n", col_idx, value); break; case SQL_DESC_PRECISION: /* different from SQL_COLUMN_PRECISION */ - value = (fi && fi->column_size > 0) ? fi->column_size : pgtype_precision(stmt, field_type, col_idx, unknown_sizes); + if (value = FI_precision(fi), value <= 0) + value = pgtype_precision(stmt, field_type, col_idx, unknown_sizes); if (value < 0) value = 0; @@ -881,7 +877,7 @@ inolog("Column 0 is type %d not of type SQL_C_BOOKMARK", fCType); if (stmt->manual_result || !SC_is_fetchcursor(stmt)) { /* make sure we're positioned on a valid row */ - num_rows = QR_get_num_tuples(res); + num_rows = QR_get_num_total_tuples(res); if ((stmt->currTuple < 0) || (stmt->currTuple >= num_rows)) { @@ -897,7 +893,12 @@ inolog("Column 0 is type %d not of type SQL_C_BOOKMARK", fCType); if (stmt->manual_result) value = QR_get_value_manual(res, stmt->currTuple, icol); else - value = QR_get_value_backend_row(res, stmt->currTuple, icol); + { + Int4 curt = res->base; + if (stmt->rowset_start >= 0) + curt += (stmt->currTuple - stmt->rowset_start); + value = QR_get_value_backend_row(res, curt, icol); + } mylog(" value = '%s'\n", value); } } @@ -1046,12 +1047,93 @@ PGAPI_Fetch( } QR_set_rowset_size(res, 1); - QR_inc_base(res, stmt->last_fetch_count); + QR_inc_base(res, stmt->last_fetch_count_include_ommitted); return SC_fetch(stmt); } +#ifdef DRIVER_CURSOR_IMPLEMENT +static RETCODE SQL_API +SC_pos_reload_needed(StatementClass *stmt, UDWORD flag); +static Int4 +getNthValid(QResultClass *res, Int4 sta, UWORD orientation, UInt4 nth, Int4 *nearest) +{ + Int4 i, num_tuples = QR_get_num_total_tuples(res); + UInt4 count; + KeySet *keyset; + + if (0 == res->dl_count) + { + if (SQL_FETCH_PRIOR == orientation) + { + if (sta + 1 >= (Int4) nth) + { + *nearest = sta + 1 - nth; + return nth; + } + *nearest = -1; + return -(Int4)(sta + 1); + } + else + { + if ((*nearest = sta + nth - 1) < num_tuples) + return nth; + *nearest = num_tuples; + return -(Int4)(num_tuples - sta); + } + } + count = 0; + if (SQL_FETCH_PRIOR == orientation) + { + for (i = sta, keyset = res->keyset + sta; + i >= 0; i--, keyset--) + { + if (0 == (keyset->status & (CURS_SELF_DELETING | CURS_SELF_DELETING | CURS_OTHER_DELETED))) + { + *nearest = i; + if (++count == nth) + return count; + } + } + *nearest = -1; + } + else + { + for (i = sta, keyset = res->keyset + sta; + i < num_tuples; i++, keyset++) + { + if (0 == (keyset->status & (CURS_SELF_DELETING | CURS_SELF_DELETING | CURS_OTHER_DELETED))) + { + *nearest = i; + if (++count == nth) + return count; + } + } + *nearest = num_tuples; + } + return -(Int4)count; +} +#endif /* DRIVER_CURSOR_IMPLEMENT */ +/* + * return NO_DATA_FOUND macros + * save_rowset_start or num_tuples must be defined + */ +#define EXTFETCH_RETURN_BOF(stmt, res) \ +{ \ + stmt->rowset_start = -1; \ + stmt->currTuple = -1; \ + res->base += (stmt->rowset_start - save_rowset_start); \ + return SQL_NO_DATA_FOUND; \ +} +#define EXTFETCH_RETURN_EOF(stmt, res) \ +{ \ + stmt->rowset_start = num_tuples; \ + stmt->currTuple = -1; \ + res->base += (stmt->rowset_start - save_rowset_start); \ + return SQL_NO_DATA_FOUND; \ +} + /* This fetchs a block of data (rowset). */ RETCODE SQL_API PGAPI_ExtendedFetch( @@ -1059,7 +1141,8 @@ PGAPI_ExtendedFetch( UWORD fFetchType, SDWORD irow, UDWORD FAR * pcrow, - UWORD FAR * rgfRowStatus) + UWORD FAR * rgfRowStatus, + SQLINTEGER bookmark_offset) { static char *func = "PGAPI_ExtendedFetch"; StatementClass *stmt = (StatementClass *) hstmt; @@ -1067,11 +1150,17 @@ PGAPI_ExtendedFetch( QResultClass *res; int num_tuples, i, - save_rowset_size; + save_rowset_size, + save_rowset_start, + progress_size; RETCODE result; char truncated, error; ConnInfo *ci; + DWORD currp; +#ifdef DRIVER_CURSOR_IMPLEMENT + UWORD pstatus; +#endif /* DRIVER_CURSOR_IMPLEMENT */ mylog("PGAPI_ExtendedFetch: stmt=%u\n", stmt); @@ -1082,12 +1171,13 @@ PGAPI_ExtendedFetch( } ci = &(SC_get_conn(stmt)->connInfo); - if (SC_is_fetchcursor(stmt) && !stmt->manual_result) + /* if (SC_is_fetchcursor(stmt) && !stmt->manual_result) */ + if (SQL_CURSOR_FORWARD_ONLY == stmt->options.cursor_type && !stmt->manual_result) { if (fFetchType != SQL_FETCH_NEXT) { - stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; - stmt->errormsg = "Unsupported fetch type for PGAPI_ExtendedFetch with UseDeclareFetch option."; + stmt->errornumber = STMT_FETCH_OUT_OF_RANGE; + stmt->errormsg = "The fetch type for PGAPI_ExtendedFetch isn't allowed with ForwardOnly cursor."; return SQL_ERROR; } } @@ -1149,9 +1239,10 @@ PGAPI_ExtendedFetch( if (pcrow) *pcrow = 0; - num_tuples = QR_get_num_tuples(res); + num_tuples = QR_get_num_total_tuples(res); /* Save and discard the saved rowset size */ + save_rowset_start = stmt->rowset_start; save_rowset_size = stmt->save_rowset_size; stmt->save_rowset_size = -1; @@ -1165,11 +1256,29 @@ PGAPI_ExtendedFetch( * SQL_FETCH_FIRST. */ + progress_size = (save_rowset_size > 0 ? save_rowset_size : opts->rowset_size); if (stmt->rowset_start < 0) stmt->rowset_start = 0; +#ifdef DRIVER_CURSOR_IMPLEMENT + else if (res->keyset) + { + if (stmt->last_fetch_count <= progress_size) + { + stmt->rowset_start += stmt->last_fetch_count_include_ommitted; + progress_size -= stmt->last_fetch_count; + } + if (progress_size > 0 && + getNthValid(res, stmt->rowset_start, + SQL_FETCH_NEXT, progress_size + 1, + &stmt->rowset_start) <= 0) + { + EXTFETCH_RETURN_EOF(stmt, res) + } + } +#endif /* DRIVER_CURSOR_IMPLEMENT */ else - stmt->rowset_start += (save_rowset_size > 0 ? save_rowset_size : opts->rowset_size); + stmt->rowset_start += progress_size; mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple); break; @@ -1182,6 +1291,10 @@ PGAPI_ExtendedFetch( * RESULT SET, then this should be equivalent to * SQL_FETCH_LAST. */ + if (stmt->rowset_start <= 0) + { + EXTFETCH_RETURN_BOF(stmt, res) + } if (stmt->rowset_start >= num_tuples) { if (opts->rowset_size > num_tuples) @@ -1194,12 +1307,27 @@ PGAPI_ExtendedFetch( } else { +#ifdef DRIVER_CURSOR_IMPLEMENT + if (i = getNthValid(res, stmt->rowset_start - 1, SQL_FETCH_PRIOR, opts->rowset_size, &stmt->rowset_start), i < -1) + { + stmt->errormsg = "fetch prior and before the beggining"; + stmt->errornumber = STMT_POS_BEFORE_RECORDSET; + stmt->rowset_start = 0; + } + else if (i <= 0) + { + EXTFETCH_RETURN_BOF(stmt, res) + } +#else if (stmt->rowset_start < opts->rowset_size) { stmt->errormsg = "fetch prior and before the beggining"; stmt->errornumber = STMT_POS_BEFORE_RECORDSET; + stmt->rowset_start = 0; } - stmt->rowset_start -= opts->rowset_size; + else + stmt->rowset_start -= opts->rowset_size; +#endif /* DRIVER_CURSOR_IMPLEMENT */ } break; @@ -1221,16 +1349,32 @@ PGAPI_ExtendedFetch( /* Position before result set, but dont fetch anything */ if (irow == 0) { - stmt->rowset_start = -1; - stmt->currTuple = -1; - return SQL_NO_DATA_FOUND; + EXTFETCH_RETURN_BOF(stmt, res) } /* Position before the desired row */ else if (irow > 0) +#ifdef DRIVER_CURSOR_IMPLEMENT + { + if (getNthValid(res, 0, SQL_FETCH_NEXT, irow, &stmt->rowset_start) <= 0) + { + EXTFETCH_RETURN_EOF(stmt, res) + } + } +#else stmt->rowset_start = irow - 1; +#endif /* DRIVER_CURSOR_IMPLEMENT */ /* Position with respect to the end of the result set */ else +#ifdef DRIVER_CURSOR_IMPLEMENT + { + if (getNthValid(res, num_tuples - 1, SQL_FETCH_PRIOR, -irow, &stmt->rowset_start) <= 0) + { + EXTFETCH_RETURN_BOF(stmt, res) + } + } +#else stmt->rowset_start = num_tuples + irow; +#endif /* DRIVER_CURSOR_IMPLEMENT */ break; case SQL_FETCH_RELATIVE: @@ -1242,11 +1386,43 @@ PGAPI_ExtendedFetch( if (irow == 0) break; +#ifdef DRIVER_CURSOR_IMPLEMENT + if (irow > 0) + { + if (getNthValid(res, stmt->rowset_start + 1, SQL_FETCH_NEXT, irow, &stmt->rowset_start) <= 0) + { + EXTFETCH_RETURN_EOF(stmt, res) + } + } + else + { + if (getNthValid(res, stmt->rowset_start - 1, SQL_FETCH_PRIOR, -irow, &stmt->rowset_start) <= 0) + { + EXTFETCH_RETURN_BOF(stmt, res) + } + } +#else stmt->rowset_start += irow; +#endif /* DRIVER_CURSOR_IMPLEMENT */ break; case SQL_FETCH_BOOKMARK: - stmt->rowset_start = irow - 1; +#ifdef DRIVER_CURSOR_IMPLEMENT + if (bookmark_offset > 0) + { + if (getNthValid(res, irow - 1, SQL_FETCH_NEXT, bookmark_offset + 1, &stmt->rowset_start) <= 0) + { + EXTFETCH_RETURN_EOF(stmt, res) + } + } + else if (getNthValid(res, irow - 1, SQL_FETCH_PRIOR, 1 - bookmark_offset, &stmt->rowset_start) <= 0) + { + stmt->currTuple = -1; + EXTFETCH_RETURN_BOF(stmt, res) + } +#else + stmt->rowset_start = irow + bookmark_offset - 1; +#endif /* DRIVER_CURSOR_IMPLEMENT */ break; default: @@ -1272,8 +1448,7 @@ PGAPI_ExtendedFetch( /* If *new* rowset is after the result_set, return no data found */ if (stmt->rowset_start >= num_tuples) { - stmt->rowset_start = num_tuples; - return SQL_NO_DATA_FOUND; + EXTFETCH_RETURN_EOF(stmt, res) } } @@ -1282,8 +1457,7 @@ PGAPI_ExtendedFetch( { if (stmt->rowset_start + opts->rowset_size <= 0) { - stmt->rowset_start = -1; - return SQL_NO_DATA_FOUND; + EXTFETCH_RETURN_BOF(stmt, res) } else { /* overlap with beginning of result set, @@ -1298,19 +1472,30 @@ PGAPI_ExtendedFetch( /* increment the base row in the tuple cache */ QR_set_rowset_size(res, opts->rowset_size); if (SC_is_fetchcursor(stmt)) - QR_inc_base(res, stmt->last_fetch_count); + QR_inc_base(res, stmt->last_fetch_count_include_ommitted); else res->base = stmt->rowset_start; +#ifdef DRIVER_CURSOR_IMPLEMENT + if (res->keyset) + SC_pos_reload_needed(stmt, SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type); +#endif /* DRIVER_CURSOR_IMPLEMENT */ /* Physical Row advancement occurs for each row fetched below */ mylog("PGAPI_ExtendedFetch: new currTuple = %d\n", stmt->currTuple); truncated = error = FALSE; - for (i = 0; i < opts->rowset_size; i++) + for (i = 0, currp = stmt->rowset_start; i < opts->rowset_size; currp++) { stmt->bind_row = i; /* set the binding location */ result = SC_fetch(stmt); +#ifdef DRIVER_CURSOR_IMPLEMENT + if (SQL_SUCCESS_WITH_INFO == result && 0 == stmt->last_fetch_count && res->keyset) + { + res->keyset[stmt->currTuple].status &= ~CURS_IN_ROWSET; + continue; + } +#endif /* DRIVER_CURSOR_IMPLEMENT */ /* Determine Function status */ if (result == SQL_NO_DATA_FOUND) @@ -1328,26 +1513,28 @@ PGAPI_ExtendedFetch( #ifdef DRIVER_CURSOR_IMPLEMENT else if (res->keyset) { - DWORD currp = stmt->rowset_start + i; - UWORD pstatus = res->keyset[currp].status & KEYSET_INFO_PUBLIC; - if (pstatus != 0) + pstatus = (res->keyset[currp].status & KEYSET_INFO_PUBLIC); + if (pstatus != 0 && pstatus != SQL_ROW_ADDED) { rgfRowStatus[i] = pstatus; - /* refresh the status */ - if (SQL_ROW_DELETED != pstatus) - res->keyset[currp].status &= (~KEYSET_INFO_PUBLIC); } else rgfRowStatus[i] = SQL_ROW_SUCCESS; + res->keyset[currp].status |= CURS_IN_ROWSET; + /* refresh the status */ + /* if (SQL_ROW_DELETED != pstatus) */ + res->keyset[currp].status &= (~KEYSET_INFO_PUBLIC); } #endif /* DRIVER_CURSOR_IMPLEMENT */ else *(rgfRowStatus + i) = SQL_ROW_SUCCESS; } + i++; } /* Save the fetch count for SQLSetPos */ stmt->last_fetch_count = i; + stmt->last_fetch_count_include_ommitted = currp - stmt->rowset_start; /* Reset next binding row */ stmt->bind_row = 0; @@ -1393,8 +1580,11 @@ PGAPI_MoreResults( mylog("%s: entering...\n", func); if (stmt && (res = SC_get_Curres(stmt))) SC_set_Curres(stmt, res->next); - if (SC_get_Curres(stmt)) - return SQL_SUCCESS; + if (res = SC_get_Curres(stmt), res) + { + stmt->diag_row_count = res->recent_processed_row_count; + return SQL_SUCCESS; + } return SQL_NO_DATA_FOUND; } @@ -1424,6 +1614,7 @@ static void KeySetSet(const TupleField *tuple, int num_fields, KeySet *keyset) sscanf(tuple[num_fields - 1].value, "%u", &keyset->oid); } +static void DiscardDeleted(QResultClass *res, int index); static void AddRollback(ConnectionClass *conn, QResultClass *res, int index, const KeySet *keyset) { Rollback *rollback; @@ -1479,6 +1670,8 @@ static void DiscardRollback(QResultClass *res) { index = rollback[i].index; status = keyset[index].status; + if (0 != (status & CURS_SELF_DELETING)) + DiscardDeleted(res, index); keyset[index].status &= ~(CURS_SELF_DELETING | CURS_SELF_UPDATING | CURS_SELF_ADDING); keyset[index].status |= ((status & (CURS_SELF_DELETING | CURS_SELF_UPDATING | CURS_SELF_ADDING)) << 3); } @@ -1487,9 +1680,9 @@ static void DiscardRollback(QResultClass *res) res->rb_count = res->rb_alloc = 0; } -static void UndoRollback(QResultClass *res) +static void UndoRollback(StatementClass *stmt, QResultClass *res) { - int i, index; + int i, index, ridx; UWORD status; Rollback *rollback; KeySet *keyset; @@ -1502,16 +1695,34 @@ static void UndoRollback(QResultClass *res) { index = rollback[i].index; status = keyset[index].status; - if ((status & CURS_SELF_ADDING) != 0) + if (0 != (status & CURS_SELF_ADDING)) { - if (index < res->fcount) - res->fcount = index; + ridx = index - stmt->rowset_start + res->base; + if (ridx >=0 && ridx < res->num_backend_rows) + { + TupleField *tuple = res->backend_tuples + res->num_fields * ridx; + int j; + + for (j = 0; j < res->num_fields; j++, tuple++) + { + if (tuple->len > 0 && tuple->value) + { + free(tuple->value); + tuple->value = NULL; + } + tuple->len = 0; + } + } + if (index < res->num_total_rows) + res->num_total_rows = index; } else { + if (0 != (status & CURS_SELF_DELETING)) + DiscardDeleted(res, index); + if (0 != (keyset[index].status & CURS_SELF_UPDATING)) + keyset[index].status |= CURS_NEEDS_REREAD; keyset[index].status &= ~(CURS_SELF_DELETING | CURS_SELF_UPDATING | CURS_SELF_ADDING | KEYSET_INFO_PUBLIC); - keyset[index].blocknum = rollback[i].blocknum; - keyset[index].offset = rollback[i].offset; } } free(rollback); @@ -1532,13 +1743,66 @@ void ProcessRollback(ConnectionClass *conn, BOOL undo) for (res = SC_get_Result(stmt); res; res = res->next) { if (undo) - UndoRollback(res); + UndoRollback(stmt, res); else DiscardRollback(res); } } } + +static void AddDeleted(QResultClass *res, int index) +{ + int i; + UInt4 *deleted; + + if (!res->deleted) + { + res->dl_count = 0; + res->dl_alloc = 10; + deleted = res->deleted = malloc(sizeof(UInt4) * res->dl_alloc); + } + else + { + if (res->dl_count >= res->dl_alloc) + { + res->dl_alloc *= 2; + if (deleted = realloc(res->deleted, sizeof(UInt4) * res->dl_alloc), !deleted) + { + res->dl_alloc = res->dl_count = 0; + return; + } + res->deleted = deleted; + } + for (i = 0, deleted = res->deleted; i < res->dl_count; i++, deleted++) + { + if (index < (int) *deleted) + break; + } + memmove(deleted + 1, deleted, sizeof(UInt4) * (res->dl_count - i)); + } + *deleted = index; + res->dl_count++; +} +static void DiscardDeleted(QResultClass *res, int index) +{ + int i; + UInt4 *deleted; + + if (!res->deleted) + return; + + for (i = 0, deleted = res->deleted; i < res->dl_count; i++, deleted++) + { + if (index == (int) *deleted) + break; + } + if (i >= res->dl_count) + return; + memmove(deleted, deleted + 1, sizeof(UInt4) * (res->dl_count - i - 1)); + res->dl_count--; +} + #define LATEST_TUPLE_LOAD 1L #define USE_INSERTED_TID (1L << 1) static QResultClass * @@ -1555,7 +1819,14 @@ positioned_load(StatementClass *stmt, UInt4 flag, UInt4 oid, const char *tidval) len += 100; selstr = malloc(len); if (latest) - sprintf(selstr, "%s where ctid = currtid2('%s', '%s') and oid = %u", stmt->load_statement, stmt->ti[0]->name, tidval, oid); + { + if (stmt->ti[0]->schema[0]) + sprintf(selstr, "%s where ctid = currtid2('\"%s\".\"%s\"', '%s') and oid = %u", + stmt->load_statement, stmt->ti[0]->schema, + stmt->ti[0]->name, tidval, oid); + else + sprintf(selstr, "%s where ctid = currtid2('%s', '%s') and oid = %u", stmt->load_statement, stmt->ti[0]->name, tidval, oid); + } else sprintf(selstr, "%s where ctid = '%s' and oid = %u", stmt->load_statement, tidval, oid); } @@ -1579,11 +1850,12 @@ positioned_load(StatementClass *stmt, UInt4 flag, UInt4 oid, const char *tidval) } RETCODE SQL_API -SC_pos_reload(StatementClass *stmt, UWORD irow, UDWORD global_ridx, UWORD *count) +SC_pos_reload(StatementClass *stmt, UDWORD global_ridx, UWORD *count, BOOL logChanges) { int i, res_cols; UWORD rcnt, offset; + Int4 res_ridx; UInt4 oid, blocknum; QResultClass *res, *qres; @@ -1604,7 +1876,7 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UDWORD global_ridx, UWORD *count stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; return SQL_ERROR; } - global_ridx = irow + stmt->rowset_start; + res_ridx = global_ridx - stmt->rowset_start + res->base; if (!(oid = getOid(res, global_ridx))) return SQL_SUCCESS_WITH_INFO; getTid(res, global_ridx, &blocknum, &offset); @@ -1613,9 +1885,12 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UDWORD global_ridx, UWORD *count if (qres = positioned_load(stmt, LATEST_TUPLE_LOAD, oid, tidval), qres) { TupleField *tupleo, *tuplen; + ConnectionClass *conn = SC_get_conn(stmt); - rcnt = QR_get_num_tuples(qres); - tupleo = res->backend_tuples + res->num_fields * global_ridx; + rcnt = QR_get_num_backend_tuples(qres); + tupleo = res->backend_tuples + res->num_fields * res_ridx; + if (logChanges && CC_is_in_trans(conn)) + AddRollback(conn, res, global_ridx, res->keyset); if (rcnt == 1) { int effective_fields = res_cols; @@ -1647,8 +1922,6 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UDWORD global_ridx, UWORD *count ret = SQL_SUCCESS_WITH_INFO; if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) { - res->keyset[global_ridx].blocknum = 0; - res->keyset[global_ridx].offset = 0; res->keyset[global_ridx].status |= SQL_ROW_DELETED; } } @@ -1661,6 +1934,162 @@ SC_pos_reload(StatementClass *stmt, UWORD irow, UDWORD global_ridx, UWORD *count return ret; } +static RETCODE SQL_API +SC_pos_reload_needed(StatementClass *stmt, UDWORD flag) +{ + Int4 i, limitrow; + UWORD qcount; + QResultClass *res; + IRDFields *irdflds = SC_get_IRD(stmt); + RETCODE ret = SQL_ERROR; + ConnectionClass *conn = SC_get_conn(stmt); + UInt4 oid, blocknum, lodlen; + char *qval = NULL, *sval; + Int4 rowc; + UWORD offset; + BOOL create_from_scratch = (0 != flag); + + mylog("SC_pos_reload_needed\n"); + if (!(res = SC_get_Curres(stmt))) + return SQL_ERROR; + if (!stmt->ti) + parse_statement(stmt); /* not preferable */ + if (!stmt->updatable) + { + stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; + return SQL_ERROR; + } + limitrow = stmt->rowset_start + res->rowset_size; + if (limitrow > res->num_total_rows) + limitrow = res->num_total_rows; + if (create_from_scratch) + { + int flds_cnt = res->num_backend_rows * res->num_fields, + brows; + + for (i = 0; i < flds_cnt; i++) + { + if (res->backend_tuples[i].value) + free(res->backend_tuples[i].value); + } + brows = limitrow - stmt->rowset_start; + if (brows > res->count_backend_allocated) + { + res->backend_tuples = realloc(res->backend_tuples, sizeof(TupleField) * res->num_fields * brows); + res->count_backend_allocated = brows; + } + if (brows > 0) + memset(res->backend_tuples, 0, sizeof(TupleField) * res->num_fields * brows); + res->num_backend_rows = brows; + res->base = 0; + for (i = stmt->rowset_start; i < limitrow; i++) + { + if (0 == (res->keyset[i].status & (CURS_SELF_DELETING | CURS_SELF_DELETED | CURS_OTHER_DELETED))) + res->keyset[i].status |= CURS_NEEDS_REREAD; + } + } + + for (i = stmt->rowset_start, rowc = 0;; i++) + { + if (i >= limitrow) + { + if (!rowc) + break; + rowc = -1; /* end of loop */ + } + if (rowc < 0 || rowc >= 10) + { + QResultClass *qres; + + strcpy(sval, ")"); + qres = CC_send_query(conn, qval, NULL, CLEAR_RESULT_ON_ABORT | CREATE_KEYSET); + if (qres) + { + int j, k, l, m; + TupleField *tuple, *tuplew; + + for (j = 0; j < qres->num_total_rows; j++) + { + oid = getOid(qres, j); + getTid(qres, j, &blocknum, &offset); + for (k = stmt->rowset_start; k < limitrow; k++) + { + if (oid == getOid(res, k)) + { + l = k - stmt->rowset_start + res->base; + tuple = res->backend_tuples + res->num_fields * l; + tuplew = qres->backend_tuples + qres->num_fields * j; + for (m = 0; m < res->num_fields; m++, tuple++, tuplew++) + { + if (tuple->len > 0 && tuple->value) + free(tuple->value); + tuple->value = tuplew->value; + tuple->len = tuplew->len; + tuplew->value = NULL; + tuplew->len = 0; + } + res->keyset[k].status &= ~CURS_NEEDS_REREAD; + break; + } + } + } + QR_Destructor(qres); + } + if (rowc < 0) + break; + rowc = 0; + } + if (!rowc) + { + if (!qval) + { + UInt4 allen; + + lodlen = strlen(stmt->load_statement); + allen = lodlen + 20 + 23 * 10; + qval = malloc(allen); + } + memcpy(qval, stmt->load_statement, lodlen); + sval = qval + lodlen; + sval[0]= '\0'; + strcpy(sval, " where ctid in ("); + sval = strchr(sval, '\0'); + } + if (0 != (res->keyset[i].status & CURS_NEEDS_REREAD)) + { + getTid(res, i, &blocknum, &offset); + if (rowc) + sprintf(sval, ", '(%u, %u)'", blocknum, offset); + else + sprintf(sval, "'(%u, %u)'", blocknum, offset); + sval = strchr(sval, '\0'); + rowc++; + } + } + if (qval) + free(qval); + else + return SQL_SUCCESS; + + for (i = stmt->rowset_start; i < limitrow; i++) + { + if (0 != (res->keyset[i].status & CURS_NEEDS_REREAD)) + { + ret = SC_pos_reload(stmt, i, &qcount, FALSE); + if (SQL_ERROR == ret) + { + break; + } + if (SQL_ROW_DELETED == (res->keyset[i].status & KEYSET_INFO_PUBLIC)) + { + res->keyset[i].status |= CURS_OTHER_DELETED; + } + res->keyset[i].status &= ~CURS_NEEDS_REREAD; + } + } + return ret; +} + RETCODE SQL_API SC_pos_newload(StatementClass *stmt, UInt4 oid, BOOL tidRef) { @@ -1681,51 +2110,64 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, BOOL tidRef) if (qres = positioned_load(stmt, tidRef ? USE_INSERTED_TID : 0, oid, NULL), qres) { TupleField *tupleo, *tuplen; - int count = QR_get_num_tuples(qres); + int count = QR_get_num_backend_tuples(qres); QR_set_position(qres, 0); if (count == 1) { int effective_fields = res->num_fields; + int tuple_size; tuplen = qres->tupleField; - if (res->fcount >= res->count_allocated) + if (res->haskeyset && + res->num_total_rows >= res->count_keyset_allocated) { - int tuple_size; - if (!res->count_allocated) + if (!res->count_keyset_allocated) tuple_size = TUPLE_MALLOC_INC; else - tuple_size = res->count_allocated * 2; - res->backend_tuples = (TupleField *) realloc( - res->backend_tuples, - res->num_fields * sizeof(TupleField) * tuple_size); - if (!res->backend_tuples) - { - stmt->errornumber = res->status = PGRES_FATAL_ERROR; - stmt->errormsg = "Out of memory while reading tuples."; - QR_Destructor(qres); - return SQL_ERROR; - } - if (res->haskeyset) - res->keyset = (KeySet *) realloc(res->keyset, sizeof(KeySet) * tuple_size); - res->count_allocated = tuple_size; + tuple_size = res->count_keyset_allocated * 2; + res->keyset = (KeySet *) realloc(res->keyset, sizeof(KeySet) * tuple_size); + res->count_keyset_allocated = tuple_size; } - tupleo = res->backend_tuples + res->num_fields * res->fcount; - KeySetSet(tuplen, qres->num_fields, res->keyset + res->fcount); - for (i = 0; i < effective_fields; i++) - { - tupleo[i].len = tuplen[i].len; - tuplen[i].len = 0; - tupleo[i].value = tuplen[i].value; - tuplen[i].value = NULL; - } - for (; i < res->num_fields; i++) + KeySetSet(tuplen, qres->num_fields, res->keyset + res->num_total_rows); + + if (res->num_total_rows == res->num_backend_rows - res->base + stmt->rowset_start) { - tupleo[i].len = 0; - tupleo[i].value = NULL; + if (res->num_backend_rows >= res->count_backend_allocated) + { + if (!res->count_backend_allocated) + tuple_size = TUPLE_MALLOC_INC; + else + tuple_size = res->count_backend_allocated * 2; + res->backend_tuples = (TupleField *) realloc( + res->backend_tuples, + res->num_fields * sizeof(TupleField) * tuple_size); + if (!res->backend_tuples) + { + stmt->errornumber = res->status = PGRES_FATAL_ERROR; + stmt->errormsg = "Out of memory while reading tuples."; + QR_Destructor(qres); + return SQL_ERROR; + } + res->count_backend_allocated = tuple_size; + } + tupleo = res->backend_tuples + res->num_fields * res->num_backend_rows; + for (i = 0; i < effective_fields; i++) + { + tupleo[i].len = tuplen[i].len; + tuplen[i].len = 0; + tupleo[i].value = tuplen[i].value; + tuplen[i].value = NULL; + } + for (; i < res->num_fields; i++) + { + tupleo[i].len = 0; + tupleo[i].value = NULL; + } + res->num_backend_rows++; } - res->fcount++; + res->num_total_rows++; ret = SQL_SUCCESS; } else if (0 == count) @@ -1737,7 +2179,7 @@ SC_pos_newload(StatementClass *stmt, UInt4 oid, BOOL tidRef) ret = SQL_ERROR; } QR_Destructor(qres); - /* stmt->currTuple = stmt->rowset_start + irow; */ + /* stmt->currTuple = stmt->rowset_start + ridx; */ } return ret; } @@ -1754,14 +2196,14 @@ irow_update(RETCODE ret, StatementClass *stmt, StatementClass *ustmt, UWORD irow sscanf(cmdstr, "UPDATE %d", &updcnt) == 1) { if (updcnt == 1) - SC_pos_reload(stmt, irow, global_ridx, (UWORD *) 0); + ret = SC_pos_reload(stmt, global_ridx, (UWORD *) 0, TRUE); else if (updcnt == 0) { stmt->errornumber = STMT_ROW_VERSION_CHANGED; stmt->errormsg = "the content was changed before updation"; ret = SQL_ERROR; if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) - SC_pos_reload(stmt, irow, global_ridx, (UWORD *) 0); + SC_pos_reload(stmt, global_ridx, (UWORD *) 0, FALSE); } else ret = SQL_ERROR; @@ -1812,7 +2254,10 @@ SC_pos_update(StatementClass *stmt, } getTid(res, global_ridx, &blocknum, &pgoffset); - sprintf(updstr, "update \"%s\" set", stmt->ti[0]->name); + if (stmt->ti[0]->schema[0]) + sprintf(updstr, "update \"%s\".\"%s\" set", stmt->ti[0]->schema, stmt->ti[0]->name); + else + sprintf(updstr, "update \"%s\" set", stmt->ti[0]->name); num_cols = irdflds->nfields; offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0; for (i = upd_cols = 0; i < num_cols; i++) @@ -1842,8 +2287,10 @@ SC_pos_update(StatementClass *stmt, HSTMT hstmt; int j; int res_cols = QR_NumResultCols(res); + ConnInfo *ci = &(conn->connInfo); StatementClass *qstmt; APDFields *apdopts; + Int4 fieldtype = 0; /*sprintf(updstr, "%s where ctid = '%s' and oid = %s", updstr, tidval, oidval);*/ @@ -1868,10 +2315,11 @@ SC_pos_update(StatementClass *stmt, mylog("%d used=%d\n", i, *used); if (*used != SQL_IGNORE && fi[i]->updatable) { + fieldtype = QR_get_field_type(res, i); PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++j, SQL_PARAM_INPUT, bindings[i].returntype, - pgtype_to_concise_type(stmt, QR_get_field_type(res, i)), - QR_get_fieldsize(res, i), + pgtype_to_concise_type(stmt, fieldtype), + fi[i]->column_size > 0 ? fi[i]->column_size : pgtype_column_size(stmt, fieldtype, i, ci->drivers.unknown_sizes), (SQLSMALLINT) fi[i]->decimal_digits, bindings[i].buffer, bindings[i].buflen, @@ -1905,7 +2353,6 @@ SC_pos_update(StatementClass *stmt, { if (CC_is_in_trans(conn)) { - AddRollback(conn, res, global_ridx, res->keyset); res->keyset[global_ridx].status |= (SQL_ROW_UPDATED | CURS_SELF_UPDATING); } else @@ -1939,7 +2386,7 @@ SC_pos_delete(StatementClass *stmt, BindInfoClass *bindings = opts->bindings; char dltstr[4096]; RETCODE ret; - UInt4 oid, blocknum; + UInt4 oid, blocknum, qflag; mylog("POS DELETE ti=%x\n", stmt->ti); if (!(res = SC_get_Curres(stmt))) @@ -1958,11 +2405,19 @@ SC_pos_delete(StatementClass *stmt, } getTid(res, global_ridx, &blocknum, &offset); /*sprintf(dltstr, "delete from \"%s\" where ctid = '%s' and oid = %s",*/ - sprintf(dltstr, "delete from \"%s\" where ctid = '(%u, %u)' and oid = %u", + if (stmt->ti[0]->schema[0]) + sprintf(dltstr, "delete from \"%s\".\"%s\" where ctid = '(%u, %u)' and oid = %u", + stmt->ti[0]->schema, stmt->ti[0]->name, blocknum, offset, oid); + else + sprintf(dltstr, "delete from \"%s\" where ctid = '(%u, %u)' and oid = %u", stmt->ti[0]->name, blocknum, offset, oid); mylog("dltstr=%s\n", dltstr); - qres = CC_send_query(conn, dltstr, NULL, CLEAR_RESULT_ON_ABORT); + qflag = CLEAR_RESULT_ON_ABORT; + if (!stmt->internal && !CC_is_in_trans(conn) && + (!CC_is_in_autocommit(conn))) + qflag |= GO_INTO_TRANSACTION; + qres = CC_send_query(conn, dltstr, NULL, qflag); ret = SQL_SUCCESS; if (qres && QR_command_maybe_successful(qres)) { @@ -1973,14 +2428,14 @@ SC_pos_delete(StatementClass *stmt, sscanf(cmdstr, "DELETE %d", &dltcnt) == 1) { if (dltcnt == 1) - SC_pos_reload(stmt, irow, global_ridx, (UWORD *) 0); + SC_pos_reload(stmt, global_ridx, (UWORD *) 0, TRUE); else if (dltcnt == 0) { stmt->errornumber = STMT_ROW_VERSION_CHANGED; stmt->errormsg = "the content was changed before deletion"; ret = SQL_ERROR; if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) - SC_pos_reload(stmt, irow, global_ridx, (UWORD *) 0); + SC_pos_reload(stmt, global_ridx, (UWORD *) 0, FALSE); } else ret = SQL_ERROR; @@ -1999,9 +2454,9 @@ SC_pos_delete(StatementClass *stmt, QR_Destructor(qres); if (SQL_SUCCESS == ret && res->keyset) { + AddDeleted(res, global_ridx); if (CC_is_in_trans(conn)) { - AddRollback(conn, res, global_ridx, res->keyset); res->keyset[global_ridx].status |= (SQL_ROW_DELETED | CURS_SELF_DELETING); } else @@ -2085,6 +2540,7 @@ SC_pos_add(StatementClass *stmt, HSTMT hstmt; StatementClass *qstmt; ConnectionClass *conn; + ConnInfo *ci; QResultClass *res; ARDFields *opts = SC_get_ARD(stmt); IRDFields *irdflds = SC_get_IRD(stmt); @@ -2095,6 +2551,7 @@ SC_pos_add(StatementClass *stmt, RETCODE ret; UInt4 offset; Int4 *used, bind_size = opts->bind_size; + Int4 fieldtype; mylog("POS ADD fi=%x ti=%x\n", fi, stmt->ti); if (!(res = SC_get_Curres(stmt))) @@ -2108,7 +2565,10 @@ SC_pos_add(StatementClass *stmt, } num_cols = irdflds->nfields; conn = SC_get_conn(stmt); - sprintf(addstr, "insert into \"%s\" (", stmt->ti[0]->name); + if (stmt->ti[0]->schema[0]) + sprintf(addstr, "insert into \"%s\".\"%s\" (", stmt->ti[0]->schema, stmt->ti[0]->name); + else + sprintf(addstr, "insert into \"%s\" (", stmt->ti[0]->name); if (PGAPI_AllocStmt(conn, &hstmt) != SQL_SUCCESS) return SQL_ERROR; if (opts->row_offset_ptr) @@ -2119,6 +2579,7 @@ SC_pos_add(StatementClass *stmt, apdopts = SC_get_APD(qstmt); apdopts->param_bind_type = opts->bind_size; apdopts->param_offset_ptr = opts->row_offset_ptr; + ci = &(conn->connInfo); for (i = add_cols = 0; i < num_cols; i++) { if (used = bindings[i].used, used != NULL) @@ -2131,14 +2592,15 @@ SC_pos_add(StatementClass *stmt, mylog("%d used=%d\n", i, *used); if (*used != SQL_IGNORE && fi[i]->updatable) { + fieldtype = QR_get_field_type(res, i); if (add_cols) sprintf(addstr, "%s, \"%s\"", addstr, fi[i]->name); else sprintf(addstr, "%s\"%s\"", addstr, fi[i]->name); PGAPI_BindParameter(hstmt, (SQLUSMALLINT) ++add_cols, SQL_PARAM_INPUT, bindings[i].returntype, - pgtype_to_concise_type(stmt, QR_get_field_type(res, i)), - QR_get_fieldsize(res, i), + pgtype_to_concise_type(stmt, fieldtype), + fi[i]->column_size > 0 ? fi[i]->column_size : pgtype_column_size(stmt, fieldtype, i, ci->drivers.unknown_sizes), (SQLSMALLINT) fi[i]->decimal_digits, bindings[i].buffer, bindings[i].buflen, @@ -2178,7 +2640,7 @@ SC_pos_add(StatementClass *stmt, } brow_save = stmt->bind_row; stmt->bind_row = irow; - ret = irow_insert(ret, stmt, qstmt, res->fcount); + ret = irow_insert(ret, stmt, qstmt, res->num_total_rows); stmt->bind_row = brow_save; } else @@ -2189,7 +2651,7 @@ SC_pos_add(StatementClass *stmt, PGAPI_FreeStmt(hstmt, SQL_DROP); if (SQL_SUCCESS == ret && res->keyset) { - int global_ridx = res->fcount - 1; + int global_ridx = res->num_total_rows + stmt->rowset_start - res->base - 1; if (CC_is_in_trans(conn)) { @@ -2230,16 +2692,18 @@ SC_pos_refresh(StatementClass *stmt, UWORD irow , UDWORD global_ridx) #endif /* ODBCVER */ /* save the last_fetch_count */ int last_fetch = stmt->last_fetch_count; + int last_fetch2 = stmt->last_fetch_count_include_ommitted; int bind_save = stmt->bind_row; #ifdef DRIVER_CURSOR_IMPLEMENT if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) - SC_pos_reload(stmt, irow, global_ridx, (UWORD *) 0); + SC_pos_reload(stmt, global_ridx, (UWORD *) 0, FALSE); #endif /* DRIVER_CURSOR_IMPLEMENT */ stmt->bind_row = irow; ret = SC_fetch(stmt); /* restore the last_fetch_count */ stmt->last_fetch_count = last_fetch; + stmt->last_fetch_count_include_ommitted = last_fetch2; stmt->bind_row = bind_save; #if (ODBCVER >= 0x0300) if (irdflds->rowStatusArray) @@ -2252,6 +2716,7 @@ SC_pos_refresh(StatementClass *stmt, UWORD irow , UDWORD global_ridx) case SQL_SUCCESS: irdflds->rowStatusArray[irow] = SQL_ROW_SUCCESS; break; + case SQL_SUCCESS_WITH_INFO: default: irdflds->rowStatusArray[irow] = ret; break; @@ -2278,10 +2743,11 @@ PGAPI_SetPos( StatementClass *stmt = (StatementClass *) hstmt; ConnectionClass *conn = SC_get_conn(stmt); QResultClass *res; - int num_cols, i, start_row, end_row, processed; + int num_cols, i, start_row, end_row, processed, ridx; + UWORD nrow; ARDFields *opts; BindInfoClass *bindings; - UDWORD global_ridx, fcount; + UDWORD global_ridx; BOOL auto_commit_needed = FALSE; if (!stmt) @@ -2318,8 +2784,8 @@ PGAPI_SetPos( { if (SQL_POSITION == fOption) { - stmt->errornumber = STMT_ROW_OUT_OF_RANGE; - stmt->errormsg = "Bulk Fresh operations not allowed."; + stmt->errornumber = STMT_INVALID_CURSOR_POSITION; + stmt->errormsg = "Bulk Position operations not allowed."; SC_log_error(func, "", stmt); return SQL_ERROR; } @@ -2344,7 +2810,6 @@ PGAPI_SetPos( for (i = 0; i < num_cols; i++) bindings[i].data_left = -1; ret = SQL_SUCCESS; - fcount = res->fcount; #ifdef DRIVER_CURSOR_IMPLEMENT switch (fOption) { @@ -2356,28 +2821,47 @@ PGAPI_SetPos( break; } #endif /* DRIVER_CURSOR_IMPLEMENT */ - for (i = start_row, processed = 0; i <= end_row; i++) + ridx = -1; + for (i = nrow = 0, processed = 0; nrow <= end_row; i++) { + global_ridx = i + stmt->rowset_start; + if (SQL_ADD != fOption) + { + if ((int) global_ridx >= res->num_total_rows) + break; +#ifdef DRIVER_CURSOR_IMPLEMENT + if (res->keyset) /* the row may be deleted and not in the rowset */ + { + if (0 == (res->keyset[global_ridx].status & CURS_IN_ROWSET)) + continue; + } +#endif /* DRIVER_CURSOR_IMPLEMENT */ + } + if (nrow < start_row) + { + nrow++; + continue; + } + ridx = nrow; #if (ODBCVER >= 0x0300) - if (0 != irow || !opts->row_operation_ptr || opts->row_operation_ptr[i] == SQL_ROW_PROCEED) + if (0 != irow || !opts->row_operation_ptr || opts->row_operation_ptr[nrow] == SQL_ROW_PROCEED) { #endif /* ODBCVER */ - global_ridx = i + stmt->rowset_start; switch (fOption) { #ifdef DRIVER_CURSOR_IMPLEMENT case SQL_UPDATE: - ret = SC_pos_update(stmt, (UWORD) i, global_ridx); + ret = SC_pos_update(stmt, nrow, global_ridx); break; case SQL_DELETE: - ret = SC_pos_delete(stmt, (UWORD) i, global_ridx); + ret = SC_pos_delete(stmt, nrow, global_ridx); break; case SQL_ADD: - ret = SC_pos_add(stmt, (UWORD) i); + ret = SC_pos_add(stmt, nrow); break; #endif /* DRIVER_CURSOR_IMPLEMENT */ case SQL_REFRESH: - ret = SC_pos_refresh(stmt, (UWORD) i, global_ridx); + ret = SC_pos_refresh(stmt, nrow, global_ridx); break; } processed++; @@ -2386,21 +2870,23 @@ PGAPI_SetPos( #if (ODBCVER >= 0x0300) } #endif /* ODBCVER */ + nrow++; } if (SQL_ERROR == ret) - res->fcount = fcount; /* restore the count */ + CC_abort(conn); if (auto_commit_needed) PGAPI_SetConnectOption(conn, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON); if (irow > 0) { - if (SQL_ADD != fOption) /* for SQLGetData */ + if (SQL_ADD != fOption && ridx >= 0) /* for SQLGetData */ { - stmt->currTuple = stmt->rowset_start + irow - 1; - QR_set_position(res, irow - 1); + stmt->currTuple = stmt->rowset_start + ridx; + QR_set_position(res, ridx); } } else if (SC_get_IRD(stmt)->rowsFetched) - *SC_get_IRD(stmt)->rowsFetched = processed; + *(SC_get_IRD(stmt)->rowsFetched) = processed; + res->recent_processed_row_count = stmt->diag_row_count = processed; inolog("rowset=%d processed=%d ret=%d\n", opts->rowset_size, processed, ret); return ret; } @@ -2408,11 +2894,10 @@ inolog("rowset=%d processed=%d ret=%d\n", opts->rowset_size, processed, ret); /* Sets options that control the behavior of cursors. */ RETCODE SQL_API -PGAPI_SetScrollOptions( - HSTMT hstmt, - UWORD fConcurrency, - SDWORD crowKeyset, - UWORD crowRowset) +PGAPI_SetScrollOptions( HSTMT hstmt, + UWORD fConcurrency, + SDWORD crowKeyset, + UWORD crowRowset) { static char *func = "PGAPI_SetScrollOptions"; StatementClass *stmt = (StatementClass *) hstmt; diff --git a/src/interfaces/odbc/statement.c b/src/interfaces/odbc/statement.c index 122fcbff9b..551c315879 100644 --- a/src/interfaces/odbc/statement.c +++ b/src/interfaces/odbc/statement.c @@ -272,7 +272,7 @@ SC_Constructor(void) rv->rowset_start = -1; rv->current_col = -1; rv->bind_row = 0; - rv->last_fetch_count = 0; + rv->last_fetch_count = rv->last_fetch_count_include_ommitted = 0; rv->save_rowset_size = -1; rv->data_at_exec = -1; @@ -302,6 +302,7 @@ SC_Constructor(void) rv->miscinfo = 0; rv->updatable = FALSE; rv->error_recsize = -1; + rv->diag_row_count = 0; } return rv; } @@ -533,7 +534,7 @@ SC_recycle_statement(StatementClass *self) self->rowset_start = -1; self->current_col = -1; self->bind_row = 0; - self->last_fetch_count = 0; + self->last_fetch_count = self->last_fetch_count_include_ommitted = 0; self->errormsg = NULL; self->errornumber = 0; @@ -619,6 +620,7 @@ SC_clear_error(StatementClass *self) self->errormsg_created = FALSE; self->errorpos = 0; self->error_recsize = -1; + self->diag_row_count = 0; } @@ -733,21 +735,21 @@ SC_fetch(StatementClass *self) /* TupleField *tupleField; */ ConnInfo *ci = &(SC_get_conn(self)->connInfo); - self->last_fetch_count = 0; + self->last_fetch_count = self->last_fetch_count_include_ommitted = 0; coli = QR_get_fields(res); /* the column info */ mylog("manual_result = %d, use_declarefetch = %d\n", self->manual_result, ci->drivers.use_declarefetch); if (self->manual_result || !SC_is_fetchcursor(self)) { - if (self->currTuple >= QR_get_num_tuples(res) - 1 || + if (self->currTuple >= QR_get_num_total_tuples(res) - 1 || (self->options.maxRows > 0 && self->currTuple == self->options.maxRows - 1)) { /* * if at the end of the tuples, return "no data found" and set * the cursor past the end of the result set */ - self->currTuple = QR_get_num_tuples(res); + self->currTuple = QR_get_num_total_tuples(res); return SQL_NO_DATA_FOUND; } @@ -774,11 +776,23 @@ SC_fetch(StatementClass *self) return SQL_ERROR; } } +#ifdef DRIVER_CURSOR_IMPLEMENT + if (res->haskeyset) + { + UWORD pstatus = res->keyset[self->currTuple].status; + if (0 != (pstatus & (CURS_SELF_DELETING | CURS_SELF_DELETED))) + return SQL_SUCCESS_WITH_INFO; + if (SQL_ROW_DELETED != (pstatus & KEYSET_INFO_PUBLIC) && + 0 != (pstatus & CURS_OTHER_DELETED)) + return SQL_SUCCESS_WITH_INFO; + } +#endif /* DRIVER_CURSOR_IMPLEMENT */ num_cols = QR_NumResultCols(res); result = SQL_SUCCESS; - self->last_fetch_count = 1; + self->last_fetch_count++; + self->last_fetch_count_include_ommitted++; opts = SC_get_ARD(self); /* @@ -830,7 +844,12 @@ SC_fetch(StatementClass *self) else if (SC_is_fetchcursor(self)) value = QR_get_value_backend(res, lf); else - value = QR_get_value_backend_row(res, self->currTuple, lf); + { + int curt = res->base; + if (self->rowset_start >= 0) + curt += (self->currTuple - self->rowset_start); + value = QR_get_value_backend_row(res, curt, lf); + } mylog("value = '%s'\n", (value == NULL) ? "" : value); @@ -1152,7 +1171,7 @@ SC_log_error(const char *func, const char *desc, const StatementClass *self) if (res) { qlog(" fields=%u, manual_tuples=%u, backend_tuples=%u, tupleField=%d, conn=%u\n", res->fields, res->manual_tuples, res->backend_tuples, res->tupleField, res->conn); - qlog(" fetch_count=%d, fcount=%d, num_fields=%d, cursor='%s'\n", res->fetch_count, res->fcount, res->num_fields, nullcheck(res->cursor)); + qlog(" fetch_count=%d, num_total_rows=%d, num_fields=%d, cursor='%s'\n", res->fetch_count, res->num_total_rows, res->num_fields, nullcheck(res->cursor)); qlog(" message='%s', command='%s', notice='%s'\n", nullcheck(res->message), nullcheck(res->command), nullcheck(res->notice)); qlog(" status=%d, inTuples=%d\n", res->status, res->inTuples); } diff --git a/src/interfaces/odbc/statement.h b/src/interfaces/odbc/statement.h index e0a01b7eab..f1c2ddd4c1 100644 --- a/src/interfaces/odbc/statement.h +++ b/src/interfaces/odbc/statement.h @@ -80,6 +80,7 @@ typedef enum #define STMT_ERROR_IN_ROW 30 #define STMT_INVALID_DESCRIPTOR_IDENTIFIER 31 #define STMT_OPTION_NOT_FOR_THE_DRIVER 32 +#define STMT_FETCH_OUT_OF_RANGE 33 /* statement types */ enum @@ -137,15 +138,6 @@ struct StatementClass_ char *errormsg; int errornumber; - /* information on bindings */ - /*** BindInfoClass *bindings; ***/ /* array to store the binding information */ - /*** BindInfoClass bookmark; - int bindings_allocated; ***/ - - /* information on statement parameters */ - /*** int parameters_allocated; - ParameterInfoClass *parameters; ***/ - Int4 currTuple; /* current absolute row number (GetData, * SetPos, SQLFetch) */ int save_rowset_size; /* saved rowset size in case of @@ -200,9 +192,11 @@ struct StatementClass_ char updatable; SWORD errorpos; SWORD error_recsize; + Int4 diag_row_count; char *load_statement; /* to (re)load updatable individual rows */ Int4 from_pos; Int4 where_pos; + Int4 last_fetch_count_include_ommitted; }; #define SC_get_conn(a) (a->hdbc) diff --git a/src/interfaces/odbc/tuple.h b/src/interfaces/odbc/tuple.h index eb00cfd509..c7fe46def4 100644 --- a/src/interfaces/odbc/tuple.h +++ b/src/interfaces/odbc/tuple.h @@ -53,6 +53,8 @@ struct Rollback_ #define CURS_SELF_DELETED (1L << 7) #define CURS_SELF_UPDATED (1L << 8) #define CURS_NEEDS_REREAD (1L << 9) +#define CURS_IN_ROWSET (1L << 10) +#define CURS_OTHER_DELETED (1L << 11) /* These macros are wrappers for the corresponding set_tuplefield functions but these handle automatic NULL determination and call set_tuplefield_null() -- 2.40.0