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.
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
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
{
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);
}
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;
}
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 */
};
/*
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;
};
{
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));
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;
}
#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;
}
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))
{
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;
}
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,
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];
{
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;
}
{
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;
}
{
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;
}
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;
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;
}
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);
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);
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;
{
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;
}
{
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;
}
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);
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;
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);
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;
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;
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;
};
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__ */
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)
#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;
#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;
{
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;
#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 */
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++)
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))
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)
{
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;
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)
{
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";
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 */
y = 0;
for (i = 1; i <= 3; i++)
- y += (s[i] - '0') << (3 * (3 - i));
+ y += (s[i] - '0') << (3 * (3 - i));
return y;
*
* 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 $
*
*/
char schema[MAX_SCHEMA_LEN + 1];
char name[MAX_TABLE_LEN + 1];
char alias[MAX_TABLE_LEN + 1];
+ char updatable;
} TABLE_INFO;
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_
{
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 */
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 */
INI_LFCONVERSION,
ci->lf_conversion,
INI_UPDATABLECURSORS,
- ci->updatable_cursors,
+ ci->allow_keyset,
INI_DISALLOWPREMATURE,
ci->disallow_premature,
INI_TRUEISMINUS1,
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;
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;
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)
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)
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)
INI_DISALLOWPREMATURE,
temp,
ODBC_INI);
- sprintf(temp, "%d", ci->updatable_cursors);
+ sprintf(temp, "%d", ci->allow_keyset);
SQLWritePrivateProfileString(DSN,
INI_UPDATABLECURSORS,
temp,
}
+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
{
/* 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),
{
/* 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;
}
UWORD flag)
{
ConnectionClass *conn = (ConnectionClass *) hdbc;
+ EnvironmentClass *env = (EnvironmentClass *) conn->henv;
char *msg;
int status;
BOOL once_again = FALSE;
{
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
/* -> 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;
}
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))
{
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;
}
int i,
retval, start_row, end_row;
int cursor_type, scroll_concurrency;
+ QResultClass *res;
mylog("%s: entering...\n", func);
{
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)
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;
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";
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;
}
if (!ok)
{
- CC_on_abort(conn, TRUE);
+ CC_on_abort(conn, NO_TRANS);
CC_log_error(func, "", conn);
return SQL_ERROR;
}
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 */
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);
* 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 */
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);
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 */
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)
{
}
}
}
+ 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;
{
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);
}
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));
}
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;
* 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" ","
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])
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);
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++)
{
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;
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 */
| 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
| 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:
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 */
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;
if (!LOGFP)
{
generate_filename(MYLOGDIR, MYLOGFILE, filebuf);
- LOGFP = fopen(filebuf, PG_BINARY_W);
+ LOGFP = fopen(filebuf, PG_BINARY_A);
setbuf(LOGFP, NULL);
}
if (!LOGFP)
{
generate_filename(QLOGDIR, QLOGFILE, filebuf);
- LOGFP = fopen(filebuf, PG_BINARY_W);
+ LOGFP = fopen(filebuf, PG_BINARY_A);
setbuf(LOGFP, NULL);
}
{
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);
#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
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
HSTMT *StatementHandle)
{
mylog("[SQLAllocStmt]");
+ CC_clear_error((ConnectionClass *) ConnectionHandle);
return PGAPI_AllocStmt(ConnectionHandle, StatementHandle);
}
SQLINTEGER *StrLen_or_Ind)
{
mylog("[SQLBindCol]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_BindCol(StatementHandle, ColumnNumber,
TargetType, TargetValue, BufferLength, StrLen_or_Ind);
}
SQLCancel(HSTMT StatementHandle)
{
mylog("[SQLCancel]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_Cancel(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);
SQLCHAR *Authentication, SQLSMALLINT NameLength3)
{
mylog("[SQLConnect]");
+ CC_clear_error((ConnectionClass *) ConnectionHandle);
return PGAPI_Connect(ConnectionHandle, ServerName, NameLength1,
UserName, NameLength2, Authentication, NameLength3);
}
UWORD fDriverCompletion)
{
mylog("[SQLDriverConnect]");
+ CC_clear_error((ConnectionClass *) hdbc);
return PGAPI_DriverConnect(hdbc, hwnd, szConnStrIn, cbConnStrIn,
szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion);
}
SQLSMALLINT *pcbConnStrOut)
{
mylog("[SQLBrowseConnect]");
+ CC_clear_error((ConnectionClass *) hdbc);
return PGAPI_BrowseConnect(hdbc, szConnStrIn, cbConnStrIn,
szConnStrOut, cbConnStrOutMax, pcbConnStrOut);
}
SQLSMALLINT *DecimalDigits, SQLSMALLINT *Nullable)
{
mylog("[SQLDescribeCol]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_DescribeCol(StatementHandle, ColumnNumber,
ColumnName, BufferLength, NameLength,
DataType, ColumnSize, DecimalDigits, Nullable);
SQLDisconnect(HDBC ConnectionHandle)
{
mylog("[SQLDisconnect]");
+ CC_clear_error((ConnectionClass *) ConnectionHandle);
return PGAPI_Disconnect(ConnectionHandle);
}
SQLCHAR *StatementText, SQLINTEGER TextLength)
{
mylog("[SQLExecDirect]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_ExecDirect(StatementHandle, StatementText, TextLength);
}
SQLExecute(HSTMT StatementHandle)
{
mylog("[SQLExecute]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_Execute(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);
mylog("[[%s]]", func);
return PGAPI_ExtendedFetch(StatementHandle, SQL_FETCH_NEXT, 0,
- pcRow, rowStatusArray);
+ pcRow, rowStatusArray, 0);
}
#endif
mylog("[%s]", func);
SQLUSMALLINT Option, PTR Value)
{
mylog("[SQLGetConnectOption]");
+ CC_clear_error((ConnectionClass *) ConnectionHandle);
return PGAPI_GetConnectOption(ConnectionHandle, Option, Value);
}
RETCODE SQL_API
SQLSMALLINT *NameLength)
{
mylog("[SQLGetCursorName]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_GetCursorName(StatementHandle, CursorName, BufferLength,
NameLength);
}
SQLINTEGER *StrLen_or_Ind)
{
mylog("[SQLGetData]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_GetData(StatementHandle, ColumnNumber, TargetType,
TargetValue, BufferLength, StrLen_or_Ind);
}
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);
SQLUSMALLINT Option, PTR Value)
{
mylog("[SQLGetStmtOption]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_GetStmtOption(StatementHandle, Option, Value);
}
SQLSMALLINT DataType)
{
mylog("[SQLGetTypeInfo]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_GetTypeInfo(StatementHandle, DataType);
}
SQLSMALLINT *ColumnCount)
{
mylog("[SQLNumResultCols]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_NumResultCols(StatementHandle, ColumnCount);
}
PTR *Value)
{
mylog("[SQLParamData]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_ParamData(StatementHandle, Value);
}
SQLCHAR *StatementText, SQLINTEGER TextLength)
{
mylog("[SQLPrepare]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_Prepare(StatementHandle, StatementText, TextLength);
}
PTR Data, SQLINTEGER StrLen_or_Ind)
{
mylog("[SQLPutData]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_PutData(StatementHandle, Data, StrLen_or_Ind);
}
SQLINTEGER *RowCount)
{
mylog("[SQLRowCount]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_RowCount(StatementHandle, RowCount);
}
SQLUSMALLINT Option, SQLUINTEGER Value)
{
mylog("[SQLSetConnectionOption]");
+ CC_clear_error((ConnectionClass *) ConnectionHandle);
return PGAPI_SetConnectOption(ConnectionHandle, Option, Value);
}
SQLCHAR *CursorName, SQLSMALLINT NameLength)
{
mylog("[SQLSetCursorName]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_SetCursorName(StatementHandle, CursorName, NameLength);
}
SQLINTEGER *StrLen_or_Ind)
{
mylog("[SQLSetParam]");
+ SC_clear_error((StatementClass *) StatementHandle);
/*
* return PGAPI_SetParam(StatementHandle, ParameterNumber, ValueType,
SQLUSMALLINT Option, SQLUINTEGER Value)
{
mylog("[SQLSetStmtOption]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_SetStmtOption(StatementHandle, Option, Value);
}
SQLUSMALLINT Nullable)
{
mylog("[SQLSpecialColumns]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_SpecialColumns(StatementHandle, IdentifierType, CatalogName,
NameLength1, SchemaName, NameLength2, TableName, NameLength3,
Scope, Nullable);
SQLUSMALLINT Unique, SQLUSMALLINT Reserved)
{
mylog("[SQLStatistics]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_Statistics(StatementHandle, CatalogName, NameLength1,
SchemaName, NameLength2, TableName, NameLength3, Unique,
Reserved);
SQLCHAR *TableType, SQLSMALLINT NameLength4)
{
mylog("[SQLTables]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_Tables(StatementHandle, CatalogName, NameLength1,
SchemaName, NameLength2, TableName, NameLength3,
TableType, NameLength4);
SQLINTEGER *pfDesc)
{
mylog("[SQLColAttributes]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_ColAttributes(hstmt, icol, fDescType, rgbDesc,
cbDescMax, pcbDesc, pfDesc);
}
SQLSMALLINT cbColumnName)
{
mylog("[SQLColumnPrivileges]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_ColumnPrivileges(hstmt, szCatalogName, cbCatalogName,
szSchemaName, cbSchemaName, szTableName, cbTableName,
szColumnName, cbColumnName);
SQLSMALLINT *pfNullable)
{
mylog("[SQLDescribeParam]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_DescribeParam(hstmt, ipar, pfSqlType, pcbParamDef,
pibScale, pfNullable);
}
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
SQLSMALLINT cbFkTableName)
{
mylog("[SQLForeignKeys]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_ForeignKeys(hstmt, szPkCatalogName, cbPkCatalogName,
szPkSchemaName, cbPkSchemaName, szPkTableName,
cbPkTableName, szFkCatalogName, cbFkCatalogName,
SQLMoreResults(HSTMT hstmt)
{
mylog("[SQLMoreResults]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_MoreResults(hstmt);
}
SQLINTEGER *pcbSqlStr)
{
mylog("[SQLNativeSql]");
+ CC_clear_error((ConnectionClass *) hdbc);
return PGAPI_NativeSql(hdbc, szSqlStrIn, cbSqlStrIn, szSqlStr,
cbSqlStrMax, pcbSqlStr);
}
SQLSMALLINT *pcpar)
{
mylog("[SQLNumParams]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_NumParams(hstmt, pcpar);
}
SQLUINTEGER *pirow)
{
mylog("[SQLParamOptions]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_ParamOptions(hstmt, crow, pirow);
}
SQLSMALLINT cbTableName)
{
mylog("[SQLPrimaryKeys]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_PrimaryKeys(hstmt, szCatalogName, cbCatalogName,
szSchemaName, cbSchemaName, szTableName, cbTableName);
}
SQLSMALLINT cbColumnName)
{
mylog("[SQLProcedureColumns]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_ProcedureColumns(hstmt, szCatalogName, cbCatalogName,
szSchemaName, cbSchemaName, szProcName, cbProcName,
szColumnName, cbColumnName);
SQLSMALLINT cbProcName)
{
mylog("[SQLProcedures]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_Procedures(hstmt, szCatalogName, cbCatalogName,
szSchemaName, cbSchemaName, szProcName, cbProcName);
}
SQLUSMALLINT fLock)
{
mylog("[SQLSetPos]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_SetPos(hstmt, irow, fOption, fLock);
}
SQLSMALLINT cbTableName)
{
mylog("[SQLTablePrivileges]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_TablePrivileges(hstmt, szCatalogName, cbCatalogName,
szSchemaName, cbSchemaName, szTableName, cbTableName, 0);
}
SQLINTEGER *pcbValue)
{
mylog("[SQLBindParameter]");
+ SC_clear_error((StatementClass *) hstmt);
return PGAPI_BindParameter(hstmt, ipar, fParamType, fCType,
fSqlType, cbColDef, ibScale, rgbValue, cbValueMax,
pcbValue);
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);
}
SQLCloseCursor(HSTMT StatementHandle)
{
mylog("[[SQLCloseCursor]]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_FreeStmt(StatementHandle, SQL_CLOSE);
}
SQLSMALLINT *StringLength, PTR NumericAttribute)
{
mylog("[[SQLColAttribute]]");
+ SC_clear_error((StatementClass *) StatementHandle);
return PGAPI_ColAttributes(StatementHandle, ColumnNumber,
FieldIdentifier, CharacterAttribute, BufferLength,
StringLength, NumericAttribute);
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;
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;
}
}
ret = PGAPI_ExtendedFetch(StatementHandle, FetchOrientation, FetchOffset,
- pcRow, rowStatusArray);
+ pcRow, rowStatusArray, bkmarkoff);
if (ret != SQL_SUCCESS)
mylog("%s return = %d\n", func, ret);
return ret;
SQLINTEGER BufferLength, SQLINTEGER *StringLength)
{
mylog("[[SQLGetConnectAttr]] %d\n", Attribute);
+ CC_clear_error((ConnectionClass *) ConnectionHandle);
return PGAPI_GetConnectAttr(ConnectionHandle, Attribute,Value,
BufferLength, StringLength);
}
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);
}
ConnectionClass *conn = (ConnectionClass *) ConnectionHandle;
mylog("[[SQLSetConnectAttr]] %d\n", Attribute);
+ CC_clear_error(conn);
return PGAPI_SetConnectAttr(ConnectionHandle, Attribute, Value,
StringLength);
}
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);
}
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);
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 */
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 */
}
RETCODE ret;
mylog("[SQLGetStmtAttrW]");
+ SC_clear_error((StatementClass *) hstmt);
ret = PGAPI_GetStmtAttr(hstmt, fAttribute, rgbValue,
cbValueMax, pcbValue);
return ret;
RETCODE ret;
mylog("[SQLSetStmtAttrW]");
+ SC_clear_error((StatementClass *) hstmt);
ret = PGAPI_SetStmtAttr(hstmt, fAttribute, rgbValue,
cbValueMax);
return ret;
RETCODE ret;
mylog("[SQLGetConnectAttrW]");
+ CC_clear_error((ConnectionClass *) hdbc);
ret = PGAPI_GetConnectAttr(hdbc, fAttribute, rgbValue,
cbValueMax, pcbValue);
return ret;
RETCODE ret;
mylog("[SQLSetConnectAttrW]");
+ CC_clear_error((ConnectionClass *) hdbc);
ret = PGAPI_SetConnectAttr(hdbc, fAttribute, rgbValue,
cbValue);
return ret;
char *rgbD = NULL;
mylog("[SQLColAttributeW]");
+ SC_clear_error((StatementClass *) hstmt);
switch (fDescType)
{
case SQL_DESC_BASE_COLUMN_NAME:
;
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)
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 */
*((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 */
{
/* 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;
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(
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)
parse_statement(StatementClass *stmt)
{
static char *func = "parse_statement";
- char token[256];
+ char token[256], stoken[256];
char delim,
quote,
dquote,
i,
k = 0,
n,
- blevel = 0;
+ blevel = 0, old_blevel, subqlevel = 0;
FIELD_INFO **fi;
TABLE_INFO **ti;
char parse;
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") ||
!stricmp(token, "group") ||
!stricmp(token, "having")))
{
- in_select = FALSE;
in_from = FALSE;
in_where = TRUE;
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");
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
}
/* Function */
- if (token[0] == '(')
+ if (0 == old_blevel && blevel > 0)
{
in_dot = FALSE;
in_func = TRUE;
- blevel = 1;
fi[irdflds->nfields - 1]->func = TRUE;
/*
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)
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);
/*
* 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;
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;
{
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++)
{
#include "connection.h"
#include "statement.h"
#include "descriptor.h"
+#include "qresult.h"
#include "pgapifunc.h"
static HSTMT statementHandleFromDescHandle(SQLHDESC, SQLINTEGER *descType);
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);
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;
*((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;
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;
}
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;
}
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;
}
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;
}
}
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 */
SQLUSMALLINT fFetchType,
SQLINTEGER irow,
SQLUINTEGER *pcrow,
- SQLUSMALLINT *rgfRowStatus);
+ SQLUSMALLINT *rgfRowStatus,
+ SQLINTEGER FetchOffset);
RETCODE SQL_API PGAPI_ForeignKeys(
HSTMT hstmt,
SQLCHAR *szPkCatalogName,
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);
};
#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
/* 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;
*
* 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 $
*
*/
#include <stdio.h> /* for FILE* pointers: see GLOBAL_VALUES */
+#include "version.h"
+
/* Must come before sql.h */
#ifndef ODBCVER
#define ODBCVER 0x0250
#define DBMS_NAME "PostgreSQL"
#endif /* ODBCVER */
-#define POSTGRESDRIVERVERSION "07.02.0001"
-
#ifdef WIN32
#if (ODBCVER >= 0x0300)
#ifdef UNICODE_SUPPORT
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
+#include "version.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
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
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
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;
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");
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++)
}
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)
{
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");
}
/* 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);
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;
/* 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;
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 */
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. */
/* need to correct */
corrected = TRUE;
- fetch_size = end_tuple - fcount;
+ fetch_size = end_tuple - num_backend_rows;
self->cache_size += fetch_size;
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,
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);
}
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
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);
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)
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)))
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 */
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);
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;
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;
}
}
/* 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++;
/*
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;
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)
#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))
static char *func = "PGAPI_RowCount";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
- char *msg,
- *ptr;
ConnInfo *ci;
mylog("%s: entering...\n", func);
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;
}
* 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;
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;
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))
{
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);
}
}
}
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(
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;
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);
}
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;
}
}
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;
* 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;
* 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)
}
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;
/* 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:
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:
/* 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)
}
}
{
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,
/* 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)
#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;
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;
}
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;
{
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);
}
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;
{
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);
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 *
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);
}
}
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;
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);
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;
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;
}
}
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)
{
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)
ret = SQL_ERROR;
}
QR_Destructor(qres);
- /* stmt->currTuple = stmt->rowset_start + irow; */
+ /* stmt->currTuple = stmt->rowset_start + ridx; */
}
return ret;
}
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;
}
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++)
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);*/
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,
{
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
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)))
}
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))
{
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;
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
HSTMT hstmt;
StatementClass *qstmt;
ConnectionClass *conn;
+ ConnInfo *ci;
QResultClass *res;
ARDFields *opts = SC_get_ARD(stmt);
IRDFields *irdflds = SC_get_IRD(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)))
}
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)
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)
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,
}
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
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))
{
#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)
case SQL_SUCCESS:
irdflds->rowStatusArray[irow] = SQL_ROW_SUCCESS;
break;
+ case SQL_SUCCESS_WITH_INFO:
default:
irdflds->rowStatusArray[irow] = ret;
break;
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)
{
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;
}
for (i = 0; i < num_cols; i++)
bindings[i].data_left = -1;
ret = SQL_SUCCESS;
- fcount = res->fcount;
#ifdef DRIVER_CURSOR_IMPLEMENT
switch (fOption)
{
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++;
#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;
}
/* 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;
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;
rv->miscinfo = 0;
rv->updatable = FALSE;
rv->error_recsize = -1;
+ rv->diag_row_count = 0;
}
return rv;
}
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;
self->errormsg_created = FALSE;
self->errorpos = 0;
self->error_recsize = -1;
+ self->diag_row_count = 0;
}
/* 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;
}
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);
/*
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) ? "<NULL>" : value);
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);
}
#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
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
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)
#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()