/src/dblib/dblib.c
C | 8050 lines | 5049 code | 893 blank | 2108 comment | 840 complexity | 613ab4e5b267f6016141cd88ff0f7bbc MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0
Large files files are truncated, but you can click here to view the full file
- /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Brian Bruns
- * Copyright (C) 2006-2015 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.
- */
- #include <config.h>
- #include <stdarg.h>
- #include <freetds/time.h>
- #include <assert.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 */
- #if HAVE_UNISTD_H
- #include <unistd.h>
- #endif /* HAVE_UNISTD_H */
- #if HAVE_ERRNO_H
- # include <errno.h>
- #endif /* HAVE_ERRNO_H */
- /**
- * \ingroup dblib_core
- * \remarks Either SYBDBLIB or MSDBLIB (not both) must be defined.
- * This affects how certain application-addressable
- * strucures are defined.
- */
- #include <freetds/tds.h>
- #include <freetds/thread.h>
- #include <freetds/convert.h>
- #include <freetds/string.h>
- #include <freetds/data.h>
- #include <replacements.h>
- #include <sybfront.h>
- #include <sybdb.h>
- #include <syberror.h>
- #include <dblib.h>
- static RETCODE _dbresults(DBPROCESS * dbproc);
- static int _get_printable_size(TDSCOLUMN * colinfo);
- static char *_dbprdate(char *timestr);
- static int _dbnullable(DBPROCESS * dbproc, int column);
- static const char *tds_prdatatype(TDS_SERVER_TYPE datatype_token);
- static int default_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr);
- void copy_data_to_host_var(DBPROCESS *, int, const BYTE *, int, BYTE *, DBINT, int, DBINT *);
- RETCODE dbgetnull(DBPROCESS *dbproc, int bindtype, int varlen, BYTE* varaddr);
- /**
- * \file dblib.c
- * Main implementation file for \c db-lib.
- */
- /**
- * \file bcp.c
- * Implementation of \c db-lib bulk copy functions.
- */
- /**
- * \defgroup dblib_api The db-lib API
- * Functions callable by \c db-lib client programs
- *
- * The \c db_lib interface is implemented by both Sybase and Microsoft. FreeTDS seeks to implement
- * first the intersection of the functions defined by the vendors.
- */
-
- /**
- * \ingroup dblib_api
- * \defgroup dblib_core Primary functions
- * Core functions needed by most db-lib programs.
- */
- /**
- * \ingroup dblib_api
- * \defgroup dblib_rpc Remote Procedure functions
- * Functions used with stored procedures.
- * Especially useful for OUTPUT parameters, because modern Microsoft servers do not
- * return output parameter data to the client unless the procedure was invoked
- * with dbrpcsend().
- */
- /**
- * \ingroup dblib_api
- * \defgroup dblib_bcp Bulk copy functions
- * Functions to bulk-copy (a/k/a \em bcp) data to/from the database.
- */
- /**
- * \ingroup dblib_bcp
- * \defgroup dblib_bcp_internal Internal bcp functions
- * Static functions internal to the bcp library.
- */
- /**
- * \ingroup dblib_api
- * \defgroup dblib_money Money functions
- * Functions to manipulate the MONEY datatype.
- */
- /**
- * \ingroup dblib_api
- * \defgroup dblib_datetime Datetime functions
- * Functions to manipulate DBDATETIME structures. Defined by Sybase only.
- * These are not implemented:
- * - dbdate4cmp()
- * - dbdate4zero()
- * - dbdatechar()
- * - dbdatename()
- * - dbdateorder()
- * - dbdatepart()
- * - dbdatezero()
- * - dbdayname()
- */
- /**
- * \ingroup dblib_api
- * \defgroup dblib_internal Internals
- * Functions called within \c db-lib for self-help.
- * These functions are of interest only to people hacking on the FreeTDS db-lib implementation.
- */
- /**
- * \ingroup dblib_api
- * \defgroup dblib_unimplemented Unimplemented
- * Functions thus far not implemented in the FreeTDS db-lib implementation.
- * While some of these are simply awaiting someone with time and skill (and inclination)
- * it might be noted here that the old browse functions (e.g. dbcolbrowse())
- * are on the never-to-do list.
- * They were defined by Sybase and were superseded long ago, although they're still
- * present in Microsoft's implementation.
- * They were never popular and today better alternatives are available.
- * For completeness, they are:
- * - dbcolbrowse()
- * - dbcolsource()
- * - dbfreequal()
- * - dbqual()
- * - dbtabbrowse()
- * - dbtabcount()
- * - dbtabname()
- * - dbtabsource()
- * - dbtsnewlen()
- * - dbtsnewval()
- * - dbtsput()
- */
- /* info/err message handler functions (or rather pointers to them) */
- MHANDLEFUNC _dblib_msg_handler = NULL;
- EHANDLEFUNC _dblib_err_handler = default_err_handler;
- /** \internal
- * \dblib_internal
- * \remarks A db-lib connection has an implicit TDS context.
- */
- typedef struct dblib_context
- {
- /** reference count, time dbinit called */
- int ref_count;
- /** libTDS context */
- TDSCONTEXT *tds_ctx;
- /** libTDS context reference counter */
- int tds_ctx_ref_count;
- /* save all connection in a list */
- TDSSOCKET **connection_list;
- int connection_list_size;
- int connection_list_size_represented;
- char *recftos_filename;
- int recftos_filenum;
- int login_timeout; /**< not used unless positive */
- int query_timeout; /**< not used unless positive */
- }
- DBLIBCONTEXT;
- static DBLIBCONTEXT g_dblib_ctx;
- static tds_mutex dblib_mutex = TDS_MUTEX_INITIALIZER;
- static int g_dblib_version =
- #ifdef TDS42
- DBVERSION_42;
- #endif
- #ifdef TDS50
- DBVERSION_100;
- #endif
- #ifdef TDS46
- DBVERSION_46;
- #endif
- #ifdef TDS70
- DBVERSION_70;
- #endif
- #ifdef TDS71
- DBVERSION_71;
- #endif
- #ifdef TDS72
- DBVERSION_72;
- #endif
- #ifdef TDS73
- DBVERSION_73;
- #endif
- static int
- dblib_add_connection(DBLIBCONTEXT * ctx, TDSSOCKET * tds)
- {
- int i = 0;
- const int list_size = ctx->connection_list_size_represented;
- tdsdump_log(TDS_DBG_FUNC, "dblib_add_connection(%p, %p)\n", ctx, tds);
- while (i < list_size && ctx->connection_list[i])
- i++;
- if (i == list_size) {
- fprintf(stderr, "Max connections reached, increase value of TDS_MAX_CONN\n");
- return 1;
- } else {
- ctx->connection_list[i] = tds;
- return 0;
- }
- }
- static void
- dblib_del_connection(DBLIBCONTEXT * ctx, TDSSOCKET * tds)
- {
- int i = 0;
- const int list_size = ctx->connection_list_size;
- tdsdump_log(TDS_DBG_FUNC, "dblib_del_connection(%p, %p)\n", ctx, tds);
- while (i < list_size && ctx->connection_list[i] != tds)
- i++;
- if (i == list_size) {
- /* connection wasn't on the free list...now what */
- } else {
- /* remove it */
- ctx->connection_list[i] = NULL;
- }
- }
- static TDSCONTEXT*
- dblib_get_tds_ctx(void)
- {
- tdsdump_log(TDS_DBG_FUNC, "dblib_get_tds_ctx(void)\n");
- tds_mutex_lock(&dblib_mutex);
- ++g_dblib_ctx.tds_ctx_ref_count;
- if (g_dblib_ctx.tds_ctx == NULL) {
- g_dblib_ctx.tds_ctx = tds_alloc_context(&g_dblib_ctx);
- /*
- * Set the functions in the TDS layer to point to the correct handler functions
- */
- g_dblib_ctx.tds_ctx->msg_handler = _dblib_handle_info_message;
- g_dblib_ctx.tds_ctx->err_handler = _dblib_handle_err_message;
- g_dblib_ctx.tds_ctx->int_handler = _dblib_check_and_handle_interrupt;
- if (g_dblib_ctx.tds_ctx->locale && !g_dblib_ctx.tds_ctx->locale->date_fmt) {
- /* set default in case there's no locale file */
- const static char date_format[] =
- #ifndef _WIN32
- "%b %e %Y %I:%M:%S:%z%p";
- #else
- "%b %d %Y %I:%M:%S:%z%p";
- #endif
- g_dblib_ctx.tds_ctx->locale->date_fmt = strdup(date_format);
- }
- }
- tds_mutex_unlock(&dblib_mutex);
- return g_dblib_ctx.tds_ctx;
- }
- static void
- dblib_release_tds_ctx(int count)
- {
- tdsdump_log(TDS_DBG_FUNC, "dblib_release_tds_ctx(%d)\n", count);
- tds_mutex_lock(&dblib_mutex);
- g_dblib_ctx.tds_ctx_ref_count -= count;
- if (g_dblib_ctx.tds_ctx_ref_count <= 0) {
- tds_free_context(g_dblib_ctx.tds_ctx);
- g_dblib_ctx.tds_ctx = NULL;
- }
- tds_mutex_unlock(&dblib_mutex);
- }
- #include "buffering.h"
- static void
- db_env_chg(TDSSOCKET * tds, int type, char *oldval, char *newval)
- {
- DBPROCESS *dbproc;
- assert(oldval != NULL && newval != NULL);
- if (strlen(oldval) == 1 && *oldval == 1)
- oldval = "(0x1)";
-
- tdsdump_log(TDS_DBG_FUNC, "db_env_chg(%p, %d, %s, %s)\n", tds, type, oldval, newval);
- if (!tds || !tds_get_parent(tds))
- return;
- dbproc = (DBPROCESS *) tds_get_parent(tds);
- dbproc->envchange_rcv |= (1 << (type - 1));
- switch (type) {
- case TDS_ENV_DATABASE:
- tds_strlcpy(dbproc->dbcurdb, newval, sizeof(dbproc->dbcurdb));
- break;
- case TDS_ENV_CHARSET:
- tds_strlcpy(dbproc->servcharset, newval, sizeof(dbproc->servcharset));
- break;
- default:
- break;
- }
- return;
- }
- /** \internal
- * \ingroup dblib_internal
- * \brief Sanity checks for column-oriented functions.
- *
- * \param dbproc contains all information needed by db-lib to manage communications with the server.
- * \param pcolinfo address of pointer to a TDSCOLUMN structure.
- * \remarks Makes sure dbproc and the requested column are valid.
- * Calls dbperror() if not.
- * \returns appropriate error or SUCCEED
- */
- static TDSCOLUMN*
- dbcolptr(DBPROCESS* dbproc, int column)
- {
- if (!dbproc) {
- dbperror(dbproc, SYBENULL, 0);
- return NULL;
- }
- if (IS_TDSDEAD(dbproc->tds_socket)) {
- dbperror(dbproc, SYBEDDNE, 0);
- return NULL;
- }
- if (!dbproc->tds_socket->res_info)
- return NULL;
- if (column < 1 || column > dbproc->tds_socket->res_info->num_cols) {
- dbperror(dbproc, SYBECNOR, 0);
- return NULL;
- }
-
- return dbproc->tds_socket->res_info->columns[column - 1];
- }
- static TDSCOLUMN*
- dbacolptr(DBPROCESS* dbproc, int computeid, int column, int is_bind)
- {
- int i;
- TDSSOCKET *tds;
- TDSCOMPUTEINFO *info;
- if (!dbproc) {
- dbperror(dbproc, SYBENULL, 0);
- return NULL;
- }
- tds = dbproc->tds_socket;
- if (IS_TDSDEAD(tds)) {
- dbperror(dbproc, SYBEDDNE, 0);
- return NULL;
- }
- for (i = 0;; ++i) {
- if (i >= tds->num_comp_info) {
- /* Attempt to bind user variable to a non-existent compute row */
- if (is_bind)
- dbperror(dbproc, SYBEBNCR, 0);
- return NULL;
- }
- info = tds->comp_info[i];
- if (info->computeid == computeid)
- break;
- }
- /* Fail if either the compute id or the column number is invalid. */
- if (column < 1 || column > info->num_cols) {
- dbperror(dbproc, is_bind ? SYBEABNC : SYBECNOR, 0);
- return NULL;
- }
- return info->columns[column - 1];
- }
- /*
- * Default null substitution values
- * Binding Type Null Substitution Value
- * TINYBIND 0
- * SMALLBIND 0
- * INTBIND 0
- * CHARBIND Empty string (padded with blanks)
- * STRINGBIND Empty string (padded with blanks, null-terminated)
- * NTBSTRINGBIND Empty string (null-terminated)
- * VARYCHARBIND Empty string
- * BINARYBIND Empty array (padded with zeros)
- * VARYBINBIND Empty array
- * DATETIMEBIND 8 bytes of zeros
- * SMALLDATETIMEBIND 8 bytes of zeros
- * MONEYBIND $0.00
- * SMALLMONEYBIND $0.00
- * FLT8BIND 0.0
- * REALBIND 0.0
- * DECIMALBIND 0.0 (with default scale and precision)
- * NUMERICBIND 0.0 (with default scale and precision)
- * BOUNDARYBIND Empty string (null-terminated)
- * SENSITIVITYBIND Empty string (null-terminated)
- */
- static const DBBIT null_BIT = 0;
- static const DBTINYINT null_TINYINT = 0;
- static const DBSMALLINT null_SMALLINT = 0;
- static const DBINT null_INT = 0;
- static const DBBIGINT null_BIGINT = 0;
- static const DBFLT8 null_FLT8 = 0;
- static const DBREAL null_REAL = 0;
- static const DBCHAR null_CHAR = '\0';
- static const DBVARYCHAR null_VARYCHAR = { 0, {0} };
- static const DBBINARY null_BINARY = 0;
- static const DBDATETIME null_DATETIME = { 0, 0 };
- static const DBDATETIME4 null_SMALLDATETIME = { 0, 0 };
- static const DBMONEY null_MONEY = { 0, 0 };
- static const DBMONEY4 null_SMALLMONEY = {0};
- static const DBNUMERIC null_NUMERIC = { 0, 0, {0} };
- static const TDS_DATETIMEALL null_DATETIMEALL = { 0, 0, 0, 0 };
- static NULLREP default_null_representations[MAXBINDTYPES] = {
- /* CHARBIND 0 */ { NULL, 0 }
- /* STRINGBIND 1 */ , { NULL, 0 }
- /* NTBSTRINGBIND 2 */ , { (BYTE*) &null_CHAR, sizeof(null_CHAR) }
- /* VARYCHARBIND 3 */ , { (BYTE*) &null_VARYCHAR, sizeof(null_VARYCHAR) }
- /* VARYBINBIND 4 */ , { (BYTE*) &null_VARYCHAR, sizeof(null_VARYCHAR) }
- /* no such bind 5 */ , { NULL, 0 }
- /* TINYBIND 6 */ , { &null_TINYINT, sizeof(null_TINYINT) }
- /* SMALLBIND 7 */ , { (BYTE*) &null_SMALLINT, sizeof(null_SMALLINT) }
- /* INTBIND 8 */ , { (BYTE*) &null_INT, sizeof(null_INT) }
- /* FLT8BIND 9 */ , { (BYTE*) &null_FLT8, sizeof(null_FLT8) }
- /* REALBIND 10 */ , { (BYTE*) &null_REAL, sizeof(null_REAL) }
- /* DATETIMEBIND 11 */ , { (BYTE*) &null_DATETIME, sizeof(null_DATETIME) }
- /* SMALLDATETIMEBIND 12 */ , { (BYTE*) &null_SMALLDATETIME, sizeof(null_SMALLDATETIME) }
- /* MONEYBIND 13 */ , { (BYTE*) &null_MONEY, sizeof(null_MONEY) }
- /* SMALLMONEYBIND 14 */ , { (BYTE*) &null_SMALLMONEY, sizeof(null_SMALLMONEY) }
- /* BINARYBIND 15 */ , { NULL, 0 }
- /* BITBIND 16 */ , { &null_BIT, sizeof(null_BIT) }
- /* NUMERICBIND 17 */ , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
- /* DECIMALBIND 18 */ , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
- /* SRCNUMERICBIND 19 */ , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
- /* SRCDECIMALBIND 20 */ , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
- /* 21 */ , { NULL, 0 }
- /* 22 */ , { NULL, 0 }
- /* 23 */ , { NULL, 0 }
- /* 24 */ , { NULL, 0 }
- /* 25 */ , { NULL, 0 }
- /* 26 */ , { NULL, 0 }
- /* 27 */ , { NULL, 0 }
- /* 28 */ , { NULL, 0 }
- /* 29 */ , { NULL, 0 }
- /* BIGINTBIND 30 */ , { (BYTE*) &null_BIGINT, sizeof(null_BIGINT) }
- /* DATETIME2BIND 31 */ , { (BYTE*) &null_DATETIMEALL, sizeof(null_DATETIMEALL) }
- /* MAXBINDTYPES 32 */
- };
- static int
- dbbindtype(int datatype)
- {
- switch (datatype) {
- case SYBIMAGE:
- case SYBVARBINARY:
- case SYBBINARY: return BINARYBIND;
-
- case SYBBIT: return BITBIND;
- case SYBTEXT:
- case SYBVARCHAR:
- case SYBCHAR: return NTBSTRINGBIND;
-
- case SYBDATETIME: return DATETIMEBIND;
- case SYBDATETIME4: return SMALLDATETIMEBIND;
-
- case SYBDECIMAL: return DECIMALBIND;
- case SYBNUMERIC: return NUMERICBIND;
-
- case SYBFLT8: return FLT8BIND;
- case SYBREAL: return REALBIND;
- case SYBINT1: return TINYBIND;
- case SYBINT2: return SMALLBIND;
- case SYBINT4: return INTBIND;
- case SYBINT8: return BIGINTBIND;
- case SYBMONEY: return MONEYBIND;
- case SYBMONEY4: return SMALLMONEYBIND;
- case SYBMSDATE:
- case SYBMSTIME:
- case SYBMSDATETIME2:
- case SYBMSDATETIMEOFFSET:
- return DATETIME2BIND;
- default:
- assert(0 == "no such datatype");
- }
-
- return 0;
- }
- /** \internal
- * dbbind() says: "Note that if varlen is 0, no padding takes place"
- * dbgetnull() will not pad varaddr unless varlen is positive.
- * Vartype Program Type Padding Terminator
- * ------------------- -------------- -------------- ----------
- * CHARBIND DBCHAR blanks none
- * STRINGBIND DBCHAR blanks \0
- * NTBSTRINGBIND DBCHAR none \0
- * VARYCHARBIND DBVARYCHAR none none
- * BOUNDARYBIND DBCHAR none \0
- * SENSITIVITYBIND DBCHAR none \0
- */
- RETCODE
- dbgetnull(DBPROCESS *dbproc, int bindtype, int varlen, BYTE* varaddr)
- {
- NULLREP *pnullrep = default_null_representations + bindtype;
- tdsdump_log(TDS_DBG_FUNC, "dbgetnull(%p, %d, %d, %p)\n", dbproc, bindtype, varlen, varaddr);
- CHECK_PARAMETER(varaddr, SYBENULL, FAIL);
- CHECK_PARAMETER(0 <= bindtype && bindtype < MAXBINDTYPES, SYBEBTYP, FAIL);
-
- if (!varaddr) {
- dbperror(dbproc, SYBENULP, 0, "dbgetnull", "varaddr");
- return FAIL;
- }
-
- /* dbproc can be NULL */
- if (NULL != dbproc) {
- assert(dbproc->nullreps);
- pnullrep = dbproc->nullreps + bindtype;
- }
-
- /*
- * Fixed types: ignore varlen
- * Other types: ignore varlen if <= 0, else varlen must be >= pnullrep->len.
- */
- switch (bindtype) {
- case DATETIMEBIND:
- case DECIMALBIND:
- case SRCDECIMALBIND:
- case FLT8BIND:
- case INTBIND:
- case MONEYBIND:
- case NUMERICBIND:
- case SRCNUMERICBIND:
- case REALBIND:
- case SMALLBIND:
- case SMALLDATETIMEBIND:
- case SMALLMONEYBIND:
- case TINYBIND:
- case BIGINTBIND:
- memcpy(varaddr, pnullrep->bindval, pnullrep->len);
- return SUCCEED;
- default:
- if (pnullrep->bindval && (varlen <= 0 || (size_t)varlen >= pnullrep->len)) {
- memcpy(varaddr, pnullrep->bindval, pnullrep->len);
- }
- }
- /*
- * For variable-length types, nonpositive varlen indicates
- * buffer is "big enough" but also not to pad.
- * Apply terminator (if applicable) and go home.
- */
- if (varlen <= 0) {
- switch (bindtype) {
- case STRINGBIND:
- case NTBSTRINGBIND:
- varaddr[pnullrep->len] = '\0';
- /* fall thru */
- case CHARBIND:
- case VARYCHARBIND:
- break;
- #if 0
- case BOUNDARYBIND:
- case SENSITIVITYBIND:
- #endif
- default:
- assert(!"unknown bindtype with unknown varlen");
- }
- return SUCCEED;
- }
-
- if (varlen < (long)pnullrep->len) {
- tdsdump_log(TDS_DBG_FUNC, "dbgetnull: error: not setting varaddr(%p) because %d < %lu\n",
- varaddr, varlen, (unsigned long int) pnullrep->len);
- return FAIL;
- }
-
- tdsdump_log(TDS_DBG_FUNC, "varaddr(%p) varlen %d < %lu?\n",
- varaddr, varlen, (unsigned long int) pnullrep->len);
- assert(varlen > 0);
- /*
- * CHARBIND Empty string (padded with blanks)
- * STRINGBIND Empty string (padded with blanks, null-terminated)
- * NTBSTRINGBIND Empty string (unpadded, null-terminated)
- * BINARYBIND Empty array (padded with zeros)
- */
- varaddr += pnullrep->len;
- varlen -= (int)pnullrep->len;
- if (varlen > 0) {
- switch (bindtype) {
- case CHARBIND:
- memset(varaddr, ' ', varlen);
- break;
- case STRINGBIND:
- memset(varaddr, ' ', varlen);
- varaddr[varlen-1] = '\0';
- break;
- case NTBSTRINGBIND:
- varaddr[0] = '\0';
- break;
- case BINARYBIND:
- memset(varaddr, 0, varlen);
- break;
- default:
- assert(!"unknown bindtype");
- }
- }
- return SUCCEED;
- }
- /**
- * \ingroup dblib_core
- * \brief Initialize db-lib.
- *
- * \remarks Call this function before trying to use db-lib in any way.
- * Allocates various internal structures and reads \c locales.conf (if any) to determine the default
- * date format.
- * \retval SUCCEED normal.
- * \retval FAIL cannot allocate an array of \c TDS_MAX_CONN \c TDSSOCKET pointers.
- */
- RETCODE
- dbinit(void)
- {
- _dblib_err_handler = default_err_handler;
- tds_mutex_lock(&dblib_mutex);
- tdsdump_log(TDS_DBG_FUNC, "dbinit(void)\n");
- if (++g_dblib_ctx.ref_count != 1) {
- tds_mutex_unlock(&dblib_mutex);
- return SUCCEED;
- }
- /*
- * DBLIBCONTEXT stores a list of current connections so they may be closed with dbexit()
- */
- g_dblib_ctx.connection_list = (TDSSOCKET**) calloc(TDS_MAX_CONN, sizeof(TDSSOCKET *));
- if (g_dblib_ctx.connection_list == NULL) {
- tdsdump_log(TDS_DBG_FUNC, "dbinit: out of memory\n");
- tds_mutex_unlock(&dblib_mutex);
- return FAIL;
- }
- g_dblib_ctx.connection_list_size = TDS_MAX_CONN;
- g_dblib_ctx.connection_list_size_represented = TDS_MAX_CONN;
- g_dblib_ctx.login_timeout = -1;
- g_dblib_ctx.query_timeout = -1;
- tds_mutex_unlock(&dblib_mutex);
- dblib_get_tds_ctx();
- return SUCCEED;
- }
- /**
- * \ingroup dblib_core
- * \brief Allocate a \c LOGINREC structure.
- *
- * \remarks A \c LOGINREC structure is passed to \c dbopen() to create a connection to the database.
- * Does not communicate to the server; interacts strictly with library.
- * \retval NULL the \c LOGINREC cannot be allocated.
- * \retval LOGINREC* to valid memory, otherwise.
- */
- LOGINREC *
- dblogin(void)
- {
- LOGINREC *loginrec;
- tdsdump_log(TDS_DBG_FUNC, "dblogin(void)\n");
- if ((loginrec = (LOGINREC*) malloc(sizeof(LOGINREC))) == NULL) {
- dbperror(NULL, SYBEMEM, errno);
- return NULL;
- }
- if ((loginrec->tds_login = tds_alloc_login(1)) == NULL) {
- dbperror(NULL, SYBEMEM, errno);
- free(loginrec);
- return NULL;
- }
- /* set default values for loginrec */
- tds_set_library(loginrec->tds_login, "DB-Library");
- return loginrec;
- }
- /**
- * \ingroup dblib_core
- * \brief free the \c LOGINREC
- *
- */
- void
- dbloginfree(LOGINREC * login)
- {
- tdsdump_log(TDS_DBG_FUNC, "dbloginfree(%p)\n", login);
- if (login) {
- tds_free_login(login->tds_login);
- TDS_ZERO_FREE(login);
- }
- }
- /** \internal
- * \ingroup dblib_internal
- * \brief Set the value of a string in a \c LOGINREC structure.
- *
- * Called by various macros to populate \a login.
- * \param login the \c LOGINREC* to modify.
- * \param value the value to set it to.
- * \param which the field to set.
- * \retval SUCCEED the value was set.
- * \retval FAIL \c DBSETHID or other invalid \a which was tried.
- */
- RETCODE
- dbsetlname(LOGINREC * login, const char *value, int which)
- {
- tdsdump_log(TDS_DBG_FUNC, "dbsetlname(%p, %s, %d)\n", login, value, which);
- if( login == NULL ) {
- dbperror(NULL, SYBEASNL, 0);
- return FAIL;
- }
- if (TDS_MAX_LOGIN_STR_SZ < strlen(value)) {
- dbperror(NULL, SYBENTLL, 0);
- return FAIL;
- }
- switch (which) {
- case DBSETHOST:
- tds_set_host(login->tds_login, value);
- return SUCCEED;
- break;
- case DBSETUSER:
- tds_set_user(login->tds_login, value);
- return SUCCEED;
- break;
- case DBSETPWD:
- tds_set_passwd(login->tds_login, value);
- return SUCCEED;
- break;
- case DBSETAPP:
- tds_set_app(login->tds_login, value);
- return SUCCEED;
- break;
- case DBSETCHARSET:
- tds_set_client_charset(login->tds_login, value ? value : "");
- return SUCCEED;
- break;
- case DBSETNATLANG:
- tds_set_language(login->tds_login, value);
- return SUCCEED;
- break;
- case DBSETDBNAME:
- if (!tds_dstr_copy(&login->tds_login->database, value ? value : ""))
- return FAIL;
- return SUCCEED;
- break;
- default:
- dbperror(NULL, SYBEASUL, 0); /* Attempt to set unknown LOGINREC field */
- return FAIL;
- break;
- }
- }
- /** \internal
- * \ingroup dblib_internal
- * \brief Set an integer value in a \c LOGINREC structure.
- *
- * Called by various macros to populate \a login.
- * \param login the \c LOGINREC* to modify.
- * \param value the value to set it to.
- * \param which the field to set.
- * \retval SUCCEED the value was set.
- * \retval FAIL anything other than \c DBSETPACKET was passed for \a which.
- */
- RETCODE
- dbsetllong(LOGINREC * login, long value, int which)
- {
- tdsdump_log(TDS_DBG_FUNC, "dbsetllong(%p, %ld, %d)\n", login, value, which);
- if( login == NULL ) {
- dbperror(NULL, SYBEASNL, 0);
- return FAIL;
- }
- switch (which) {
- case DBSETPACKET:
- if (0 <= value && value <= 999999) {
- tds_set_packet(login->tds_login, value);
- return SUCCEED;
- }
- dbperror(0, SYBEBADPK, 0, (int) value, (int) login->tds_login->block_size);
- return FAIL;
- break;
- default:
- tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetllong() which = %d\n", which);
- return FAIL;
- break;
- }
- }
- #if defined(DBLIB_UNIMPLEMENTED)
- /** \internal
- * \ingroup dblib_internal
- * \brief Set an integer value in a \c LOGINREC structure.
- *
- * Called by various macros to populate \a login.
- * \param login the \c LOGINREC* to modify.
- * \param value the value to set it to.
- * \param which the field to set.
- * \retval SUCCEED the value was set.
- * \retval FAIL anything other than \c DBSETHIER was passed for \a which.
- */
- RETCODE
- dbsetlshort(LOGINREC * login, int value, int which)
- {
- tdsdump_log(TDS_DBG_FUNC, "dbsetlshort(%p, %d, %d)\n", login, value, which);
- if( login == NULL ) {
- dbperror(NULL, SYBEASNL, 0);
- return FAIL;
- }
- switch (which) {
- case DBSETHIER:
- default:
- tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetlshort() which = %d\n", which);
- return FAIL;
- break;
- }
- }
- #endif
- /** \internal
- * \ingroup dblib_internal
- * \brief Set a boolean value in a \c LOGINREC structure.
- *
- * Called by various macros to populate \a login.
- * \param login the \c LOGINREC* to modify.
- * \param value the value to set it to.
- * \param which the field to set.
- * \remark Only DBSETBCP is implemented.
- * \retval SUCCEED the value was set.
- * \retval FAIL invalid value passed for \a which.
- * \todo DBSETNOSHORT, DBSETENCRYPT, DBSETLABELED
- */
- RETCODE
- dbsetlbool(LOGINREC * login, int value, int which)
- {
- tdsdump_log(TDS_DBG_FUNC, "dbsetlbool(%p, %d, %d)\n", login, value, which);
- if( login == NULL ) {
- dbperror(NULL, SYBEASNL, 0);
- return FAIL;
- }
- switch (which) {
- case DBSETBCP:
- tds_set_bulk(login->tds_login, (TDS_TINYINT) value);
- return SUCCEED;
- break;
- case DBSETENCRYPT:
- case DBSETLABELED:
- default:
- tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetlbool() which = %d\n", which);
- return FAIL;
- break;
- }
- }
- /**
- * \ingroup dblib_core
- * \brief Set TDS version for future connections
- *
- */
- RETCODE
- dbsetlversion (LOGINREC * login, BYTE version)
- {
- tdsdump_log(TDS_DBG_FUNC, "dbsetlversion(%p, %x)\n", login, version);
- if( login == NULL ) {
- dbperror(NULL, SYBEASNL, 0);
- return FAIL;
- }
- assert(login->tds_login != NULL);
-
- switch (version) {
- case DBVER42:
- login->tds_login->tds_version = 0x402;
- return SUCCEED;
- case DBVER60:
- login->tds_login->tds_version = 0x700;
- return SUCCEED;
- case DBVERSION_100:
- tds_set_version(login->tds_login, 5, 0);
- return SUCCEED;
- case DBVERSION_71:
- tds_set_version(login->tds_login, 7, 1);
- return SUCCEED;
- case DBVERSION_72:
- tds_set_version(login->tds_login, 7, 2);
- return SUCCEED;
- case DBVERSION_73:
- tds_set_version(login->tds_login, 7, 3);
- return SUCCEED;
- }
-
- return FAIL;
- }
- static void
- dbstring_free(DBSTRING ** dbstrp)
- {
- DBSTRING *curr, *next;
- /* tdsdump_log(TDS_DBG_FUNC, "dbstring_free(%p)\n", dbstrp); */
- if (!dbstrp)
- return;
- curr = *dbstrp;
- *dbstrp = NULL;
- for (; curr; ) {
- next = curr->strnext;
- free(curr->strtext);
- free(curr);
- curr = next;
- }
- }
- static RETCODE
- dbstring_concat(DBSTRING ** dbstrp, const char *p)
- {
- DBSTRING **strp = dbstrp;
- /* tdsdump_log(TDS_DBG_FUNC, "dbstring_concat(%p, %s)\n", *dbstrp, p); */
- while (*strp != NULL) {
- strp = &((*strp)->strnext);
- }
- if ((*strp = (DBSTRING*) malloc(sizeof(DBSTRING))) == NULL) {
- dbperror(NULL, SYBEMEM, errno);
- return FAIL;
- }
- (*strp)->strtotlen = (DBINT)strlen(p);
- if (((*strp)->strtext = (BYTE*) malloc((*strp)->strtotlen)) == NULL) {
- TDS_ZERO_FREE(*strp);
- dbperror(NULL, SYBEMEM, errno);
- return FAIL;
- }
- memcpy((*strp)->strtext, p, (*strp)->strtotlen);
- (*strp)->strnext = NULL;
- return SUCCEED;
- }
- static RETCODE
- dbstring_assign(DBSTRING ** dbstrp, const char *p)
- {
- /* tdsdump_log(TDS_DBG_FUNC, "dbstring_assign(%p, %s)\n", *dbstrp, p); */
- dbstring_free(dbstrp);
- return dbstring_concat(dbstrp, p);
- }
- static DBINT
- dbstring_length(DBSTRING * dbstr)
- {
- DBINT len = 0;
- DBSTRING *next;
- /* tdsdump_log(TDS_DBG_FUNC, "dbstring_length(%p)\n", dbstr); */
- for (next = dbstr; next != NULL; next = next->strnext) {
- len += next->strtotlen;
- }
- return len;
- }
- static int
- dbstring_getchar(DBSTRING * dbstr, int i)
- {
- /* tdsdump_log(TDS_DBG_FUNC, "dbstring_getchar(%p, %d)\n", dbstr, i); */
- if (dbstr == NULL) {
- return -1;
- }
- if (i < 0) {
- return -1;
- }
- if (i < dbstr->strtotlen) {
- return dbstr->strtext[i];
- }
- return dbstring_getchar(dbstr->strnext, i - dbstr->strtotlen);
- }
- static char *
- dbstring_get(DBSTRING * dbstr)
- {
- DBSTRING *next;
- int len;
- char *ret;
- char *cp;
- /* tdsdump_log(TDS_DBG_FUNC, "dbstring_get(%p)\n", dbstr); */
- if (dbstr == NULL) {
- return NULL;
- }
- len = dbstring_length(dbstr);
- if ((ret = (char*) malloc(len + 1)) == NULL) {
- dbperror(NULL, SYBEMEM, errno);
- return NULL;
- }
- cp = ret;
- for (next = dbstr; next != NULL; next = next->strnext) {
- memcpy(cp, next->strtext, next->strtotlen);
- cp += next->strtotlen;
- }
- *cp = '\0';
- return ret;
- }
- static const char *const opttext[DBNUMOPTIONS] = {
- "parseonly",
- "estimate",
- "showplan",
- "noexec",
- "arithignore",
- "nocount",
- "arithabort",
- "textlimit",
- "browse",
- "offsets",
- "statistics",
- "errlvl",
- "confirm",
- "spid",
- "buffer",
- "noautofree",
- "rowcount",
- "textsize",
- "language",
- "dateformat",
- "prpad",
- "prcolsep",
- "prlinelen",
- "prlinesep",
- "lfconvert",
- "datefirst",
- "chained",
- "fipsflagger",
- "transaction isolation level",
- "auth",
- "identity_insert",
- "no_identity_column",
- "cnv_date2char_short",
- "client cursors",
- "set time",
- "quoted_identifier"
- };
- static DBOPTION *
- init_dboptions(void)
- {
- DBOPTION *dbopts;
- int i;
- if ((dbopts = (DBOPTION*) calloc(DBNUMOPTIONS, sizeof(DBOPTION))) == NULL) {
- dbperror(NULL, SYBEMEM, errno);
- return NULL;
- }
- for (i = 0; i < DBNUMOPTIONS; i++) {
- tds_strlcpy(dbopts[i].text, opttext[i], sizeof(dbopts[i].text));
- dbopts[i].param = NULL;
- dbopts[i].factive = FALSE;
- }
- dbstring_assign(&(dbopts[DBPRPAD].param), " ");
- dbstring_assign(&(dbopts[DBPRCOLSEP].param), " ");
- dbstring_assign(&(dbopts[DBPRLINELEN].param), "80");
- dbstring_assign(&(dbopts[DBPRLINESEP].param), "\n");
- dbstring_assign(&(dbopts[DBCLIENTCURSORS].param), " ");
- dbstring_assign(&(dbopts[DBSETTIME].param), " ");
- return dbopts;
- }
- /** \internal
- * \ingroup dblib_internal
- * \brief Form a connection with the server.
- *
- * Called by the \c dbopen() macro, normally. If FreeTDS was configured with \c --enable-msdblib, this
- * function is called by (exported) \c dbopen() function. \c tdsdbopen is so-named to avoid
- * namespace conflicts with other database libraries that use the same function name.
- * \param login \c LOGINREC* carrying the account information.
- * \param server name of the dataserver to connect to.
- * \return valid pointer on successful login.
- * \retval NULL insufficient memory, unable to connect for any reason.
- * \sa dbopen()
- * \todo use \c asprintf() to avoid buffer overflow.
- * \todo separate error messages for \em no-such-server and \em no-such-user.
- */
- DBPROCESS *
- tdsdbopen(LOGINREC * login, const char *server, int msdblib)
- {
- DBPROCESS *dbproc = NULL;
- TDSLOGIN *connection;
- char *tdsdump = getenv("TDSDUMP");
- if (tdsdump && *tdsdump) {
- tdsdump_open(tdsdump);
- tdsdump_log(TDS_DBG_FUNC, "tdsdbopen(%p, %s, [%s])\n", login, server? server : "0x0", msdblib? "microsoft" : "sybase");
- }
- /*
- * Sybase supports the DSQUERY environment variable and falls back to "SYBASE" if server is NULL.
- * Microsoft uses a NULL or "" server to indicate a local server.
- * FIXME: support local server for win32.
- */
- if (!server && !msdblib) {
- if ((server = getenv("TDSQUERY")) == NULL)
- if ((server = getenv("DSQUERY")) == NULL)
- server = "SYBASE";
- tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: servername set to %s\n", server);
- }
- if ((dbproc = (DBPROCESS*) calloc(1, sizeof(DBPROCESS))) == NULL) {
- dbperror(NULL, SYBEMEM, errno);
- return NULL;
- }
- dbproc->msdblib = msdblib;
- dbproc->dbopts = init_dboptions();
- if (dbproc->dbopts == NULL) {
- free(dbproc);
- return NULL;
- }
- tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: dbproc->dbopts = %p\n", dbproc->dbopts);
-
- dbproc->dboptcmd = NULL;
- dbproc->avail_flag = TRUE;
- dbproc->command_state = DBCMDNONE;
- tds_set_server(login->tds_login, server);
- tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: tds_set_server(%p, \"%s\")\n", login->tds_login, server);
- if ((dbproc->tds_socket = tds_alloc_socket(dblib_get_tds_ctx(), 512)) == NULL ){
- dbperror(NULL, SYBEMEM, 0);
- free(dbproc);
- return NULL;
- }
-
- tds_set_parent(dbproc->tds_socket, dbproc);
- dbproc->tds_socket->env_chg_func = db_env_chg;
- dbproc->envchange_rcv = 0;
- dbproc->dbcurdb[0] = '\0';
- dbproc->servcharset[0] = '\0';
- tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: About to call tds_read_config_info...\n");
- connection = tds_read_config_info(dbproc->tds_socket, login->tds_login, g_dblib_ctx.tds_ctx->locale);
- if (!connection) {
- dbclose(dbproc);
- return NULL;
- }
- connection->option_flag2 &= ~0x02; /* we're not an ODBC driver */
- tds_fix_login(connection); /* initialize from Environment variables */
- dbproc->chkintr = NULL;
- dbproc->hndlintr = NULL;
- tds_mutex_lock(&dblib_mutex);
- /* override connection timeout if dbsetlogintime() was called */
- if (g_dblib_ctx.login_timeout > 0) {
- connection->connect_timeout = g_dblib_ctx.login_timeout;
- }
- /* override query timeout if dbsettime() was called */
- if (g_dblib_ctx.query_timeout > 0) {
- connection->query_timeout = g_dblib_ctx.query_timeout;
- }
- tds_mutex_unlock(&dblib_mutex);
- tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: Calling tds_connect_and_login(%p, %p)\n",
- dbproc->tds_socket, connection);
- if (TDS_FAILED(tds_connect_and_login(dbproc->tds_socket, connection))) {
- tdsdump_log(TDS_DBG_ERROR, "tdsdbopen: tds_connect_and_login failed for \"%s\"!\n",
- tds_dstr_cstr(&connection->server_name));
- tds_free_login(connection);
- dbclose(dbproc);
- return NULL;
- }
- tds_free_login(connection);
- dbproc->dbbuf = NULL;
- dbproc->dbbufsz = 0;
- tds_mutex_lock(&dblib_mutex);
- dblib_add_connection(&g_dblib_ctx, dbproc->tds_socket);
- tds_mutex_unlock(&dblib_mutex);
- /* set the DBBUFFER capacity to nil */
- buffer_set_capacity(dbproc, 0);
- tds_mutex_lock(&dblib_mutex);
- if (g_dblib_ctx.recftos_filename != NULL) {
- char *temp_filename = NULL;
- const int len = asprintf(&temp_filename, "%s.%d",
- g_dblib_ctx.recftos_filename, g_dblib_ctx.recftos_filenum);
- if (len >= 0) {
- dbproc->ftos = fopen(temp_filename, "w");
- if (dbproc->ftos != NULL) {
- fprintf(dbproc->ftos, "/* dbopen() at %s */\n", _dbprdate(temp_filename));
- fflush(dbproc->ftos);
- g_dblib_ctx.recftos_filenum++;
- }
- free(temp_filename);
- }
- }
-
- memcpy(dbproc->nullreps, default_null_representations, sizeof(default_null_representations));
- tds_mutex_unlock(&dblib_mutex);
- tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: Returning dbproc = %p\n", dbproc);
- return dbproc;
- }
- /**
- * \ingroup dblib_core
- * \brief \c printf-like way to form SQL to send to the server.
- *
- * Forms a command string and writes to the command buffer with dbcmd().
- * \param dbproc contains all information needed by db-lib to manage communications with the server.
- * \param fmt <tt> man vasprintf</tt> for details.
- * \retval SUCCEED success.
- * \retval FAIL insufficient memory, or dbcmd() failed.
- * \sa dbcmd(), dbfreebuf(), dbgetchar(), dbopen(), dbstrcpy(), dbstrlen().
- */
- RETCODE
- dbfcmd(DBPROCESS * dbproc, const char *fmt, ...)
- {
- va_list ap;
- char *s;
- int len;
- RETCODE ret;
- tdsdump_log(TDS_DBG_FUNC, "dbfcmd(%p, %s, ...)\n", dbproc, fmt);
- CHECK_CONN(FAIL);
- CHECK_NULP(fmt, "dbfcmd", 2, FAIL);
-
- va_start(ap, fmt);
- len = vasprintf(&s, fmt, ap);
- va_end(ap);
- if (len < 0) {
- dbperror(dbproc, SYBEMEM, errno);
- return FAIL;
- }
- ret = dbcmd(dbproc, s);
- free(s);
- return ret;
- }
- /**
- * \ingroup dblib_core
- * \brief \c Append SQL to the command buffer.
- *
- * \param dbproc contains all information needed by db-lib to manage communications with the server.
- * \param cmdstring SQL to append to the command buffer.
- * \retval SUCCEED success.
- * \retval FAIL insufficient memory.
- * \remarks set command state to \c DBCMDPEND unless the command state is DBCMDSENT, in which case
- * it frees the command buffer. This latter may or may not be the Right Thing to do.
- * \sa dbfcmd(), dbfreebuf(), dbgetchar(), dbopen(), dbstrcpy(), dbstrlen().
- */
- RETCODE
- dbcmd(DBPROCESS * dbproc, const char cmdstring[])
- {
- tdsdump_log(TDS_DBG_FUNC, "dbcmd(%p, %s)\n", dbproc, cmdstring);
- CHECK_CONN(FAIL);
- CHECK_NULP(cmdstring, "dbcmd", 2, FAIL);
- dbproc->avail_flag = FALSE;
- tdsdump_log(TDS_DBG_FUNC, "dbcmd() bufsz = %d\n", dbproc->dbbufsz);
- if (dbproc->command_state == DBCMDSENT) {
- if (!dbproc->noautofree) {
- dbfreebuf(dbproc);
- }
- }
- if (dbproc->dbbufsz == 0) {
- dbproc->dbbuf = (unsigned char*) malloc(strlen(cmdstring) + 1);
- if (dbproc->dbbuf == NULL) {
- dbperror(dbproc, SYBEMEM, errno);
- return FAIL;
- }
- strcpy((char *) dbproc->dbbuf, cmdstring);
- dbproc->dbbufsz = (int)strlen(cmdstring) + 1;
- } else {
- size_t newsz = strlen(cmdstring) + dbproc->dbbufsz;
- if (!TDS_RESIZE(dbproc->dbbuf, newsz)) {
- dbperror(dbproc, SYBEMEM, errno);
- return FAIL;
- }
- strcat((char *) dbproc->dbbuf, cmdstring);
- dbproc->dbbufsz = (int)newsz;
- }
- dbproc->command_state = DBCMDPEND;
- return SUCCEED;
- }
- /**
- * \ingroup dblib_core
- * \brief send the SQL command to the server and wait for an answer.
- *
- * Please be patient. This function waits for the server to respond. \c dbsqlexec is equivalent
- * to dbsqlsend() followed by dbsqlok().
- * \param dbproc contains all information needed by db-lib to manage communications with the server.
- * \retval SUCCEED query was processed without errors.
- * \retval FAIL was returned by dbsqlsend() or dbsqlok().
- * \sa dbcmd(), dbfcmd(), dbnextrow(), dbresults(), dbretstatus(), dbsettime(), dbsqlok(), dbsqlsend()
- */
- RETCODE
- dbsqlexec(DBPROCESS * dbproc)
- {
- RETCODE rc = FAIL;
- tdsdump_log(TDS_DBG_FUNC, "dbsqlexec(%p)\n", dbproc);
- CHECK_CONN(FAIL);
- if (SUCCEED == (rc = dbsqlsend(dbproc))) {
- rc = dbsqlok(dbproc);
- }
- return rc;
- }
- /**
- * \ingroup dblib_core
- * \brief Change current database.
- *
- * Analagous to the unix command \c cd, dbuse() makes \a name the default database. Waits for an answer
- * from the server.
- * \param dbproc contains all information needed by db-lib to manage communications with the server.
- * \param name database to use.
- * \retval SUCCEED query was processed without errors.
- * \retval FAIL query was not processed
- * \sa dbchange(), dbname().
- */
- RETCODE
- dbuse(DBPROCESS * dbproc, const char *name)
- {
- RETCODE rc;
- char *query;
- tdsdump_log(TDS_DBG_FUNC, "dbuse(%p, %s)\n", dbproc, name);
- CHECK_CONN(FAIL);
- CHECK_NULP(name, "dbuse", 2, FAIL);
- if (!dbproc->tds_socket)
- return FAIL;
- /* quote name */
- query = (char*) malloc(tds_quote_id(dbproc->tds_socket, NULL, name, -1) + 6);
- if (!query) {
- dbperror(dbproc, SYBEMEM, errno);
- return FAIL;
- }
- strcpy(query, "use ");
- /* TODO PHP suggest to quote by yourself with []... what should I do ?? quote or not ?? */
- if (name[0] == '[' && name[strlen(name)-1] == ']')
- strcat(query, name);
- else
- tds_quote_id(dbproc->tds_socket, query + 4, name, -1);
- rc = SUCCEED;
- if ((dbcmd(dbproc, query) == FAIL)
- || (dbsqlexec(dbproc) == FAIL)
- || (dbresults(dbproc) == FAIL)
- || (dbcanquery(dbproc) == FAIL))
- rc = FAIL;
- free(query);
- return rc;
- }
- /**
- * \ingroup dblib_core
- * \brief Close a connection to the server and free associated resources.
- *
- * \param dbproc contains all information needed by db-lib to manage communications with the server.
- * \sa dbexit(), dbopen().
- */
- void
- dbclose(DBPROCESS * dbproc)
- {
- TDSSOCKET *tds;
- int i;
- char timestr[256];
- tdsdump_log(TDS_DBG_FUNC, "dbclose(%p)\n", dbproc);
- CHECK_PARAMETER(dbproc, SYBENULL, );
- tds = dbproc->tds_socket;
- if (tds) {
- /*
- * this MUST be done before socket destruction
- * it is possible that a TDSSOCKET is allocated on same position
- */
- tds_mutex_lock(&dblib_mutex);
- dblib_del_connection(&g_dblib_ctx, dbproc->tds_socket);
- tds_mutex_unlock(&dblib_mutex);
- tds_close_socket(tds);
- tds_free_socket(tds);
- dblib_release_tds_ctx(1);
- }
- buffer_free(&(dbproc->row_buf));
- if (dbproc->ftos != NULL) {
- fprintf(dbproc->ftos, "/* dbclose() at %s */\n", _dbprdate(timestr));
- fclose(dbproc->ftos);
- }
- if (dbproc->bcpinfo)
- free(dbproc->bcpinfo->tablename);
- if (dbproc->hostfileinfo) {
- free(dbproc->hostfileinfo->hostfile);
- free(dbproc->hostfileinfo->errorfile);
- if (dbproc->hostfileinfo->host_columns) {
- for (i = 0; i < dbproc->hostfileinfo->host_colcount; i++) {
- free(dbproc->hostfileinfo->host_columns[i]->terminator);
- free(dbproc->hostfileinfo->host_columns[i]);
- }
- free(dbproc->hostfileinfo->host_columns);
- }
- }
- for (i = 0; i < DBNUMOPTIONS; i++) {
- dbstring_free(&(dbproc->dbopts[i].param));
- }
- free(dbproc->dbopts);
- dbstring_free(&(dbproc->dboptcmd));
- for (i=0; i < MAXBINDTYPES; i++) {
- if (dbproc->nullreps[i].bindval != default_null_representations[i].bindval)
- free((BYTE*)dbproc->nullreps[i].bindval);
- }
- dbfreebuf(dbproc);
- free(dbproc);
- return;
- }
- /**
- * \ingroup dblib_core
- * \brief Close server connections and free all related structures.
- *
- * \sa dbclose(), dbinit(), dbopen().
- * \todo breaks if ctlib/dblib used in same process.
- */
- void
- dbexit()
- {
- TDSSOCKET *tds;
- DBPROCESS *dbproc;
- int i, list_size, count = 1;
- tdsdump_log(TDS_DBG_FUNC, "dbexit(void)\n");
- tds_mutex_lock(&dblib_mutex);
- if (--g_dblib_ctx.ref_count != 0) {
- tds_mutex_unlock(&dblib_mutex);
- return;
- }
- list_size = g_dblib_ctx.connection_list_size;
- for (i = 0; i < list_size; i++) {
- tds = g_dblib_ctx.connection_list[i];
- g_dblib_ctx.connection_list[i] = NULL;
- if (tds) {
- ++count;
- dbproc = (DBPROCESS *) tds_get_parent(tds);
- tds_close_socket(tds);
- tds_free_socket(tds);
- if (dbproc) {
- /* avoid locking in dbclose */
- dbproc->tds_socket = NULL;
- dbclose(dbproc);
- }
- }
- }
- if (g_dblib_ctx.connection_list) {
- TDS_ZERO_FREE(g_dblib_ctx.connection_list);
- g_dblib_ctx.connection_list_size = 0;
- }
- tds_mutex_unlock(&dblib_mutex);
- dblib_release_tds_ctx(count);
- }
- static const char *
- prdbresults_state(int retcode)
- {
- static char unknown[24];
- switch(retcode) {
- case _DB_RES_INIT: return "_DB_RES_INIT";
- case _DB_RES_RESULTSET_EMPTY: return "_DB_RES_RESULTSET_EMPTY";
- case _DB_RES_RESULTSET_ROWS: return "_DB_RES_RESULTSET_ROWS";
- case _DB_RES_NEXT_RESULT: return "_DB_RES_NEXT_RESULT";
- case _DB_RES_NO_MORE_RESULTS: return "_DB_RES_NO_MORE_RESULTS";
- case _DB_RES_SUCCEED: return "_DB_RES_SUCCEED";
- default:
- sprintf(unknown, "oops: %u ??", retcode);
- }
- return unknown;
- }
- static const char *
- prdbretcode(RETCODE retcode)
- {
- static char unknown[24];
- switch(retcode) {
- case REG_ROW: return "REG_ROW/MORE_ROWS";
- case NO_MORE_ROWS: return "NO_MORE_ROWS";
- case BUF_FULL: return "BUF_FULL";
- case NO_MORE_RESULTS: return "NO_MORE_RESULTS";
- case SUCCEED: return "SUCCEED";
- case FAIL: return "FAIL";
- default:
- sprintf(unknown, "oops: %u ??", retcode);
- }
- return unknown;
- }
- static const char *
- prretcode(int retcode)
- {
- static char unknown[24];
- switch(retcode) {
- case TDS_SUCCESS: return "TDS_SUCCESS";
- case TDS_FAIL: return "TDS_FAIL";
- case TDS_NO_MORE_RESULTS: return "TDS_NO_MORE_RESULTS";
- case TDS_CANCELLED: return "TDS_CANCELLED";
- default:
- sprintf(unknown, "oops: %u ??", retcode);
- }
- return unknown;
- }
- static const char *
- prresult_type(int result_type)
- {
- static char unknown[24];
- switch(result_type) {
- case TDS_ROW_RESULT: return "TDS_ROW_RESULT";
- case TDS_PARAM_RESULT: return "TDS_PARAM_RESULT";
- case TDS_STATUS_RESULT: return "TDS_STATUS_RESULT";
- case TDS_MSG_RESULT: return "TDS_MSG_RESULT";
- case TDS_COMPUTE_RESULT: return "TDS_COMPUTE_RESULT";
- case TDS_CMD_DONE: return "TDS_CMD_DONE";
- case TDS_CMD_SUCCEED: return "TDS_CMD_SUCCEED";
- case TDS_CMD_FAIL: return "TDS_CMD_FAIL";
- case TDS_ROWFMT_RESULT: return "TDS_ROWFMT_RESULT";
- case TDS_COMPUTEFMT_RESULT: return "TDS_COMPUTEFMT_RESULT";
- case TDS_DESCRIBE_RESULT: return "TDS_DESCRIBE_RESULT";
- case TDS_DONE_RESULT: return "TDS_DONE_RESULT";
- case TDS_DONEPROC_RESULT: return "TDS_DONEPROC_RESULT";
- case TDS_DONEINPROC_RESULT: return "TDS_DONEINPROC_RESULT";
- case TDS_OTHERS_RESULT: return "TDS_OTHERS_RESULT";
- default:
- sprintf(unknown, "oops: %u ??", result_type);
- }
- return unknown;
- }
- /**
- * \ingroup dblib_core
- * \brief Set up query results.
- *
- * \param dbproc contains all information needed by db-lib to manage communications with the server.
- * \retval SUCCEED Some results are available.
- * \retval FAIL query was not processed successfully by the server
- * \retval NO_MORE_RESULTS query produced no results.
- *
- * \remarks Call dbresults() after calling dbsqlexec() or dbsqlok(), or dbrpcsend() returns SUCCEED. Unless
- * one of them fails, dbresults will return either SUCCEED or NO_MORE_RESULTS.
- *
- * The meaning of \em results is very specific and not very intuitive. Results are created by either
- * - a SELECT statement
- * - a stored procedure
- *
- * When dbresults returns SUCCEED, therefore, it indicates the server processed the query successfully and
- * that one or more of these is present:
- * - metadata -- dbnumcols() returns 1 or more
- * - data -- dbnextrow() returns SUCCEED
- * - return status -- dbhasretstat() returns TRUE
- * - output parameters -- dbnumrets() returns 1 or more
- *
- * If none of the above are present, dbresults() returns NO_MORE_RESULTS.
- *
- * SUCCEED does not imply that DBROWS() will return TRUE or even that dbnumcols() will return nonzero.
- * A general algorithm for reading results will call dbresults() until it return NO_MORE_RESULTS (or FAIL).
- * An application should check for all the above kinds of results within the dbresults() loop.
- *
- * \sa dbsqlexec(), dbsqlok(), dbrpcsend(), dbcancel(), DBROWS(), dbnextrow(), dbnumcols(), dbhasretstat(), dbretstatus(), dbnumrets()
- */
- RETCODE
- dbresults(DBPROCESS * dbproc)
- {
- RETCODE erc = _dbresults(dbproc);
- tdsdump_log(TDS_DBG_FUNC, "dbresults returning %d (%s)\n", erc, prdbretcode(erc));
- return erc;
- }
- static RETCODE
- _dbresults(DBPROCESS * dbproc)
- {
- TDSSOCKET *tds;
- int result_type = 0, done_flags;
- tdsdump_log(TDS_DBG_FUNC, "dbresults(%p)\n", dbproc);
- CHECK_CONN(FAIL);
- tds = dbproc->tds_socket;
- tdsdump_log(TDS_DBG_FUNC, "dbresults: dbresults_state is %d (%s)\n",
- dbproc->dbresults_state, prdbresults_state(dbproc->dbresults_state));
- switch ( dbproc->dbresults_state ) {
- case _DB_RES_SUCCEED:
- dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
- return SUCCEED;
- break;
- case _DB_RES_RESULTSET_ROWS:
- dbperror(dbproc, SYBERPND, 0); /* dbresults called while rows outstanding.... */
- return FAIL;
- break;
- case _DB_RES_NO_MORE_RESULTS:
- return NO_MORE_RESULTS;
- break;
- default:
- break;
- }
- for (;;) {
- TDSRET retcode = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS);
- tdsdump_log(TDS_DBG_FUNC, "dbresults() tds_process_tokens returned %d (%s),\n\t\t\tresult_type %s\n",
- retcode, prretcode(retcode), prresult_type(result_type));
- switch (retcode) {
- case TDS_SUCCESS:
- switch (result_type) {
-
- case TDS_ROWFMT_RESULT:
- buffer_free(&dbproc->row_buf);
- buffer_alloc(dbproc);
- dbproc->dbresults_state = _DB_RES_RESULTSET_EMPTY;
- break;
-
- case TDS_COMPUTEFMT_RESULT:
- break;
-
- case TDS_ROW_RESULT:
- case TDS_COMPUTE_RESULT:
-
- dbproc->dbresults_state = _DB_RES_RESULTSET_ROWS;
- return SUCCEED;
- break;
-
- case TDS_DONE_RESULT:
- case TDS_DONEPROC_RESULT:
- tdsdump_log(TDS_DBG_FUNC, "dbresults(): dbresults_state is %d (%s)\n",
- dbproc->dbresults_state, prdbresults_state(dbproc->dbresults_state));
- /* A done token signifies the end of a logical command.
- * There are three possibilities:
- * 1. Simple command with no result set, i.e. update, delete, insert
- * 2. Command with result set but no rows
- * 3. Command with result set and rows
- */
- switch (dbproc->dbresults_state) {
- case _DB_RES_INIT:
- case _DB_RES_NEXT_RESULT:
- dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
- if (done_flags & TDS_DONE_ERROR)
- return FAIL;
- break;
- case _DB_RES_RESULTSET_EMPTY:
- case _DB_RES_RESULTSET_ROWS:
- dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
- return SUCCEED;
- break;
- default:
- assert(0);
- break;
- }
- break;
- case TDS_DONEINPROC_RESULT:
- /*
- * Return SUCCEED on a command within a stored procedure
- * only if the command returned a result set.
- */
- switch (dbproc->dbresults_state) {
- case _DB_RES_INIT:
- case _DB_RES_NEXT_RESULT:
- dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
- break;
- case _DB_RES_RESULTSET_EMPTY :
- case _DB_RES_RESULTSET_ROWS :
- dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
- return SUCCEED;
- break;
- case _DB_RES_NO_MORE_RESULTS:
- case _DB_RES_SUCCEED:
- break;
- }
- break;
- case TDS_STATUS_RESULT:
- case TDS_MSG_RESULT:
- case TDS_DESCRIBE_RESULT:
- case TDS_PARAM_RESULT:
- default:
- break;
- }
- break;
- case TDS_NO_MORE_RESULTS:
- dbproc->dbresults_state = _DB_RES_NO_MORE_RESULTS;
- return NO_MORE_RESULTS;
- break;
- default:
- assert(TDS_FAILED(retcode));
- dbproc->dbresults_state = _DB_RES_INIT;
- return FAIL;
- break;
- }
- }
- }
- /**
- * \ingroup dblib_core
- * \brief Return number of regular columns in a result set.
- *
- * \param dbproc contains all information needed by db-lib to manage communications with the server.
- * \sa dbcollen(), dbcolname(), dbnumalts().
- */
- int
- dbnumcols(DBPROCESS * dbproc)
- {
- tdsdump_log(TDS_DBG_FUNC, "dbnumcols(%p)\n", dbproc);
- CHECK_PARAMETER(dbproc, SYBENULL, 0);
- if (dbproc && dbproc->tds_socket && dbproc->tds_socket->res_info)
- return dbproc->tds_socket->res_info->num_cols;
- return 0;
- }
- /**
- * \ingroup dblib_core
- * \brief Return name of a regular result column.
- *
- * \param dbproc contains all information needed by db-lib to manage communications with the server.
- * \param column Nth in the result set, starting with 1.
- * \return pointer to ASCII null-terminated string, the name of the column.
- * \retval NULL \a column is not in range.
- * \sa dbcollen(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols().
- * \bug Relies on ASCII column names, post iconv conversion.
- * Will not work as described for UTF-8 or UCS-2 clients.
- * But maybe it shouldn't.
- */
- char *
- dbcolname(DBPROCESS * dbproc, int column)
- {
- TDSCOLUMN *colinfo;
-
- tdsdump_log(TDS_DBG_FUNC, "dbcolname(%p, %d)\n", dbproc, column);
- CHECK_PARAMETER(dbproc, SYBENULL, 0);
- colinfo = dbcolptr(dbproc, column);
- if (!colinfo)
- return NULL;
-
- return tds_dstr_buf(&colinfo->column_name);
- }
- /**
- * \ingroup dblib_core
- * \brief Read a row from the row buffer.
- …
Large files files are truncated, but you can click here to view the full file