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, 2025 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 : #include "gdk.h" // COLnew(), BUNappend()
15 : #include "gdk_time.h" // date_create(), daytime_create(), timestamp_create()
16 : #include "mal_exception.h"
17 : #include "mal_builder.h"
18 : #include "mal_client.h"
19 : #include "mutils.h" /* utf8toutf16(), utf16toutf8() */
20 : #include "rel_proto_loader.h"
21 : #include "rel_exp.h"
22 : // #include "sql_decimal.h" /* decimal_from_str() */
23 :
24 : #ifdef _MSC_VER
25 : #include <WTypes.h>
26 : #endif
27 : #include <stdint.h>
28 : #include <ctype.h>
29 : #include <wchar.h>
30 :
31 : /**** Define the ODBC Version our ODBC application complies with ****/
32 : #define ODBCVER 0x0352 /* Important: this must be defined before include of sql.h and sqlext.h */
33 : #include <sql.h>
34 : #include <sqlext.h>
35 :
36 :
37 : #define ODBC_RELATION 1
38 : #define ODBC_LOADER 2
39 :
40 : #define QUERY_MAX_COLUMNS 4096
41 : #define MAX_COL_NAME_LEN 1023
42 : #define MAX_TBL_NAME_LEN 1023
43 :
44 : #ifdef HAVE_HGE
45 : #define MAX_PREC 38
46 : #else
47 : #define MAX_PREC 18
48 : #endif
49 :
50 : /* MonetDB ODBC Driver defines in ODBCGlobal.h SQL_HUGEINT 0x4000 */
51 : #define SQL_HUGEINT 0x4000
52 :
53 : typedef struct {
54 : SQLSMALLINT dataType; /* ODBC datatype */
55 : SQLULEN columnSize; /* ODBC colsize, contains precision for decimals */
56 : SQLSMALLINT decimalDigits; /* ODBC dec. digits, contains scale for decimals */
57 : int battype; /* MonetDB atom type, used to create the BAT */
58 : BAT * bat; /* MonetDB BAT */
59 : SQLSMALLINT targetType; /* needed for SQLGetData */
60 : SQLPOINTER * targetValuePtr; /* needed for SQLGetData */
61 : SQLLEN bufferLength; /* needed for SQLGetData */
62 : } rescol_t;
63 :
64 : /* map ODBC SQL datatype to MonetDB SQL datatype */
65 : static sql_subtype *
66 192 : map_rescol_type(SQLSMALLINT dataType, SQLULEN columnSize, SQLSMALLINT decimalDigits, mvc * sql)
67 : {
68 192 : char * typenm;
69 192 : unsigned int interval_type = 0;
70 :
71 192 : switch (dataType) {
72 45 : case SQL_CHAR:
73 : case SQL_VARCHAR:
74 : case SQL_LONGVARCHAR:
75 : case SQL_WCHAR:
76 : case SQL_WVARCHAR:
77 : case SQL_WLONGVARCHAR:
78 : default: /* all other ODBC types are also mapped to varchar for now */
79 : /* all ODBC char datatypes are mapped to varchar. char and clob are internally not used anymore */
80 45 : if (columnSize > (SQLULEN) INT_MAX)
81 : columnSize = INT_MAX;
82 45 : return sql_bind_subtype(sql->sa, "varchar", (unsigned int) columnSize, 0);
83 :
84 3 : case SQL_BINARY:
85 : case SQL_VARBINARY:
86 : case SQL_LONGVARBINARY:
87 3 : if (columnSize > (SQLULEN) INT_MAX)
88 : columnSize = INT_MAX;
89 3 : return sql_bind_subtype(sql->sa, "blob", (unsigned int) columnSize, 0);
90 :
91 9 : case SQL_DECIMAL:
92 : case SQL_NUMERIC:
93 : {
94 : /* columnSize contains the defined number of digits, so precision. */
95 : /* decimalDigits contains the scale (which can be negative). */
96 9 : if (columnSize > MAX_PREC || abs(decimalDigits) > MAX_PREC) {
97 : /* too large precision/scale, not supported by MonetDB. Map this column to a string */
98 0 : if (columnSize > (SQLULEN) INT_MAX)
99 : columnSize = INT_MAX;
100 0 : return sql_bind_subtype(sql->sa, "varchar", (unsigned int) columnSize +3, 0);
101 : }
102 :
103 9 : return sql_bind_subtype(sql->sa, "varchar", (unsigned int) columnSize +3, 0);
104 : // unsigned int prec = MAX(1, columnSize); /* precision must be >= 1 */
105 : // unsigned int scale = MAX(0, decimalDigits); /* negative scales are not supported by MonetDB */
106 : // if (prec < scale)
107 : // prec = scale; /* make precision large enough to contain all decimal digits */
108 : // return sql_bind_subtype(sql->sa, "decimal", prec, scale);
109 : }
110 3 : case SQL_GUID:
111 : {
112 : /* represents a uuid of length 36, such as: dbe7343c-1f11-4fa9-a9c8-a31cd26f92fe */
113 3 : sql_subtype * tp = sql_bind_subtype(sql->sa, "uuid", 0, 0); // this fails to return a valid pointer
114 3 : if (tp != NULL)
115 : return tp;
116 : // try a different way
117 3 : sql_schema *syss = mvc_bind_schema(sql, "sys");
118 3 : if (syss) {
119 3 : tp = SA_ZNEW(sql->sa, sql_subtype);
120 3 : if (tp != NULL) {
121 3 : tp->digits = tp->scale = 0;
122 3 : tp->type = schema_bind_type(sql, syss, "uuid");
123 3 : if (tp->type != NULL)
124 : return tp;
125 : }
126 : }
127 : /* fall back to map it to a char(36) result column type */
128 0 : return sql_bind_subtype(sql->sa, "char", (unsigned int) UUID_STRLEN, 0);
129 : }
130 :
131 : case SQL_BIT:
132 : typenm = "boolean";
133 : break;
134 :
135 4 : case SQL_TINYINT:
136 4 : typenm = "tinyint";
137 4 : break;
138 4 : case SQL_SMALLINT:
139 4 : typenm = "smallint";
140 4 : break;
141 31 : case SQL_INTEGER:
142 31 : typenm = "int";
143 31 : break;
144 4 : case SQL_BIGINT:
145 4 : typenm = "bigint";
146 4 : break;
147 : #ifdef HAVE_HGE
148 4 : case SQL_HUGEINT:
149 4 : typenm = "hugeint";
150 4 : break;
151 : #endif
152 :
153 : case SQL_REAL:
154 4 : typenm = "real";
155 : break;
156 : case SQL_DOUBLE:
157 8 : typenm = "double";
158 : break;
159 0 : case SQL_FLOAT:
160 : /* the precision of SQL_FLOAT can be either 24 or 53:
161 : if it is 24, the SQL_FLOAT data type is the same as SQL_REAL;
162 : if it is 53, the SQL_FLOAT data type is the same as SQL_DOUBLE. */
163 0 : typenm = (columnSize == 7) ? "real" : "double";
164 : break;
165 :
166 4 : case SQL_TYPE_DATE:
167 4 : typenm = "date";
168 4 : break;
169 4 : case SQL_TYPE_TIME:
170 : /* decimalDigits contains the precision of fractions of a second */
171 4 : typenm = "time";
172 4 : break;
173 6 : case SQL_DATETIME:
174 : case SQL_TYPE_TIMESTAMP:
175 : /* decimalDigits contains the precision of fractions of a second */
176 6 : typenm = "timestamp";
177 6 : break;
178 :
179 4 : case SQL_INTERVAL_YEAR:
180 4 : typenm = "month_interval";
181 4 : interval_type = 1;
182 4 : break;
183 4 : case SQL_INTERVAL_YEAR_TO_MONTH:
184 4 : typenm = "month_interval";
185 4 : interval_type = 2;
186 4 : break;
187 4 : case SQL_INTERVAL_MONTH:
188 4 : typenm = "month_interval";
189 4 : interval_type = 3;
190 4 : break;
191 6 : case SQL_INTERVAL_DAY:
192 6 : typenm = "day_interval";
193 6 : interval_type = 4;
194 6 : break;
195 4 : case SQL_INTERVAL_HOUR:
196 4 : typenm = "sec_interval";
197 4 : interval_type = 8;
198 4 : break;
199 4 : case SQL_INTERVAL_MINUTE:
200 4 : typenm = "sec_interval";
201 4 : interval_type = 11;
202 4 : break;
203 4 : case SQL_INTERVAL_SECOND:
204 4 : typenm = "sec_interval";
205 4 : interval_type = 13;
206 4 : break;
207 4 : case SQL_INTERVAL_DAY_TO_HOUR:
208 4 : typenm = "sec_interval";
209 4 : interval_type = 5;
210 4 : break;
211 4 : case SQL_INTERVAL_DAY_TO_MINUTE:
212 4 : typenm = "sec_interval";
213 4 : interval_type = 6;
214 4 : break;
215 4 : case SQL_INTERVAL_DAY_TO_SECOND:
216 4 : typenm = "sec_interval";
217 4 : interval_type = 7;
218 4 : break;
219 4 : case SQL_INTERVAL_HOUR_TO_MINUTE:
220 4 : typenm = "sec_interval";
221 4 : interval_type = 9;
222 4 : break;
223 4 : case SQL_INTERVAL_HOUR_TO_SECOND:
224 4 : typenm = "sec_interval";
225 4 : interval_type = 10;
226 4 : break;
227 4 : case SQL_INTERVAL_MINUTE_TO_SECOND:
228 4 : typenm = "sec_interval";
229 4 : interval_type = 12;
230 4 : break;
231 : }
232 132 : return sql_bind_subtype(sql->sa, typenm, interval_type, 0);
233 : }
234 :
235 : /* return name for ODBC SQL datatype */
236 : static char *
237 0 : nameofSQLtype(SQLSMALLINT dataType)
238 : {
239 : /* https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/sql-data-types */
240 0 : switch (dataType) {
241 : case SQL_CHAR: return "CHAR";
242 0 : case SQL_VARCHAR: return "VARCHAR";
243 0 : case SQL_LONGVARCHAR: return "LONG VARCHAR";
244 0 : case SQL_WCHAR: return "WCHAR";
245 0 : case SQL_WVARCHAR: return "WVARCHAR";
246 0 : case SQL_WLONGVARCHAR: return "WLONGVARCHAR";
247 0 : case SQL_DECIMAL: return "DECIMAL";
248 0 : case SQL_NUMERIC: return "NUMERIC";
249 0 : case SQL_SMALLINT: return "SMALLINT";
250 0 : case SQL_INTEGER: return "INTEGER";
251 0 : case SQL_REAL: return "REAL";
252 0 : case SQL_FLOAT: return "FLOAT";
253 0 : case SQL_DOUBLE: return "DOUBLE";
254 0 : case SQL_BIT: return "BIT";
255 0 : case SQL_TINYINT: return "TINYINT";
256 0 : case SQL_BIGINT: return "BIGINT";
257 0 : case SQL_BINARY: return "BINARY";
258 0 : case SQL_VARBINARY: return "VARBINARY";
259 0 : case SQL_LONGVARBINARY: return "LONG VARBINARY";
260 0 : case SQL_DATETIME: return "DATETIME";
261 0 : case SQL_TYPE_DATE: return "DATE";
262 0 : case SQL_TYPE_TIME: return "TIME";
263 0 : case SQL_TYPE_TIMESTAMP: return "TIMESTAMP";
264 0 : case SQL_INTERVAL_MONTH: return "INTERVAL MONTH";
265 0 : case SQL_INTERVAL_YEAR: return "INTERVAL YEAR";
266 0 : case SQL_INTERVAL_YEAR_TO_MONTH: return "INTERVAL YEAR TO MONTH";
267 0 : case SQL_INTERVAL_DAY: return "INTERVAL DAY";
268 0 : case SQL_INTERVAL_HOUR: return "INTERVAL HOUR";
269 0 : case SQL_INTERVAL_MINUTE: return "INTERVAL MINUTE";
270 0 : case SQL_INTERVAL_SECOND: return "INTERVAL SECOND";
271 0 : case SQL_INTERVAL_DAY_TO_HOUR: return "INTERVAL DAY TO HOUR";
272 0 : case SQL_INTERVAL_DAY_TO_MINUTE: return "INTERVAL DAY TO MINUTE";
273 0 : case SQL_INTERVAL_DAY_TO_SECOND: return "INTERVAL DAY TO SECOND";
274 0 : case SQL_INTERVAL_HOUR_TO_MINUTE: return "INTERVAL HOUR TO MINUTE";
275 0 : case SQL_INTERVAL_HOUR_TO_SECOND: return "INTERVAL HOUR TO SECOND";
276 0 : case SQL_INTERVAL_MINUTE_TO_SECOND: return "INTERVAL MINUTE TO SECOND";
277 0 : case SQL_GUID: return "GUID";
278 0 : case SQL_HUGEINT: return "HUGEINT";
279 0 : default: return "Driver specific type";
280 : }
281 : }
282 :
283 : /* name of ODBC SQLRETURN codes */
284 : static char *
285 0 : nameOfRetCode(SQLRETURN code)
286 : {
287 0 : switch (code) {
288 : case SQL_SUCCESS: return "SQL_SUCCESS";
289 0 : case SQL_SUCCESS_WITH_INFO: return "SQL_SUCCESS_WITH_INFO";
290 0 : case SQL_ERROR: return "SQL_ERROR";
291 0 : case SQL_INVALID_HANDLE: return "SQL_INVALID_HANDLE";
292 0 : case SQL_STILL_EXECUTING: return "SQL_STILL_EXECUTING";
293 0 : case SQL_NEED_DATA: return "SQL_NEED_DATA";
294 0 : case SQL_NO_DATA: return "SQL_NO_DATA";
295 0 : default: return "SQLRETURN ??";
296 : }
297 : }
298 :
299 : #ifdef HAVE_HGE
300 : static hge
301 3 : str_to_hge(const char *s) {
302 3 : char c;
303 3 : char sign = '+';
304 3 : int i = 0;
305 3 : hge ret = 0;
306 :
307 3 : if (!s)
308 : return 0;
309 :
310 3 : c = s[i];
311 3 : if (c == '-' || c == '+') {
312 1 : sign = c;
313 1 : c = s[++i];
314 : }
315 82 : while (c) {
316 79 : if (c >= '0' && c <= '9') {
317 79 : ret *= 10;
318 79 : ret += (int) c - '0';
319 : }
320 79 : c = s[++i];
321 : }
322 3 : if (sign == '-')
323 1 : ret = -ret;
324 : return ret;
325 : }
326 : #endif
327 :
328 : /* an ODBC function call returned an error, get the error msg from the ODBC driver */
329 : static char *
330 5 : getErrMsg(SQLSMALLINT handleType, SQLHANDLE handle) {
331 5 : SQLRETURN ret;
332 5 : SQLCHAR state[SQL_SQLSTATE_SIZE +1];
333 5 : SQLINTEGER errnr;
334 5 : SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH] = { 0 };
335 5 : SQLSMALLINT msglen = SQL_MAX_MESSAGE_LENGTH -1;
336 :
337 5 : if (handle == SQL_NULL_HSTMT)
338 : return NULL;
339 :
340 : // TODO use ODBC W function
341 5 : ret = SQLGetDiagRec(handleType, handle, 1, state, &errnr, msg, SQL_MAX_MESSAGE_LENGTH -1, &msglen);
342 5 : if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
343 5 : const char format[] = "SQLSTATE %s, Error code %d, Message %s";
344 : /* ignore msg when using MS Excel ODBC driver, which does not support setting connection timeout */
345 5 : if ((strcmp("IM006", (char *)state) == 0)
346 0 : && (strcmp("[Microsoft][ODBC Driver Manager] Driver's SQLSetConnectAttr failed", (char *)msg) == 0)) {
347 5 : return NULL;
348 : }
349 :
350 5 : if (msglen <= 0) {
351 : /* e.g SQL_NTS */
352 0 : msglen = (SQLSMALLINT) strlen((char *)msg);
353 : }
354 5 : char * retmsg = (char *) GDKmalloc(sizeof(format) + SQL_SQLSTATE_SIZE + 10 + msglen);
355 5 : if (retmsg != NULL) {
356 5 : if (state[SQL_SQLSTATE_SIZE] != '\0')
357 0 : state[SQL_SQLSTATE_SIZE] = '\0';
358 5 : sprintf(retmsg, format, (char *)state, errnr, (char *)msg);
359 5 : return retmsg;
360 : }
361 : }
362 : return NULL;
363 : }
364 :
365 : /* utility function to safely close all opened ODBC resources */
366 : static void
367 107 : odbc_cleanup(SQLHANDLE env, SQLHANDLE dbc, SQLHANDLE stmt) {
368 107 : SQLRETURN ret = SQL_SUCCESS;
369 :
370 107 : if (stmt != SQL_NULL_HSTMT) {
371 103 : ret = SQLFreeStmt(stmt, SQL_CLOSE);
372 103 : if (ret != SQL_INVALID_HANDLE)
373 103 : SQLFreeHandle(SQL_HANDLE_STMT, stmt);
374 : }
375 107 : if (dbc != SQL_NULL_HDBC) {
376 107 : ret = SQLDisconnect(dbc);
377 107 : if (ret != SQL_INVALID_HANDLE)
378 107 : SQLFreeHandle(SQL_HANDLE_DBC, dbc);
379 : }
380 107 : if (env != SQL_NULL_HENV) {
381 107 : SQLFreeHandle(SQL_HANDLE_ENV, env);
382 : }
383 107 : }
384 :
385 : /* copied from monetdb5/modules/mal/tablet.c */
386 : static BAT *
387 192 : bat_create(int adt, BUN nr)
388 : {
389 192 : BAT *b = COLnew(0, adt, nr, TRANSIENT);
390 :
391 : /* check for correct structures */
392 192 : if (b == NULL)
393 : return NULL;
394 192 : if ((b = BATsetaccess(b, BAT_APPEND)) == NULL) {
395 : return NULL;
396 : }
397 :
398 : /* disable all properties here */
399 192 : b->tsorted = false;
400 192 : b->trevsorted = false;
401 192 : b->tnosorted = 0;
402 192 : b->tnorevsorted = 0;
403 192 : b->tseqbase = oid_nil;
404 192 : b->tkey = false;
405 192 : b->tnokey[0] = 0;
406 192 : b->tnokey[1] = 0;
407 192 : return b;
408 : }
409 :
410 : /* convert interval.day_second.fraction values to millisec fractions as needed by MonetDB interval types.
411 : * we need the columns decimalDigits specification to adjust the fractions value to millisec.
412 : */
413 : static SQLUINTEGER
414 12 : fraction2msec(SQLUINTEGER fraction, SQLSMALLINT decimaldigits) {
415 12 : SQLUINTEGER msec = fraction;
416 12 : if (msec == 0)
417 : return 0;
418 :
419 8 : switch (decimaldigits) {
420 8 : case 6: msec = fraction / 1000; break;
421 : case 3: msec = fraction; break;
422 0 : case 0: msec = fraction * 1000; break;
423 0 : case 1: msec = fraction * 100; break;
424 0 : case 2: msec = fraction * 10; break;
425 0 : case 4: msec = fraction / 10; break;
426 0 : case 5: msec = fraction / 100; break;
427 0 : case 7: msec = fraction / 10000; break;
428 0 : case 8: msec = fraction / 100000; break;
429 0 : case 9: msec = fraction / 1000000; break;
430 : }
431 :
432 : // millisec value should be no larger than 999
433 8 : while (msec > 999) {
434 0 : msec = msec / 10;
435 : }
436 : return msec;
437 : }
438 :
439 : /*
440 : * odbc_query() contains the logic for both odbc_relation() and ODBCloader()
441 : * the caller argument is ODBC_RELATION when called from odbc_relation and ODBC_LOADER when called from ODBCloader
442 : */
443 : static str
444 118 : odbc_query(int caller, mvc *sql, sql_subfunc *f, char *url, list *res_exps, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
445 : {
446 118 : if (sql == NULL)
447 : return "Missing mvc value.";
448 118 : if (f == NULL)
449 : return "Missing sql_subfunc value.";
450 :
451 : /* check received url and extract the ODBC connection string and the SQL query */
452 118 : if (!url || (url && strncasecmp("odbc:", url, 5) != 0))
453 : return "Invalid URI. Must start with 'odbc:'.";
454 :
455 : // skip 'odbc:' prefix from url so we get a connection string including the query
456 118 : char * con_str = &url[5];
457 : /* the connection string must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='
458 : else the ODBC driver manager can't load the ODBC driver */
459 118 : if (con_str
460 118 : && (strncmp("DSN=", con_str, 4) != 0)
461 46 : && (strncmp("DRIVER=", con_str, 7) != 0)
462 10 : && (strncmp("FILEDSN=", con_str, 8) != 0))
463 : return "Invalid ODBC connection string. Must start with 'DSN=' or 'FILEDSN=' or 'DRIVER='.";
464 :
465 : // locate the 'QUERY=' part to extract the SQL query string to execute
466 109 : char * qry_str = strstr(con_str, "QUERY=");
467 109 : if (qry_str == NULL)
468 : return "Incomplete ODBC URI string. Missing 'QUERY=' part to specify the SQL SELECT query to execute.";
469 :
470 107 : char * query = GDKstrdup(&qry_str[6]); // we expect that QUERY= is at the end of the connection string
471 107 : if (query == NULL || *query == 0) {
472 0 : GDKfree(query);
473 0 : return "Incomplete ODBC URI string. Missing SQL SELECT query after 'QUERY='.";
474 : }
475 :
476 : // create a new ODBC connection string without the QUERY= part
477 107 : char * odbc_con_str = GDKstrndup(con_str, qry_str - con_str);
478 107 : if (odbc_con_str == NULL) {
479 0 : GDKfree(query);
480 0 : return "Missing ODBC connection string.";
481 : }
482 :
483 107 : TRC_INFO(LOADER, "\nExtracted ODBC connection string: %s\n and SQL query: %s\n", odbc_con_str, query);
484 :
485 : /* now we can (try to) connect to the ODBC driver and execute the SQL query */
486 107 : SQLRETURN ret = SQL_INVALID_HANDLE;
487 107 : SQLHANDLE env = SQL_NULL_HENV;
488 107 : SQLHANDLE dbc = SQL_NULL_HDBC;
489 107 : SQLHANDLE stmt = SQL_NULL_HSTMT;
490 107 : char * errmsg = NULL;
491 :
492 107 : ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
493 107 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
494 0 : errmsg = "Allocate ODBC ENV handle failed.";
495 0 : goto finish;
496 : }
497 107 : ret = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) (uintptr_t) SQL_OV_ODBC3, 0);
498 107 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
499 0 : errmsg = "SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION ODBC3) failed.";
500 0 : goto finish;
501 : }
502 :
503 107 : ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
504 107 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
505 0 : errmsg = "Allocate ODBC DBC handle failed.";
506 0 : goto finish;
507 : }
508 : /* to avoid an endless blocking SQLDriverConnect() set a login timeout of 8s */
509 107 : ret = SQLSetConnectAttr(dbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER) (uintptr_t) 8UL, 0);
510 107 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
511 0 : errmsg = "SQLSetConnectAttr(SQL_ATTR_LOGIN_TIMEOUT 8 sec) failed.";
512 0 : goto finish;
513 : }
514 :
515 107 : SQLSMALLINT len = 0;
516 107 : uint16_t * odbc_con_Wstr = utf8toutf16(odbc_con_str);
517 : #define MAX_CONNECT_OUT_STR 2048
518 107 : if (odbc_con_Wstr != NULL) {
519 107 : SQLWCHAR outstr[MAX_CONNECT_OUT_STR];
520 107 : ret = SQLDriverConnectW(dbc, NULL, (SQLWCHAR *) odbc_con_Wstr, SQL_NTS, outstr, MAX_CONNECT_OUT_STR, &len, SQL_DRIVER_NOPROMPT);
521 : /* we no longer need odbc_con_Wstr */
522 107 : free(odbc_con_Wstr);
523 : } else {
524 0 : SQLCHAR outstr[MAX_CONNECT_OUT_STR];
525 0 : ret = SQLDriverConnect(dbc, NULL, (SQLCHAR *) odbc_con_str, SQL_NTS, outstr, MAX_CONNECT_OUT_STR, &len, SQL_DRIVER_NOPROMPT);
526 : }
527 107 : if (ret == SQL_SUCCESS_WITH_INFO && caller == ODBC_RELATION) {
528 : /* show the info warning, but only once */
529 0 : char * ODBCmsg = getErrMsg(SQL_HANDLE_DBC, dbc);
530 0 : TRC_INFO(LOADER, "SQLDriverConnect(%s) returned %s ODBCmsg: %s\n", odbc_con_str, nameOfRetCode(ret), (ODBCmsg) ? ODBCmsg : "");
531 0 : if (ODBCmsg)
532 0 : GDKfree(ODBCmsg);
533 : } else {
534 107 : TRC_DEBUG(LOADER, "SQLDriverConnect(%s) returned %s\n", odbc_con_str, nameOfRetCode(ret));
535 : }
536 107 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
537 4 : errmsg = "SQLDriverConnect failed.";
538 4 : goto finish;
539 : }
540 : /* we no longer need odbc_con_str */
541 103 : GDKfree(odbc_con_str);
542 103 : odbc_con_str = NULL;
543 :
544 103 : ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
545 103 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
546 0 : errmsg = "Allocate ODBC STMT handle failed.";
547 0 : goto finish;
548 : }
549 :
550 : #ifdef HAVE_HGE
551 : {
552 103 : char DBMSname[128];
553 103 : ret = SQLGetInfo(dbc, SQL_DBMS_NAME, (SQLPOINTER) &DBMSname, 127, NULL);
554 103 : if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
555 103 : TRC_DEBUG(LOADER, "SQLGetInfo(dbc, SQL_DBMS_NAME) returned %s\n", DBMSname);
556 103 : if (strcmp("MonetDB", DBMSname) == 0) {
557 : /* instruct the MonetDB ODBC driver to return SQL_HUGEINT as column datatype */
558 70 : ret = SQLGetTypeInfo(stmt, SQL_HUGEINT);
559 70 : TRC_DEBUG(LOADER, "SQLGetTypeInfo(stmt, SQL_HUGEINT) returned %s\n", nameOfRetCode(ret));
560 70 : if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
561 70 : ret = SQLCloseCursor(stmt);
562 : }
563 : }
564 : }
565 : }
566 : #endif
567 :
568 103 : uint16_t * query_Wstr = utf8toutf16(query);
569 103 : if (query_Wstr != NULL) {
570 103 : ret = SQLExecDirectW(stmt, (SQLWCHAR *) query_Wstr, SQL_NTS);
571 : /* we no longer need query_Wstr */
572 103 : free(query_Wstr);
573 : } else {
574 0 : ret = SQLExecDirect(stmt, (SQLCHAR *) query, SQL_NTS);
575 : }
576 103 : if (ret == SQL_SUCCESS_WITH_INFO && caller == ODBC_RELATION) {
577 : /* show the info warning, but only once */
578 0 : char * ODBCmsg = getErrMsg(SQL_HANDLE_STMT, stmt);
579 0 : TRC_INFO(LOADER, "SQLExecDirect(%s) returned %s ODBCmsg: %s\n", query, nameOfRetCode(ret), (ODBCmsg) ? ODBCmsg : "");
580 0 : if (ODBCmsg)
581 0 : GDKfree(ODBCmsg);
582 : } else {
583 103 : TRC_DEBUG(LOADER, "SQLExecDirect(%s) returned %s\n", query, nameOfRetCode(ret));
584 : }
585 103 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
586 1 : errmsg = "SQLExecDirect query failed.";
587 1 : goto finish;
588 : }
589 : /* we no longer need query string */
590 102 : GDKfree(query);
591 102 : query = NULL;
592 :
593 102 : SQLSMALLINT nr_cols = 0;
594 102 : ret = SQLNumResultCols(stmt, &nr_cols);
595 102 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
596 0 : errmsg = "SQLNumResultCols failed.";
597 0 : goto finish;
598 : }
599 102 : if (nr_cols <= 0) {
600 0 : errmsg = "ODBC query did not return a resultset.";
601 0 : goto finish;
602 : }
603 102 : TRC_INFO(LOADER, "Query has %d result columns\n", nr_cols);
604 102 : if (nr_cols > QUERY_MAX_COLUMNS) {
605 : /* limit the number of data columns, as we do not want to block or blow up the mserver */
606 0 : nr_cols = QUERY_MAX_COLUMNS;
607 0 : TRC_INFO(LOADER, "ODBC_loader limited Query result to first %d columns.\n", nr_cols);
608 : }
609 :
610 : /* when called from odbc_relation() */
611 102 : if (caller == ODBC_RELATION) {
612 51 : char tname[MAX_TBL_NAME_LEN +1];
613 51 : char cname[MAX_COL_NAME_LEN +1];
614 51 : sql_alias * tblname = NULL;
615 51 : char * colname;
616 51 : SQLSMALLINT dataType = 0;
617 51 : SQLULEN columnSize = 0;
618 51 : SQLSMALLINT decimalDigits = 0;
619 51 : sql_subtype * sql_mtype;
620 51 : list * typelist = sa_list(sql->sa);
621 51 : list * nameslist = sa_list(sql->sa);
622 243 : for (SQLUSMALLINT col = 1; col <= (SQLUSMALLINT) nr_cols; col++) {
623 : /* for each result column get name, datatype, size and decdigits */
624 : // TODO use ODBC W function
625 192 : ret = SQLDescribeCol(stmt, col, (SQLCHAR *) cname, (SQLSMALLINT) MAX_COL_NAME_LEN,
626 : NULL, &dataType, &columnSize, &decimalDigits, NULL);
627 192 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
628 0 : errmsg = "SQLDescribeCol failed.";
629 0 : goto finish;
630 : }
631 192 : TRC_DEBUG(LOADER, "ResCol %u, name: %s, type %d (%s), size %u, decdigits %d\n",
632 : col, cname, dataType, nameofSQLtype(dataType), (unsigned int)columnSize, decimalDigits);
633 192 : sql_mtype = map_rescol_type(dataType, columnSize, decimalDigits, sql);
634 192 : if (sql_mtype == NULL)
635 0 : continue; /* skip this column */
636 :
637 192 : colname = sa_strdup(sql->sa, cname);
638 192 : list_append(nameslist, colname);
639 192 : list_append(typelist, sql_mtype);
640 :
641 192 : if (res_exps) {
642 : /* also get the table name for this result column */
643 : // TODO use ODBC W function
644 192 : ret = SQLColAttribute(stmt, col, SQL_DESC_TABLE_NAME, (SQLPOINTER) tname, (SQLSMALLINT) MAX_TBL_NAME_LEN, NULL, NULL);
645 192 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
646 0 : strcpy(tname, "");
647 : }
648 192 : tblname = a_create(sql->sa, tname);
649 192 : sql_exp *ne = exp_column(sql->sa, tblname, colname, sql_mtype, CARD_MULTI, 1, 0, 0);
650 192 : set_basecol(ne);
651 192 : ne->alias.label = -(sql->nid++);
652 192 : list_append(res_exps, ne);
653 : }
654 : }
655 :
656 51 : f->tname = sa_strdup(sql->sa, tname);
657 51 : f->colnames = nameslist;
658 51 : f->coltypes = typelist;
659 51 : f->res = typelist;
660 51 : goto finish;
661 : }
662 :
663 : /* when called from ODBCloader() */
664 51 : if (caller == ODBC_LOADER) {
665 51 : rescol_t * colmetadata = (rescol_t *) GDKzalloc(nr_cols * sizeof(rescol_t));
666 51 : if (colmetadata == NULL) {
667 0 : errmsg = "GDKzalloc colmetadata[nr_cols] failed.";
668 0 : goto finish;
669 : }
670 :
671 : /* allocate buffers for each of the fixed size atom types. */
672 51 : bit bit_val = 0;
673 51 : bte bte_val = 0;
674 51 : sht sht_val = 0;
675 51 : int int_val = 0;
676 51 : lng lng_val = 0;
677 : #ifdef HAVE_HGE
678 51 : hge hge_val = 0; // for hugeint and decimals with precision > 18
679 : #endif
680 51 : flt flt_val = 0;
681 51 : dbl dbl_val = 0;
682 51 : DATE_STRUCT date_val;
683 51 : TIME_STRUCT time_val;
684 51 : TIMESTAMP_STRUCT ts_val;
685 51 : SQL_INTERVAL_STRUCT itv_val;
686 51 : SQLGUID guid_val;
687 51 : union {
688 : uuid uuid_val;
689 : uint8_t u[UUID_SIZE];
690 : } u_val;
691 :
692 51 : bool hasStrCols = false;
693 51 : SQLULEN largestStringSize = 0;
694 51 : bool hasBlobCols = false;
695 51 : SQLULEN largestBlobSize = 0;
696 :
697 : /* make bats with right atom type */
698 243 : for (SQLUSMALLINT col = 0; col < (SQLUSMALLINT) nr_cols; col++) {
699 192 : char cname[MAX_COL_NAME_LEN +1];
700 192 : SQLSMALLINT dataType = 0;
701 192 : SQLULEN columnSize = 0;
702 192 : SQLSMALLINT decimalDigits = 0;
703 192 : int battype = TYPE_str;
704 192 : BAT * b = NULL;
705 :
706 : /* for each result column get SQL datatype, size and decdigits */
707 : // TODO use ODBC W function
708 192 : ret = SQLDescribeCol(stmt, col+1, (SQLCHAR *) cname, (SQLSMALLINT) MAX_COL_NAME_LEN,
709 : NULL, &dataType, &columnSize, &decimalDigits, NULL);
710 192 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
711 0 : errmsg = "SQLDescribeCol failed.";
712 : /* cleanup already created bats */
713 0 : while (col > 0) {
714 0 : col--;
715 0 : BBPreclaim(colmetadata[col].bat);
716 : }
717 0 : GDKfree(colmetadata);
718 0 : goto finish;
719 : }
720 192 : TRC_DEBUG(LOADER, "DescCol %u, name: %s, type %d (%s), size %u, decdigits %d\n",
721 : col+1, cname, dataType, nameofSQLtype(dataType), (unsigned int)columnSize, decimalDigits);
722 :
723 192 : colmetadata[col].dataType = dataType;
724 192 : colmetadata[col].columnSize = columnSize;
725 192 : colmetadata[col].decimalDigits = decimalDigits;
726 192 : colmetadata[col].bufferLength = 0;
727 :
728 192 : battype = getBatType(getArgType(mb, pci, col));
729 192 : colmetadata[col].battype = battype;
730 192 : if (battype == TYPE_str) {
731 54 : hasStrCols = true;
732 54 : if (dataType == SQL_DECIMAL || dataType == SQL_NUMERIC) {
733 : /* read it as string */
734 9 : if (columnSize < 38) {
735 7 : columnSize = 38;
736 : }
737 : /* add 3 for: sign, possible leading 0 and decimal separator */
738 9 : columnSize += 3;
739 9 : colmetadata[col].columnSize = columnSize;
740 : }
741 54 : if (columnSize > largestStringSize) {
742 : largestStringSize = columnSize;
743 : }
744 : } else
745 : #ifdef HAVE_HGE
746 138 : if (battype == TYPE_hge) {
747 4 : if (dataType == SQL_HUGEINT) {
748 : /* read it as string */
749 4 : hasStrCols = true;
750 4 : if (columnSize < 50) {
751 4 : columnSize = 50;
752 4 : colmetadata[col].columnSize = columnSize;
753 : }
754 4 : if (columnSize > largestStringSize) {
755 : largestStringSize = columnSize;
756 : }
757 4 : colmetadata[col].bufferLength = largestStringSize;
758 : }
759 : } else
760 : #endif
761 134 : if (battype == TYPE_blob) {
762 3 : hasBlobCols = true;
763 3 : if (columnSize > largestBlobSize) {
764 : largestBlobSize = columnSize;
765 : }
766 : }
767 :
768 : /* mapping based on https://learn.microsoft.com/en-us/sql/odbc/reference/appendixes/c-data-types */
769 192 : switch(dataType) {
770 45 : case SQL_CHAR:
771 : case SQL_VARCHAR:
772 : case SQL_LONGVARCHAR:
773 : case SQL_WCHAR:
774 : case SQL_WVARCHAR:
775 : case SQL_WLONGVARCHAR:
776 : default:
777 45 : colmetadata[col].targetType = SQL_C_CHAR; // TODO later: SQL_C_WCHAR
778 : // colmetadata[col].targetValuePtr = (SQLPOINTER *) str_val; // will be done after allocation
779 45 : break;
780 5 : case SQL_BIT:
781 5 : colmetadata[col].targetType = SQL_C_BIT;
782 5 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &bit_val;
783 5 : break;
784 4 : case SQL_TINYINT:
785 4 : colmetadata[col].targetType = SQL_C_STINYINT;
786 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &bte_val;
787 4 : break;
788 4 : case SQL_SMALLINT:
789 4 : colmetadata[col].targetType = SQL_C_SSHORT;
790 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &sht_val;
791 4 : break;
792 31 : case SQL_INTEGER:
793 31 : colmetadata[col].targetType = SQL_C_SLONG;
794 31 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &int_val;
795 31 : break;
796 4 : case SQL_BIGINT:
797 4 : colmetadata[col].targetType = SQL_C_SBIGINT;
798 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &lng_val;
799 4 : break;
800 4 : case SQL_HUGEINT:
801 : /* read huge int data as string data as there is no SQL_C_SHUGEINT */
802 4 : colmetadata[col].targetType = SQL_C_CHAR;
803 : // colmetadata[col].targetValuePtr = (SQLPOINTER *) str_val; // will be done after allocation
804 4 : break;
805 9 : case SQL_DECIMAL:
806 : case SQL_NUMERIC:
807 : /* read decimal data always as string data and convert it to the right internal decimal format and bat type */
808 9 : colmetadata[col].targetType = SQL_C_CHAR;
809 : // colmetadata[col].targetValuePtr = (SQLPOINTER *) str_val; // will be done after allocation
810 9 : break;
811 4 : case SQL_REAL:
812 4 : colmetadata[col].targetType = SQL_C_FLOAT;
813 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &flt_val;
814 4 : break;
815 0 : case SQL_FLOAT:
816 : /* use same logic as used in map_rescol_type() for SQL_FLOAT and SQL_DECIMAL */
817 0 : if (colmetadata[col].battype == TYPE_flt) {
818 0 : colmetadata[col].dataType = SQL_REAL;
819 0 : colmetadata[col].targetType = SQL_C_FLOAT;
820 0 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &flt_val;
821 : } else {
822 0 : colmetadata[col].dataType = SQL_DOUBLE;
823 0 : colmetadata[col].targetType = SQL_C_DOUBLE;
824 0 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &dbl_val;
825 : }
826 : break;
827 8 : case SQL_DOUBLE:
828 8 : colmetadata[col].targetType = SQL_C_DOUBLE;
829 8 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &dbl_val;
830 8 : break;
831 4 : case SQL_TYPE_DATE:
832 4 : colmetadata[col].targetType = SQL_C_TYPE_DATE;
833 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &date_val;
834 4 : break;
835 4 : case SQL_TYPE_TIME:
836 4 : colmetadata[col].targetType = SQL_C_TYPE_TIME;
837 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &time_val;
838 4 : break;
839 6 : case SQL_DATETIME:
840 : case SQL_TYPE_TIMESTAMP:
841 6 : colmetadata[col].targetType = SQL_C_TYPE_TIMESTAMP;
842 6 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &ts_val;
843 6 : break;
844 4 : case SQL_INTERVAL_YEAR:
845 4 : colmetadata[col].targetType = SQL_C_INTERVAL_YEAR;
846 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
847 4 : break;
848 4 : case SQL_INTERVAL_YEAR_TO_MONTH:
849 4 : colmetadata[col].targetType = SQL_C_INTERVAL_YEAR_TO_MONTH;
850 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
851 4 : break;
852 4 : case SQL_INTERVAL_MONTH:
853 4 : colmetadata[col].targetType = SQL_C_INTERVAL_MONTH;
854 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
855 4 : break;
856 6 : case SQL_INTERVAL_DAY:
857 6 : colmetadata[col].targetType = SQL_C_INTERVAL_DAY;
858 6 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
859 6 : break;
860 4 : case SQL_INTERVAL_HOUR:
861 4 : colmetadata[col].targetType = SQL_C_INTERVAL_HOUR;
862 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
863 4 : break;
864 4 : case SQL_INTERVAL_MINUTE:
865 4 : colmetadata[col].targetType = SQL_C_INTERVAL_MINUTE;
866 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
867 4 : break;
868 4 : case SQL_INTERVAL_SECOND:
869 4 : colmetadata[col].targetType = SQL_C_INTERVAL_SECOND;
870 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
871 4 : break;
872 4 : case SQL_INTERVAL_DAY_TO_HOUR:
873 4 : colmetadata[col].targetType = SQL_C_INTERVAL_DAY_TO_HOUR;
874 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
875 4 : break;
876 4 : case SQL_INTERVAL_DAY_TO_MINUTE:
877 4 : colmetadata[col].targetType = SQL_C_INTERVAL_DAY_TO_MINUTE;
878 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
879 4 : break;
880 4 : case SQL_INTERVAL_DAY_TO_SECOND:
881 4 : colmetadata[col].targetType = SQL_C_INTERVAL_DAY_TO_SECOND;
882 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
883 4 : break;
884 4 : case SQL_INTERVAL_HOUR_TO_MINUTE:
885 4 : colmetadata[col].targetType = SQL_C_INTERVAL_HOUR_TO_MINUTE;
886 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
887 4 : break;
888 4 : case SQL_INTERVAL_HOUR_TO_SECOND:
889 4 : colmetadata[col].targetType = SQL_C_INTERVAL_HOUR_TO_SECOND;
890 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
891 4 : break;
892 4 : case SQL_INTERVAL_MINUTE_TO_SECOND:
893 4 : colmetadata[col].targetType = SQL_C_INTERVAL_MINUTE_TO_SECOND;
894 4 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &itv_val;
895 4 : break;
896 3 : case SQL_GUID:
897 3 : colmetadata[col].targetType = SQL_C_GUID;
898 3 : colmetadata[col].targetValuePtr = (SQLPOINTER *) &guid_val;
899 3 : colmetadata[col].bufferLength = (SQLLEN) sizeof(SQLGUID);
900 3 : break;
901 3 : case SQL_BINARY:
902 : case SQL_VARBINARY:
903 : case SQL_LONGVARBINARY:
904 3 : colmetadata[col].targetType = SQL_C_BINARY;
905 : // colmetadata[col].targetValuePtr = (SQLPOINTER *) bin_data; // will be done after allocation
906 3 : break;
907 : }
908 :
909 192 : TRC_INFO(LOADER, "ResCol %u, name: %s, type %d (%s), size %u, decdigits %d, battype %d\n",
910 : col+1, cname, dataType, nameofSQLtype(dataType), (unsigned int)columnSize, decimalDigits, battype);
911 :
912 192 : TRC_DEBUG(LOADER, "Before create BAT %d type %d\n", col+1, battype);
913 192 : b = bat_create(battype, 0);
914 192 : if (b) {
915 192 : colmetadata[col].bat = b;
916 192 : TRC_DEBUG(LOADER, "Created BAT %d\n", col+1);
917 : } else {
918 0 : errmsg = "Failed to create bat.";
919 : /* cleanup already created bats */
920 0 : while (col > 0) {
921 0 : col--;
922 0 : BBPreclaim(colmetadata[col].bat);
923 : }
924 0 : GDKfree(colmetadata);
925 0 : goto finish;
926 : }
927 : }
928 :
929 : /* allocate large enough read buffers for storing string (and binary blob) data */
930 51 : char * str_val = NULL; // TODO: change to wchar
931 51 : uint8_t * bin_data = NULL;
932 :
933 51 : if (largestStringSize == 0 && hasStrCols) // no valid string length, use 65535 (64kB) as default
934 : largestStringSize = 65535;
935 51 : else if (largestStringSize < 1023) // for very large decimals read as strings
936 : largestStringSize = 1023;
937 51 : else if (largestStringSize > 16777215) // string length very large, limit to 16MB for now
938 : largestStringSize = 16777215;
939 51 : str_val = (char *)GDKmalloc((largestStringSize +1) * sizeof(char)); // +1 for the eos char
940 51 : if (!str_val) {
941 0 : errmsg = "Failed to alloc memory for largest rescol string buffer.";
942 0 : goto finish_fetch;
943 : }
944 51 : TRC_DEBUG(LOADER, "Allocated str_val buffer of size %zu\n", (largestStringSize +1) * sizeof(char));
945 :
946 51 : if (hasBlobCols) {
947 1 : if (largestBlobSize == 0) // no valid blob/binary data size, assume 1048576 (1MB) as default
948 : largestBlobSize = 1048576;
949 0 : if (largestBlobSize > 16777216) // blob length very large, limit to 16MB for now
950 : largestBlobSize = 16777216;
951 1 : bin_data = (uint8_t *)GDKmalloc(largestBlobSize * sizeof(uint8_t));
952 1 : if (!bin_data) {
953 0 : errmsg = "Failed to alloc memory for largest rescol binary data buffer.";
954 0 : goto finish_fetch;
955 : }
956 1 : TRC_DEBUG(LOADER, "Allocated bin_data buffer of size %zu\n", largestBlobSize * sizeof(uint8_t));
957 : }
958 :
959 : /* after allocation of var sized buffers, update targetValuePtr and bufferLength for those columns */
960 243 : for (SQLUSMALLINT col = 0; col < (SQLUSMALLINT) nr_cols; col++) {
961 192 : switch (colmetadata[col].targetType) {
962 58 : case SQL_C_CHAR:
963 58 : colmetadata[col].targetValuePtr = (SQLPOINTER *) str_val;
964 58 : colmetadata[col].bufferLength = largestStringSize;
965 58 : break;
966 3 : case SQL_C_BINARY:
967 3 : colmetadata[col].targetValuePtr = (SQLPOINTER *) bin_data;
968 3 : colmetadata[col].bufferLength = largestBlobSize;
969 3 : break;
970 : // TODO case SQL_C_WCHAR:
971 : // colmetadata[col].targetValuePtr = (SQLPOINTER *) Wstr_val;
972 : // colmetadata[col].bufferLength = largestWStringSize;
973 : }
974 : }
975 :
976 51 : gdk_return gdkret = GDK_SUCCEED;
977 51 : unsigned long row = 0;
978 51 : ret = SQLFetch(stmt); // TODO optimisation: use SQLExtendedFetch() to pull data array wise and use BUNappendmulti()
979 15747 : while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) {
980 15696 : row++;
981 15696 : TRC_DEBUG(LOADER, "Fetched row %lu\n", row);
982 :
983 82395 : for (SQLUSMALLINT col = 0; col < (SQLUSMALLINT) nr_cols; col++) {
984 66699 : SQLSMALLINT sqltype = colmetadata[col].dataType;
985 66699 : BAT * b = colmetadata[col].bat;
986 66699 : SQLSMALLINT targetType = colmetadata[col].targetType;
987 66699 : SQLPOINTER * targetValuePtr = colmetadata[col].targetValuePtr;
988 66699 : SQLLEN bufferLength = colmetadata[col].bufferLength;
989 66699 : SQLLEN strLen = 0;
990 :
991 66699 : TRC_DEBUG(LOADER, "Before SQLGetData(col %u C_type %d buflen %d\n", col+1, targetType, (int)bufferLength);
992 66699 : ret = SQLGetData(stmt, col+1, targetType, targetValuePtr, bufferLength, &strLen);
993 66699 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
994 0 : char * ODBCmsg = getErrMsg(SQL_HANDLE_STMT, stmt);
995 0 : TRC_DEBUG(LOADER, "Failed to get C_type %d data for col %u of row %lu. ODBCmsg: %s\n",
996 : targetType, col+1, row, (ODBCmsg) ? ODBCmsg : "");
997 0 : if (ODBCmsg)
998 0 : GDKfree(ODBCmsg);
999 :
1000 : /* as all bats need to be the same length, append NULL value */
1001 0 : if (BUNappend(b, ATOMnilptr(b->ttype), false) != GDK_SUCCEED)
1002 0 : TRC_ERROR(LOADER, "BUNappend(b, ATOMnilptr(b->ttype), false) failed after SQLGetData failed\n");
1003 : } else {
1004 66699 : if (strLen == SQL_NULL_DATA) {
1005 1378 : TRC_DEBUG(LOADER, "Data row %lu col %u: NULL\n", row, col+1);
1006 1378 : if (BUNappend(b, ATOMnilptr(b->ttype), false) != GDK_SUCCEED)
1007 0 : TRC_ERROR(LOADER, "BUNappend(b, ATOMnilptr(b->ttype), false) failed for setting SQL_NULL_DATA\n");
1008 : } else {
1009 65321 : switch(sqltype) {
1010 9246 : case SQL_CHAR:
1011 : case SQL_VARCHAR:
1012 : case SQL_LONGVARCHAR:
1013 : case SQL_WCHAR:
1014 : case SQL_WVARCHAR:
1015 : case SQL_WLONGVARCHAR:
1016 : default:
1017 9246 : if (strLen != SQL_NTS && strLen >= 0) {
1018 : /* make sure it is a Nul Terminated String */
1019 9246 : if ((SQLULEN) strLen < largestStringSize) {
1020 9246 : if (str_val[strLen] != '\0')
1021 0 : str_val[strLen] = '\0';
1022 : } else {
1023 0 : if (str_val[largestStringSize] != '\0')
1024 0 : str_val[largestStringSize] = '\0';
1025 : }
1026 : }
1027 9246 : TRC_DEBUG(LOADER, "Data row %lu col %u: %s\n", row, col+1, str_val);
1028 9246 : switch (colmetadata[col].battype) {
1029 9243 : case TYPE_str:
1030 9243 : gdkret = BUNappend(b, (void *) str_val, false);
1031 9243 : break;
1032 : #ifdef HAVE_HGE
1033 3 : case TYPE_hge:
1034 : /* HUGEINT values are read as string */
1035 3 : hge_val = str_to_hge(str_val);
1036 3 : gdkret = BUNappend(b, (void *) &hge_val, false);
1037 3 : break;
1038 : #endif
1039 0 : default:
1040 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1041 0 : break;
1042 : }
1043 : break;
1044 4 : case SQL_BIT:
1045 4 : if (colmetadata[col].battype == TYPE_bit) {
1046 4 : TRC_DEBUG(LOADER, "Data row %lu col %u: %x\n", row, col+1, bit_val);
1047 4 : gdkret = BUNappend(b, (void *) &bit_val, false);
1048 : } else {
1049 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1050 : }
1051 : break;
1052 3 : case SQL_TINYINT:
1053 3 : if (colmetadata[col].battype == TYPE_bte) {
1054 3 : TRC_DEBUG(LOADER, "Data row %lu col %u: %hd\n", row, col+1, (sht) bte_val);
1055 3 : gdkret = BUNappend(b, (void *) &bte_val, false);
1056 : } else {
1057 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1058 : }
1059 : break;
1060 3 : case SQL_SMALLINT:
1061 3 : if (colmetadata[col].battype == TYPE_sht) {
1062 3 : TRC_DEBUG(LOADER, "Data row %lu col %u: %hd\n", row, col+1, sht_val);
1063 3 : gdkret = BUNappend(b, (void *) &sht_val, false);
1064 : } else {
1065 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1066 : }
1067 : break;
1068 49404 : case SQL_INTEGER:
1069 49404 : if (colmetadata[col].battype == TYPE_int) {
1070 49404 : TRC_DEBUG(LOADER, "Data row %lu col %u: %d\n", row, col+1, int_val);
1071 49404 : gdkret = BUNappend(b, (void *) &int_val, false);
1072 : } else {
1073 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1074 : }
1075 : break;
1076 3 : case SQL_BIGINT:
1077 3 : if (colmetadata[col].battype == TYPE_lng) {
1078 3 : TRC_DEBUG(LOADER, "Data row %lu col %u: %" PRId64 "\n", row, col+1, lng_val);
1079 3 : gdkret = BUNappend(b, (void *) &lng_val, false);
1080 : } else {
1081 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1082 : }
1083 : break;
1084 9 : case SQL_DECIMAL:
1085 : case SQL_NUMERIC:
1086 9 : if (colmetadata[col].battype == TYPE_str) {
1087 9 : if (strLen != SQL_NTS && strLen >= 0) {
1088 : /* make sure it is a Nul Terminated String */
1089 9 : if ((SQLULEN) strLen < largestStringSize) {
1090 9 : if (str_val[strLen] != '\0')
1091 0 : str_val[strLen] = '\0';
1092 : } else {
1093 0 : if (str_val[largestStringSize] != '\0')
1094 0 : str_val[largestStringSize] = '\0';
1095 : }
1096 : }
1097 9 : TRC_DEBUG(LOADER, "Data row %lu col %u: %s\n", row, col+1, str_val);
1098 9 : gdkret = BUNappend(b, (void *) str_val, false);
1099 : } else {
1100 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1101 : }
1102 : // TODO add support for battypes: bte, sht, int, lng, hge
1103 : // this requires conversion of string to the target bat type, with the scale (decimalDigits) semantics.
1104 : break;
1105 3 : case SQL_REAL:
1106 3 : if (colmetadata[col].battype == TYPE_flt) {
1107 3 : TRC_DEBUG(LOADER, "Data row %lu col %u: %f\n", row, col+1, flt_val);
1108 3 : gdkret = BUNappend(b, (void *) &flt_val, false);
1109 : } else {
1110 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1111 : }
1112 : break;
1113 6169 : case SQL_DOUBLE:
1114 6169 : if (colmetadata[col].battype == TYPE_dbl) {
1115 6169 : TRC_DEBUG(LOADER, "Data row %lu col %u: %f\n", row, col+1, dbl_val);
1116 6169 : gdkret = BUNappend(b, (void *) &dbl_val, false);
1117 : } else {
1118 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1119 : }
1120 : break;
1121 0 : case SQL_FLOAT:
1122 0 : if (colmetadata[col].battype == TYPE_flt) {
1123 0 : TRC_DEBUG(LOADER, "Data row %lu col %u: %f\n", row, col+1, flt_val);
1124 0 : gdkret = BUNappend(b, (void *) &flt_val, false);
1125 : } else {
1126 0 : TRC_DEBUG(LOADER, "Data row %lu col %u: %f\n", row, col+1, dbl_val);
1127 0 : gdkret = BUNappend(b, (void *) &dbl_val, false);
1128 : }
1129 : break;
1130 3 : case SQL_TYPE_DATE:
1131 3 : if (colmetadata[col].battype == TYPE_date) {
1132 3 : date mdate_val = date_create(date_val.year, date_val.month, date_val.day);
1133 3 : TRC_DEBUG(LOADER, "Data row %lu col %u: date(%04d-%02u-%02u)\n",
1134 : row, col+1, date_val.year, date_val.month, date_val.day);
1135 3 : gdkret = BUNappend(b, (void *) &mdate_val, false);
1136 : } else {
1137 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1138 : }
1139 : break;
1140 3 : case SQL_TYPE_TIME:
1141 3 : if (colmetadata[col].battype == TYPE_daytime) {
1142 3 : daytime daytime_val = daytime_create(time_val.hour, time_val.minute, time_val.second, 0);
1143 3 : TRC_DEBUG(LOADER, "Data row %lu col %u: daytime(%02u:%02u:%02u)\n",
1144 : row, col+1, time_val.hour, time_val.minute, time_val.second);
1145 3 : gdkret = BUNappend(b, (void *) &daytime_val, false);
1146 : } else {
1147 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1148 : }
1149 : break;
1150 426 : case SQL_DATETIME:
1151 : case SQL_TYPE_TIMESTAMP:
1152 426 : if (colmetadata[col].battype == TYPE_timestamp) {
1153 426 : date mdate_val = date_create(ts_val.year, ts_val.month, ts_val.day);
1154 426 : daytime daytime_val = daytime_create(ts_val.hour, ts_val.minute, ts_val.second, ts_val.fraction);
1155 426 : timestamp timestamp_val = timestamp_create(mdate_val, daytime_val);
1156 426 : TRC_DEBUG(LOADER, "Data row %lu col %u: timestamp(%04d-%02u-%02u %02u:%02u:%02u.%06u)\n", row, col+1,
1157 : ts_val.year, ts_val.month, ts_val.day, ts_val.hour, ts_val.minute, ts_val.second, ts_val.fraction);
1158 426 : gdkret = BUNappend(b, (void *) ×tamp_val, false);
1159 : } else {
1160 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1161 : }
1162 : break;
1163 9 : case SQL_INTERVAL_YEAR:
1164 : case SQL_INTERVAL_YEAR_TO_MONTH:
1165 : case SQL_INTERVAL_MONTH:
1166 9 : if (colmetadata[col].battype == TYPE_int) {
1167 9 : switch (itv_val.interval_type) {
1168 3 : case SQL_IS_YEAR:
1169 3 : int_val = (int) itv_val.intval.year_month.year *12;
1170 3 : break;
1171 3 : case SQL_IS_YEAR_TO_MONTH:
1172 3 : int_val = (int) (itv_val.intval.year_month.year *12)
1173 3 : + itv_val.intval.year_month.month;
1174 3 : break;
1175 3 : case SQL_IS_MONTH:
1176 3 : int_val = (int) itv_val.intval.year_month.month;
1177 3 : break;
1178 0 : default:
1179 0 : int_val = 0;
1180 : }
1181 :
1182 9 : if (itv_val.interval_sign == SQL_TRUE)
1183 3 : int_val = -int_val;
1184 9 : TRC_DEBUG(LOADER, "Data row %lu col %u: %d\n", row, col+1, int_val);
1185 9 : gdkret = BUNappend(b, (void *) &int_val, false);
1186 : } else {
1187 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1188 : }
1189 : break;
1190 32 : case SQL_INTERVAL_DAY:
1191 : case SQL_INTERVAL_HOUR:
1192 : case SQL_INTERVAL_MINUTE:
1193 : case SQL_INTERVAL_SECOND:
1194 : case SQL_INTERVAL_DAY_TO_HOUR:
1195 : case SQL_INTERVAL_DAY_TO_MINUTE:
1196 : case SQL_INTERVAL_DAY_TO_SECOND:
1197 : case SQL_INTERVAL_HOUR_TO_MINUTE:
1198 : case SQL_INTERVAL_HOUR_TO_SECOND:
1199 : case SQL_INTERVAL_MINUTE_TO_SECOND:
1200 32 : if (colmetadata[col].battype == TYPE_lng) {
1201 32 : switch (itv_val.interval_type) {
1202 5 : case SQL_IS_DAY:
1203 5 : lng_val = (lng) itv_val.intval.day_second.day * (24*60*60*1000);
1204 5 : break;
1205 3 : case SQL_IS_HOUR:
1206 3 : lng_val = (lng) itv_val.intval.day_second.hour * (60*60*1000);
1207 3 : break;
1208 3 : case SQL_IS_MINUTE:
1209 3 : lng_val = (lng) itv_val.intval.day_second.minute * (60*1000);
1210 3 : break;
1211 3 : case SQL_IS_SECOND:
1212 6 : lng_val = (lng) (itv_val.intval.day_second.second * 1000)
1213 3 : + fraction2msec(itv_val.intval.day_second.fraction, colmetadata[col].decimalDigits);
1214 3 : break;
1215 3 : case SQL_IS_DAY_TO_HOUR:
1216 3 : lng_val = (lng) ((itv_val.intval.day_second.day *24)
1217 3 : + itv_val.intval.day_second.hour) * (60*60*1000);
1218 3 : break;
1219 3 : case SQL_IS_DAY_TO_MINUTE:
1220 3 : lng_val = (lng) ((((itv_val.intval.day_second.day *24)
1221 3 : + itv_val.intval.day_second.hour) *60)
1222 3 : + itv_val.intval.day_second.minute) * (60*1000);
1223 3 : break;
1224 3 : case SQL_IS_DAY_TO_SECOND:
1225 6 : lng_val = (lng) (((((((itv_val.intval.day_second.day *24)
1226 3 : + itv_val.intval.day_second.hour) *60)
1227 3 : + itv_val.intval.day_second.minute) *60)
1228 3 : + itv_val.intval.day_second.second) *1000)
1229 3 : + fraction2msec(itv_val.intval.day_second.fraction, colmetadata[col].decimalDigits);
1230 3 : break;
1231 3 : case SQL_IS_HOUR_TO_MINUTE:
1232 3 : lng_val = (lng) ((itv_val.intval.day_second.hour *60)
1233 3 : + itv_val.intval.day_second.minute) * (60*1000);
1234 3 : break;
1235 3 : case SQL_IS_HOUR_TO_SECOND:
1236 6 : lng_val = (lng) (((((itv_val.intval.day_second.hour *60)
1237 3 : + itv_val.intval.day_second.minute) *60)
1238 3 : + itv_val.intval.day_second.second) *1000)
1239 3 : + fraction2msec(itv_val.intval.day_second.fraction, colmetadata[col].decimalDigits);
1240 3 : break;
1241 3 : case SQL_IS_MINUTE_TO_SECOND:
1242 6 : lng_val = (lng) (((itv_val.intval.day_second.minute *60)
1243 3 : + itv_val.intval.day_second.second) *1000)
1244 3 : + fraction2msec(itv_val.intval.day_second.fraction, colmetadata[col].decimalDigits);
1245 3 : break;
1246 0 : default:
1247 0 : lng_val = 0;
1248 0 : break;
1249 : }
1250 :
1251 32 : if (itv_val.interval_sign == SQL_TRUE)
1252 11 : lng_val = -lng_val;
1253 32 : TRC_DEBUG(LOADER, "Data row %lu col %u: %" PRId64 "\n", row, col+1, lng_val);
1254 32 : gdkret = BUNappend(b, (void *) &lng_val, false);
1255 : } else {
1256 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1257 : }
1258 : break;
1259 2 : case SQL_GUID:
1260 2 : TRC_DEBUG(LOADER, "Data row %lu col %u: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", row, col+1,
1261 : guid_val.Data1, guid_val.Data2, guid_val.Data3, guid_val.Data4[0], guid_val.Data4[1], guid_val.Data4[2],
1262 : guid_val.Data4[3], guid_val.Data4[4], guid_val.Data4[5], guid_val.Data4[6], guid_val.Data4[7]);
1263 2 : if (colmetadata[col].battype == TYPE_uuid) {
1264 2 : uint8_t u;
1265 : // uuid is 16 bytes, same as SQLGUID guid_val
1266 2 : memcpy((void *) &u_val.uuid_val, (void *) &guid_val, sizeof(uuid));
1267 : // guid_str: beefc4f7-0264-4735-9b7a-75fd371ef803
1268 : // becomes
1269 : // uuid_str: f7c4efbe-6402-3547-9b7a-75fd371ef803
1270 : // have to fix the swapped bytes
1271 2 : u = u_val.u[0];
1272 2 : u_val.u[0] = u_val.u[3];
1273 2 : u_val.u[3] = u;
1274 2 : u = u_val.u[1];
1275 2 : u_val.u[1] = u_val.u[2];
1276 2 : u_val.u[2] = u;
1277 2 : u = u_val.u[4];
1278 2 : u_val.u[4] = u_val.u[5];
1279 2 : u_val.u[5] = u;
1280 2 : u = u_val.u[6];
1281 2 : u_val.u[6] = u_val.u[7];
1282 2 : u_val.u[7] = u;
1283 2 : gdkret = BUNappend(b, (void *) &u_val.uuid_val, false);
1284 : } else {
1285 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1286 : }
1287 : break;
1288 2 : case SQL_BINARY:
1289 : case SQL_VARBINARY:
1290 : case SQL_LONGVARBINARY:
1291 2 : TRC_DEBUG(LOADER, "Data row %lu col %u: binary data[%d]\n", row, col+1, (int) strLen);
1292 2 : if (colmetadata[col].battype == TYPE_blob && strLen > 0) {
1293 : // convert bin_data to blob struct.
1294 2 : size_t bin_size = (size_t)strLen;
1295 2 : if (bin_size > (size_t)largestBlobSize)
1296 : /* the data has been truncated */
1297 : bin_size = (size_t)largestBlobSize;
1298 2 : blob * blb = (blob *) GDKmalloc(blobsize(bin_size));
1299 2 : if (blb) {
1300 2 : blb->nitems = bin_size;
1301 2 : memcpy(blb->data, bin_data, bin_size);
1302 2 : gdkret = BUNappend(b, (void *) blb, false);
1303 2 : GDKfree(blb);
1304 : } else {
1305 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1306 : }
1307 : } else {
1308 0 : gdkret = BUNappend(b, ATOMnilptr(b->ttype), false);
1309 : }
1310 : break;
1311 : }
1312 65321 : if (gdkret != GDK_SUCCEED)
1313 0 : TRC_ERROR(LOADER, "BUNappend(b, val, false) failed!\n");
1314 : }
1315 : }
1316 : }
1317 15696 : ret = SQLFetch(stmt); // get data of next row
1318 : }
1319 : /* the last SQLFetch() will return SQL_NO_DATA at end, treat it as success */
1320 51 : if (ret == SQL_NO_DATA)
1321 51 : ret = SQL_SUCCESS; // we retrieved all rows
1322 51 : TRC_INFO(LOADER, "Fetched %lu rows\n", row);
1323 :
1324 51 : finish_fetch:
1325 51 : if (str_val)
1326 51 : GDKfree(str_val);
1327 51 : if (bin_data)
1328 1 : GDKfree(bin_data);
1329 :
1330 : /* pass bats to caller */
1331 : if (colmetadata) {
1332 243 : for (int col = 0; col < (int) nr_cols; col++) {
1333 192 : bat * rescol = getArgReference_bat(stk, pci, col);
1334 192 : BAT * b = colmetadata[col].bat;
1335 192 : if (rescol && b) {
1336 192 : *rescol = b->batCacheid;
1337 192 : BBPkeepref(b);
1338 : }
1339 192 : TRC_DEBUG(LOADER, "col %d pass bat %d\n", col, b->ttype);
1340 : }
1341 : /* free locally allocated memory */
1342 51 : GDKfree(colmetadata);
1343 : }
1344 : } /* end of: if (caller == ODBC_LOADER) */
1345 :
1346 0 : finish:
1347 107 : if (query)
1348 5 : GDKfree(query);
1349 107 : if (odbc_con_str)
1350 4 : GDKfree(odbc_con_str);
1351 :
1352 107 : TRC_DEBUG(LOADER, "caller %d at finish, ret %d (%s) errmsg %s\n", caller, ret, nameOfRetCode(ret), (errmsg) ? errmsg : "");
1353 :
1354 107 : if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
1355 : /* an ODBC function call returned an error or warning, get the error msg from the ODBC driver */
1356 5 : SQLSMALLINT handleType;
1357 5 : SQLHANDLE handle;
1358 5 : str retmsg;
1359 5 : char * ODBCmsg;
1360 :
1361 : /* get err message(s) from the right handle */
1362 5 : if (stmt != SQL_NULL_HSTMT) {
1363 : handleType = SQL_HANDLE_STMT;
1364 : handle = stmt;
1365 : } else
1366 4 : if (dbc != SQL_NULL_HDBC) {
1367 : handleType = SQL_HANDLE_DBC;
1368 : handle = dbc;
1369 : } else {
1370 0 : handleType = SQL_HANDLE_ENV;
1371 0 : handle = env;
1372 : }
1373 5 : ODBCmsg = getErrMsg(handleType, handle);
1374 5 : if (errmsg != NULL) {
1375 5 : retmsg = sa_message(sql->sa, "odbc_loader" " %s %s", errmsg, (ODBCmsg) ? ODBCmsg : "");
1376 : } else {
1377 0 : retmsg = sa_message(sql->sa, "odbc_loader" " %s", (ODBCmsg) ? ODBCmsg : "");
1378 : }
1379 5 : if (ODBCmsg)
1380 5 : GDKfree(ODBCmsg);
1381 5 : odbc_cleanup(env, dbc, stmt);
1382 5 : return retmsg;
1383 : }
1384 :
1385 102 : odbc_cleanup(env, dbc, stmt);
1386 102 : TRC_DEBUG(LOADER, "after odbc_cleanup(%p, %p, %p) errmsg %s\n", env, dbc, stmt, (errmsg) ? errmsg : "");
1387 : return (errmsg != NULL) ? (str)errmsg : MAL_SUCCEED;
1388 : }
1389 :
1390 : /*
1391 : * returns an error string (static or via tmp sa_allocator allocated), NULL on success
1392 : *
1393 : * Extend the subfunc f with result columns, ie.
1394 : f->res = typelist;
1395 : f->coltypes = typelist;
1396 : f->colnames = nameslist; use tname if passed, for the relation name
1397 : * Fill the list res_exps, with one result expressions per resulting column.
1398 : */
1399 : static str
1400 67 : odbc_relation(mvc *sql, sql_subfunc *f, char *url, list *res_exps, char *aname)
1401 : {
1402 67 : (void) aname;
1403 67 : return odbc_query(ODBC_RELATION, sql, f, url, res_exps, NULL, NULL, NULL);
1404 : }
1405 :
1406 : static void *
1407 51 : odbc_load(void *BE, sql_subfunc *f, char *url, sql_exp *topn)
1408 : {
1409 51 : backend *be = (backend*)BE;
1410 51 : if (!f)
1411 : return NULL;
1412 :
1413 51 : (void)topn;
1414 :
1415 51 : InstrPtr q = newStmtArgs(be->mb, "odbc", "loader", list_length(f->coltypes) + 2);
1416 51 : int col = 0;
1417 51 : list *l = sa_list(be->mvc->sa);
1418 243 : for (node *n = f->coltypes->h, *nn = f->colnames->h; n && nn; col++, n = n->next, nn = nn->next) {
1419 192 : const char *name = nn->data;
1420 192 : sql_subtype *tp = n->data;
1421 192 : if (tp) {
1422 192 : int type = newBatType(tp->type->localtype);
1423 192 : if (col)
1424 141 : q = pushReturn(be->mb, q, newTmpVariable(be->mb, type));
1425 : else
1426 51 : getArg(q, 0) = newTmpVariable(be->mb, type);
1427 192 : stmt *s = stmt_blackbox_result(be, q, col, tp);
1428 192 : sql_alias *ta = a_create(be->mvc->sa, f->tname);
1429 192 : s = stmt_alias(be, s, col+1, ta, name);
1430 192 : list_append(l, s);
1431 : }
1432 : }
1433 51 : q = pushStr(be->mb, q, url);
1434 51 : q = pushPtr(be->mb, q, f);
1435 51 : pushInstruction(be->mb, q);
1436 51 : return stmt_list(be, l);
1437 : }
1438 :
1439 : static str
1440 51 : ODBCloader(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
1441 : {
1442 51 : backend *be = NULL;
1443 51 : str msg;
1444 51 : if ((msg = getBackendContext(cntxt, &be)) != NULL)
1445 : return msg;
1446 51 : str uri = *getArgReference_str(stk, pci, pci->retc);
1447 51 : sql_subfunc *f = *(sql_subfunc**)getArgReference_ptr(stk, pci, pci->retc+1);
1448 :
1449 51 : return odbc_query(ODBC_LOADER, be->mvc, f, uri, NULL, mb, stk, pci);
1450 : //return MAL_SUCCEED;
1451 : }
1452 :
1453 : static str
1454 1 : ODBCprelude(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
1455 : {
1456 1 : (void)cntxt; (void)mb; (void)stk; (void)pci;
1457 1 : pl_register("odbc", &odbc_relation, &odbc_load);
1458 1 : return MAL_SUCCEED;
1459 : }
1460 :
1461 : static str
1462 1 : ODBCepilogue(void *ret)
1463 : {
1464 1 : (void)ret;
1465 1 : pl_unregister("odbc");
1466 1 : return MAL_SUCCEED;
1467 : }
1468 :
1469 : #include "sql_scenario.h"
1470 : #include "mel.h"
1471 :
1472 : static mel_func odbc_init_funcs[] = {
1473 : pattern("odbc", "prelude", ODBCprelude, false, "", noargs),
1474 : command("odbc", "epilogue", ODBCepilogue, false, "", noargs),
1475 : pattern("odbc", "loader", ODBCloader, true, "Import a query result via the odbc uri", args(1,3, batvarargany("",0),arg("uri",str),arg("func",ptr))),
1476 : { .imp=NULL }
1477 : };
1478 :
1479 : #include "mal_import.h"
1480 : #ifdef _MSC_VER
1481 : #undef read
1482 : #pragma section(".CRT$XCU",read)
1483 : #endif
1484 1 : LIB_STARTUP_FUNC(init_odbc_mal)
1485 1 : { mal_module("odbc", NULL, odbc_init_funcs); }
|