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"
|