rv->transact_status = CONN_IN_AUTOCOMMIT; /* autocommit by default */
memset(&rv->connInfo, 0, sizeof(ConnInfo));
-memcpy(&(rv->connInfo.drivers), &globals, sizeof(globals));
+#ifdef DRIVER_CURSOR_IMPLEMENT
+ rv->connInfo.updatable_cursors = 1;
+#endif /* DRIVER_CURSOR_IMPLEMENT */
+ memcpy(&(rv->connInfo.drivers), &globals, sizeof(globals));
rv->sock = SOCK_Constructor(rv);
if (!rv->sock)
return NULL;
rv->pg_version_number = .0;
rv->pg_version_major = 0;
rv->pg_version_minor = 0;
+ rv->ms_jet = 0;
#ifdef MULTIBYTE
rv->client_encoding = NULL;
rv->server_encoding = NULL;
mylog("CC_connect(): DSN = '%s', server = '%s', port = '%s', database = '%s', username = '%s', password='%s'\n", ci->dsn, ci->server, ci->port, ci->database, ci->username, ci->password);
+another_version_retry:
/*
* If the socket was closed for some reason (like a SQLDisconnect,
* but no SQLFreeConnect then create a socket now.
self->errornumber = CONN_INVALID_AUTHENTICATION;
self->errormsg = msgbuffer;
qlog("ERROR from backend during authentication: '%s'\n", self->errormsg);
+ if (strncmp(msgbuffer, "Unsupported frontend protocol", 29) == 0)
+ { /* retry older version */
+ if (PROTOCOL_63(ci))
+ strcpy(ci->protocol, PG62);
+ else
+ strcpy(ci->protocol, PG63);
+ SOCK_Destructor(sock);
+ self->sock = (SocketClass *) 0;
+ CC_initialize_pg_version(self);
+ goto another_version_retry;
+ }
+
return 0;
case 'R':
char translation_option[SMALL_REGISTRY_LEN];
char focus_password;
char disallow_premature;
+ char updatable_cursors;
GLOBAL_VALUES drivers; /* moved from driver's option */
} ConnInfo;
float pg_version_number;
Int2 pg_version_major;
Int2 pg_version_minor;
+ char ms_jet;
#ifdef MULTIBYTE
char *client_encoding;
char *server_encoding;
char got_dsn = (ci->dsn[0] != '\0');
char encoded_conn_settings[LARGE_REGISTRY_LEN];
UWORD hlen;
+ BOOL abbrev = (len <= 400);
/* fundamental info */
sprintf(connect_string, "%s=%s;DATABASE=%s;SERVER=%s;PORT=%s;UID=%s;PWD=%s",
encode(ci->conn_settings, encoded_conn_settings);
/* extra info */
- hlen = strlen(connect_string),
- sprintf(&connect_string[hlen],
+ hlen = strlen(connect_string);
+ if (!abbrev)
+ sprintf(&connect_string[hlen],
";READONLY=%s;PROTOCOL=%s;FAKEOIDINDEX=%s;SHOWOIDCOLUMN=%s;ROWVERSIONING=%s;SHOWSYSTEMTABLES=%s;CONNSETTINGS=%s;FETCH=%d;SOCKET=%d;UNKNOWNSIZES=%d;MAXVARCHARSIZE=%d;MAXLONGVARCHARSIZE=%d;DEBUG=%d;COMMLOG=%d;OPTIMIZER=%d;KSQO=%d;USEDECLAREFETCH=%d;TEXTASLONGVARCHAR=%d;UNKNOWNSASLONGVARCHAR=%d;BOOLSASCHAR=%d;PARSE=%d;CANCELASFREESTMT=%d;EXTRASYSTABLEPREFIXES=%s",
ci->onlyread,
ci->protocol,
ci->drivers.cancel_as_freestmt,
ci->drivers.extra_systable_prefixes);
/* Abbrebiation is needed ? */
- if (strlen(connect_string) >= len)
+ if (abbrev || strlen(connect_string) >= len)
sprintf(&connect_string[hlen],
";A0=%s;A1=%s;A2=%s;A3=%s;A4=%s;A5=%s;A6=%s;A7=%d;A8=%d;A9=%d;B0=%d;B1=%d;B2=%d;B3=%d;B4=%d;B5=%d;B6=%d;B7=%d;B8=%d;B9=%d;C0=%d;C1=%d;C2=%s",
ci->onlyread,
else if (stricmp(attribute, INI_DISALLOWPREMATURE) == 0 || stricmp(attribute, "C3") == 0)
{
ci->disallow_premature = atoi(value);
- /* strcpy(ci->conn_settings, value); */
+ }
+ else if (stricmp(attribute, INI_UPDATABLECURSORS) == 0 || stricmp(attribute, "C4") == 0)
+ {
+ ci->updatable_cursors = atoi(value);
}
mylog("copyAttributes: DSN='%s',server='%s',dbase='%s',user='%s',passwd='%s',port='%s',onlyread='%s',protocol='%s',conn_settings='%s',disallow_premature=%d)\n", ci->dsn, ci->server, ci->database, ci->username, ci->password, ci->port, ci->onlyread, ci->protocol, ci->conn_settings, ci->disallow_premature);
ci->disallow_premature = atoi(temp);
}
+ if (ci->updatable_cursors == 0 || overwrite)
+ {
+ SQLGetPrivateProfileString(DSN, INI_UPDATABLECURSORS, "", temp, sizeof(temp), ODBC_INI);
+ ci->updatable_cursors = atoi(temp);
+ }
+
/* Allow override of odbcinst.ini parameters here */
getCommonDefaults(DSN, ODBC_INI, ci);
INI_DISALLOWPREMATURE,
temp,
ODBC_INI);
+ sprintf(temp, "%d", ci->updatable_cursors);
+ SQLWritePrivateProfileString(DSN,
+ INI_UPDATABLECURSORS,
+ temp,
+ ODBC_INI);
}
#define INI_TRANSLATIONDLL "TranslationDLL"
#define INI_TRANSLATIONOPTION "TranslationOption"
#define INI_DISALLOWPREMATURE "DisallowPremature"
+#define INI_UPDATABLECURSORS "UpdatableCursors"
/* Connection Defaults */
/*-------
- * Module: drvconn.c
+ Module: drvconn.c
*
* Description: This module contains only routines related to
* implementing SQLDriverConnect.
int retval;
char password_required = FALSE;
int len = 0;
+ SWORD lenStrout;
mylog("%s: entering...\n", func);
*/
result = SQL_SUCCESS;
- makeConnectString(connStrOut, ci, cbConnStrOutMax);
+ lenStrout = cbConnStrOutMax;
+ if (conn->ms_jet && lenStrout > 255)
+ lenStrout = 255;
+ makeConnectString(connStrOut, ci, lenStrout);
len = strlen(connStrOut);
if (szConnStrOut)
case SQL_CURSOR_COMMIT_BEHAVIOR: /* ODBC 1.0 */
len = 2;
value = SQL_CB_CLOSE;
-#ifdef DRIVER_CURSOR_IMPLEMENT
- if (!ci->drivers.use_declarefetch)
- value = SQL_CB_PRESERVE;
-#endif /* DRIVER_CURSOR_IMPLEMENT */
+ if (ci->updatable_cursors)
+ if (!ci->drivers.use_declarefetch)
+ value = SQL_CB_PRESERVE;
break;
case SQL_CURSOR_ROLLBACK_BEHAVIOR: /* ODBC 1.0 */
len = 2;
value = SQL_CB_CLOSE;
-#ifdef DRIVER_CURSOR_IMPLEMENT
- if (!ci->drivers.use_declarefetch)
- value = SQL_CB_PRESERVE;
-#endif /* DRIVER_CURSOR_IMPLEMENT */
+ if (ci->updatable_cursors)
+ if (!ci->drivers.use_declarefetch)
+ value = SQL_CB_PRESERVE;
break;
case SQL_DATA_SOURCE_NAME: /* ODBC 1.0 */
if (dver[0])
{
int major, minor;
- mylog("REIGISTRY_ODBC_VER = %s\n", dver)
+ mylog("REGISTRY_ODBC_VER = %s\n", dver)
;
if (sscanf(dver, "%x.%x", &major, &minor) >= 2)
{
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);
+ if (ci->updatable_cursors)
+ value |= (SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD);
break;
case SQL_POSITIONED_STATEMENTS: /* ODBC 2.0 */
* Driver doesn't support keyset-driven or mixed cursors, so
* not much point in saying row updates are supported
*/
- p = ci->drivers.lie ? "Y" : "N";
+ p = (ci->drivers.lie || ci->updatable_cursors) ? "Y" : "N";
break;
case SQL_SCROLL_CONCURRENCY: /* ODBC 1.0 */
value = ci->drivers.lie ? (SQL_SCCO_READ_ONLY |
SQL_SCCO_LOCK |
SQL_SCCO_OPT_ROWVER |
- SQL_SCCO_OPT_VALUES) : (SQL_SCCO_READ_ONLY);
+ SQL_SCCO_OPT_VALUES) :
+ (SQL_SCCO_READ_ONLY);
+ if (ci->updatable_cursors)
+ value |= SQL_SCCO_OPT_ROWVER;
break;
case SQL_SCROLL_OPTIONS: /* ODBC 1.0 */
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));
+ SQL_SO_MIXED)
+ : (ci->drivers.use_declarefetch ? SQL_SO_FORWARD_ONLY : (SQL_SO_FORWARD_ONLY | SQL_SO_STATIC));
+ if (ci->updatable_cursors)
+ value |= 0; /* SQL_SO_KEYSET_DRIVEN in the furure */
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;
+ if (ci->updatable_cursors)
+ value |= (SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES);
break;
case SQL_STRING_FUNCTIONS: /* ODBC 1.0 */
conn->errormsg = "Unknown connect option (Set)";
conn->errornumber = CONN_UNSUPPORTED_OPTION;
sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
+ if (fOption == 30002 && vParam)
+ {
+ if (strcmp((char *) vParam, "Microsoft Jet") == 0)
+ {
+ conn->errornumber = 0;
+ conn->ms_jet = 1;
+ return SQL_SUCCESS;
+ }
+ }
CC_log_error(func, option, conn);
return SQL_ERROR;
}
}
*pccol = QR_NumResultCols(result);
-#ifdef DRIVER_CURSOR_IMPLEMENT
- if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
+ /* updatable cursors */
+ if (ci->updatable_cursors &&
+ stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY)
{
*pccol -= 2;
}
-#endif /* DRIVER_CURSOR_IMPLEMENT */
}
return SQL_SUCCESS;
#ifdef DRIVER_CURSOR_IMPLEMENT
+/*
+ * Stuff for updatable cursors.
+ */
+static QResultClass *
+positioned_load(StatementClass *stmt, BOOL latest, int res_cols, UInt4 oid, const char *tidval)
+{
+ int i;
+ QResultClass *qres;
+ char selstr[4096];
+
+ sprintf(selstr, "select");
+ for (i = 0; i < res_cols; i++)
+ sprintf(selstr, "%s \"%s\",", selstr, stmt->fi[i]->name);
+ sprintf(selstr, "%s CTID, OID from \"%s\" where", selstr, stmt->ti[0]->name);
+ if (tidval)
+ {
+ if (latest)
+ sprintf(selstr, "%s ctid = currtid2('%s', '%s') and",
+ selstr, stmt->ti[0]->name, tidval);
+ else
+ sprintf(selstr, "%s ctid = '%s' and", selstr, tidval);
+ }
+ sprintf(selstr, "%s oid = %u", selstr, oid),
+ mylog("selstr=%s\n", selstr);
+ qres = CC_send_query(SC_get_conn(stmt), selstr, NULL);
+ if (qres && QR_aborted(qres))
+ {
+ QR_Destructor(qres);
+ qres = (QResultClass *) 0;
+ }
+ return qres;
+}
+
RETCODE SQL_API
SC_pos_reload(StatementClass *stmt, UWORD irow, UWORD *count)
{
int i, res_cols;
UWORD rcnt, global_ridx;
+ UInt4 oid;
QResultClass *res, *qres;
- char selstr[4096];
RETCODE ret = SQL_ERROR;
char *tidval, *oidval;
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
return SQL_ERROR;
}
- strcpy(selstr, "select");
global_ridx = irow + stmt->rowset_start;
res_cols = QR_NumResultCols(res);
if (!(oidval = QR_get_value_backend_row(res, global_ridx, res_cols - 1)))
{
return SQL_SUCCESS_WITH_INFO;
- }
+ }
+ sscanf(oidval, "%u", &oid);
tidval = QR_get_value_backend_row(res, global_ridx, res_cols - 2);
res_cols -= 2;
- for (i = 0; i < res_cols; i++)
- sprintf(selstr, "%s \"%s\",", selstr, stmt->fi[i]->name);
- sprintf(selstr, "%s CTID, OID from \"%s\" where ctid = currtid2('%s', '%s') and oid = %s",
- selstr, stmt->ti[0]->name, stmt->ti[0]->name, tidval, oidval),
- mylog("selstr=%s\n", selstr);
- qres = CC_send_query(SC_get_conn(stmt), selstr, NULL);
- if (qres && QR_command_successful(qres))
+ if (qres = positioned_load(stmt, TRUE, res_cols, oid, tidval), qres)
{
TupleField *tupleo, *tuplen;
tupleo[res_cols + 1].len = 0;
}
}
+ QR_Destructor(qres);
}
else if (stmt->errornumber == 0)
stmt->errornumber = STMT_ERROR_TAKEN_FROM_BACKEND;
- if (qres)
- QR_Destructor(qres);
if (count)
*count = rcnt;
return ret;
}
RETCODE SQL_API
-SC_pos_newload(StatementClass *stmt, Int4 oid, const char *tidval)
+SC_pos_newload(StatementClass *stmt, UInt4 oid, const char *tidval)
{
- int i, res_cols;
+ int i;
QResultClass *res, *qres;
- char selstr[4096];
RETCODE ret = SQL_ERROR;
mylog("positioned new fi=%x ti=%x\n", stmt->fi, stmt->ti);
stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
return SQL_ERROR;
}
- sprintf(selstr, "select");
- res_cols = QR_NumResultCols(res);
- res_cols -= 2;
- for (i = 0; i < res_cols; i++)
- sprintf(selstr, "%s \"%s\",", selstr, stmt->fi[i]->name);
- sprintf(selstr, "%s CTID, OID from \"%s\" where", selstr, stmt->ti[0]->name);
- if (tidval)
- sprintf(selstr, "%s ctid = currtid2('%s', '%s') and",
- selstr, stmt->ti[0]->name, tidval);
- sprintf(selstr, "%s oid = %u", selstr, oid),
- mylog("selstr=%s\n", selstr);
- qres = CC_send_query(SC_get_conn(stmt), selstr, NULL);
- if (qres && QR_command_successful(qres))
- {
+ if (qres = positioned_load(stmt, TRUE, QR_NumResultCols(res) - 2, oid, tidval), qres)
+ {
TupleField *tupleo, *tuplen;
int count = QR_get_num_tuples(qres);
QR_set_position(qres, 0);
stmt->errormsg = "the content was changed before updation";
ret = SQL_SUCCESS_WITH_INFO;
}
+ QR_Destructor(qres);
/*stmt->currTuple = stmt->rowset_start + irow;*/
}
- if (qres)
- QR_Destructor(qres);
return ret;
}
else
{
int addcnt;
- Int4 oid;
+ UInt4 oid;
const char *cmdstr = QR_get_command(qstmt->result);
if (cmdstr &&
sscanf(cmdstr, "INSERT %u %d", &oid, &addcnt) == 2 &&
PGAPI_FreeStmt(hstmt, SQL_DROP);
return ret;
}
+/*
+ * Stuff for updatable cursors end.
+ */
#endif /* DRIVER_CURSOR_IMPLEMENT */
/*