Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * This code was created by Peter Harvey (mostly during Christmas 98/99).
15 : * This code is LGPL. Please ensure that this message remains in future
16 : * distributions and uses of this code (thats about all I get out of it).
17 : * - Peter Harvey pharvey@codebydesign.com
18 : *
19 : * This file has been modified for the MonetDB project. See the file
20 : * Copyright in this directory for more information.
21 : */
22 :
23 : /**********************************************************************
24 : * SQLProcedureColumns()
25 : * CLI Compliance: ODBC (Microsoft)
26 : *
27 : * Author: Sjoerd Mullender
28 : * Date : 28 Feb 2018
29 : *
30 : **********************************************************************/
31 :
32 : #include "ODBCGlobal.h"
33 : #include "ODBCStmt.h"
34 : #include "ODBCUtil.h"
35 : #include "ODBCQueries.h"
36 :
37 :
38 : static SQLRETURN
39 4 : MNDBProcedureColumns(ODBCStmt *stmt,
40 : const SQLCHAR *CatalogName,
41 : SQLSMALLINT NameLength1,
42 : const SQLCHAR *SchemaName,
43 : SQLSMALLINT NameLength2,
44 : const SQLCHAR *ProcName,
45 : SQLSMALLINT NameLength3,
46 : const SQLCHAR *ColumnName,
47 : SQLSMALLINT NameLength4)
48 : {
49 4 : RETCODE rc;
50 :
51 : /* buffer for the constructed query to do meta data retrieval */
52 4 : char *query = NULL;
53 4 : size_t querylen;
54 4 : size_t pos = 0;
55 4 : char *sch = NULL, *prc = NULL, *col = NULL;
56 :
57 4 : fixODBCstring(CatalogName, NameLength1, SQLSMALLINT,
58 : addStmtError, stmt, return SQL_ERROR);
59 4 : fixODBCstring(SchemaName, NameLength2, SQLSMALLINT,
60 : addStmtError, stmt, return SQL_ERROR);
61 4 : fixODBCstring(ProcName, NameLength3, SQLSMALLINT,
62 : addStmtError, stmt, return SQL_ERROR);
63 4 : fixODBCstring(ColumnName, NameLength4, SQLSMALLINT,
64 : addStmtError, stmt, return SQL_ERROR);
65 :
66 : #ifdef ODBCDEBUG
67 4 : ODBCLOG("\"%.*s\" \"%.*s\" \"%.*s\" \"%.*s\"\n",
68 : (int) NameLength1, CatalogName ? (char *) CatalogName : "",
69 : (int) NameLength2, SchemaName ? (char *) SchemaName : "",
70 : (int) NameLength3, ProcName ? (char *) ProcName : "",
71 : (int) NameLength4, ColumnName ? (char *) ColumnName : "");
72 : #endif
73 :
74 4 : if (stmt->Dbc->sql_attr_metadata_id == SQL_FALSE) {
75 4 : if (NameLength2 > 0) {
76 4 : sch = ODBCParsePV("s", "name",
77 : (const char *) SchemaName,
78 : (size_t) NameLength2,
79 : stmt->Dbc);
80 4 : if (sch == NULL)
81 0 : goto nomem;
82 : }
83 4 : if (NameLength3 > 0) {
84 8 : prc = ODBCParsePV("p", "name",
85 : (const char *) ProcName,
86 : (size_t) NameLength3,
87 4 : stmt->Dbc);
88 4 : if (prc == NULL)
89 0 : goto nomem;
90 : }
91 4 : if (NameLength4 > 0) {
92 8 : col = ODBCParsePV("a", "name",
93 : (const char *) ColumnName,
94 : (size_t) NameLength4,
95 4 : stmt->Dbc);
96 4 : if (col == NULL)
97 0 : goto nomem;
98 : }
99 : } else {
100 0 : if (NameLength2 > 0) {
101 0 : sch = ODBCParseID("s", "name",
102 : (const char *) SchemaName,
103 : (size_t) NameLength2);
104 0 : if (sch == NULL)
105 0 : goto nomem;
106 : }
107 0 : if (NameLength3 > 0) {
108 0 : prc = ODBCParseID("p", "name",
109 : (const char *) ProcName,
110 : (size_t) NameLength3);
111 0 : if (prc == NULL)
112 0 : goto nomem;
113 : }
114 0 : if (NameLength4 > 0) {
115 0 : col = ODBCParseID("a", "name",
116 : (const char *) ColumnName,
117 : (size_t) NameLength4);
118 0 : if (col == NULL)
119 0 : goto nomem;
120 : }
121 : }
122 :
123 : /* construct the query now */
124 4 : querylen = 6700 + (sch ? strlen(sch) : 0) + (prc ? strlen(prc) : 0) +
125 4 : (col ? strlen(col) : 0);
126 4 : query = malloc(querylen);
127 4 : if (query == NULL)
128 0 : goto nomem;
129 :
130 : /* SQLProcedureColumns returns a table with the following columns:
131 : VARCHAR PROCEDURE_CAT
132 : VARCHAR PROCEDURE_SCHEM
133 : VARCHAR PROCEDURE_NAME NOT NULL
134 : VARCHAR COLUMN_NAME NOT NULL
135 : SMALLINT COLUMN_TYPE NOT NULL
136 : SMALLINT DATA_TYPE NOT NULL
137 : VARCHAR TYPE_NAME NOT NULL
138 : INTEGER COLUMN_SIZE
139 : INTEGER BUFFER_LENGTH
140 : SMALLINT DECIMAL_DIGITS
141 : SMALLINT NUM_PREC_RADIX
142 : SMALLINT NULLABLE NOT NULL
143 : VARCHAR REMARKS
144 : VARCHAR COLUMN_DEF
145 : SMALLINT SQL_DATA_TYPE NOT NULL
146 : SMALLINT SQL_DATETIME_SUB
147 : INTEGER CHAR_OCTET_LENGTH
148 : INTEGER ORDINAL_POSITION NOT NULL
149 : VARCHAR IS_NULLABLE
150 : VARCHAR SPECIFIC_NAME (Note this is a MonetDB extension, needed to differentiate between overloaded procedure/function names)
151 : (similar to JDBC DatabaseMetaData methods getProcedureColumns() and getFunctionColumns())
152 : */
153 :
154 : /* see sql/include/sql_catalog.h */
155 : #define F_FUNC 1
156 : #define F_PROC 2
157 : #define F_UNION 5
158 4 : pos += snprintf(query + pos, querylen - pos,
159 : "select cast(null as varchar(1)) as \"PROCEDURE_CAT\", "
160 : "s.name as \"PROCEDURE_SCHEM\", "
161 : "p.name as \"PROCEDURE_NAME\", "
162 : "a.name as \"COLUMN_NAME\", "
163 : "cast(case when a.inout = 1 then %d "
164 : "when p.type = %d then %d "
165 : "else %d "
166 : "end as smallint) as \"COLUMN_TYPE\", "
167 : DATA_TYPE(a) ", "
168 : TYPE_NAME(a) ", "
169 : COLUMN_SIZE(a) ", "
170 : BUFFER_LENGTH(a) ", "
171 : DECIMAL_DIGITS(a) ", "
172 : NUM_PREC_RADIX(a) ", "
173 : "cast(%d as smallint) as \"NULLABLE\", "
174 : "%s as \"REMARKS\", "
175 : "cast(null as varchar(1)) as \"COLUMN_DEF\", "
176 : SQL_DATA_TYPE(a) ", "
177 : SQL_DATETIME_SUB(a) ", "
178 : CHAR_OCTET_LENGTH(a) ", "
179 : "cast(case when p.type = 5 and a.inout = 0 then a.number + 1 "
180 : "when p.type = 5 and a.inout = 1 then a.number - x.maxout "
181 : "when p.type = 2 and a.inout = 1 then a.number + 1 "
182 : "when a.inout = 0 then 0 "
183 : "else a.number "
184 : "end as integer) as \"ORDINAL_POSITION\", "
185 : "'' as \"IS_NULLABLE\", "
186 : /* Only the id value uniquely identifies a specific procedure.
187 : Include it to be able to differentiate between multiple
188 : overloaded procedures with the same name and schema */
189 : "cast(p.id as varchar(10)) AS \"SPECIFIC_NAME\" "
190 : "from sys.schemas s, "
191 : "sys.functions p left outer join (select func_id, max(number) as maxout from sys.args where inout = 0 group by func_id) as x on p.id = x.func_id, "
192 : "sys.args a%s "
193 : "where s.id = p.schema_id and "
194 : "p.id = a.func_id and "
195 : "p.type in (%d, %d, %d)",
196 : /* column_type: */
197 : SQL_PARAM_INPUT, F_UNION, SQL_RESULT_COL, SQL_RETURN_VALUE,
198 : #ifdef DATA_TYPE_ARGS
199 : DATA_TYPE_ARGS,
200 : #endif
201 : #ifdef TYPE_NAME_ARGS
202 : TYPE_NAME_ARGS,
203 : #endif
204 : #ifdef COLUMN_SIZE_ARGS
205 : COLUMN_SIZE_ARGS,
206 : #endif
207 : #ifdef BUFFER_LENGTH_ARGS
208 : BUFFER_LENGTH_ARGS,
209 : #endif
210 : #ifdef DECIMAL_DIGITS_ARGS
211 : DECIMAL_DIGITS_ARGS,
212 : #endif
213 : #ifdef NUM_PREC_RADIX_ARGS
214 : NUM_PREC_RADIX_ARGS,
215 : #endif
216 : /* nullable: */
217 : SQL_NULLABLE_UNKNOWN,
218 : /* remarks: */
219 : stmt->Dbc->has_comment ? "c.remark" : "cast(null as varchar(1))",
220 : #ifdef SQL_DATA_TYPE_ARGS
221 : SQL_DATA_TYPE_ARGS,
222 : #endif
223 : #ifdef SQL_DATETIME_SUB_ARGS
224 : SQL_DATETIME_SUB_ARGS,
225 : #endif
226 : #ifdef CHAR_OCTET_LENGTH_ARGS
227 : CHAR_OCTET_LENGTH_ARGS,
228 : #endif
229 : /* from clause: */
230 4 : stmt->Dbc->has_comment ? " left outer join sys.comments c on c.id = a.id" : "",
231 : /* where clause: */
232 : F_FUNC, F_PROC, F_UNION);
233 :
234 : /* depending on the input parameter values we must add a
235 : variable selection condition dynamically */
236 :
237 : /* Construct the selection condition query part */
238 4 : if (NameLength1 > 0 && CatalogName != NULL) {
239 : /* filtering requested on catalog name */
240 0 : if (strcmp((char *) CatalogName, stmt->Dbc->dbname) != 0) {
241 : /* catalog name does not match the database name, so return no rows */
242 0 : pos += snprintf(query + pos, querylen - pos, " and 1=2");
243 : }
244 : }
245 4 : if (sch) {
246 : /* filtering requested on schema name */
247 4 : pos += snprintf(query + pos, querylen - pos, " and %s", sch);
248 4 : free(sch);
249 : }
250 4 : if (prc) {
251 : /* filtering requested on procedure name */
252 4 : pos += snprintf(query + pos, querylen - pos, " and %s", prc);
253 4 : free(prc);
254 : }
255 4 : if (col) {
256 : /* filtering requested on column name */
257 4 : pos += snprintf(query + pos, querylen - pos, " and %s", col);
258 4 : free(col);
259 : }
260 :
261 : /* add the ordering (exclude procedure_cat as it is the same for all rows) */
262 4 : pos += strcpy_len(query + pos, " order by \"PROCEDURE_SCHEM\", \"PROCEDURE_NAME\", \"SPECIFIC_NAME\", \"COLUMN_TYPE\", \"ORDINAL_POSITION\"", querylen - pos);
263 4 : if (pos >= querylen)
264 0 : fprintf(stderr, "pos >= querylen, %zu > %zu\n", pos, querylen);
265 4 : assert(pos < querylen);
266 :
267 : /* debug: fprintf(stdout, "SQLProcedureColumns query (pos: %zu, len: %zu):\n%s\n\n", pos, strlen(query), query); */
268 :
269 : /* query the MonetDB data dictionary tables */
270 4 : rc = MNDBExecDirect(stmt, (SQLCHAR *) query, (SQLINTEGER) pos);
271 :
272 4 : free(query);
273 :
274 4 : return rc;
275 :
276 0 : nomem:
277 : /* note that query must be NULL when we get here */
278 0 : if (sch)
279 0 : free(sch);
280 0 : if (prc)
281 0 : free(prc);
282 0 : if (col)
283 0 : free(col);
284 : /* Memory allocation error */
285 0 : addStmtError(stmt, "HY001", NULL, 0);
286 0 : return SQL_ERROR;
287 : }
288 :
289 : SQLRETURN SQL_API
290 : SQLProcedureColumns(SQLHSTMT StatementHandle,
291 : SQLCHAR *CatalogName,
292 : SQLSMALLINT NameLength1,
293 : SQLCHAR *SchemaName,
294 : SQLSMALLINT NameLength2,
295 : SQLCHAR *ProcName,
296 : SQLSMALLINT NameLength3,
297 : SQLCHAR *ColumnName,
298 : SQLSMALLINT NameLength4)
299 : {
300 4 : ODBCStmt *stmt = (ODBCStmt *) StatementHandle;
301 :
302 : #ifdef ODBCDEBUG
303 4 : ODBCLOG("SQLProcedureColumns %p ", StatementHandle);
304 : #endif
305 :
306 4 : if (!isValidStmt(stmt))
307 : return SQL_INVALID_HANDLE;
308 :
309 4 : clearStmtErrors(stmt);
310 :
311 4 : return MNDBProcedureColumns(stmt,
312 : CatalogName, NameLength1,
313 : SchemaName, NameLength2,
314 : ProcName, NameLength3,
315 : ColumnName, NameLength4);
316 : }
317 :
318 : SQLRETURN SQL_API
319 : SQLProcedureColumnsA(SQLHSTMT StatementHandle,
320 : SQLCHAR *CatalogName,
321 : SQLSMALLINT NameLength1,
322 : SQLCHAR *SchemaName,
323 : SQLSMALLINT NameLength2,
324 : SQLCHAR *ProcName,
325 : SQLSMALLINT NameLength3,
326 : SQLCHAR *ColumnName,
327 : SQLSMALLINT NameLength4)
328 : {
329 0 : return SQLProcedureColumns(StatementHandle,
330 : CatalogName, NameLength1,
331 : SchemaName, NameLength2,
332 : ProcName, NameLength3,
333 : ColumnName, NameLength4);
334 : }
335 :
336 : SQLRETURN SQL_API
337 : SQLProcedureColumnsW(SQLHSTMT StatementHandle,
338 : SQLWCHAR *CatalogName,
339 : SQLSMALLINT NameLength1,
340 : SQLWCHAR *SchemaName,
341 : SQLSMALLINT NameLength2,
342 : SQLWCHAR *ProcName,
343 : SQLSMALLINT NameLength3,
344 : SQLWCHAR *ColumnName,
345 : SQLSMALLINT NameLength4)
346 : {
347 0 : ODBCStmt *stmt = (ODBCStmt *) StatementHandle;
348 0 : SQLRETURN rc = SQL_ERROR;
349 0 : SQLCHAR *catalog = NULL, *schema = NULL, *proc = NULL, *column = NULL;
350 :
351 : #ifdef ODBCDEBUG
352 0 : ODBCLOG("SQLProcedureColumnsW %p ", StatementHandle);
353 : #endif
354 :
355 0 : if (!isValidStmt(stmt))
356 : return SQL_INVALID_HANDLE;
357 :
358 0 : clearStmtErrors(stmt);
359 :
360 0 : fixWcharIn(CatalogName, NameLength1, SQLCHAR, catalog,
361 : addStmtError, stmt, goto bailout);
362 0 : fixWcharIn(SchemaName, NameLength2, SQLCHAR, schema,
363 : addStmtError, stmt, goto bailout);
364 0 : fixWcharIn(ProcName, NameLength3, SQLCHAR, proc,
365 : addStmtError, stmt, goto bailout);
366 0 : fixWcharIn(ColumnName, NameLength4, SQLCHAR, column,
367 : addStmtError, stmt, goto bailout);
368 :
369 0 : rc = MNDBProcedureColumns(stmt,
370 : catalog, SQL_NTS,
371 : schema, SQL_NTS,
372 : proc, SQL_NTS,
373 : column, SQL_NTS);
374 :
375 0 : bailout:
376 0 : if (catalog)
377 0 : free(catalog);
378 0 : if (schema)
379 0 : free(schema);
380 0 : if (proc)
381 0 : free(proc);
382 0 : if (column)
383 0 : free(column);
384 :
385 : return rc;
386 : }
|