📄 odbc.c
字号:
/* FreeTDS - Library of routines accessing Sybase and Microsoft databases * Copyright (C) 1998, 1999, 2000, 2001 Brian Bruns * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Frediano Ziglio * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. *//* * PROGRAMMER NAME CONTACT *============================================================== * BSB Brian Bruns camber@ais.org * PAH Peter Harvey pharvey@codebydesign.com * SMURPH Steve Murphree smurph@smcomp.com * *************************************************************** * DATE PROGRAMMER CHANGE *============================================================== * Original. * 03.FEB.02 PAH Started adding use of SQLGetPrivateProfileString(). * 04.FEB.02 PAH Fixed small error preventing SQLBindParameter from being called */#if HAVE_CONFIG_H#include <config.h>#endif /* HAVE_CONFIG_H */#include <stdarg.h>#include <stdio.h>#if HAVE_STDLIB_H#include <stdlib.h>#endif /* HAVE_STDLIB_H */#if HAVE_STRING_H#include <string.h>#endif /* HAVE_STRING_H */#include <assert.h>#include <ctype.h>#include "tdsodbc.h"#include "tdsstring.h"#include "tdsconvert.h"#include "replacements.h"#ifdef DMALLOC#include <dmalloc.h>#endifTDS_RCSID(var, "$Id: odbc.c,v 1.464.2.9 2008/03/11 08:25:30 freddy77 Exp $");static SQLRETURN _SQLAllocConnect(SQLHENV henv, SQLHDBC FAR * phdbc);static SQLRETURN _SQLAllocEnv(SQLHENV FAR * phenv);static SQLRETURN _SQLAllocStmt(SQLHDBC hdbc, SQLHSTMT FAR * phstmt);static SQLRETURN _SQLAllocDesc(SQLHDBC hdbc, SQLHDESC FAR * phdesc);static SQLRETURN _SQLFreeConnect(SQLHDBC hdbc);static SQLRETURN _SQLFreeEnv(SQLHENV henv);static SQLRETURN _SQLFreeStmt(SQLHSTMT hstmt, SQLUSMALLINT fOption, int force);static SQLRETURN _SQLFreeDesc(SQLHDESC hdesc);static SQLRETURN _SQLExecute(TDS_STMT * stmt);static SQLRETURN _SQLGetConnectAttr(SQLHDBC hdbc, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER BufferLength, SQLINTEGER * StringLength);static SQLRETURN _SQLSetConnectAttr(SQLHDBC hdbc, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);static SQLRETURN _SQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);static SQLRETURN _SQLGetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER BufferLength, SQLINTEGER * StringLength);static SQLRETURN _SQLColAttribute(SQLHSTMT hstmt, SQLUSMALLINT icol, SQLUSMALLINT fDescType, SQLPOINTER rgbDesc, SQLSMALLINT cbDescMax, SQLSMALLINT FAR * pcbDesc, SQLLEN FAR * pfDesc);static SQLRETURN _SQLFetch(TDS_STMT * stmt, SQLSMALLINT FetchOrientation, SQLLEN FetchOffset);static SQLRETURN odbc_populate_ird(TDS_STMT * stmt);static int odbc_errmsg_handler(const TDSCONTEXT * ctx, TDSSOCKET * tds, TDSMESSAGE * msg);static void odbc_log_unimplemented_type(const char function_name[], int fType);static void odbc_upper_column_names(TDS_STMT * stmt);static void odbc_col_setname(TDS_STMT * stmt, int colpos, const char *name);static SQLRETURN odbc_stat_execute(TDS_STMT * stmt, const char *begin, int nparams, ...);static SQLRETURN odbc_free_dynamic(TDS_STMT * stmt);static SQLRETURN odbc_free_cursor(TDS_STMT * stmt);static SQLSMALLINT odbc_swap_datetime_sql_type(SQLSMALLINT sql_type);static int odbc_process_tokens(TDS_STMT * stmt, unsigned flag);static int odbc_lock_statement(TDS_STMT* stmt);#if ENABLE_EXTRA_CHECKSstatic void odbc_ird_check(TDS_STMT * stmt);#define IRD_CHECK odbc_ird_check(stmt)#else#define IRD_CHECK#endif/** * \defgroup odbc_api ODBC API * Functions callable by \c ODBC client programs *//* utils to check handles */#define CHECK_HDBC if ( SQL_NULL_HDBC == hdbc || !IS_HDBC(hdbc) ) return SQL_INVALID_HANDLE;#define CHECK_HSTMT if ( SQL_NULL_HSTMT == hstmt || !IS_HSTMT(hstmt) ) return SQL_INVALID_HANDLE;#define CHECK_HENV if ( SQL_NULL_HENV == henv || !IS_HENV(henv) ) return SQL_INVALID_HANDLE;#define CHECK_HDESC if ( SQL_NULL_HDESC == hdesc || !IS_HDESC(hdesc) ) return SQL_INVALID_HANDLE;#define INIT_HSTMT \ TDS_STMT *stmt = (TDS_STMT*)hstmt; \ CHECK_HSTMT; \ CHECK_STMT_EXTRA(stmt); \ odbc_errs_reset(&stmt->errs); \#define INIT_HDBC \ TDS_DBC *dbc = (TDS_DBC*)hdbc; \ CHECK_HDBC; \ CHECK_DBC_EXTRA(dbc); \ odbc_errs_reset(&dbc->errs); \#define INIT_HENV \ TDS_ENV *env = (TDS_ENV*)henv; \ CHECK_HENV; \ CHECK_ENV_EXTRA(env); \ odbc_errs_reset(&env->errs); \#define INIT_HDESC \ TDS_DESC *desc = (TDS_DESC*)hdesc; \ CHECK_HDESC; \ CHECK_DESC_EXTRA(desc); \ odbc_errs_reset(&desc->errs); \#define IS_VALID_LEN(len) ((len) >= 0 || (len) == SQL_NTS || (len) == SQL_NULL_DATA)#define ODBC_SAFE_ERROR(stmt) \ do { \ if (!stmt->errs.num_errors) \ odbc_errs_add(&stmt->errs, "HY000", "Unknown error"); \ } while(0)#define DEFAULT_QUERY_TIMEOUT (~((SQLUINTEGER) 0))/* * Note: I *HATE* hungarian notation, it has to be the most idiotic thing * I've ever seen. So, you will note it is avoided other than in the function * declarations. "Gee, let's make our code totally hard to read and they'll * beg for GUI tools" * Bah! */static voidodbc_col_setname(TDS_STMT * stmt, int colpos, const char *name){#if ENABLE_EXTRA_CHECKS TDSRESULTINFO *resinfo;#endif IRD_CHECK;#if ENABLE_EXTRA_CHECKS if (colpos > 0 && stmt->dbc->tds_socket != NULL && (resinfo = stmt->dbc->tds_socket->current_results) != NULL) { if (colpos <= resinfo->num_cols) { /* no overflow possible, name is always shorter */ strcpy(resinfo->columns[colpos - 1]->column_name, name); resinfo->columns[colpos - 1]->column_namelen = strlen(name); if (resinfo->columns[colpos - 1]->table_column_name) TDS_ZERO_FREE(resinfo->columns[colpos - 1]->table_column_name); } }#endif if (colpos > 0 && colpos <= stmt->ird->header.sql_desc_count) { --colpos; tds_dstr_copy(&stmt->ird->records[colpos].sql_desc_label, name); tds_dstr_copy(&stmt->ird->records[colpos].sql_desc_name, name); }}/* spinellia@acm.org : copied shamelessly from change_database */static SQLRETURNchange_autocommit(TDS_DBC * dbc, int state){ TDSSOCKET *tds = dbc->tds_socket; char query[80]; /* * We may not be connected yet and dbc->tds_socket * may not initialized. */ if (tds) { /* * mssql: SET IMPLICIT_TRANSACTION ON * sybase: SET CHAINED ON */ /* implicit transactions are on if autocommit is off :-| */ if (TDS_IS_MSSQL(tds)) sprintf(query, "SET IMPLICIT_TRANSACTIONS %s", (state == SQL_AUTOCOMMIT_ON) ? "OFF" : "ON"); else { /* Sybase, do not use SET CHAINED but emulate for compatility */ if (state == SQL_AUTOCOMMIT_ON) strcpy(query, "WHILE @@TRANCOUNT > 0 COMMIT"); else strcpy(query, "BEGIN TRANSACTION"); } tdsdump_log(TDS_DBG_INFO1, "change_autocommit: executing %s\n", query); /* TODO better idle check, not thread safe */ if (tds->state == TDS_IDLE) tds->query_timeout = dbc->default_query_timeout; if (tds_submit_query(tds, query) != TDS_SUCCEED) { odbc_errs_add(&dbc->errs, "HY000", "Could not change transaction status"); ODBC_RETURN(dbc, SQL_ERROR); } if (tds_process_simple_query(tds) != TDS_SUCCEED) { odbc_errs_add(&dbc->errs, "HY000", "Could not change transaction status"); ODBC_RETURN(dbc, SQL_ERROR); } dbc->attr.autocommit = state; } else { /* if not connected we will change auto-commit after login */ dbc->attr.autocommit = state; } ODBC_RETURN_(dbc);}static SQLRETURNchange_database(TDS_DBC * dbc, char *database, int database_len){ TDSSOCKET *tds = dbc->tds_socket; /* * We may not be connected yet and dbc->tds_socket * may not initialized. */ if (tds) { /* build query */ char *query = (char *) malloc(6 + tds_quote_id(tds, NULL, database, database_len)); if (!query) { odbc_errs_add(&dbc->errs, "HY001", NULL); ODBC_RETURN(dbc, SQL_ERROR); } strcpy(query, "USE "); tds_quote_id(tds, query + 4, database, database_len); tdsdump_log(TDS_DBG_INFO1, "change_database: executing %s\n", query); /* TODO better idle check, not thread safe */ if (tds->state == TDS_IDLE) tds->query_timeout = dbc->default_query_timeout; if (tds_submit_query(tds, query) != TDS_SUCCEED) { free(query); odbc_errs_add(&dbc->errs, "HY000", "Could not change database"); ODBC_RETURN(dbc, SQL_ERROR); } free(query); if (tds_process_simple_query(tds) != TDS_SUCCEED) { odbc_errs_add(&dbc->errs, "HY000", "Could not change database"); ODBC_RETURN(dbc, SQL_ERROR); } } else { tds_dstr_copyn(&dbc->attr.current_catalog, database, database_len); } ODBC_RETURN_(dbc);}static voidodbc_env_change(TDSSOCKET * tds, int type, char *oldval, char *newval){ TDS_DBC *dbc; if (tds == NULL) { return; } dbc = (TDS_DBC *) tds->parent; if (!dbc) return; switch (type) { case TDS_ENV_DATABASE: tds_dstr_copy(&dbc->attr.current_catalog, newval); break; case TDS_ENV_PACKSIZE: dbc->attr.packet_size = atoi(newval); break; }}static SQLRETURNodbc_connect(TDS_DBC * dbc, TDSCONNECTION * connection){ TDS_ENV *env = dbc->env; dbc->tds_socket = tds_alloc_socket(env->tds_ctx, 512); if (!dbc->tds_socket) { odbc_errs_add(&dbc->errs, "HY001", NULL); ODBC_RETURN(dbc, SQL_ERROR); } tds_set_parent(dbc->tds_socket, (void *) dbc); /* Set up our environment change hook */ dbc->tds_socket->env_chg_func = odbc_env_change; tds_fix_connection(connection); connection->connect_timeout = dbc->attr.connection_timeout; if (tds_connect(dbc->tds_socket, connection) == TDS_FAIL) { tds_free_socket(dbc->tds_socket); dbc->tds_socket = NULL; odbc_errs_add(&dbc->errs, "08001", NULL); ODBC_RETURN(dbc, SQL_ERROR); } dbc->default_query_timeout = dbc->tds_socket->query_timeout; if (IS_TDS7_PLUS(dbc->tds_socket)) dbc->cursor_support = 1; if (dbc->attr.autocommit != SQL_AUTOCOMMIT_ON) if (!SQL_SUCCEEDED(change_autocommit(dbc, dbc->attr.autocommit))) ODBC_RETURN_(dbc); /* this overwrite any error arrived (wanted behavior, Sybase return error for conversion errors) */ ODBC_RETURN(dbc, SQL_SUCCESS);}SQLRETURN ODBC_APISQLDriverConnect(SQLHDBC hdbc, SQLHWND hwnd, SQLCHAR FAR * szConnStrIn, SQLSMALLINT cbConnStrIn, SQLCHAR FAR * szConnStrOut, SQLSMALLINT cbConnStrOutMax, SQLSMALLINT FAR * pcbConnStrOut, SQLUSMALLINT fDriverCompletion){ TDSCONNECTION *connection; int conlen = odbc_get_string_size(cbConnStrIn, szConnStrIn); INIT_HDBC; tdsdump_log(TDS_DBG_FUNC, "SQLDriverConnect(%p, %p, %s, %d, %p, %d, %p, %d)\n", hdbc, hwnd, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut, fDriverCompletion);#ifdef TDS_NO_DM /* Check string length */ if (!IS_VALID_LEN(conlen) || conlen == 0) { odbc_errs_add(&dbc->errs, "HY090", NULL); ODBC_RETURN(dbc, SQL_ERROR); } /* Check completion param */ switch (fDriverCompletion) { case SQL_DRIVER_NOPROMPT: case SQL_DRIVER_COMPLETE: case SQL_DRIVER_PROMPT: case SQL_DRIVER_COMPLETE_REQUIRED: break; default: odbc_errs_add(&dbc->errs, "HY110", NULL); ODBC_RETURN(dbc, SQL_ERROR); }#endif connection = tds_alloc_connection(dbc->env->tds_ctx->locale); if (!connection) { odbc_errs_add(&dbc->errs, "HY001", NULL); ODBC_RETURN(dbc, SQL_ERROR); } if (!tds_dstr_isempty(&dbc->attr.current_catalog)) tds_dstr_dup(&connection->database, &dbc->attr.current_catalog); /* parse the DSN string */ odbc_parse_connect_string((const char *) szConnStrIn, (const char *) szConnStrIn + conlen, connection); /* add login info */ if (hwnd) {#ifdef WIN32 /* prompt for login information */ if (!get_login_info(hwnd, connection)) { tds_free_connection(connection); odbc_errs_add(&dbc->errs, "08001", "User canceled login"); ODBC_RETURN(dbc, SQL_ERROR); }#else /* we dont support a dialog box */ odbc_errs_add(&dbc->errs, "HYC00", NULL);#endif } /* TODO what should be correct behavior for output string?? -- freddy77 */ if (szConnStrOut) odbc_set_string(szConnStrOut, cbConnStrOutMax, pcbConnStrOut, (const char *) szConnStrIn, conlen); if (tds_dstr_isempty(&connection->server_name)) { tds_free_connection(connection); odbc_errs_add(&dbc->errs, "IM007", "Could not find Servername or server parameter"); ODBC_RETURN(dbc, SQL_ERROR); } if (tds_dstr_isempty(&connection->user_name)) { tds_free_connection(connection); odbc_errs_add(&dbc->errs, "IM007", "Could not find UID parameter"); ODBC_RETURN(dbc, SQL_ERROR); } if (odbc_connect(dbc, connection) != SQL_SUCCESS) { tds_free_connection(connection); ODBC_RETURN_(dbc); } tds_free_connection(connection); ODBC_RETURN_(dbc);}#if 0SQLRETURN ODBC_APISQLBrowseConnect(SQLHDBC hdbc, SQLCHAR FAR * szConnStrIn, SQLSMALLINT cbConnStrIn, SQLCHAR FAR * szConnStrOut, SQLSMALLINT cbConnStrOutMax, SQLSMALLINT FAR * pcbConnStrOut){ tdsdump_log(TDS_DBG_FUNC, "SQLBrowseConnect(%p, %s, %d, %p, %d, %p)\n", hdbc, szConnStrIn, cbConnStrIn, szConnStrOut, cbConnStrOutMax, pcbConnStrOut); INIT_HDBC; odbc_errs_add(&dbc->errs, "HYC00", "SQLBrowseConnect: function not implemented"); ODBC_RETURN(dbc, SQL_ERROR);}#endifSQLRETURN ODBC_API
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -