LTP GCOV extension - code coverage report
Current view: directory - odbc - odbc.c
Test: FreeTDS coverage
Date: 2008-10-06 Instrumented lines: 2722
Code covered: 47.1 % Executed lines: 1282

       1                 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2                 :  * Copyright (C) 1998, 1999, 2000, 2001  Brian Bruns
       3                 :  * Copyright (C) 2002, 2003, 2004, 2005, 2006  Frediano Ziglio
       4                 :  *
       5                 :  * This library is free software; you can redistribute it and/or
       6                 :  * modify it under the terms of the GNU Library General Public
       7                 :  * License as published by the Free Software Foundation; either
       8                 :  * version 2 of the License, or (at your option) any later version.
       9                 :  *
      10                 :  * This library is distributed in the hope that it will be useful,
      11                 :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12                 :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13                 :  * Library General Public License for more details.
      14                 :  *
      15                 :  * You should have received a copy of the GNU Library General Public
      16                 :  * License along with this library; if not, write to the
      17                 :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      18                 :  * Boston, MA 02111-1307, USA.
      19                 :  */
      20                 : 
      21                 : /*
      22                 :  * PROGRAMMER   NAME            CONTACT
      23                 :  *==============================================================
      24                 :  * BSB          Brian Bruns     camber@ais.org
      25                 :  * PAH          Peter Harvey    pharvey@codebydesign.com
      26                 :  * SMURPH       Steve Murphree  smurph@smcomp.com
      27                 :  *
      28                 :  ***************************************************************
      29                 :  * DATE         PROGRAMMER  CHANGE
      30                 :  *==============================================================
      31                 :  *                          Original.
      32                 :  * 03.FEB.02    PAH         Started adding use of SQLGetPrivateProfileString().
      33                 :  * 04.FEB.02    PAH         Fixed small error preventing SQLBindParameter from being called
      34                 :  */
      35                 : 
      36                 : #if HAVE_CONFIG_H
      37                 : #include <config.h>
      38                 : #endif /* HAVE_CONFIG_H */
      39                 : 
      40                 : #include <stdio.h>
      41                 : 
      42                 : #if HAVE_STDLIB_H
      43                 : #include <stdlib.h>
      44                 : #endif /* HAVE_STDLIB_H */
      45                 : 
      46                 : #if HAVE_STRING_H
      47                 : #include <string.h>
      48                 : #endif /* HAVE_STRING_H */
      49                 : 
      50                 : #include <stdarg.h>
      51                 : #include <assert.h>
      52                 : #include <ctype.h>
      53                 : 
      54                 : #include "tdsodbc.h"
      55                 : #include "tdsstring.h"
      56                 : #include "tdsconvert.h"
      57                 : #include "replacements.h"
      58                 : 
      59                 : #ifdef DMALLOC
      60                 : #include <dmalloc.h>
      61                 : #endif
      62                 : 
      63                 : TDS_RCSID(var, "$Id: odbc.c,v 1.402.2.6 2007/12/21 11:19:01 freddy77 Exp $");
      64                 : 
      65                 : static SQLRETURN SQL_API _SQLAllocConnect(SQLHENV henv, SQLHDBC FAR * phdbc);
      66                 : static SQLRETURN SQL_API _SQLAllocEnv(SQLHENV FAR * phenv);
      67                 : static SQLRETURN SQL_API _SQLAllocStmt(SQLHDBC hdbc, SQLHSTMT FAR * phstmt);
      68                 : static SQLRETURN SQL_API _SQLAllocDesc(SQLHDBC hdbc, SQLHDESC FAR * phstmt);
      69                 : static SQLRETURN SQL_API _SQLFreeConnect(SQLHDBC hdbc);
      70                 : static SQLRETURN SQL_API _SQLFreeEnv(SQLHENV henv);
      71                 : static SQLRETURN SQL_API _SQLFreeStmt(SQLHSTMT hstmt, SQLUSMALLINT fOption, int force);
      72                 : static SQLRETURN SQL_API _SQLFreeDesc(SQLHDESC hdesc);
      73                 : static SQLRETURN SQL_API _SQLExecute(TDS_STMT * stmt);
      74                 : static SQLRETURN SQL_API _SQLGetConnectAttr(SQLHDBC hdbc, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER BufferLength,
      75                 :                                             SQLINTEGER * StringLength);
      76                 : static SQLRETURN SQL_API _SQLSetConnectAttr(SQLHDBC hdbc, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
      77                 : static SQLRETURN SQL_API _SQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
      78                 : static SQLRETURN SQL_API _SQLGetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER BufferLength,
      79                 :                                          SQLINTEGER * StringLength);
      80                 : static SQLRETURN SQL_API _SQLColAttribute(SQLHSTMT hstmt, SQLUSMALLINT icol, SQLUSMALLINT fDescType, SQLPOINTER rgbDesc,
      81                 :                                           SQLSMALLINT cbDescMax, SQLSMALLINT FAR * pcbDesc, SQLLEN FAR * pfDesc);
      82                 : static SQLRETURN SQL_API _SQLFetch(TDS_STMT * stmt);
      83                 : static int query_timeout_cancel(void *param, unsigned int total_timeout);
      84                 : static SQLRETURN odbc_populate_ird(TDS_STMT * stmt);
      85                 : static int odbc_errmsg_handler(const TDSCONTEXT * ctx, TDSSOCKET * tds, TDSMESSAGE * msg);
      86                 : static void odbc_log_unimplemented_type(const char function_name[], int fType);
      87                 : static void odbc_upper_column_names(TDS_STMT * stmt);
      88                 : static void odbc_col_setname(TDS_STMT * stmt, int colpos, const char *name);
      89                 : static SQLRETURN odbc_stat_execute(TDS_STMT * stmt, const char *begin, int nparams, ...);
      90                 : static SQLRETURN odbc_free_dynamic(TDS_STMT * stmt);
      91                 : static SQLSMALLINT odbc_swap_datetime_sql_type(SQLSMALLINT sql_type);
      92                 : static int odbc_process_tokens(TDS_STMT * stmt, unsigned flag);
      93                 : 
      94                 : #if ENABLE_EXTRA_CHECKS
      95                 : static void odbc_ird_check(TDS_STMT * stmt);
      96                 : 
      97                 : #define IRD_CHECK odbc_ird_check(stmt)
      98                 : #else
      99                 : #define IRD_CHECK
     100                 : #endif
     101                 : 
     102                 : /**
     103                 :  * \defgroup odbc_api ODBC API
     104                 :  * Functions callable by \c ODBC client programs
     105                 :  */
     106                 : 
     107                 : 
     108                 : /* utils to check handles */
     109                 : #define CHECK_HDBC  if ( SQL_NULL_HDBC  == hdbc || !IS_HDBC(hdbc) ) return SQL_INVALID_HANDLE;
     110                 : #define CHECK_HSTMT if ( SQL_NULL_HSTMT == hstmt || !IS_HSTMT(hstmt) ) return SQL_INVALID_HANDLE;
     111                 : #define CHECK_HENV  if ( SQL_NULL_HENV  == henv || !IS_HENV(henv) ) return SQL_INVALID_HANDLE;
     112                 : #define CHECK_HDESC if ( SQL_NULL_HDESC == hdesc || !IS_HDESC(hdesc) ) return SQL_INVALID_HANDLE;
     113                 : 
     114                 : #define INIT_HSTMT \
     115                 :         TDS_STMT *stmt = (TDS_STMT*)hstmt; \
     116                 :         CHECK_HSTMT; \
     117                 :         CHECK_STMT_EXTRA(stmt); \
     118                 :         odbc_errs_reset(&stmt->errs); \
     119                 : 
     120                 : #define INIT_HDBC \
     121                 :         TDS_DBC *dbc = (TDS_DBC*)hdbc; \
     122                 :         CHECK_HDBC; \
     123                 :         CHECK_DBC_EXTRA(dbc); \
     124                 :         odbc_errs_reset(&dbc->errs); \
     125                 : 
     126                 : #define INIT_HENV \
     127                 :         TDS_ENV *env = (TDS_ENV*)henv; \
     128                 :         CHECK_HENV; \
     129                 :         CHECK_ENV_EXTRA(env); \
     130                 :         odbc_errs_reset(&env->errs); \
     131                 : 
     132                 : #define INIT_HDESC \
     133                 :         TDS_DESC *desc = (TDS_DESC*)hdesc; \
     134                 :         CHECK_HDESC; \
     135                 :         CHECK_DESC_EXTRA(desc); \
     136                 :         odbc_errs_reset(&desc->errs); \
     137                 : 
     138                 : #define IS_VALID_LEN(len) ((len) >= 0 || (len) == SQL_NTS || (len) == SQL_NULL_DATA)
     139                 : 
     140                 : /*
     141                 :  * Note: I *HATE* hungarian notation, it has to be the most idiotic thing
     142                 :  * I've ever seen. So, you will note it is avoided other than in the function
     143                 :  * declarations. "Gee, let's make our code totally hard to read and they'll
     144                 :  * beg for GUI tools"
     145                 :  * Bah!
     146                 :  */
     147                 : 
     148                 : static void
     149                 : odbc_col_setname(TDS_STMT * stmt, int colpos, const char *name)
     150              85 : {
     151                 : #if ENABLE_EXTRA_CHECKS
     152                 :         TDSRESULTINFO *resinfo;
     153                 : #endif
     154                 : 
     155              85 :         IRD_CHECK;
     156                 : 
     157                 : #if ENABLE_EXTRA_CHECKS
     158              85 :         if (colpos > 0 && stmt->dbc->tds_socket != NULL && (resinfo = stmt->dbc->tds_socket->current_results) != NULL) {
     159              85 :                 if (colpos <= resinfo->num_cols) {
     160                 :                         /* no overflow possible, name is always shorter */
     161              85 :                         strcpy(resinfo->columns[colpos - 1]->column_name, name);
     162              85 :                         resinfo->columns[colpos - 1]->column_namelen = strlen(name);
     163                 :                 }
     164                 :         }
     165                 : #endif
     166                 : 
     167              85 :         if (colpos > 0 && colpos <= stmt->ird->header.sql_desc_count) {
     168              85 :                 --colpos;
     169              85 :                 tds_dstr_copy(&stmt->ird->records[colpos].sql_desc_label, name);
     170              85 :                 tds_dstr_copy(&stmt->ird->records[colpos].sql_desc_name, name);
     171                 :         }
     172              85 : }
     173                 : 
     174                 : /* spinellia@acm.org : copied shamelessly from change_database */
     175                 : static SQLRETURN
     176                 : change_autocommit(TDS_DBC * dbc, int state)
     177              22 : {
     178              22 :         TDSSOCKET *tds = dbc->tds_socket;
     179                 :         char query[80];
     180                 : 
     181                 :         /*
     182                 :          * We may not be connected yet and dbc->tds_socket
     183                 :          * may not initialized.
     184                 :          */
     185              22 :         if (tds) {
     186                 :                 /*
     187                 :                  * mssql: SET IMPLICIT_TRANSACTION ON
     188                 :                  * sybase: SET CHAINED ON
     189                 :                  */
     190                 : 
     191                 :                 /* implicit transactions are on if autocommit is off :-| */
     192              22 :                 if (TDS_IS_MSSQL(tds))
     193              11 :                         sprintf(query, "SET IMPLICIT_TRANSACTIONS %s", (state == SQL_AUTOCOMMIT_ON) ? "OFF" : "ON");
     194                 :                 else {
     195                 :                         /* Sybase, do not use SET CHAINED but emulate for compatility */
     196              11 :                         if (state == SQL_AUTOCOMMIT_ON)
     197               4 :                                 strcpy(query, "WHILE @@TRANCOUNT > 0 COMMIT");
     198                 :                         else
     199               7 :                                 strcpy(query, "BEGIN TRANSACTION");
     200                 :                 }
     201                 : 
     202              22 :                 tdsdump_log(TDS_DBG_INFO1, "change_autocommit: executing %s\n", query);
     203                 : 
     204              22 :                 if (tds_submit_query(tds, query) != TDS_SUCCEED) {
     205               0 :                         odbc_errs_add(&dbc->errs, "HY000", "Could not change transaction status");
     206               0 :                         ODBC_RETURN(dbc, SQL_ERROR);
     207                 :                 }
     208              22 :                 if (tds_process_simple_query(tds) != TDS_SUCCEED) {
     209               0 :                         odbc_errs_add(&dbc->errs, "HY000", "Could not change transaction status");
     210               0 :                         ODBC_RETURN(dbc, SQL_ERROR);
     211                 :                 }
     212              22 :                 dbc->attr.autocommit = state;
     213                 :         }
     214              22 :         ODBC_RETURN_(dbc);
     215                 : }
     216                 : 
     217                 : static SQLRETURN
     218                 : change_database(TDS_DBC * dbc, char *database, int database_len)
     219               0 : {
     220               0 :         TDSSOCKET *tds = dbc->tds_socket;
     221                 : 
     222                 :         /* 
     223                 :          * We may not be connected yet and dbc->tds_socket
     224                 :          * may not initialized.
     225                 :          */
     226               0 :         if (tds) {
     227                 :                 /* build query */
     228               0 :                 char *query = (char *) malloc(6 + tds_quote_id(tds, NULL, database, database_len));
     229                 : 
     230               0 :                 if (!query) {
     231               0 :                         odbc_errs_add(&dbc->errs, "HY001", NULL);
     232               0 :                         ODBC_RETURN(dbc, SQL_ERROR);
     233                 :                 }
     234               0 :                 strcpy(query, "USE ");
     235               0 :                 tds_quote_id(tds, query + 4, database, database_len);
     236                 : 
     237                 : 
     238               0 :                 tdsdump_log(TDS_DBG_INFO1, "change_database: executing %s\n", query);
     239                 : 
     240               0 :                 if (tds_submit_query(tds, query) != TDS_SUCCEED) {
     241               0 :                         free(query);
     242               0 :                         odbc_errs_add(&dbc->errs, "HY000", "Could not change database");
     243               0 :                         ODBC_RETURN(dbc, SQL_ERROR);
     244                 :                 }
     245               0 :                 free(query);
     246               0 :                 if (tds_process_simple_query(tds) != TDS_SUCCEED) {
     247               0 :                         odbc_errs_add(&dbc->errs, "HY000", "Could not change database");
     248               0 :                         ODBC_RETURN(dbc, SQL_ERROR);
     249                 :                 }
     250                 :         }
     251               0 :         ODBC_RETURN_(dbc);
     252                 : }
     253                 : 
     254                 : static void
     255                 : odbc_env_change(TDSSOCKET * tds, int type, char *oldval, char *newval)
     256             410 : {
     257                 :         TDS_DBC *dbc;
     258                 : 
     259             410 :         if (tds == NULL) {
     260               0 :                 return;
     261                 :         }
     262             410 :         dbc = (TDS_DBC *) tds->parent;
     263             410 :         if (!dbc)
     264               0 :                 return;
     265                 : 
     266             410 :         switch (type) {
     267                 :         case TDS_ENV_DATABASE:
     268             226 :                 tds_dstr_copy(&dbc->attr.current_catalog, newval);
     269             226 :                 break;
     270                 :         case TDS_ENV_PACKSIZE:
     271              92 :                 dbc->attr.packet_size = atoi(newval);
     272                 :                 break;
     273                 :         }
     274                 : }
     275                 : 
     276                 : static SQLRETURN
     277                 : odbc_connect(TDS_DBC * dbc, TDSCONNECTION * connection)
     278              94 : {
     279              94 :         TDS_ENV *env = dbc->env;
     280                 :         TDSSOCKET *tds;
     281                 : 
     282              94 :         dbc->tds_socket = tds = tds_alloc_socket(env->tds_ctx, 512);
     283              94 :         if (!tds) {
     284               0 :                 odbc_errs_add(&dbc->errs, "HY001", NULL);
     285               0 :                 ODBC_RETURN(dbc, SQL_ERROR);
     286                 :         }
     287              94 :         tds_set_parent(tds, (void *) dbc);
     288                 : 
     289                 :         /* Set up our environment change hook */
     290              94 :         tds->env_chg_func = odbc_env_change;
     291                 : 
     292              94 :         tds_fix_connection(connection);
     293                 : 
     294              94 :         connection->connect_timeout = dbc->attr.connection_timeout;
     295                 : 
     296              94 :         tds->query_timeout_func = query_timeout_cancel;
     297              94 :         tds->query_timeout_param = dbc;
     298              94 :         tds->query_timeout = dbc->attr.connection_timeout;
     299                 : 
     300              94 :         if (tds_connect(tds, connection) == TDS_FAIL) {
     301               2 :                 tds_free_socket(tds);
     302               2 :                 dbc->tds_socket = NULL;
     303               2 :                 odbc_errs_add(&dbc->errs, "08001", NULL);
     304               2 :                 ODBC_RETURN(dbc, SQL_ERROR);
     305                 :         }
     306              92 :         tds->query_timeout_func = NULL;
     307              92 :         tds->query_timeout_param = NULL;
     308                 : 
     309                 :         /* this overwrite any error arrived (wanted behavior, Sybase return error for conversion errors) */
     310              92 :         ODBC_RETURN(dbc, SQL_SUCCESS);
     311                 : }
     312                 : 
     313                 : SQLRETURN SQL_API
     314                 : SQLDriverConnect(SQLHDBC hdbc, SQLHWND hwnd, SQLCHAR FAR * szConnStrIn, SQLSMALLINT cbConnStrIn, SQLCHAR FAR * szConnStrOut,
     315                 :                  SQLSMALLINT cbConnStrOutMax, SQLSMALLINT FAR * pcbConnStrOut, SQLUSMALLINT fDriverCompletion)
     316               8 : {
     317                 :         TDSCONNECTION *connection;
     318               8 :         int conlen = odbc_get_string_size(cbConnStrIn, szConnStrIn);
     319                 : 
     320               8 :         INIT_HDBC;
     321                 : 
     322                 : #ifdef TDS_NO_DM
     323                 :         /* Check string length */
     324               8 :         if (!IS_VALID_LEN(conlen) || conlen == 0) {
     325               0 :                 odbc_errs_add(&dbc->errs, "HY090", NULL);
     326               0 :                 ODBC_RETURN(dbc, SQL_ERROR);
     327                 :         }
     328                 : 
     329                 :         /* Check completion param */
     330               8 :         switch (fDriverCompletion) {
     331                 :         case SQL_DRIVER_NOPROMPT:
     332                 :         case SQL_DRIVER_COMPLETE:
     333                 :         case SQL_DRIVER_PROMPT:
     334                 :         case SQL_DRIVER_COMPLETE_REQUIRED:
     335                 :                 break;
     336                 :         default:
     337               0 :                 odbc_errs_add(&dbc->errs, "HY110", NULL);
     338               0 :                 ODBC_RETURN(dbc, SQL_ERROR);
     339                 :         }
     340                 : #endif
     341                 : 
     342               8 :         connection = tds_alloc_connection(dbc->env->tds_ctx->locale);
     343               8 :         if (!connection) {
     344               0 :                 odbc_errs_add(&dbc->errs, "HY001", NULL);
     345               0 :                 ODBC_RETURN(dbc, SQL_ERROR);
     346                 :         }
     347                 : 
     348                 :         /* parse the DSN string */
     349               8 :         odbc_parse_connect_string((const char *) szConnStrIn, (const char *) szConnStrIn + conlen, connection);
     350                 : 
     351                 :         /* add login info */
     352               8 :         if (hwnd) {
     353                 : #ifdef WIN32
     354                 :                 /* prompt for login information */
     355                 :                 if (!get_login_info(hwnd, connection)) {
     356                 :                         tds_free_connection(connection);
     357                 :                         odbc_errs_add(&dbc->errs, "08001", "User canceled login");
     358                 :                         ODBC_RETURN(dbc, SQL_ERROR);
     359                 :                 }
     360                 : #else
     361                 :                 /* we dont support a dialog box */
     362               0 :                 odbc_errs_add(&dbc->errs, "HYC00", NULL);
     363                 : #endif
     364                 :         }
     365                 : 
     366                 :         /* TODO what should be correct behavior for output string?? -- freddy77 */
     367               8 :         if (szConnStrOut)
     368               8 :                 odbc_set_string(szConnStrOut, cbConnStrOutMax, pcbConnStrOut, (const char *) szConnStrIn, conlen);
     369                 : 
     370               8 :         if (tds_dstr_isempty(&connection->server_name)) {
     371               0 :                 tds_free_connection(connection);
     372               0 :                 odbc_errs_add(&dbc->errs, "IM007", "Could not find Servername or server parameter");
     373               0 :                 ODBC_RETURN(dbc, SQL_ERROR);
     374                 :         }
     375                 : 
     376               8 :         if (tds_dstr_isempty(&connection->user_name)) {
     377               0 :                 tds_free_connection(connection);
     378               0 :                 odbc_errs_add(&dbc->errs, "IM007", "Could not find UID parameter");
     379               0 :                 ODBC_RETURN(dbc, SQL_ERROR);
     380                 :         }
     381                 : 
     382               8 :         if (odbc_connect(dbc, connection) != SQL_SUCCESS) {
     383               2 :                 tds_free_connection(connection);
     384               2 :                 ODBC_RETURN_(dbc);
     385                 :         }
     386                 : 
     387               6 :         tds_free_connection(connection);
     388               6 :         ODBC_RETURN_(dbc);
     389                 : }
     390                 : 
     391                 : #if 0
     392                 : SQLRETURN SQL_API
     393                 : SQLBrowseConnect(SQLHDBC hdbc, SQLCHAR FAR * szConnStrIn, SQLSMALLINT cbConnStrIn, SQLCHAR FAR * szConnStrOut,
     394                 :                  SQLSMALLINT cbConnStrOutMax, SQLSMALLINT FAR * pcbConnStrOut)
     395                 : {
     396                 :         INIT_HDBC;
     397                 :         odbc_errs_add(&dbc->errs, "HYC00", "SQLBrowseConnect: function not implemented");
     398                 :         ODBC_RETURN(dbc, SQL_ERROR);
     399                 : }
     400                 : #endif
     401                 : 
     402                 : SQLRETURN SQL_API
     403                 : SQLColumnPrivileges(SQLHSTMT hstmt, SQLCHAR FAR * szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR FAR * szSchemaName,
     404                 :                     SQLSMALLINT cbSchemaName, SQLCHAR FAR * szTableName, SQLSMALLINT cbTableName, SQLCHAR FAR * szColumnName,
     405                 :                     SQLSMALLINT cbColumnName)
     406               0 : {
     407                 :         int retcode;
     408                 : 
     409               0 :         INIT_HSTMT;
     410                 : 
     411               0 :         retcode =
     412                 :                 odbc_stat_execute(stmt, "sp_column_privileges ", 4, "O@table_qualifier", szCatalogName, cbCatalogName,
     413                 :                                   "O@table_owner", szSchemaName, cbSchemaName, "O@table_name", szTableName, cbTableName,
     414                 :                                   "P@column_name", szColumnName, cbColumnName);
     415               0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     416               0 :                 odbc_col_setname(stmt, 1, "TABLE_CAT");
     417               0 :                 odbc_col_setname(stmt, 2, "TABLE_SCHEM");
     418                 :         }
     419               0 :         ODBC_RETURN_(stmt);
     420                 : }
     421                 : 
     422                 : #if 0
     423                 : SQLRETURN SQL_API
     424                 : SQLDescribeParam(SQLHSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT FAR * pfSqlType, SQLUINTEGER FAR * pcbParamDef,
     425                 :                  SQLSMALLINT FAR * pibScale, SQLSMALLINT FAR * pfNullable)
     426                 : {
     427                 :         INIT_HSTMT;
     428                 :         odbc_errs_add(&stmt->errs, "HYC00", "SQLDescribeParam: function not implemented");
     429                 :         ODBC_RETURN(stmt, SQL_ERROR);
     430                 : }
     431                 : #endif
     432                 : 
     433                 : SQLRETURN SQL_API
     434                 : SQLExtendedFetch(SQLHSTMT hstmt, SQLUSMALLINT fFetchType, SQLLEN irow, SQLULEN FAR * pcrow, SQLUSMALLINT FAR * rgfRowStatus)
     435               0 : {
     436                 :         SQLRETURN ret;
     437                 :         SQLULEN * tmp_rows;
     438                 :         SQLUSMALLINT * tmp_status;
     439                 :         SQLULEN tmp_size;
     440                 :         SQLLEN * tmp_offset;
     441                 : 
     442               0 :         INIT_HSTMT;
     443                 : 
     444                 :         /* still we do not support cursors and scrolling */
     445                 :         /* TODO cursors */
     446               0 :         if (fFetchType != SQL_FETCH_NEXT) {
     447               0 :                 odbc_errs_add(&stmt->errs, "HY106", NULL);
     448               0 :                 ODBC_RETURN(stmt, SQL_ERROR);
     449                 :         }
     450                 : 
     451                 :         /* save and change IRD/ARD state */
     452               0 :         tmp_rows = stmt->ird->header.sql_desc_rows_processed_ptr;
     453               0 :         stmt->ird->header.sql_desc_rows_processed_ptr = pcrow;
     454               0 :         tmp_status = stmt->ird->header.sql_desc_array_status_ptr;
     455               0 :         stmt->ird->header.sql_desc_array_status_ptr = rgfRowStatus;
     456               0 :         tmp_size = stmt->ard->header.sql_desc_array_size;
     457               0 :         stmt->ard->header.sql_desc_array_size = stmt->sql_rowset_size;
     458               0 :         tmp_offset = stmt->ard->header.sql_desc_bind_offset_ptr;
     459               0 :         stmt->ard->header.sql_desc_bind_offset_ptr = NULL;
     460                 : 
     461                 :         /* TODO errors are sligthly different ... perhaps it's better to leave DM do this job ?? */
     462               0 :         ret = _SQLFetch(stmt);
     463                 : 
     464                 :         /* restore IRD/ARD */
     465               0 :         stmt->ird->header.sql_desc_rows_processed_ptr = tmp_rows;
     466               0 :         stmt->ird->header.sql_desc_array_status_ptr = tmp_status;
     467               0 :         stmt->ard->header.sql_desc_array_size = tmp_size;
     468               0 :         stmt->ard->header.sql_desc_bind_offset_ptr = tmp_offset;
     469                 : 
     470               0 :         ODBC_RETURN(stmt, ret);
     471                 : }
     472                 : 
     473                 : SQLRETURN SQL_API
     474                 : SQLForeignKeys(SQLHSTMT hstmt, SQLCHAR FAR * szPkCatalogName, SQLSMALLINT cbPkCatalogName, SQLCHAR FAR * szPkSchemaName,
     475                 :                SQLSMALLINT cbPkSchemaName, SQLCHAR FAR * szPkTableName, SQLSMALLINT cbPkTableName, SQLCHAR FAR * szFkCatalogName,
     476                 :                SQLSMALLINT cbFkCatalogName, SQLCHAR FAR * szFkSchemaName, SQLSMALLINT cbFkSchemaName, SQLCHAR FAR * szFkTableName,
     477                 :                SQLSMALLINT cbFkTableName)
     478               0 : {
     479                 :         int retcode;
     480                 : 
     481               0 :         INIT_HSTMT;
     482                 : 
     483               0 :         retcode =
     484                 :                 odbc_stat_execute(stmt, "sp_fkeys ", 6, "O@pktable_qualifier", szPkCatalogName, cbPkCatalogName, "O@pktable_owner",
     485                 :                                   szPkSchemaName, cbPkSchemaName, "O@pktable_name", szPkTableName, cbPkTableName,
     486                 :                                   "O@fktable_qualifier", szFkCatalogName, cbFkCatalogName, "O@fktable_owner", szFkSchemaName,
     487                 :                                   cbFkSchemaName, "O@fktable_name", szFkTableName, cbFkTableName);
     488               0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     489               0 :                 odbc_col_setname(stmt, 1, "PKTABLE_CAT");
     490               0 :                 odbc_col_setname(stmt, 2, "PKTABLE_SCHEM");
     491               0 :                 odbc_col_setname(stmt, 5, "FKTABLE_CAT");
     492               0 :                 odbc_col_setname(stmt, 6, "FKTABLE_SCHEM");
     493                 :         }
     494               0 :         ODBC_RETURN_(stmt);
     495                 : }
     496                 : 
     497                 : SQLRETURN SQL_API
     498                 : SQLMoreResults(SQLHSTMT hstmt)
     499             348 : {
     500                 :         TDSSOCKET *tds;
     501                 :         TDS_INT result_type;
     502                 :         int tdsret;
     503             348 :         int in_row = 0;
     504                 :         SQLUSMALLINT param_status;
     505                 :         int token_flags;
     506                 : 
     507             348 :         INIT_HSTMT;
     508                 : 
     509             348 :         tds = stmt->dbc->tds_socket;
     510                 : 
     511                 :         /* we already readed all results... */
     512                 :         /* TODO cursor */
     513             348 :         if (stmt->dbc->current_statement != stmt)
     514              57 :                 ODBC_RETURN(stmt, SQL_NO_DATA);
     515                 : 
     516             291 :         stmt->row_count = TDS_NO_COUNT;
     517             291 :         stmt->special_row = 0;
     518                 : 
     519                 :         /* TODO this code is TOO similar to _SQLExecute, merge it - freddy77 */
     520                 :         /* try to go to the next recordset */
     521             291 :         if (stmt->row_status == IN_COMPUTE_ROW) {
     522                 :                 /* FIXME doesn't seem so fine ... - freddy77 */
     523               4 :                 tds_process_tokens(stmt->dbc->tds_socket, &result_type, NULL, TDS_TOKEN_TRAILING);
     524               4 :                 stmt->row_status = IN_COMPUTE_ROW;
     525               4 :                 in_row = 1;
     526                 :         }
     527                 : 
     528             291 :         param_status = SQL_PARAM_SUCCESS;
     529             291 :         token_flags = (TDS_TOKEN_RESULTS & (~TDS_STOPAT_COMPUTE)) | TDS_RETURN_COMPUTE;
     530             291 :         if (stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3)
     531             132 :                 token_flags |= TDS_RETURN_MSG;
     532                 :         for (;;) {
     533             357 :                 result_type = odbc_process_tokens(stmt, token_flags);
     534             357 :                 switch (result_type) {
     535                 :                 case TDS_CMD_DONE:
     536             207 :                         if (stmt->dbc->current_statement == stmt)
     537             207 :                                 stmt->dbc->current_statement = NULL;
     538                 : #if 1 /* !UNIXODBC */
     539             207 :                         tds_free_all_results(tds);
     540                 : #endif
     541             207 :                         odbc_populate_ird(stmt);
     542             207 :                         if (stmt->row_count == TDS_NO_COUNT && !in_row) {
     543             205 :                                 stmt->row_status = NOT_IN_ROW;
     544             205 :                                 if (stmt->next_row_count != TDS_NO_COUNT) {
     545              24 :                                         stmt->row_count = stmt->next_row_count;
     546              24 :                                         stmt->row_status = PRE_NORMAL_ROW;
     547                 :                                 }
     548                 :                         }
     549             207 :                         stmt->next_row_count = TDS_NO_COUNT;
     550             207 :                         if (stmt->row_count == TDS_NO_COUNT && (stmt->errs.lastrc == SQL_SUCCESS || stmt->errs.lastrc == SQL_SUCCESS_WITH_INFO))
     551             183 :                                 ODBC_RETURN(stmt, SQL_NO_DATA);
     552              24 :                         ODBC_RETURN_(stmt);
     553                 : 
     554                 :                 case TDS_CMD_FAIL:
     555               0 :                         ODBC_RETURN(stmt, SQL_ERROR);
     556                 : 
     557                 :                 case TDS_COMPUTE_RESULT:
     558              12 :                         switch (stmt->row_status) {
     559                 :                         /* skip this recordset */
     560                 :                         case IN_COMPUTE_ROW:
     561                 :                                 /* TODO here we should set current_results to normal results */
     562               0 :                                 in_row = 1;
     563                 :                                 /* fall throgh */
     564                 :                         /* in normal row, put in compute status */
     565                 :                         case AFTER_COMPUTE_ROW:
     566                 :                         case IN_NORMAL_ROW:
     567                 :                         case PRE_NORMAL_ROW:
     568              12 :                                 stmt->row_status = IN_COMPUTE_ROW;
     569              12 :                                 odbc_populate_ird(stmt);
     570              12 :                                 ODBC_RETURN_(stmt);
     571                 :                         case NOT_IN_ROW:
     572                 :                                 /* this should never happen, protocol error */
     573               0 :                                 ODBC_RETURN(stmt, SQL_ERROR);
     574                 :                                 break;
     575                 :                         }
     576               0 :                         break;
     577                 : 
     578                 :                 case TDS_ROW_RESULT:
     579              33 :                         if (in_row || (stmt->row_status != IN_NORMAL_ROW && stmt->row_status != PRE_NORMAL_ROW)) {
     580              23 :                                 stmt->row_status = PRE_NORMAL_ROW;
     581              23 :                                 odbc_populate_ird(stmt);
     582              23 :                                 ODBC_RETURN_(stmt);
     583                 :                         }
     584                 :                         /* Skipping current result set's rows to access next resultset or proc's retval */
     585              10 :                         tdsret = tds_process_tokens(tds, &result_type, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_STOPAT_COMPUTE);
     586                 :                         /* TODO should we set in_row ?? */
     587              10 :                         if (tdsret == TDS_FAIL || tdsret == TDS_CANCELLED)
     588               0 :                                 ODBC_RETURN(stmt, SQL_ERROR);
     589              10 :                         break;
     590                 : 
     591                 :                 case TDS_DONE_RESULT:
     592                 :                 case TDS_DONEPROC_RESULT:
     593                 :                         /* FIXME here ??? */
     594              48 :                         if (!in_row)
     595              34 :                                 tds_free_all_results(tds);
     596              48 :                         switch (stmt->errs.lastrc) {
     597                 :                         case SQL_ERROR:
     598              10 :                                 param_status = SQL_PARAM_ERROR;
     599              10 :                                 break;
     600                 :                         case SQL_SUCCESS_WITH_INFO:
     601               2 :                                 param_status = SQL_PARAM_SUCCESS_WITH_INFO;
     602                 :                                 break;
     603                 :                         }
     604              48 :                         if (stmt->curr_param_row < stmt->num_param_rows) {
     605               8 :                                 if (stmt->ipd->header.sql_desc_array_status_ptr)
     606               8 :                                         stmt->ipd->header.sql_desc_array_status_ptr[stmt->curr_param_row] = param_status;
     607               8 :                                 ++stmt->curr_param_row;
     608               8 :                                 if (stmt->ipd->header.sql_desc_rows_processed_ptr)
     609               8 :                                         *stmt->ipd->header.sql_desc_rows_processed_ptr = stmt->curr_param_row;
     610                 :                         }
     611              48 :                         if (stmt->curr_param_row < stmt->num_param_rows) {
     612                 : #if 0
     613                 :                                 if (stmt->errs.lastrc == SQL_SUCCESS_WITH_INFO)
     614                 :                                         found_info = 1;
     615                 :                                 if (stmt->errs.lastrc == SQL_ERROR)
     616                 :                                         found_error = 1;
     617                 : #endif
     618               7 :                                 stmt->errs.lastrc = SQL_SUCCESS;
     619               7 :                                 param_status = SQL_PARAM_SUCCESS;
     620               7 :                                 break;
     621                 :                         }
     622              41 :                         odbc_populate_ird(stmt);
     623              41 :                         ODBC_RETURN_(stmt);
     624                 :                         break;
     625                 : 
     626                 :                         /*
     627                 :                          * TODO test flags ? check error and change result ? 
     628                 :                          * see also other DONEINPROC handle (below)
     629                 :                          */
     630                 :                 case TDS_DONEINPROC_RESULT:
     631               6 :                         if (in_row) {
     632               6 :                                 odbc_populate_ird(stmt);
     633               6 :                                 ODBC_RETURN_(stmt);
     634                 :                         }
     635                 :                         /* TODO perhaps it can be a problem if SET NOCOUNT ON, test it */
     636               0 :                         tds_free_all_results(tds);
     637               0 :                         odbc_populate_ird(stmt);
     638               0 :                         break;
     639                 : 
     640                 :                         /* do not stop at metadata, an error can follow... */
     641                 :                 case TDS_ROWFMT_RESULT:
     642              29 :                         if (in_row) {
     643               2 :                                 odbc_populate_ird(stmt);
     644               2 :                                 ODBC_RETURN_(stmt);
     645                 :                         }
     646              27 :                         stmt->row = 0;
     647              27 :                         stmt->row_count = TDS_NO_COUNT;
     648              27 :                         stmt->next_row_count = TDS_NO_COUNT;
     649                 :                         /* we expect a row */
     650              27 :                         stmt->row_status = PRE_NORMAL_ROW;
     651              27 :                         in_row = 1;
     652              27 :                         break;
     653                 : 
     654                 :                 case TDS_MSG_RESULT:
     655              22 :                         if (!in_row) {
     656              12 :                                 tds_free_all_results(tds);
     657              12 :                                 odbc_populate_ird(stmt);
     658                 :                         }
     659              22 :                         in_row = 1;
     660                 :                         break;
     661                 :                 }
     662              66 :         }
     663                 :         ODBC_RETURN(stmt, SQL_ERROR);
     664                 : }
     665                 : 
     666                 : SQLRETURN SQL_API
     667                 : SQLNativeSql(SQLHDBC hdbc, SQLCHAR FAR * szSqlStrIn, SQLINTEGER cbSqlStrIn, SQLCHAR FAR * szSqlStr, SQLINTEGER cbSqlStrMax,
     668                 :              SQLINTEGER FAR * pcbSqlStr)
     669               0 : {
     670               0 :         SQLRETURN ret = SQL_SUCCESS;
     671                 :         DSTR query;
     672                 : 
     673               0 :         INIT_HDBC;
     674                 : 
     675               0 :         tds_dstr_init(&query);
     676                 : 
     677                 : #ifdef TDS_NO_DM
     678               0 :         if (!szSqlStrIn || !IS_VALID_LEN(cbSqlStrIn)) {
     679               0 :                 odbc_errs_add(&dbc->errs, "HY009", NULL);
     680               0 :                 ODBC_RETURN(dbc, SQL_ERROR);
     681                 :         }
     682                 : #endif
     683                 : 
     684               0 :         if (!tds_dstr_copyn(&query, (const char *) szSqlStrIn, odbc_get_string_size(cbSqlStrIn, szSqlStrIn))) {
     685               0 :                 odbc_errs_add(&dbc->errs, "HY001", NULL);
     686               0 :                 ODBC_RETURN(dbc, SQL_ERROR);
     687                 :         }
     688                 : 
     689                 :         /* TODO support not null terminated in native_sql */
     690               0 :         native_sql(dbc, tds_dstr_cstr(&query));
     691                 : 
     692               0 :         ret = odbc_set_string_i(szSqlStr, cbSqlStrMax, pcbSqlStr, tds_dstr_cstr(&query), -1);
     693                 : 
     694               0 :         tds_dstr_free(&query);
     695                 : 
     696               0 :         ODBC_RETURN(dbc, ret);
     697                 : }
     698                 : 
     699                 : SQLRETURN SQL_API
     700                 : SQLNumParams(SQLHSTMT hstmt, SQLSMALLINT FAR * pcpar)
     701               0 : {
     702               0 :         INIT_HSTMT;
     703               0 :         *pcpar = stmt->param_count;
     704               0 :         ODBC_RETURN_(stmt);
     705                 : }
     706                 : 
     707                 : SQLRETURN SQL_API
     708                 : SQLParamOptions(SQLHSTMT hstmt, SQLULEN crow, SQLULEN FAR * pirow)
     709               0 : {
     710                 :         SQLRETURN res;
     711                 : 
     712                 :         /* emulate for ODBC 2 DM */
     713               0 :         res = _SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, pirow, 0);
     714               0 :         if (res != SQL_SUCCESS)
     715               0 :                 return res;
     716                 :         /* crow is converted back to SQLINTEGER in _SQLSetStmtAttr so the TDS_INTPTR cast is safe */
     717               0 :         return _SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) (TDS_INTPTR) crow, 0);
     718                 : }
     719                 : 
     720                 : SQLRETURN SQL_API
     721                 : SQLPrimaryKeys(SQLHSTMT hstmt, SQLCHAR FAR * szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR FAR * szSchemaName,
     722                 :                SQLSMALLINT cbSchemaName, SQLCHAR FAR * szTableName, SQLSMALLINT cbTableName)
     723               0 : {
     724                 :         int retcode;
     725                 : 
     726               0 :         INIT_HSTMT;
     727                 : 
     728               0 :         retcode =
     729                 :                 odbc_stat_execute(stmt, "sp_pkeys ", 3, "O@table_qualifier", szCatalogName, cbCatalogName, "O@table_owner",
     730                 :                                   szSchemaName, cbSchemaName, "O@table_name", szTableName, cbTableName);
     731               0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     732               0 :                 odbc_col_setname(stmt, 1, "TABLE_CAT");
     733               0 :                 odbc_col_setname(stmt, 2, "TABLE_SCHEM");
     734                 :         }
     735               0 :         ODBC_RETURN_(stmt);
     736                 : }
     737                 : 
     738                 : SQLRETURN SQL_API
     739                 : SQLProcedureColumns(SQLHSTMT hstmt, SQLCHAR FAR * szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR FAR * szSchemaName,
     740                 :                     SQLSMALLINT cbSchemaName, SQLCHAR FAR * szProcName, SQLSMALLINT cbProcName, SQLCHAR FAR * szColumnName,
     741                 :                     SQLSMALLINT cbColumnName)
     742               0 : {
     743                 :         int retcode;
     744                 : 
     745               0 :         INIT_HSTMT;
     746                 : 
     747               0 :         retcode =
     748                 :                 odbc_stat_execute(stmt, "sp_sproc_columns ", 4, "O@procedure_qualifier", szCatalogName, cbCatalogName,
     749                 :                                   "P@procedure_owner", szSchemaName, cbSchemaName, "P@procedure_name", szProcName, cbProcName,
     750                 :                                   "P@column_name", szColumnName, cbColumnName);
     751               0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     752               0 :                 odbc_col_setname(stmt, 1, "PROCEDURE_CAT");
     753               0 :                 odbc_col_setname(stmt, 2, "PROCEDURE_SCHEM");
     754               0 :                 odbc_col_setname(stmt, 8, "COLUMN_SIZE");
     755               0 :                 odbc_col_setname(stmt, 9, "BUFFER_LENGTH");
     756               0 :                 odbc_col_setname(stmt, 10, "DECIMAL_DIGITS");
     757               0 :                 odbc_col_setname(stmt, 11, "NUM_PREC_RADIX");
     758                 :         }
     759               0 :         ODBC_RETURN_(stmt);
     760                 : }
     761                 : 
     762                 : SQLRETURN SQL_API
     763                 : SQLProcedures(SQLHSTMT hstmt, SQLCHAR FAR * szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR FAR * szSchemaName,
     764                 :               SQLSMALLINT cbSchemaName, SQLCHAR FAR * szProcName, SQLSMALLINT cbProcName)
     765               0 : {
     766                 :         int retcode;
     767                 : 
     768               0 :         INIT_HSTMT;
     769                 : 
     770               0 :         retcode =
     771                 :                 odbc_stat_execute(stmt, "..sp_stored_procedures ", 3, "P@sp_name", szProcName, cbProcName, "P@sp_owner", szSchemaName,
     772                 :                                   cbSchemaName, "O@sp_qualifier", szCatalogName, cbCatalogName);
     773               0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     774               0 :                 odbc_col_setname(stmt, 1, "PROCEDURE_CAT");
     775               0 :                 odbc_col_setname(stmt, 2, "PROCEDURE_SCHEM");
     776                 :         }
     777               0 :         ODBC_RETURN_(stmt);
     778                 : }
     779                 : 
     780                 : #if 0
     781                 : SQLRETURN SQL_API
     782                 : SQLSetPos(SQLHSTMT hstmt, SQLUSMALLINT irow, SQLUSMALLINT fOption, SQLUSMALLINT fLock)
     783                 : {
     784                 :         INIT_HSTMT;
     785                 :         /* TODO cursors */
     786                 :         odbc_errs_add(&stmt->errs, "HYC00", "SQLSetPos: function not implemented");
     787                 :         ODBC_RETURN(stmt, SQL_ERROR);
     788                 : }
     789                 : #endif
     790                 : 
     791                 : SQLRETURN SQL_API
     792                 : SQLTablePrivileges(SQLHSTMT hstmt, SQLCHAR FAR * szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR FAR * szSchemaName,
     793                 :                    SQLSMALLINT cbSchemaName, SQLCHAR FAR * szTableName, SQLSMALLINT cbTableName)
     794               0 : {
     795                 :         int retcode;
     796                 : 
     797               0 :         INIT_HSTMT;
     798                 : 
     799               0 :         retcode =
     800                 :                 odbc_stat_execute(stmt, "sp_table_privileges ", 3, "O@table_qualifier", szCatalogName, cbCatalogName,
     801                 :                                   "P@table_owner", szSchemaName, cbSchemaName, "P@table_name", szTableName, cbTableName);
     802               0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     803               0 :                 odbc_col_setname(stmt, 1, "TABLE_CAT");
     804               0 :                 odbc_col_setname(stmt, 2, "TABLE_SCHEM");
     805                 :         }
     806               0 :         ODBC_RETURN_(stmt);
     807                 : }
     808                 : 
     809                 : #if (ODBCVER >= 0x0300)
     810                 : SQLRETURN SQL_API
     811                 : SQLSetEnvAttr(SQLHENV henv, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER StringLength)
     812              25 : {
     813              25 :         SQLINTEGER i_val = (SQLINTEGER) (TDS_INTPTR) Value;
     814                 : 
     815              25 :         INIT_HENV;
     816                 : 
     817              25 :         switch (Attribute) {
     818                 :         case SQL_ATTR_CONNECTION_POOLING:
     819                 :         case SQL_ATTR_CP_MATCH:
     820               0 :                 odbc_errs_add(&env->errs, "HYC00", NULL);
     821               0 :                 ODBC_RETURN(env, SQL_ERROR);
     822                 :                 break;
     823                 :         case SQL_ATTR_ODBC_VERSION:
     824              25 :                 switch (i_val) {
     825                 :                 case SQL_OV_ODBC3:
     826                 :                 case SQL_OV_ODBC2:
     827                 :                         break;
     828                 :                 default:
     829               0 :                         odbc_errs_add(&env->errs, "HY024", NULL);
     830               0 :                         ODBC_RETURN(env, SQL_ERROR);
     831                 :                 }
     832              25 :                 env->attr.odbc_version = i_val;
     833              25 :                 ODBC_RETURN_(env);
     834                 :                 break;
     835                 :         case SQL_ATTR_OUTPUT_NTS:
     836               0 :                 env->attr.output_nts = i_val;
     837                 :                 /* TODO - Make this really work */
     838               0 :                 env->attr.output_nts = SQL_TRUE;
     839               0 :                 ODBC_RETURN_(env);
     840                 :                 break;
     841                 :         }
     842               0 :         odbc_errs_add(&env->errs, "HY092", NULL);
     843               0 :         ODBC_RETURN(env, SQL_ERROR);
     844                 : }
     845                 : 
     846                 : SQLRETURN SQL_API
     847                 : SQLGetEnvAttr(SQLHENV henv, SQLINTEGER Attribute, SQLPOINTER Value, SQLINTEGER BufferLength, SQLINTEGER * StringLength)
     848               0 : {
     849                 :         size_t size;
     850                 :         void *src;
     851                 : 
     852               0 :         INIT_HENV;
     853                 : 
     854               0 :         switch (Attribute) {
     855                 :         case SQL_ATTR_CONNECTION_POOLING:
     856               0 :                 src = &env->attr.connection_pooling;
     857               0 :                 size = sizeof(env->attr.connection_pooling);
     858               0 :                 break;
     859                 :         case SQL_ATTR_CP_MATCH:
     860               0 :                 src = &env->attr.cp_match;
     861               0 :                 size = sizeof(env->attr.cp_match);
     862               0 :                 break;
     863                 :         case SQL_ATTR_ODBC_VERSION:
     864               0 :                 src = &env->attr.odbc_version;
     865               0 :                 size = sizeof(env->attr.odbc_version);
     866               0 :                 break;
     867                 :         case SQL_ATTR_OUTPUT_NTS:
     868                 :                 /* TODO handle output_nts flags */
     869               0 :                 env->attr.output_nts = SQL_TRUE;
     870               0 :                 src = &env->attr.output_nts;
     871               0 :                 size = sizeof(env->attr.output_nts);
     872               0 :                 break;
     873                 :         default:
     874               0 :                 odbc_errs_add(&env->errs, "HY092", NULL);
     875               0 :                 ODBC_RETURN(env, SQL_ERROR);
     876                 :                 break;
     877                 :         }
     878                 : 
     879               0 :         if (StringLength) {
     880               0 :                 *StringLength = size;
     881                 :         }
     882               0 :         memcpy(Value, src, size);
     883                 : 
     884               0 :         ODBC_RETURN_(env);
     885                 : }
     886                 : 
     887                 : #endif
     888                 : 
     889                 : static SQLRETURN
     890                 : _SQLBindParameter(SQLHSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT fParamType, SQLSMALLINT fCType, SQLSMALLINT fSqlType,
     891                 :                   SQLULEN cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLLEN cbValueMax, SQLLEN FAR * pcbValue)
     892             257 : {
     893                 :         TDS_DESC *apd, *ipd;
     894                 :         struct _drecord *drec;
     895                 :         SQLSMALLINT orig_apd_size, orig_ipd_size;
     896             257 :         int is_numeric = 0;
     897                 : 
     898             257 :         INIT_HSTMT;
     899                 : 
     900                 : #ifdef TDS_NO_DM
     901                 :         /* TODO - more error checking ...  XXX smurph */
     902                 : 
     903                 :         /* Check param type */
     904             257 :         switch (fParamType) {
     905                 :         case SQL_PARAM_INPUT:
     906                 :         case SQL_PARAM_INPUT_OUTPUT:
     907                 :         case SQL_PARAM_OUTPUT:
     908                 :                 break;
     909                 :         default:
     910               0 :                 odbc_errs_add(&stmt->errs, "HY105", NULL);
     911               0 :                 ODBC_RETURN(stmt, SQL_ERROR);
     912                 :         }
     913                 : 
     914                 :         /* Check max buffer length */
     915             257 :         if (cbValueMax < 0) {
     916               0 :                 odbc_errs_add(&stmt->errs, "HY090", NULL);
     917               0 :                 ODBC_RETURN(stmt, SQL_ERROR);
     918                 :         }
     919                 : #endif
     920                 : 
     921                 :         /* check cbColDef and ibScale */
     922             257 :         if (fSqlType == SQL_DECIMAL || fSqlType == SQL_NUMERIC) {
     923               8 :                 is_numeric = 1;
     924               8 :                 if (cbColDef < 1 || cbColDef > 38) {
     925               0 :                         odbc_errs_add(&stmt->errs, "HY104", "Invalid precision value");
     926               0 :                         ODBC_RETURN(stmt, SQL_ERROR);
     927                 :                 }
     928               8 :                 if (ibScale < 0 || ibScale > cbColDef) {
     929               0 :                         odbc_errs_add(&stmt->errs, "HY104", "Invalid scale value");
     930               0 :                         ODBC_RETURN(stmt, SQL_ERROR);
     931                 :                 }
     932                 :         }
     933                 : 
     934                 :         /* Check parameter number */
     935             257 :         if (ipar <= 0 || ipar > 4000) {
     936               0 :                 odbc_errs_add(&stmt->errs, "07009", NULL);
     937               0 :                 ODBC_RETURN(stmt, SQL_ERROR);
     938                 :         }
     939                 : 
     940                 :         /* fill APD related fields */
     941             257 :         apd = stmt->apd;
     942             257 :         orig_apd_size = apd->header.sql_desc_count;
     943             257 :         if (ipar > apd->header.sql_desc_count && desc_alloc_records(apd, ipar) != SQL_SUCCESS) {
     944               0 :                 odbc_errs_add(&stmt->errs, "HY001", NULL);
     945               0 :                 ODBC_RETURN(stmt, SQL_ERROR);
     946                 :         }
     947             257 :         drec = &apd->records[ipar - 1];
     948                 : 
     949             257 :         if (odbc_set_concise_c_type(fCType, drec, 0) != SQL_SUCCESS) {
     950              14 :                 desc_alloc_records(apd, orig_apd_size);
     951              14 :                 odbc_errs_add(&stmt->errs, "HY004", NULL);
     952              14 :                 ODBC_RETURN(stmt, SQL_ERROR);
     953                 :         }
     954                 : 
     955             243 :         stmt->need_reprepare = 1;
     956                 : 
     957                 :         /* TODO other types ?? handle SQL_C_DEFAULT */
     958             243 :         if (drec->sql_desc_type == SQL_C_CHAR || drec->sql_desc_type == SQL_C_BINARY)
     959              68 :                 drec->sql_desc_octet_length = cbValueMax;
     960             243 :         drec->sql_desc_indicator_ptr = pcbValue;
     961             243 :         drec->sql_desc_octet_length_ptr = pcbValue;
     962             243 :         drec->sql_desc_data_ptr = (char *) rgbValue;
     963                 : 
     964                 :         /* field IPD related fields */
     965             243 :         ipd = stmt->ipd;
     966             243 :         orig_ipd_size = ipd->header.sql_desc_count;
     967             243 :         if (ipar > ipd->header.sql_desc_count && desc_alloc_records(ipd, ipar) != SQL_SUCCESS) {
     968               0 :                 desc_alloc_records(apd, orig_apd_size);
     969               0 :                 odbc_errs_add(&stmt->errs, "HY001", NULL);
     970               0 :                 ODBC_RETURN(stmt, SQL_ERROR);
     971                 :         }
     972             243 :         drec = &ipd->records[ipar - 1];
     973                 : 
     974             243 :         drec->sql_desc_parameter_type = fParamType;
     975             243 :         if (odbc_set_concise_sql_type(fSqlType, drec, 0) != SQL_SUCCESS) {
     976               0 :                 desc_alloc_records(ipd, orig_ipd_size);
     977               0 :                 desc_alloc_records(apd, orig_apd_size);
     978               0 :                 odbc_errs_add(&stmt->errs, "HY004", NULL);
     979               0 :                 ODBC_RETURN(stmt, SQL_ERROR);
     980                 :         }
     981             243 :         if (is_numeric) {
     982               8 :                 drec->sql_desc_precision = cbColDef;
     983               8 :                 drec->sql_desc_scale = ibScale;
     984                 :         } else {
     985             235 :                 drec->sql_desc_length = cbColDef;
     986                 :         }
     987                 : 
     988             243 :         ODBC_RETURN_(stmt);
     989                 : }
     990                 : 
     991                 : SQLRETURN SQL_API
     992                 : SQLBindParameter(SQLHSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT fParamType, SQLSMALLINT fCType, SQLSMALLINT fSqlType,
     993                 :                  SQLULEN cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLLEN cbValueMax, SQLLEN FAR * pcbValue)
     994             257 : {
     995             257 :         return _SQLBindParameter(hstmt, ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, cbValueMax, pcbValue);
     996                 : }
     997                 : 
     998                 : /* compatibility with X/Open */
     999                 : SQLRETURN SQL_API
    1000                 : SQLBindParam(SQLHSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT fCType, SQLSMALLINT fSqlType, SQLULEN cbColDef, SQLSMALLINT ibScale,
    1001                 :              SQLPOINTER rgbValue, SQLLEN FAR * pcbValue)
    1002               0 : {
    1003               0 :         return _SQLBindParameter(hstmt, ipar, SQL_PARAM_INPUT, fCType, fSqlType, cbColDef, ibScale, rgbValue, 0, pcbValue);
    1004                 : }
    1005                 : 
    1006                 : #if (ODBCVER >= 0x0300)
    1007                 : SQLRETURN SQL_API
    1008                 : SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE * OutputHandle)
    1009              11 : {
    1010              11 :         switch (HandleType) {
    1011                 :         case SQL_HANDLE_STMT:
    1012              11 :                 return _SQLAllocStmt(InputHandle, OutputHandle);
    1013                 :                 break;
    1014                 :         case SQL_HANDLE_DBC:
    1015               0 :                 return _SQLAllocConnect(InputHandle, OutputHandle);
    1016                 :                 break;
    1017                 :         case SQL_HANDLE_ENV:
    1018               0 :                 return _SQLAllocEnv(OutputHandle);
    1019                 :                 break;
    1020                 :         case SQL_HANDLE_DESC:
    1021               0 :                 return _SQLAllocDesc(InputHandle, OutputHandle);
    1022                 :                 break;
    1023                 :         }
    1024               0 :         return SQL_ERROR;
    1025                 : }
    1026                 : #endif
    1027                 : 
    1028                 : static SQLRETURN SQL_API
    1029                 : _SQLAllocConnect(SQLHENV henv, SQLHDBC FAR * phdbc)
    1030              96 : {
    1031                 :         TDS_DBC *dbc;
    1032                 : 
    1033              96 :         INIT_HENV;
    1034                 : 
    1035              96 :         dbc = (TDS_DBC *) malloc(sizeof(TDS_DBC));
    1036              96 :         if (!dbc) {
    1037               0 :                 odbc_errs_add(&env->errs, "HY001", NULL);
    1038               0 :                 ODBC_RETURN(env, SQL_ERROR);
    1039                 :         }
    1040                 : 
    1041              96 :         memset(dbc, '\0', sizeof(TDS_DBC));
    1042                 : 
    1043              96 :         dbc->htype = SQL_HANDLE_DBC;
    1044              96 :         dbc->env = env;
    1045              96 :         tds_dstr_init(&dbc->server);
    1046              96 :         tds_dstr_init(&dbc->dsn);
    1047                 : 
    1048              96 :         dbc->attr.access_mode = SQL_MODE_READ_WRITE;
    1049              96 :         dbc->attr.async_enable = SQL_ASYNC_ENABLE_OFF;
    1050              96 :         dbc->attr.auto_ipd = SQL_FALSE;
    1051                 :         /*
    1052                 :          * spinellia@acm.org
    1053                 :          * after login is enabled autocommit
    1054                 :          */
    1055              96 :         dbc->attr.autocommit = SQL_AUTOCOMMIT_ON;
    1056              96 :         dbc->attr.connection_dead = SQL_CD_TRUE;     /* No connection yet */
    1057              96 :         dbc->attr.connection_timeout = 0;
    1058                 :         /* This is set in the environment change function */
    1059              96 :         tds_dstr_init(&dbc->attr.current_catalog);
    1060              96 :         dbc->attr.login_timeout = 0; /* TODO */
    1061              96 :         dbc->attr.metadata_id = SQL_FALSE;
    1062              96 :         dbc->attr.odbc_cursors = SQL_CUR_USE_IF_NEEDED;
    1063              96 :         dbc->attr.packet_size = 0;
    1064              96 :         dbc->attr.quite_mode = NULL; /* We don't support GUI dialogs yet */
    1065                 : #ifdef TDS_NO_DM
    1066              96 :         dbc->attr.trace = SQL_OPT_TRACE_OFF;
    1067              96 :         tds_dstr_init(&dbc->attr.tracefile);
    1068                 : #endif
    1069              96 :         tds_dstr_init(&dbc->attr.translate_lib);
    1070              96 :         dbc->attr.translate_option = 0;
    1071              96 :         dbc->attr.txn_isolation = SQL_TXN_READ_COMMITTED;
    1072                 : 
    1073              96 :         *phdbc = (SQLHDBC) dbc;
    1074                 : 
    1075              96 :         ODBC_RETURN_(env);
    1076                 : }
    1077                 : 
    1078                 : SQLRETURN SQL_API
    1079                 : SQLAllocConnect(SQLHENV henv, SQLHDBC FAR * phdbc)
    1080              96 : {
    1081              96 :         odbc_errs_reset(&((TDS_ENV *) henv)->errs);
    1082              96 :         return _SQLAllocConnect(henv, phdbc);
    1083                 : }
    1084                 : 
    1085                 : static SQLRETURN SQL_API
    1086                 : _SQLAllocEnv(SQLHENV FAR * phenv)
    1087              96 : {
    1088                 :         TDS_ENV *env;
    1089                 :         TDSCONTEXT *ctx;
    1090                 : 
    1091              96 :         env = (TDS_ENV *) malloc(sizeof(TDS_ENV));
    1092              96 :         if (!env)
    1093               0 :                 return SQL_ERROR;
    1094                 : 
    1095              96 :         memset(env, '\0', sizeof(TDS_ENV));
    1096                 : 
    1097              96 :         env->htype = SQL_HANDLE_ENV;
    1098              96 :         env->attr.odbc_version = SQL_OV_ODBC2;
    1099                 :         /* TODO use it */
    1100              96 :         env->attr.output_nts = SQL_TRUE;
    1101                 : 
    1102              96 :         ctx = tds_alloc_context(env);
    1103              96 :         if (!ctx) {
    1104               0 :                 free(env);
    1105               0 :                 return SQL_ERROR;
    1106                 :         }
    1107              96 :         env->tds_ctx = ctx;
    1108              96 :         ctx->msg_handler = odbc_errmsg_handler;
    1109              96 :         ctx->err_handler = odbc_errmsg_handler;
    1110                 : 
    1111                 :         /* ODBC has its own format */
    1112              96 :         if (ctx->locale->date_fmt)
    1113              96 :                 free(ctx->locale->date_fmt);
    1114              96 :         ctx->locale->date_fmt = strdup("%Y-%m-%d %H:%M:%S.%z");
    1115                 : 
    1116              96 :         *phenv = (SQLHENV) env;
    1117                 : 
    1118              96 :         return SQL_SUCCESS;
    1119                 : }
    1120                 : 
    1121                 : SQLRETURN SQL_API
    1122                 : SQLAllocEnv(SQLHENV FAR * phenv)
    1123              96 : {
    1124              96 :         return _SQLAllocEnv(phenv);
    1125                 : }
    1126                 : 
    1127                 : static SQLRETURN SQL_API
    1128                 : _SQLAllocDesc(SQLHDBC hdbc, SQLHDESC FAR * phdesc)
    1129               0 : {
    1130                 :         int i;
    1131                 : 
    1132               0 :         INIT_HDBC;
    1133                 : 
    1134               0 :         for (i = 0; i < TDS_MAX_APP_DESC; ++i) {
    1135               0 :                 if (dbc->uad[i] == NULL) {
    1136               0 :                         TDS_DESC *desc = desc_alloc(dbc, DESC_ARD, SQL_DESC_ALLOC_USER);
    1137               0 :                         if (desc == NULL) {
    1138               0 :                                 odbc_errs_add(&dbc->errs, "HY001", NULL);
    1139               0 :                                 ODBC_RETURN(dbc, SQL_ERROR);
    1140                 :                         }
    1141               0 :                         dbc->uad[i] = desc;
    1142               0 :                         *phdesc = (SQLHDESC) desc;
    1143               0 :                         ODBC_RETURN_(dbc);
    1144                 :                 }
    1145                 :         }
    1146                 : 
    1147               0 :         odbc_errs_add(&dbc->errs, "HY014", NULL);
    1148               0 :         ODBC_RETURN(dbc, SQL_ERROR);
    1149                 : }
    1150                 : 
    1151                 : static SQLRETURN SQL_API
    1152                 : _SQLAllocStmt(SQLHDBC hdbc, SQLHSTMT FAR * phstmt)
    1153             129 : {
    1154                 :         TDS_STMT *stmt;
    1155                 :         char *pstr;
    1156                 : 
    1157             129 :         INIT_HDBC;
    1158                 : 
    1159             129 :         stmt = (TDS_STMT *) malloc(sizeof(TDS_STMT));
    1160             129 :         if (!stmt) {
    1161               0 :                 odbc_errs_add(&dbc->errs, "HY001", NULL);
    1162               0 :                 ODBC_RETURN(dbc, SQL_ERROR);
    1163                 :         }
    1164             129 :         memset(stmt, '\0', sizeof(TDS_STMT));
    1165             129 :         tds_dstr_init(&stmt->cursor_name);
    1166                 : 
    1167             129 :         stmt->htype = SQL_HANDLE_STMT;
    1168             129 :         stmt->dbc = dbc;
    1169             129 :         pstr = NULL;
    1170             129 :         if (asprintf(&pstr, "C%lx", (unsigned long) stmt) < 0 || !tds_dstr_set(&stmt->cursor_name, pstr)) {
    1171               0 :                 free(stmt);
    1172               0 :                 if (pstr != NULL)
    1173               0 :                         free(pstr);
    1174               0 :                 odbc_errs_add(&dbc->errs, "HY001", NULL);
    1175               0 :                 ODBC_RETURN(dbc, SQL_ERROR);
    1176                 :         }
    1177                 :         /* do not free pstr tds_dstr_set do it if necessary */
    1178                 : 
    1179                 :         /* allocate descriptors */
    1180             129 :         stmt->ird = desc_alloc(stmt, DESC_IRD, SQL_DESC_ALLOC_AUTO);
    1181             129 :         stmt->ard = desc_alloc(stmt, DESC_ARD, SQL_DESC_ALLOC_AUTO);
    1182             129 :         stmt->ipd = desc_alloc(stmt, DESC_IPD, SQL_DESC_ALLOC_AUTO);
    1183             129 :         stmt->apd = desc_alloc(stmt, DESC_APD, SQL_DESC_ALLOC_AUTO);
    1184             129 :         if (!stmt->ird || !stmt->ard || !stmt->ipd || !stmt->apd) {
    1185               0 :                 tds_dstr_free(&stmt->cursor_name);
    1186               0 :                 desc_free(stmt->ird);
    1187               0 :                 desc_free(stmt->ard);
    1188               0 :                 desc_free(stmt->ipd);
    1189               0 :                 desc_free(stmt->apd);
    1190               0 :                 free(stmt);
    1191               0 :                 odbc_errs_add(&dbc->errs, "HY001", NULL);
    1192               0 :                 ODBC_RETURN(dbc, SQL_ERROR);
    1193                 :         }
    1194                 : 
    1195                 :         /* save original ARD and APD */
    1196             129 :         stmt->orig_apd = stmt->apd;
    1197