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 : * SQLPrimaryKeys()
25 : * CLI Compliance: ODBC (Microsoft)
26 : *
27 : * Author: Martin van Dinther, Sjoerd Mullender
28 : * Date : 30 aug 2002
29 : *
30 : **********************************************************************/
31 :
32 : #include "ODBCGlobal.h"
33 : #include "ODBCStmt.h"
34 : #include "ODBCUtil.h"
35 :
36 : static SQLRETURN
37 9 : MNDBPrimaryKeys(ODBCStmt *stmt,
38 : const SQLCHAR *CatalogName,
39 : SQLSMALLINT NameLength1,
40 : const SQLCHAR *SchemaName,
41 : SQLSMALLINT NameLength2,
42 : const SQLCHAR *TableName,
43 : SQLSMALLINT NameLength3)
44 : {
45 9 : RETCODE rc;
46 :
47 : /* buffer for the constructed query to do meta data retrieval */
48 9 : char *query = NULL;
49 9 : size_t querylen;
50 9 : size_t pos = 0;
51 9 : char *sch = NULL, *tab = NULL;
52 9 : bool addTmpQuery = false;
53 :
54 : /* deal with SQL_NTS and SQL_NULL_DATA */
55 9 : fixODBCstring(CatalogName, NameLength1, SQLSMALLINT,
56 : addStmtError, stmt, return SQL_ERROR);
57 9 : fixODBCstring(SchemaName, NameLength2, SQLSMALLINT,
58 : addStmtError, stmt, return SQL_ERROR);
59 9 : fixODBCstring(TableName, NameLength3, SQLSMALLINT,
60 : addStmtError, stmt, return SQL_ERROR);
61 :
62 : /* check if a valid (non null, not empty) table name is supplied */
63 9 : if (TableName == NULL) {
64 : /* Invalid use of null pointer */
65 0 : addStmtError(stmt, "HY009", NULL, 0);
66 0 : return SQL_ERROR;
67 : }
68 9 : if (NameLength3 == 0) {
69 : /* Invalid string or buffer length */
70 0 : addStmtError(stmt, "HY090", NULL, 0);
71 0 : return SQL_ERROR;
72 : }
73 :
74 : #ifdef ODBCDEBUG
75 9 : ODBCLOG("\"%.*s\" \"%.*s\" \"%.*s\"\n",
76 : (int) NameLength1, CatalogName ? (char *) CatalogName : "",
77 : (int) NameLength2, SchemaName ? (char *) SchemaName : "",
78 : (int) NameLength3, TableName ? (char *) TableName : "");
79 : #endif
80 :
81 9 : if (stmt->Dbc->sql_attr_metadata_id == SQL_FALSE) {
82 9 : if (NameLength2 > 0) {
83 8 : sch = ODBCParseOA("s", "name",
84 : (const char *) SchemaName,
85 : (size_t) NameLength2);
86 8 : if (sch == NULL)
87 0 : goto nomem;
88 : }
89 9 : if (NameLength3 > 0) {
90 9 : tab = ODBCParseOA("t", "name",
91 : (const char *) TableName,
92 : (size_t) NameLength3);
93 9 : if (tab == NULL)
94 0 : goto nomem;
95 : }
96 : } else {
97 0 : if (NameLength2 > 0) {
98 0 : sch = ODBCParseID("s", "name",
99 : (const char *) SchemaName,
100 : (size_t) NameLength2);
101 0 : if (sch == NULL)
102 0 : goto nomem;
103 : }
104 0 : if (NameLength3 > 0) {
105 0 : tab = ODBCParseID("t", "name",
106 : (const char *) TableName,
107 : (size_t) NameLength3);
108 0 : if (tab == NULL)
109 0 : goto nomem;
110 : }
111 : }
112 :
113 : /* determine if we need to add a query against the tmp.* tables */
114 18 : addTmpQuery = (SchemaName == NULL)
115 9 : || (SchemaName != NULL
116 8 : && (strcmp((const char *) SchemaName, "tmp") == 0
117 4 : || strchr((const char *) SchemaName, '%') != NULL
118 4 : || strchr((const char *) SchemaName, '_') != NULL));
119 :
120 : /* construct the query */
121 9 : querylen = 1000 + (sch ? strlen(sch) : 0) + (tab ? strlen(tab) : 0);
122 9 : if (addTmpQuery)
123 5 : querylen *= 2;
124 9 : query = malloc(querylen);
125 9 : if (query == NULL)
126 0 : goto nomem;
127 :
128 : /* SQLPrimaryKeys returns a table with the following columns:
129 : VARCHAR TABLE_CAT
130 : VARCHAR TABLE_SCHEM
131 : VARCHAR TABLE_NAME NOT NULL
132 : VARCHAR COLUMN_NAME NOT NULL
133 : SMALLINT KEY_SEQ NOT NULL
134 : VARCHAR PK_NAME
135 : */
136 9 : pos += snprintf(query + pos, querylen - pos,
137 : "select cast(null as varchar(1)) as \"TABLE_CAT\", "
138 : "s.name as \"TABLE_SCHEM\", "
139 : "t.name as \"TABLE_NAME\", "
140 : "kc.name as \"COLUMN_NAME\", "
141 : "cast(kc.nr + 1 as smallint) as \"KEY_SEQ\", "
142 : "k.name as \"PK_NAME\" "
143 : "from sys.keys k, sys.objects kc, sys._tables t, sys.schemas s "
144 : "where k.type = 0 and "
145 : "k.id = kc.id and "
146 : "k.table_id = t.id and "
147 : "t.schema_id = s.id");
148 9 : assert(pos < 800);
149 :
150 : /* Construct the selection condition query part */
151 9 : if (NameLength1 > 0 && CatalogName != NULL) {
152 : /* filtering requested on catalog name */
153 0 : if (strcmp((char *) CatalogName, stmt->Dbc->dbname) != 0) {
154 : /* catalog name does not match the database name, so return no rows */
155 0 : pos += snprintf(query + pos, querylen - pos, " and 1=2");
156 : }
157 : }
158 9 : if (sch) {
159 : /* filtering requested on schema name */
160 8 : pos += snprintf(query + pos, querylen - pos, " and %s", sch);
161 : }
162 9 : if (tab) {
163 : /* filtering requested on table name */
164 9 : pos += snprintf(query + pos, querylen - pos, " and %s", tab);
165 : }
166 :
167 9 : if (addTmpQuery) {
168 : /* we must also include the keys of local temporary tables
169 : which are stored in tmp.keys, tmp.objects and tmp._tables */
170 5 : pos += snprintf(query + pos, querylen - pos,
171 : " UNION ALL "
172 : "select cast(null as varchar(1)) as \"TABLE_CAT\", "
173 : "s.name as \"TABLE_SCHEM\", "
174 : "t.name as \"TABLE_NAME\", "
175 : "kc.name as \"COLUMN_NAME\", "
176 : "cast(kc.nr + 1 as smallint) as \"KEY_SEQ\", "
177 : "k.name as \"PK_NAME\" "
178 : "from tmp.keys k, tmp.objects kc, tmp._tables t, sys.schemas s "
179 : "where k.type = 0 and "
180 : "k.id = kc.id and "
181 : "k.table_id = t.id and "
182 : "t.schema_id = s.id");
183 :
184 : /* Construct the selection condition query part */
185 5 : if (NameLength1 > 0 && CatalogName != NULL) {
186 : /* filtering requested on catalog name */
187 0 : if (strcmp((char *) CatalogName, stmt->Dbc->dbname) != 0) {
188 : /* catalog name does not match the database name, so return no rows */
189 0 : pos += snprintf(query + pos, querylen - pos, " and 1=2");
190 : }
191 : }
192 5 : if (sch) {
193 : /* filtering requested on schema name */
194 4 : pos += snprintf(query + pos, querylen - pos, " and %s", sch);
195 : }
196 5 : if (tab) {
197 : /* filtering requested on table name */
198 5 : pos += snprintf(query + pos, querylen - pos, " and %s", tab);
199 : }
200 : }
201 :
202 9 : if (sch)
203 8 : free(sch);
204 9 : if (tab)
205 9 : free(tab);
206 :
207 : /* add the ordering */
208 9 : pos += strcpy_len(query + pos, " order by \"TABLE_SCHEM\", \"TABLE_NAME\", \"KEY_SEQ\"", querylen - pos);
209 9 : assert(pos < querylen);
210 :
211 : /* debug: fprintf(stdout, "SQLPrimaryKeys query (pos: %zu, len: %zu):\n%s\n\n", pos, strlen(query), query); */
212 :
213 : /* query the MonetDB data dictionary tables */
214 9 : rc = MNDBExecDirect(stmt, (SQLCHAR *) query, (SQLINTEGER) pos);
215 :
216 9 : free(query);
217 :
218 9 : return rc;
219 :
220 0 : nomem:
221 : /* note that query must be NULL when we get here */
222 0 : if (sch)
223 0 : free(sch);
224 0 : if (tab)
225 0 : free(tab);
226 : /* Memory allocation error */
227 0 : addStmtError(stmt, "HY001", NULL, 0);
228 0 : return SQL_ERROR;
229 : }
230 :
231 : SQLRETURN SQL_API
232 : SQLPrimaryKeys(SQLHSTMT StatementHandle,
233 : SQLCHAR *CatalogName,
234 : SQLSMALLINT NameLength1,
235 : SQLCHAR *SchemaName,
236 : SQLSMALLINT NameLength2,
237 : SQLCHAR *TableName,
238 : SQLSMALLINT NameLength3)
239 : {
240 9 : ODBCStmt *stmt = (ODBCStmt *) StatementHandle;
241 :
242 : #ifdef ODBCDEBUG
243 9 : ODBCLOG("SQLPrimaryKeys %p ", StatementHandle);
244 : #endif
245 :
246 9 : if (!isValidStmt(stmt))
247 : return SQL_INVALID_HANDLE;
248 :
249 9 : clearStmtErrors(stmt);
250 :
251 9 : return MNDBPrimaryKeys(stmt,
252 : CatalogName, NameLength1,
253 : SchemaName, NameLength2,
254 : TableName, NameLength3);
255 : }
256 :
257 : SQLRETURN SQL_API
258 : SQLPrimaryKeysA(SQLHSTMT StatementHandle,
259 : SQLCHAR *CatalogName,
260 : SQLSMALLINT NameLength1,
261 : SQLCHAR *SchemaName,
262 : SQLSMALLINT NameLength2,
263 : SQLCHAR *TableName,
264 : SQLSMALLINT NameLength3)
265 : {
266 0 : return SQLPrimaryKeys(StatementHandle,
267 : CatalogName, NameLength1,
268 : SchemaName, NameLength2,
269 : TableName, NameLength3);
270 : }
271 :
272 : SQLRETURN SQL_API
273 : SQLPrimaryKeysW(SQLHSTMT StatementHandle,
274 : SQLWCHAR *CatalogName,
275 : SQLSMALLINT NameLength1,
276 : SQLWCHAR *SchemaName,
277 : SQLSMALLINT NameLength2,
278 : SQLWCHAR *TableName,
279 : SQLSMALLINT NameLength3)
280 : {
281 0 : ODBCStmt *stmt = (ODBCStmt *) StatementHandle;
282 0 : SQLRETURN rc = SQL_ERROR;
283 0 : SQLCHAR *catalog = NULL, *schema = NULL, *table = NULL;
284 :
285 : #ifdef ODBCDEBUG
286 0 : ODBCLOG("SQLPrimaryKeysW %p ", StatementHandle);
287 : #endif
288 :
289 0 : if (!isValidStmt(stmt))
290 : return SQL_INVALID_HANDLE;
291 :
292 0 : clearStmtErrors(stmt);
293 :
294 0 : fixWcharIn(CatalogName, NameLength1, SQLCHAR, catalog,
295 : addStmtError, stmt, goto bailout);
296 0 : fixWcharIn(SchemaName, NameLength2, SQLCHAR, schema,
297 : addStmtError, stmt, goto bailout);
298 0 : fixWcharIn(TableName, NameLength3, SQLCHAR, table,
299 : addStmtError, stmt, goto bailout);
300 :
301 0 : rc = MNDBPrimaryKeys(stmt,
302 : catalog, SQL_NTS,
303 : schema, SQL_NTS,
304 : table, SQL_NTS);
305 :
306 0 : bailout:
307 0 : if (catalog)
308 0 : free(catalog);
309 0 : if (schema)
310 0 : free(schema);
311 0 : if (table)
312 0 : free(table);
313 :
314 : return rc;
315 : }
|