LCOV - code coverage report
Current view: top level - src/odbc - catalog.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 263 420 62.6 %
Date: 2026-11-07 23:58:45 Functions: 7 14 50.0 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 1998, 1999, 2000, 2001  Brian Bruns
       3             :  * Copyright (C) 2002-2012  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             :  * \file
      23             :  * ODBC catalog functions (SQLTables, SQLColumns and similar) and SQLGetTypeInfo
      24             :  */
      25             : 
      26             : #include <config.h>
      27             : 
      28             : #include <stdarg.h>
      29             : #include <stdio.h>
      30             : 
      31             : #if HAVE_STDLIB_H
      32             : #include <stdlib.h>
      33             : #endif /* HAVE_STDLIB_H */
      34             : 
      35             : #if HAVE_STRING_H
      36             : #include <string.h>
      37             : #endif /* HAVE_STRING_H */
      38             : 
      39             : #include <assert.h>
      40             : 
      41             : #include <freetds/utils.h>
      42             : #include <freetds/odbc.h>
      43             : #include <freetds/utils/string.h>
      44             : #include <freetds/utils/ascii.h>
      45             : #include <odbcss.h>
      46             : #include "sqlwparams.h"
      47             : 
      48             : static void odbc_upper_column_names(TDS_STMT * stmt);
      49             : static void odbc_col_setname(TDS_STMT * stmt, int colpos, const char *name);
      50             : static SQLRETURN odbc_stat_execute(TDS_STMT * stmt _WIDE, const char *begin, int nparams, ...);
      51             : 
      52             : static void
      53         516 : odbc_col_setname(TDS_STMT *stmt, int colpos, const char *name)
      54             : {
      55             : #if ENABLE_EXTRA_CHECKS
      56         516 :         odbc_ird_check(stmt);
      57             : 
      58         516 :         if (colpos > 0 && stmt->tds != NULL) {
      59         516 :                 TDSRESULTINFO *resinfo = stmt->tds->current_results;
      60             : 
      61         516 :                 if (resinfo != NULL && colpos <= resinfo->num_cols) {
      62             :                         /* no overflow possible, name is always shorter */
      63         516 :                         if (!tds_dstr_copy(&resinfo->columns[colpos - 1]->column_name, name))
      64           0 :                                 odbc_errs_add(&stmt->errs, "HY001", NULL);
      65         516 :                         tds_dstr_empty(&resinfo->columns[colpos - 1]->table_column_name);
      66             :                 }
      67             :         }
      68             : #endif
      69             : 
      70         516 :         if (colpos > 0 && colpos <= stmt->ird->header.sql_desc_count) {
      71         516 :                 --colpos;
      72         516 :                 if (!tds_dstr_copy(&stmt->ird->records[colpos].sql_desc_label, name)
      73         516 :                     || !tds_dstr_copy(&stmt->ird->records[colpos].sql_desc_name, name))
      74           0 :                         odbc_errs_add(&stmt->errs, "HY001", NULL);
      75             :         }
      76         516 : }
      77             : 
      78           0 : ODBC_FUNC(SQLColumnPrivileges, (P(SQLHSTMT, hstmt), PCHARIN(CatalogName, SQLSMALLINT), PCHARIN(SchemaName, SQLSMALLINT),
      79             :                                 PCHARIN(TableName, SQLSMALLINT), PCHARIN(ColumnName, SQLSMALLINT) WIDE))
      80             : {
      81             :         int retcode;
      82             : 
      83           0 :         ODBC_ENTER_HSTMT;
      84             : 
      85           0 :         retcode =
      86           0 :                 odbc_stat_execute(stmt _wide, "sp_column_privileges", 4, "O@table_qualifier", szCatalogName, cbCatalogName,
      87             :                                   "O@table_owner", szSchemaName, cbSchemaName, "O@table_name", szTableName, cbTableName,
      88             :                                   "P@column_name", szColumnName, cbColumnName);
      89           0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
      90           0 :                 odbc_col_setname(stmt, 1, "TABLE_CAT");
      91           0 :                 odbc_col_setname(stmt, 2, "TABLE_SCHEM");
      92             :         }
      93           0 :         ODBC_EXIT_(stmt);
      94             : }
      95             : 
      96           0 : ODBC_FUNC(SQLForeignKeys, (P(SQLHSTMT, hstmt), PCHARIN(PkCatalogName, SQLSMALLINT),
      97             :                            PCHARIN(PkSchemaName, SQLSMALLINT), PCHARIN(PkTableName, SQLSMALLINT),
      98             :                            PCHARIN(FkCatalogName, SQLSMALLINT), PCHARIN(FkSchemaName, SQLSMALLINT),
      99             :                            PCHARIN(FkTableName, SQLSMALLINT) WIDE))
     100             : {
     101             :         int retcode;
     102             : 
     103           0 :         ODBC_ENTER_HSTMT;
     104             : 
     105           0 :         retcode =
     106           0 :                 odbc_stat_execute(stmt _wide, "sp_fkeys", 6,
     107             :                                   "O@pktable_qualifier", szPkCatalogName, cbPkCatalogName,
     108             :                                   "O@pktable_owner", szPkSchemaName, cbPkSchemaName,
     109             :                                   "O@pktable_name", szPkTableName, cbPkTableName,
     110             :                                   "O@fktable_qualifier", szFkCatalogName, cbFkCatalogName,
     111             :                                   "O@fktable_owner", szFkSchemaName, cbFkSchemaName,
     112             :                                   "O@fktable_name", szFkTableName, cbFkTableName);
     113           0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     114           0 :                 odbc_col_setname(stmt, 1, "PKTABLE_CAT");
     115           0 :                 odbc_col_setname(stmt, 2, "PKTABLE_SCHEM");
     116           0 :                 odbc_col_setname(stmt, 5, "FKTABLE_CAT");
     117           0 :                 odbc_col_setname(stmt, 6, "FKTABLE_SCHEM");
     118             :         }
     119           0 :         ODBC_EXIT_(stmt);
     120             : }
     121             : 
     122           0 : ODBC_FUNC(SQLPrimaryKeys, (P(SQLHSTMT, hstmt), PCHARIN(CatalogName, SQLSMALLINT),
     123             :                            PCHARIN(SchemaName, SQLSMALLINT), PCHARIN(TableName, SQLSMALLINT) WIDE))
     124             : {
     125             :         int retcode;
     126             : 
     127           0 :         ODBC_ENTER_HSTMT;
     128             : 
     129           0 :         retcode =
     130           0 :                 odbc_stat_execute(stmt _wide, "sp_pkeys", 3, "O@table_qualifier", szCatalogName, cbCatalogName, "O@table_owner",
     131             :                                   szSchemaName, cbSchemaName, "O@table_name", szTableName, cbTableName);
     132           0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     133           0 :                 odbc_col_setname(stmt, 1, "TABLE_CAT");
     134           0 :                 odbc_col_setname(stmt, 2, "TABLE_SCHEM");
     135             :         }
     136           0 :         ODBC_EXIT_(stmt);
     137             : }
     138             : 
     139          20 : ODBC_FUNC(SQLProcedureColumns, (P(SQLHSTMT, hstmt), PCHARIN(CatalogName, SQLSMALLINT),
     140             :                                 PCHARIN(SchemaName, SQLSMALLINT), PCHARIN(ProcName, SQLSMALLINT),
     141             :                                 PCHARIN(ColumnName, SQLSMALLINT) WIDE))
     142             : {
     143             :         int retcode;
     144             : 
     145          20 :         ODBC_ENTER_HSTMT;
     146             : 
     147          20 :         retcode =
     148          20 :                 odbc_stat_execute(stmt _wide, "sp_sproc_columns", TDS_IS_MSSQL(stmt->dbc->tds_socket) ? 5 : 4,
     149             :                                   "O@procedure_qualifier", szCatalogName, cbCatalogName,
     150             :                                   "P@procedure_owner", szSchemaName, cbSchemaName, "P@procedure_name", szProcName, cbProcName,
     151             :                                   "P@column_name", szColumnName, cbColumnName, "V@ODBCVer", (char *) NULL, 0);
     152          20 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     153          10 :                 odbc_col_setname(stmt, 1, "PROCEDURE_CAT");
     154          10 :                 odbc_col_setname(stmt, 2, "PROCEDURE_SCHEM");
     155          10 :                 odbc_col_setname(stmt, 8, "COLUMN_SIZE");
     156          10 :                 odbc_col_setname(stmt, 9, "BUFFER_LENGTH");
     157          10 :                 odbc_col_setname(stmt, 10, "DECIMAL_DIGITS");
     158          10 :                 odbc_col_setname(stmt, 11, "NUM_PREC_RADIX");
     159          10 :                 if (TDS_IS_SYBASE(stmt->dbc->tds_socket))
     160           2 :                         stmt->special_row = ODBC_SPECIAL_PROCEDURECOLUMNS;
     161             :         }
     162          20 :         ODBC_EXIT_(stmt);
     163             : }
     164             : 
     165           0 : ODBC_FUNC(SQLProcedures, (P(SQLHSTMT, hstmt), PCHARIN(CatalogName, SQLSMALLINT),
     166             :                           PCHARIN(SchemaName, SQLSMALLINT), PCHARIN(ProcName, SQLSMALLINT) WIDE))
     167             : {
     168             :         int retcode;
     169             : 
     170           0 :         ODBC_ENTER_HSTMT;
     171             : 
     172           0 :         retcode =
     173           0 :                 odbc_stat_execute(stmt _wide, "..sp_stored_procedures", 3,
     174             :                                   "P@sp_name", szProcName, cbProcName,
     175             :                                   "P@sp_owner", szSchemaName, cbSchemaName, "O@sp_qualifier", szCatalogName, cbCatalogName);
     176           0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     177           0 :                 odbc_col_setname(stmt, 1, "PROCEDURE_CAT");
     178           0 :                 odbc_col_setname(stmt, 2, "PROCEDURE_SCHEM");
     179             :         }
     180           0 :         ODBC_EXIT_(stmt);
     181             : }
     182             : 
     183           0 : ODBC_FUNC(SQLTablePrivileges, (P(SQLHSTMT, hstmt), PCHARIN(CatalogName, SQLSMALLINT),
     184             :                                PCHARIN(SchemaName, SQLSMALLINT), PCHARIN(TableName, SQLSMALLINT) WIDE))
     185             : {
     186             :         int retcode;
     187             : 
     188           0 :         ODBC_ENTER_HSTMT;
     189             : 
     190           0 :         retcode =
     191           0 :                 odbc_stat_execute(stmt _wide, "sp_table_privileges", 3, "O@table_qualifier", szCatalogName, cbCatalogName,
     192             :                                   "P@table_owner", szSchemaName, cbSchemaName, "P@table_name", szTableName, cbTableName);
     193           0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     194           0 :                 odbc_col_setname(stmt, 1, "TABLE_CAT");
     195           0 :                 odbc_col_setname(stmt, 2, "TABLE_SCHEM");
     196             :         }
     197           0 :         ODBC_EXIT_(stmt);
     198             : }
     199             : 
     200             : /**
     201             :  * SQLColumns
     202             :  *
     203             :  * Return column information for a table or view. This is
     204             :  * mapped to a call to sp_columns which - lucky for us - returns
     205             :  * the exact result set we need.
     206             :  *
     207             :  * exec sp_columns [ @table_name = ] object
     208             :  *                 [ , [ @table_owner = ] owner ]
     209             :  *                 [ , [ @table_qualifier = ] qualifier ]
     210             :  *                 [ , [ @column_name = ] column ]
     211             :  *                 [ , [ @ODBCVer = ] ODBCVer ]
     212             :  *
     213             :  */
     214          40 : ODBC_FUNC(SQLColumns, (P(SQLHSTMT, hstmt), PCHARIN(CatalogName, SQLSMALLINT),   /* object_qualifier */
     215             :                        PCHARIN(SchemaName, SQLSMALLINT),        /* object_owner */
     216             :                        PCHARIN(TableName, SQLSMALLINT), /* object_name */
     217             :                        PCHARIN(ColumnName, SQLSMALLINT) /* column_name */
     218             :                        WIDE))
     219             : {
     220             :         int retcode;
     221          40 :         const char *proc = "sp_columns";
     222             : 
     223          40 :         ODBC_ENTER_HSTMT;
     224             : 
     225          40 :         if (odbc_get_string_size(cbCatalogName, szCatalogName _wide))
     226          20 :                 proc = "..sp_columns";
     227             : 
     228          40 :         retcode =
     229          40 :                 odbc_stat_execute(stmt _wide, proc, TDS_IS_MSSQL(stmt->dbc->tds_socket) ? 5 : 4,
     230             :                                   "P@table_name", szTableName, cbTableName, "P@table_owner", szSchemaName,
     231             :                                   cbSchemaName, "O@table_qualifier", szCatalogName, cbCatalogName, "P@column_name", szColumnName,
     232             :                                   cbColumnName, "V@ODBCVer", (char *) NULL, 0);
     233          40 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     234          20 :                 odbc_col_setname(stmt, 1, "TABLE_CAT");
     235          20 :                 odbc_col_setname(stmt, 2, "TABLE_SCHEM");
     236          20 :                 odbc_col_setname(stmt, 7, "COLUMN_SIZE");
     237          20 :                 odbc_col_setname(stmt, 8, "BUFFER_LENGTH");
     238          20 :                 odbc_col_setname(stmt, 9, "DECIMAL_DIGITS");
     239          20 :                 odbc_col_setname(stmt, 10, "NUM_PREC_RADIX");
     240          20 :                 if (TDS_IS_SYBASE(stmt->dbc->tds_socket))
     241           4 :                         stmt->special_row = ODBC_SPECIAL_COLUMNS;
     242             :         }
     243          40 :         ODBC_EXIT_(stmt);
     244             : }
     245             : 
     246             : static void
     247         336 : odbc_upper_column_names(TDS_STMT *stmt)
     248             : {
     249             :         int icol;
     250             :         TDS_DESC *ird;
     251             : 
     252             : #if ENABLE_EXTRA_CHECKS
     253             :         TDSSOCKET *tds;
     254             : 
     255         336 :         odbc_ird_check(stmt);
     256             : 
     257         336 :         tds = stmt->dbc->tds_socket;
     258         336 :         if (tds && tds->current_results) {
     259             :                 TDSRESULTINFO *const resinfo = tds->current_results;
     260             : 
     261        4684 :                 for (icol = 0; icol < resinfo->num_cols; ++icol) {
     262        4684 :                         TDSCOLUMN *colinfo = resinfo->columns[icol];
     263             : 
     264             :                         /* upper case */
     265        9368 :                         tds_ascii_strupr(tds_dstr_buf(&colinfo->column_name));
     266             :                 }
     267             :         }
     268             : #endif
     269             : 
     270         336 :         ird = stmt->ird;
     271        5356 :         for (icol = ird->header.sql_desc_count; --icol >= 0;) {
     272        4684 :                 struct _drecord *drec = &ird->records[icol];
     273             : 
     274             :                 /* upper case */
     275        9368 :                 tds_ascii_strupr(tds_dstr_buf(&drec->sql_desc_label));
     276        9368 :                 tds_ascii_strupr(tds_dstr_buf(&drec->sql_desc_name));
     277             :         }
     278         336 : }
     279             : 
     280             : SQLRETURN ODBC_PUBLIC ODBC_API
     281             : SQLGetTypeInfo(SQLHSTMT hstmt, SQLSMALLINT fSqlType)
     282             : {
     283             :         SQLRETURN res;
     284             :         TDSSOCKET *tds;
     285             :         TDS_INT result_type;
     286             :         TDS_INT compute_id;
     287         144 :         int varchar_pos = -1, n;
     288             :         static const char sql_templ_default[] = "sp_datatype_info %d";
     289         144 :         const char *sql_templ = sql_templ_default;
     290             :         char sql[sizeof(sql_templ_default) + 36];
     291             :         int odbc3;
     292             : 
     293         144 :         ODBC_ENTER_HSTMT;
     294             : 
     295         144 :         tdsdump_log(TDS_DBG_FUNC, "SQLGetTypeInfo(%p, %d)\n", hstmt, fSqlType);
     296             : 
     297         144 :         tds = stmt->dbc->tds_socket;
     298         144 :         odbc3 = (stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3);
     299             : 
     300         144 :         if (IS_TDS73_PLUS(tds->conn))
     301             :                 sql_templ = "sp_datatype_info_100 %d";
     302          88 :         else if (IS_TDS72_PLUS(tds->conn))
     303           0 :                 sql_templ = "sp_datatype_info_90 %d";
     304             : 
     305             :         /* For MSSQL6.5 and Sybase 11.9 sp_datatype_info work */
     306             :         /* TODO what about early Sybase products ? */
     307             :         /* TODO Does Sybase return all ODBC3 columns? Add them if not */
     308             :         /* TODO ODBC3 convert type to ODBC version 2 (date) */
     309         144 :         if (odbc3) {
     310          72 :                 if (TDS_IS_SYBASE(tds)) {
     311          16 :                         sprintf(sql, sql_templ, odbc_swap_datetime_sql_type(fSqlType, 0));
     312          16 :                         stmt->special_row = ODBC_SPECIAL_GETTYPEINFO;
     313             :                 } else {
     314          56 :                         sprintf(sql, sql_templ, fSqlType);
     315          56 :                         strcat(sql, ",3");
     316             :                 }
     317             :         } else {
     318             :                 /* MS ODBC translate SQL_TIMESTAMP to SQL_TYPE_TIMESTAMP even for ODBC 2 apps
     319             :                  * other DM just pass value straight.
     320             :                  * Convert always to version 2 to make version 2 datatype works with all DMs
     321             :                  */
     322          72 :                 sprintf(sql, sql_templ, odbc_swap_datetime_sql_type(fSqlType, 2));
     323             :         }
     324         144 :         if (SQL_SUCCESS != odbc_set_stmt_query(stmt, (ODBC_CHAR *) sql, strlen(sql) _wide0))
     325           0 :                 ODBC_EXIT(stmt, SQL_ERROR);
     326             : 
     327         148 : redo:
     328         148 :         res = odbc_SQLExecute(stmt);
     329             : 
     330         148 :         odbc_upper_column_names(stmt);
     331         148 :         if (odbc3) {
     332          74 :                 odbc_col_setname(stmt, 3, "COLUMN_SIZE");
     333          74 :                 odbc_col_setname(stmt, 11, "FIXED_PREC_SCALE");
     334          74 :                 odbc_col_setname(stmt, 12, "AUTO_UNIQUE_VALUE");
     335             :         }
     336             : 
     337             :         /* workaround for a mispelled column name in Sybase */
     338         148 :         if (TDS_IS_SYBASE(stmt->dbc->tds_socket) && !odbc3)
     339          18 :                 odbc_col_setname(stmt, 3, "PRECISION");
     340             : 
     341         148 :         if (TDS_IS_MSSQL(stmt->dbc->tds_socket) || fSqlType != SQL_VARCHAR || res != SQL_SUCCESS)
     342         140 :                 ODBC_EXIT(stmt, res);
     343             : 
     344             :         /*
     345             :          * Sybase return first nvarchar for char... and without length !!!
     346             :          * Some program use first entry so we discard all entry before varchar
     347             :          */
     348             :         n = 0;
     349          36 :         while (tds->current_results) {
     350             :                 TDSRESULTINFO *resinfo;
     351             :                 TDSCOLUMN *colinfo;
     352             :                 char *name;
     353             : 
     354             :                 /* if next is varchar leave next for SQLFetch */
     355          36 :                 if (n == (varchar_pos - 1))
     356             :                         break;
     357             : 
     358          32 :                 switch (tds_process_tokens(stmt->dbc->tds_socket, &result_type, &compute_id, TDS_STOPAT_ROWFMT | TDS_RETURN_ROW)) {
     359          28 :                 case TDS_SUCCESS:
     360          28 :                         if (result_type == TDS_ROW_RESULT)
     361             :                                 break;
     362             :                 case TDS_NO_MORE_RESULTS:
     363             :                         /* discard other tokens */
     364           4 :                         tds_process_simple_query(tds);
     365           4 :                         if (n >= varchar_pos && varchar_pos > 0)
     366             :                                 goto redo;
     367             :                         break;
     368           0 :                 case TDS_CANCELLED:
     369           0 :                         odbc_errs_add(&stmt->errs, "HY008", NULL);
     370           0 :                         res = SQL_ERROR;
     371           0 :                         break;
     372             :                 }
     373          28 :                 if (!tds->current_results)
     374             :                         break;
     375          28 :                 ++n;
     376             : 
     377          28 :                 resinfo = tds->current_results;
     378          28 :                 colinfo = resinfo->columns[0];
     379          28 :                 name = (char *) colinfo->column_data;
     380          28 :                 if (is_blob_col(colinfo))
     381           0 :                         name = (char *) ((TDSBLOB *) name)->textvalue;
     382             :                 /* skip nvarchar and sysname */
     383          28 :                 if (colinfo->column_cur_size == 7 && memcmp("varchar", name, 7) == 0) {
     384           4 :                         varchar_pos = n;
     385             :                 }
     386             :         }
     387           4 :         ODBC_EXIT(stmt, res);
     388             : }
     389             : 
     390             : #ifdef ENABLE_ODBC_WIDE
     391             : SQLRETURN ODBC_PUBLIC ODBC_API
     392             : SQLGetTypeInfoW(SQLHSTMT hstmt, SQLSMALLINT fSqlType)
     393             : {
     394          72 :         return SQLGetTypeInfo(hstmt, fSqlType);
     395             : }
     396             : #endif
     397             : 
     398           0 : ODBC_FUNC(SQLSpecialColumns, (P(SQLHSTMT, hstmt), P(SQLUSMALLINT, fColType), PCHARIN(CatalogName, SQLSMALLINT),
     399             :                               PCHARIN(SchemaName, SQLSMALLINT), PCHARIN(TableName, SQLSMALLINT),
     400             :                               P(SQLUSMALLINT, fScope), P(SQLUSMALLINT, fNullable) WIDE))
     401             : {
     402             :         int retcode;
     403             :         char nullable, scope, col_type;
     404             : 
     405           0 :         ODBC_ENTER_HSTMT;
     406             : 
     407           0 :         tdsdump_log(TDS_DBG_FUNC, "SQLSpecialColumns(%p, %d, %p, %d, %p, %d, %p, %d, %d, %d)\n",
     408             :                     hstmt, fColType, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szTableName, cbTableName,
     409             :                     fScope, fNullable);
     410             : 
     411             : #ifdef TDS_NO_DM
     412             :         /* Check column type */
     413           0 :         if (fColType != SQL_BEST_ROWID && fColType != SQL_ROWVER) {
     414           0 :                 odbc_errs_add(&stmt->errs, "HY097", NULL);
     415           0 :                 ODBC_EXIT_(stmt);
     416             :         }
     417             : 
     418             :         /* check our buffer lengths */
     419           0 :         if (!IS_VALID_LEN(cbCatalogName) || !IS_VALID_LEN(cbSchemaName) || !IS_VALID_LEN(cbTableName)) {
     420           0 :                 odbc_errs_add(&stmt->errs, "HY090", NULL);
     421           0 :                 ODBC_EXIT_(stmt);
     422             :         }
     423             : 
     424             :         /* Check nullable */
     425           0 :         if (fNullable != SQL_NO_NULLS && fNullable != SQL_NULLABLE) {
     426           0 :                 odbc_errs_add(&stmt->errs, "HY099", NULL);
     427           0 :                 ODBC_EXIT_(stmt);
     428             :         }
     429             : 
     430           0 :         if (!odbc_get_string_size(cbTableName, szTableName _wide)) {
     431           0 :                 odbc_errs_add(&stmt->errs, "HY009", "SQLSpecialColumns: The table name parameter is required");
     432           0 :                 ODBC_EXIT_(stmt);
     433             :         }
     434             : 
     435           0 :         switch (fScope) {
     436             :         case SQL_SCOPE_CURROW:
     437             :         case SQL_SCOPE_TRANSACTION:
     438             :         case SQL_SCOPE_SESSION:
     439             :                 break;
     440           0 :         default:
     441           0 :                 odbc_errs_add(&stmt->errs, "HY098", NULL);
     442           0 :                 ODBC_EXIT_(stmt);
     443             :         }
     444             : #endif
     445             : 
     446           0 :         if (fNullable == SQL_NO_NULLS)
     447           0 :                 nullable = 'O';
     448             :         else
     449           0 :                 nullable = 'U';
     450             : 
     451           0 :         if (fScope == SQL_SCOPE_CURROW)
     452           0 :                 scope = 'C';
     453             :         else
     454           0 :                 scope = 'T';
     455             : 
     456           0 :         if (fColType == SQL_BEST_ROWID)
     457           0 :                 col_type = 'R';
     458             :         else
     459           0 :                 col_type = 'V';
     460             : 
     461           0 :         retcode =
     462           0 :                 odbc_stat_execute(stmt _wide, "sp_special_columns", TDS_IS_MSSQL(stmt->dbc->tds_socket) ? 7 : 4, "O", szTableName,
     463             :                                   cbTableName, "O", szSchemaName, cbSchemaName, "O@qualifier", szCatalogName, cbCatalogName,
     464             :                                   "!@col_type", &col_type, 1, "!@scope", &scope, 1, "!@nullable", &nullable, 1,
     465             :                                   "V@ODBCVer", (char *) NULL, 0);
     466           0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     467           0 :                 odbc_col_setname(stmt, 5, "COLUMN_SIZE");
     468           0 :                 odbc_col_setname(stmt, 6, "BUFFER_LENGTH");
     469           0 :                 odbc_col_setname(stmt, 7, "DECIMAL_DIGITS");
     470           0 :                 if (TDS_IS_SYBASE(stmt->dbc->tds_socket))
     471           0 :                         stmt->special_row = ODBC_SPECIAL_SPECIALCOLUMNS;
     472             :         }
     473           0 :         ODBC_EXIT_(stmt);
     474             : }
     475             : 
     476           0 : ODBC_FUNC(SQLStatistics, (P(SQLHSTMT, hstmt), PCHARIN(CatalogName, SQLSMALLINT),
     477             :                           PCHARIN(SchemaName, SQLSMALLINT), PCHARIN(TableName, SQLSMALLINT), P(SQLUSMALLINT, fUnique),
     478             :                           P(SQLUSMALLINT, fAccuracy) WIDE))
     479             : {
     480             :         int retcode;
     481             :         char unique, accuracy;
     482             : 
     483           0 :         ODBC_ENTER_HSTMT;
     484             : 
     485           0 :         tdsdump_log(TDS_DBG_FUNC, "SQLStatistics(%p, %p, %d, %p, %d, %p, %d, %d, %d)\n",
     486             :                     hstmt, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szTableName, cbTableName, fUnique, fAccuracy);
     487             : 
     488             : #ifdef TDS_NO_DM
     489             :         /* check our buffer lengths */
     490           0 :         if (!IS_VALID_LEN(cbCatalogName) || !IS_VALID_LEN(cbSchemaName) || !IS_VALID_LEN(cbTableName)) {
     491           0 :                 odbc_errs_add(&stmt->errs, "HY090", NULL);
     492           0 :                 ODBC_EXIT_(stmt);
     493             :         }
     494             : 
     495             :         /* check our uniqueness value */
     496           0 :         if (fUnique != SQL_INDEX_UNIQUE && fUnique != SQL_INDEX_ALL) {
     497           0 :                 odbc_errs_add(&stmt->errs, "HY100", NULL);
     498           0 :                 ODBC_EXIT_(stmt);
     499             :         }
     500             : 
     501             :         /* check our accuracy value */
     502           0 :         if (fAccuracy != SQL_QUICK && fAccuracy != SQL_ENSURE) {
     503           0 :                 odbc_errs_add(&stmt->errs, "HY101", NULL);
     504           0 :                 ODBC_EXIT_(stmt);
     505             :         }
     506             : 
     507           0 :         if (!odbc_get_string_size(cbTableName, szTableName _wide)) {
     508           0 :                 odbc_errs_add(&stmt->errs, "HY009", NULL);
     509           0 :                 ODBC_EXIT_(stmt);
     510             :         }
     511             : #endif
     512             : 
     513           0 :         if (fAccuracy == SQL_ENSURE)
     514           0 :                 accuracy = 'E';
     515             :         else
     516           0 :                 accuracy = 'Q';
     517             : 
     518           0 :         if (fUnique == SQL_INDEX_UNIQUE)
     519           0 :                 unique = 'Y';
     520             :         else
     521           0 :                 unique = 'N';
     522             : 
     523           0 :         retcode =
     524           0 :                 odbc_stat_execute(stmt _wide, "sp_statistics", TDS_IS_MSSQL(stmt->dbc->tds_socket) ? 5 : 4, "O@table_qualifier",
     525             :                                   szCatalogName, cbCatalogName, "O@table_owner", szSchemaName, cbSchemaName, "O@table_name",
     526             :                                   szTableName, cbTableName, "!@is_unique", &unique, 1, "!@accuracy", &accuracy, 1);
     527           0 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     528           0 :                 odbc_col_setname(stmt, 1, "TABLE_CAT");
     529           0 :                 odbc_col_setname(stmt, 2, "TABLE_SCHEM");
     530           0 :                 odbc_col_setname(stmt, 8, "ORDINAL_POSITION");
     531           0 :                 odbc_col_setname(stmt, 10, "ASC_OR_DESC");
     532             :         }
     533           0 :         ODBC_EXIT_(stmt);
     534             : }
     535             : 
     536         128 : ODBC_FUNC(SQLTables, (P(SQLHSTMT, hstmt), PCHARIN(CatalogName, SQLSMALLINT),
     537             :                       PCHARIN(SchemaName, SQLSMALLINT), PCHARIN(TableName, SQLSMALLINT), PCHARIN(TableType, SQLSMALLINT) WIDE))
     538             : {
     539             :         int retcode;
     540         128 :         const char *proc = NULL;
     541             :         int wildcards;
     542             :         TDSSOCKET *tds;
     543         128 :         DSTR schema_name = DSTR_INITIALIZER;
     544         128 :         DSTR catalog_name = DSTR_INITIALIZER;
     545         128 :         DSTR table_name = DSTR_INITIALIZER;
     546         128 :         DSTR table_type = DSTR_INITIALIZER;
     547             : 
     548         128 :         ODBC_ENTER_HSTMT;
     549             : 
     550         128 :         tdsdump_log(TDS_DBG_FUNC, "SQLTables(%p, %p, %d, %p, %d, %p, %d, %p, %d)\n",
     551             :                     hstmt, szCatalogName, cbCatalogName, szSchemaName, cbSchemaName, szTableName, cbTableName,
     552             :                     szTableType, cbTableType);
     553             : 
     554         128 :         tds = stmt->dbc->tds_socket;
     555             : 
     556         128 :         if (!odbc_dstr_copy(stmt->dbc, &catalog_name, cbCatalogName, szCatalogName)
     557         128 :             || !odbc_dstr_copy(stmt->dbc, &schema_name, cbSchemaName, szSchemaName)
     558         128 :             || !odbc_dstr_copy(stmt->dbc, &table_name, cbTableName, szTableName)
     559         128 :             || !odbc_dstr_copy(stmt->dbc, &table_type, cbTableType, szTableType))
     560             :                 goto memory_error;
     561             : 
     562         128 :         if (cbTableName == SQL_NTS)
     563           0 :                 cbTableName = tds_dstr_len(&table_name);
     564         128 :         if (cbSchemaName == SQL_NTS)
     565           0 :                 cbSchemaName = tds_dstr_len(&schema_name);
     566             : 
     567             :         /* support wildcards on catalog (only odbc 3) */
     568         128 :         wildcards = 0;
     569         176 :         if (stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3 && stmt->dbc->attr.metadata_id == SQL_FALSE &&
     570         126 :             (strchr(tds_dstr_cstr(&catalog_name), '%') || strchr(tds_dstr_cstr(&catalog_name), '_')))
     571             :                 wildcards = 1;
     572             : 
     573         128 :         proc = "sp_tables";
     574         256 :         if (!tds_dstr_isempty(&catalog_name)) {
     575          18 :                 if (wildcards) {
     576             :                         /* if catalog specified and wildcards use sp_tableswc under mssql2k */
     577          18 :                         if (TDS_IS_MSSQL(tds) && tds->conn->product_version >= TDS_MS_VER(8, 0, 0)) {
     578          16 :                                 proc = "sp_tableswc";
     579          32 :                                 if (tds_dstr_isempty(&schema_name))
     580          16 :                                         if (!tds_dstr_copy(&schema_name, "%"))
     581             :                                                 goto memory_error;
     582             :                         }
     583             :                         /*
     584             :                          * TODO support wildcards on catalog even for Sybase
     585             :                          * first execute a select name from master..sysdatabases where name like catalog quoted
     586             :                          * build a db1..sp_tables args db2..sp_tables args ... query
     587             :                          * collapse results in a single recordset (how??)
     588             :                          */
     589             :                 } else {
     590             :                         /* if catalog specified and not wildcards use catatog on name (catalog..sp_tables) */
     591             :                         proc = "..sp_tables";
     592             :                 }
     593             :         }
     594             : 
     595             :         /* fix type if needed quoting it */
     596         256 :         if (!tds_dstr_isempty(&table_type)) {
     597          80 :                 int to_fix = 0;
     598          80 :                 int elements = 0;
     599         160 :                 const char *p = tds_dstr_cstr(&table_type);
     600          80 :                 const char *const end = p + tds_dstr_len(&table_type);
     601             : 
     602          30 :                 for (;;) {
     603         110 :                         const char *begin = p;
     604             : 
     605         110 :                         p = (const char *) memchr(p, ',', end - p);
     606         110 :                         if (!p)
     607          80 :                                 p = end;
     608         110 :                         ++elements;
     609         110 :                         if ((p - begin) < 2 || begin[0] != '\'' || p[-1] != '\'')
     610          50 :                                 to_fix = 1;
     611         110 :                         if (p >= end)
     612             :                                 break;
     613          30 :                         ++p;
     614             :                 }
     615             :                 /* fix it */
     616          80 :                 tdsdump_log(TDS_DBG_INFO1, "to_fix %d elements %d\n", to_fix, elements);
     617          80 :                 if (to_fix) {
     618             :                         char *dst, *type;
     619             : 
     620          40 :                         tdsdump_log(TDS_DBG_INFO1, "fixing type elements\n");
     621          80 :                         type = tds_new(char, tds_dstr_len(&table_type) + elements * 2 + 3);
     622             : 
     623          40 :                         if (!type)
     624             :                                 goto memory_error;
     625             : 
     626          80 :                         p = tds_dstr_cstr(&table_type);
     627          40 :                         dst = type;
     628          30 :                         for (;;) {
     629          70 :                                 const char *begin = p;
     630             : 
     631          70 :                                 p = (const char *) memchr(p, ',', end - p);
     632          70 :                                 if (!p)
     633          40 :                                         p = end;
     634          70 :                                 if ((p - begin) < 2 || begin[0] != '\'' || p[-1] != '\'') {
     635          50 :                                         *dst++ = '\'';
     636          50 :                                         memcpy(dst, begin, p - begin);
     637          50 :                                         dst += p - begin;
     638          50 :                                         *dst++ = '\'';
     639             :                                 } else {
     640          20 :                                         memcpy(dst, begin, p - begin);
     641          20 :                                         dst += p - begin;
     642             :                                 }
     643          70 :                                 if (p >= end)
     644             :                                         break;
     645          30 :                                 *dst++ = *p++;
     646             :                         }
     647          40 :                         *dst = 0;
     648          40 :                         if (!tds_dstr_set(&table_type, type)) {
     649           0 :                                 free(type);
     650           0 :                                 goto memory_error;
     651             :                         }
     652             :                 }
     653             :         }
     654             : 
     655             :         /* special case for catalog list */
     656         256 :         if (strcmp(tds_dstr_cstr(&catalog_name), "%") == 0 && cbTableName <= 0 && cbSchemaName <= 0) {
     657          10 :                 retcode =
     658          10 :                         odbc_stat_execute(stmt _wide, "sp_tables", 3, "$!P@table_name", "", 0,
     659             :                                           "$!P@table_owner", "", 0, "!P@table_qualifier", "%", 1);
     660             :         } else {
     661         118 :                 retcode =
     662         944 :                         odbc_stat_execute(stmt _wide, proc, 4,
     663             :                                           "!P@table_name", tds_dstr_cstr(&table_name), tds_dstr_len(&table_name),
     664             :                                           "!P@table_owner", tds_dstr_cstr(&schema_name), tds_dstr_len(&schema_name),
     665             :                                           "!P@table_qualifier", tds_dstr_cstr(&catalog_name), tds_dstr_len(&catalog_name),
     666             :                                           "!@table_type", tds_dstr_cstr(&table_type), tds_dstr_len(&table_type));
     667             :         }
     668         128 :         tds_dstr_free(&schema_name);
     669         128 :         tds_dstr_free(&catalog_name);
     670         128 :         tds_dstr_free(&table_name);
     671         128 :         tds_dstr_free(&table_type);
     672         128 :         if (SQL_SUCCEEDED(retcode) && stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) {
     673          48 :                 odbc_col_setname(stmt, 1, "TABLE_CAT");
     674          48 :                 odbc_col_setname(stmt, 2, "TABLE_SCHEM");
     675             :         }
     676         128 :         ODBC_EXIT_(stmt);
     677             : 
     678           0 : memory_error:
     679           0 :         tds_dstr_free(&schema_name);
     680           0 :         tds_dstr_free(&catalog_name);
     681           0 :         tds_dstr_free(&table_name);
     682           0 :         tds_dstr_free(&table_type);
     683           0 :         odbc_errs_add(&stmt->errs, "HY001", NULL);
     684           0 :         ODBC_EXIT_(stmt);
     685             : }
     686             : 
     687             : static size_t
     688         404 : odbc_quote_metadata(TDS_DBC *dbc, char type, char *dest, DSTR *dstr)
     689             : {
     690         404 :         int unquote = 0;
     691             :         char prev, buf[1200], *dst;
     692         808 :         const char *s = tds_dstr_cstr(dstr);
     693         808 :         ptrdiff_t len = tds_dstr_len(dstr);
     694             : 
     695             :         /* limit string/id lengths */
     696         404 :         if (len > 384)
     697           0 :                 len = 384;
     698             : 
     699         404 :         if (!type || (type == 'O' && dbc->attr.metadata_id == SQL_FALSE)) {
     700         100 :                 if (dest)
     701         100 :                         memcpy(dest, s, len);
     702         100 :                 return len;
     703             :         }
     704             : 
     705             :         /* where we can have ID or PV */
     706         304 :         assert(type == 'P' || (type == 'O' && dbc->attr.metadata_id != SQL_FALSE));
     707             : 
     708             :         /* ID ? */
     709         304 :         if (dbc->attr.metadata_id != SQL_FALSE) {
     710             :                 /* strip leading and trailing spaces */
     711           0 :                 while (len > 0 && *s == ' ')
     712           0 :                         ++s, --len;
     713           0 :                 while (len > 0 && s[len - 1] == ' ')
     714           0 :                         --len;
     715             :                 /* unquote if necessary */
     716           0 :                 if (len > 2 && *s == '\"' && s[len - 1] == '\"') {
     717           0 :                         ++s, len -= 2;
     718           0 :                         unquote = 1;
     719             :                 }
     720             :         }
     721             : 
     722         304 :         if (!dest)
     723           0 :                 dest = buf;
     724         304 :         dst = dest;
     725             : 
     726             :         /*
     727             :          * handle patterns
     728             :          * "'" -> "''" (normal string quoting)
     729             :          *
     730             :          * if metadata_id is FALSE
     731             :          * "\_" -> "[_]"
     732             :          * "\%" -> "[%]"
     733             :          * "[" -> "[[]"
     734             :          *
     735             :          * if metadata_id is TRUE
     736             :          * "\"\"" -> "\"" (if unquote id)
     737             :          * "_" -> "[_]"
     738             :          * "%" -> "[%]"
     739             :          * "[" -> "[[]"
     740             :          */
     741         304 :         prev = 0;
     742        1890 :         for (; --len >= 0; ++s) {
     743        1586 :                 switch (*s) {
     744           0 :                 case '\"':
     745           0 :                         if (unquote && prev == '\"') {
     746           0 :                                 prev = 0;       /* avoid "\"\"\"" -> "\"" */
     747           0 :                                 --dst;
     748           0 :                                 continue;
     749             :                         }
     750             :                         break;
     751         106 :                 case '_':
     752             :                 case '%':
     753         106 :                         if (dbc->attr.metadata_id == SQL_FALSE) {
     754         106 :                                 if (prev != '\\')
     755             :                                         break;
     756           0 :                                 --dst;
     757             :                         }
     758             :                 case '[':
     759           0 :                         if (type != 'P')
     760             :                                 break;
     761             :                         /* quote search string */
     762           0 :                         *dst++ = '[';
     763           0 :                         *dst++ = *s;
     764           0 :                         *dst++ = ']';
     765           0 :                         prev = 0;
     766           0 :                         continue;
     767             :                 }
     768        1586 :                 *dst++ = prev = *s;
     769             :         }
     770         304 :         return dst - dest;
     771             : }
     772             : 
     773             : static SQLRETURN
     774         188 : odbc_stat_execute(TDS_STMT *stmt _WIDE, const char *begin, int nparams, ...)
     775             : {
     776         188 :         int i, param_qualifier = -1;
     777             :         size_t len;
     778         188 :         char *proc = NULL, *p;
     779             :         SQLRETURN retcode;
     780             :         va_list marker;
     781         188 :         DSTR qualifier = DSTR_INITIALIZER, value = DSTR_INITIALIZER;
     782             :         TDSPARAMINFO *params;
     783             : 
     784             :         /* read all params and calc len required */
     785         188 :         va_start(marker, nparams);
     786         188 :         len = strlen(begin) + 3;
     787         188 :         params = tds_alloc_results(0);
     788         188 :         if (!params)
     789             :                 goto mem_error;
     790         790 :         for (i = 0; i < nparams; ++i) {
     791             :                 int param_len;
     792         790 :                 bool convert TDS_UNUSED = true;
     793         790 :                 bool add_always = false;
     794             :                 DSTR *out;
     795             :                 const char *name;
     796             :                 char type;
     797             : 
     798         790 :                 p = va_arg(marker, char *);
     799             : 
     800         522 :                 for (;; ++p) {
     801        1834 :                         switch (*p) {
     802          20 :                         case '$':
     803          20 :                                 add_always = true;
     804          20 :                                 continue;
     805         502 :                         case '!':
     806         502 :                                 convert = false;
     807         502 :                                 continue;
     808         672 :                         case 'V':       /* ODBC version */
     809             :                         case 'O':       /* ordinary arguments */
     810             :                         case 'P':       /* pattern value arguments */
     811         672 :                                 type = *p++;
     812         672 :                                 break;
     813             :                         default:
     814             :                                 type = 0;       /* ordinary type */
     815             :                                 break;
     816             :                         }
     817             :                         break;
     818             :                 }
     819         790 :                 name = p;
     820             : 
     821         790 :                 p = va_arg(marker, char *);
     822         790 :                 param_len = va_arg(marker, int);
     823             : 
     824         790 :                 if (type == 'V') {
     825          48 :                         int ver = (stmt->dbc->env->attr.odbc_version == SQL_OV_ODBC3) ? 3 : 2;
     826             : 
     827          48 :                         if (!odbc_add_int_param(stmt->dbc->tds_socket, params, name, ver))
     828             :                                 goto mem_error;
     829          48 :                         continue;
     830             :                 }
     831             : 
     832             : #ifdef ENABLE_ODBC_WIDE
     833         742 :                 if (!convert)
     834         502 :                         out = tds_dstr_copyn(&value, p, param_len);
     835             :                 else
     836         240 :                         out = odbc_dstr_copy(stmt->dbc, &value, param_len, (ODBC_CHAR *) p);
     837             : #else
     838             :                 out = odbc_dstr_copy(stmt->dbc, &value, param_len, (ODBC_CHAR *) p);
     839             : #endif
     840         742 :                 if (!out)
     841             :                         goto mem_error;
     842             : 
     843        1464 :                 if (add_always || !tds_dstr_isempty(&value)) {
     844             :                         char buf[1200];
     845             :                         int l;
     846             : 
     847         404 :                         l = (int) odbc_quote_metadata(stmt->dbc, type, buf, &value);
     848         404 :                         if (!odbc_add_char_param(stmt->dbc->tds_socket, params, name, buf, l))
     849             :                                 goto mem_error;
     850             : 
     851         404 :                         if (begin[0] == '.' && strstr(name, "qualifier")) {
     852          20 :                                 if (!tds_dstr_dup(&qualifier, &value))
     853             :                                         goto mem_error;
     854          40 :                                 len += tds_quote_id_rpc(stmt->dbc->tds_socket, NULL,
     855          20 :                                                         tds_dstr_cstr(&qualifier), tds_dstr_len(&qualifier));
     856          20 :                                 param_qualifier = i;
     857             :                         }
     858             :                 }
     859             :         }
     860         188 :         tds_dstr_free(&value);
     861             : 
     862             :         /* proc is neither mb or wide, is always utf encoded */
     863         188 :         retcode = odbc_set_stmt_query(stmt, (ODBC_CHAR *) "-", 1 _wide0);
     864         188 :         if (retcode != SQL_SUCCESS)
     865             :                 goto error;
     866         188 :         stmt->prepared_query_is_rpc = 1;
     867             : 
     868             :         /* set params */
     869         188 :         tds_free_param_results(stmt->params);
     870         188 :         stmt->param_count = params->num_cols;
     871         188 :         stmt->param_num = 1 + stmt->param_count;
     872         188 :         stmt->params = params;
     873         188 :         params = NULL;
     874             : 
     875             :         /* allocate space for string */
     876         188 :         if (!tds_dstr_alloc(&stmt->query, len))
     877             :                 goto mem_error;
     878         376 :         proc = tds_dstr_buf(&stmt->query);
     879             : 
     880             :         /* build query string */
     881         188 :         p = proc;
     882         188 :         if (param_qualifier >= 0)
     883          60 :                 p += tds_quote_id_rpc(stmt->dbc->tds_socket, p, tds_dstr_cstr(&qualifier), tds_dstr_len(&qualifier));
     884         188 :         tds_dstr_free(&qualifier);
     885         188 :         strcpy(p, begin);
     886         188 :         p += strlen(begin);
     887         188 :         tds_dstr_setlen(&stmt->query, p - proc);
     888         188 :         assert(p + 1 <= proc + len);
     889             : 
     890             :         /* execute it */
     891         188 :         retcode = odbc_SQLExecute(stmt);
     892         188 :         if (SQL_SUCCEEDED(retcode))
     893         188 :                 odbc_upper_column_names(stmt);
     894             : 
     895         188 :         va_end(marker);
     896         188 :         ODBC_RETURN(stmt, retcode);
     897             : 
     898           0 : mem_error:
     899           0 :         odbc_errs_add(&stmt->errs, "HY001", NULL);
     900             : 
     901           0 : error:
     902           0 :         tds_dstr_free(&value);
     903           0 :         tds_dstr_free(&qualifier);
     904           0 :         tds_free_results(params);
     905           0 :         va_end(marker);
     906           0 :         return SQL_ERROR;
     907             : }
     908             : 
     909             : #include "catalog_export.h"

Generated by: LCOV version 1.13