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 (that's 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 : * SQLExecute()
25 : * CLI Compliance: ISO 92
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 : #include <limits.h>
36 :
37 : static struct msql_types {
38 : char *name;
39 : int concise_type;
40 : } msql_types[] = {
41 : {"bigint", SQL_BIGINT},
42 : {"blob", SQL_LONGVARBINARY},
43 : {"boolean", SQL_BIT},
44 : {"char", SQL_WCHAR},
45 : {"clob", SQL_WLONGVARCHAR},
46 : {"date", SQL_TYPE_DATE},
47 : {"day_interval", SQL_INTERVAL_SECOND},
48 : {"decimal", SQL_DECIMAL},
49 : {"double", SQL_DOUBLE},
50 : {"hugeint", SQL_HUGEINT},
51 : /* {"inet", SQL_WCHAR}, */
52 : {"int", SQL_INTEGER},
53 : /* {"json", SQL_WVARCHAR}, */
54 : {"month_interval", SQL_INTERVAL_MONTH},
55 : {"oid", SQL_BIGINT},
56 : {"real", SQL_REAL},
57 : {"sec_interval", SQL_INTERVAL_SECOND},
58 : {"smallint", SQL_SMALLINT},
59 : {"table", 0},
60 : {"time", SQL_TYPE_TIME},
61 : {"timetz", SQL_TYPE_TIME},
62 : {"timestamp", SQL_TYPE_TIMESTAMP},
63 : {"timestamptz", SQL_TYPE_TIMESTAMP},
64 : {"tinyint", SQL_TINYINT},
65 : /* {"url", SQL_WVARCHAR}, */
66 : {"uuid", SQL_GUID},
67 : {"varchar", SQL_WVARCHAR},
68 : {0, 0}, /* sentinel */
69 : };
70 :
71 : int
72 951 : ODBCConciseType(const char *name)
73 : {
74 951 : struct msql_types *p;
75 :
76 18600 : for (p = msql_types; p->name; p++)
77 18600 : if (strcmp(p->name, name) == 0)
78 951 : return p->concise_type;
79 : return 0;
80 : }
81 :
82 : #ifdef ODBCDEBUG
83 : static char *
84 2162 : translatequerytype(int querytype)
85 : {
86 2162 : switch (querytype) {
87 : case Q_PARSE:
88 : return "Q_PARSE";
89 99 : case Q_TABLE:
90 99 : return "Q_TABLE";
91 2000 : case Q_UPDATE:
92 2000 : return "Q_UPDATE";
93 62 : case Q_SCHEMA:
94 62 : return "Q_SCHEMA";
95 1 : case Q_TRANS:
96 1 : return "Q_TRANS";
97 0 : case Q_PREPARE:
98 0 : return "Q_PREPARE";
99 0 : case Q_BLOCK:
100 0 : return "Q_BLOCK";
101 0 : default:
102 0 : return "unknown";
103 : }
104 : }
105 : #endif
106 :
107 : SQLRETURN
108 2110 : ODBCInitResult(ODBCStmt *stmt)
109 : {
110 2110 : int i = 0;
111 2110 : int nrCols;
112 2110 : ODBCDescRec *rec;
113 2110 : MapiHdl hdl;
114 2110 : const char *errstr;
115 :
116 2110 : hdl = stmt->hdl;
117 :
118 : /* initialize the Result meta data values */
119 2110 : stmt->currentRow = 0;
120 2110 : stmt->startRow = 0;
121 2110 : stmt->rowSetSize = 0;
122 2110 : stmt->retrieved = 0;
123 2110 : stmt->currentCol = 0;
124 :
125 2162 : repeat:
126 2162 : errstr = mapi_result_error(hdl);
127 2162 : if (errstr) {
128 0 : const char *sqlstate;
129 :
130 0 : if ((sqlstate = mapi_result_errorcode(hdl)) != NULL)
131 0 : addStmtError(stmt, sqlstate, errstr, 0);
132 : else {
133 : /* Syntax error or access violation */
134 0 : addStmtError(stmt, "42000", errstr, 0);
135 : }
136 0 : return SQL_ERROR;
137 : }
138 2162 : nrCols = mapi_get_field_count(hdl);
139 2162 : stmt->querytype = mapi_get_querytype(hdl);
140 : #if SIZEOF_SIZE_T == SIZEOF_INT
141 : if (mapi_rows_affected(hdl) >= (int64_t) 1 << (sizeof(int) * CHAR_BIT)) {
142 : /* General error */
143 : addStmtError(stmt, "HY000", "Too many rows to handle", 0);
144 : return SQL_ERROR;
145 : }
146 : #endif
147 2162 : stmt->rowcount = (SQLULEN) mapi_rows_affected(hdl);
148 :
149 : #ifdef ODBCDEBUG
150 2162 : ODBCLOG("ODBCInitResult: querytype %s, rowcount %lu\n",
151 : translatequerytype(stmt->querytype),
152 : (unsigned long) stmt->rowcount);
153 : #endif
154 :
155 2162 : switch (stmt->querytype) {
156 99 : case Q_TABLE: /* Q_TABLE */
157 : /* result set generating query */
158 99 : assert(nrCols > 0);
159 99 : stmt->State = EXECUTED1;
160 99 : break;
161 2000 : case Q_UPDATE: /* Q_UPDATE */
162 : /* result count generating query */
163 2000 : assert(nrCols == 0);
164 2000 : stmt->State = EXECUTED0;
165 2000 : break;
166 63 : default:
167 : /* resultless query */
168 63 : if (mapi_result_error(hdl) == NULL && mapi_next_result(hdl) == 1)
169 52 : goto repeat;
170 11 : stmt->State = EXECUTED0;
171 11 : stmt->rowcount = 0;
172 11 : nrCols = 0;
173 11 : break;
174 : }
175 :
176 : #if 0
177 : /* XXX is this correct? */
178 : assert(stmt->ImplRowDescr == NULL ||
179 : stmt->ImplRowDescr->sql_desc_count == nrCols ||
180 : stmt->ImplRowDescr->sql_desc_count == 0);
181 : #endif
182 2110 : setODBCDescRecCount(stmt->ImplRowDescr, nrCols);
183 :
184 2110 : if (nrCols == 0)
185 : return SQL_SUCCESS;
186 99 : if (stmt->ImplRowDescr->descRec == NULL) {
187 0 : stmt->State = stmt->queryid >= 0 ? (stmt->State == EXECUTED0 ? PREPARED0 : PREPARED1) : INITED;
188 :
189 : /* Memory allocation error */
190 0 : addStmtError(stmt, "HY001", NULL, 0);
191 0 : return SQL_ERROR;
192 : }
193 :
194 99 : rec = stmt->ImplRowDescr->descRec + 1;
195 :
196 1023 : for (i = 0; i < nrCols; i++) {
197 924 : struct sql_types *tp;
198 924 : int concise_type;
199 924 : char *s;
200 :
201 924 : rec->sql_desc_auto_unique_value = SQL_FALSE;
202 924 : rec->sql_desc_nullable = SQL_NULLABLE_UNKNOWN;
203 924 : rec->sql_desc_rowver = SQL_FALSE;
204 924 : rec->sql_desc_searchable = SQL_PRED_SEARCHABLE;
205 924 : rec->sql_desc_updatable = SQL_ATTR_READONLY;
206 :
207 924 : s = mapi_get_name(hdl, i);
208 : /* HACK to compensate for generated column names */
209 924 : if (s == NULL || strcmp(s, "single_value") == 0)
210 0 : s = "";
211 924 : if (*s) {
212 924 : rec->sql_desc_unnamed = SQL_NAMED;
213 924 : if (rec->sql_desc_label)
214 22 : free(rec->sql_desc_label);
215 924 : rec->sql_desc_label = (SQLCHAR *) strdup(s);
216 924 : if (rec->sql_desc_label == NULL)
217 0 : goto nomem;
218 924 : if (rec->sql_desc_name)
219 22 : free(rec->sql_desc_name);
220 924 : rec->sql_desc_name = (SQLCHAR *) strdup(s);
221 924 : if (rec->sql_desc_name == NULL)
222 0 : goto nomem;
223 : } else {
224 0 : rec->sql_desc_unnamed = SQL_UNNAMED;
225 0 : rec->sql_desc_label = NULL;
226 0 : rec->sql_desc_name = NULL;
227 : }
228 924 : if (rec->sql_desc_base_column_name)
229 2 : free(rec->sql_desc_base_column_name);
230 924 : rec->sql_desc_base_column_name = NULL; /* see below */
231 :
232 924 : s = mapi_get_type(hdl, i);
233 924 : if (s == NULL) /* shouldn't happen */
234 0 : s = "";
235 924 : if (!stmt->Dbc->allow_hugeint && strcmp(s, "hugeint") == 0)
236 924 : s = "bigint";
237 924 : if (rec->sql_desc_type_name)
238 22 : free(rec->sql_desc_type_name);
239 924 : rec->sql_desc_type_name = (SQLCHAR *) strdup(s);
240 924 : if (rec->sql_desc_type_name == NULL)
241 0 : goto nomem;
242 924 : concise_type = ODBCConciseType(s);
243 924 : if (concise_type == SQL_INTERVAL_MONTH) {
244 0 : switch (mapi_get_digits(hdl, i)) {
245 : case 1:
246 : concise_type = SQL_INTERVAL_YEAR;
247 : break;
248 : case 2:
249 : concise_type = SQL_INTERVAL_YEAR_TO_MONTH;
250 : break;
251 : case 3:
252 : concise_type = SQL_INTERVAL_MONTH;
253 : break;
254 : default:
255 0 : assert(0);
256 : }
257 924 : } else if (concise_type == SQL_INTERVAL_SECOND) {
258 0 : switch (mapi_get_digits(hdl, i)) {
259 : case 4:
260 : concise_type = SQL_INTERVAL_DAY;
261 : break;
262 : case 5:
263 : concise_type = SQL_INTERVAL_DAY_TO_HOUR;
264 : break;
265 : case 6:
266 : concise_type = SQL_INTERVAL_DAY_TO_MINUTE;
267 : break;
268 : case 7:
269 : concise_type = SQL_INTERVAL_DAY_TO_SECOND;
270 : break;
271 : case 8:
272 : concise_type = SQL_INTERVAL_HOUR;
273 : break;
274 : case 9:
275 : concise_type = SQL_INTERVAL_HOUR_TO_MINUTE;
276 : break;
277 : case 10:
278 : concise_type = SQL_INTERVAL_HOUR_TO_SECOND;
279 : break;
280 : case 11:
281 : concise_type = SQL_INTERVAL_MINUTE;
282 : break;
283 : case 12:
284 : concise_type = SQL_INTERVAL_MINUTE_TO_SECOND;
285 : break;
286 : case 13:
287 : concise_type = SQL_INTERVAL_SECOND;
288 : break;
289 : default:
290 0 : assert(0);
291 : }
292 : }
293 7086 : for (tp = ODBC_sql_types; tp->concise_type; tp++)
294 7086 : if (concise_type == tp->concise_type)
295 : break;
296 924 : rec->sql_desc_concise_type = tp->concise_type;
297 924 : rec->sql_desc_type = tp->type;
298 924 : rec->sql_desc_datetime_interval_code = tp->code;
299 924 : switch (concise_type) {
300 2 : case SQL_DECIMAL:
301 : case SQL_NUMERIC:
302 2 : rec->sql_desc_precision = mapi_get_digits(hdl, i);
303 2 : rec->sql_desc_scale = mapi_get_scale(hdl, i);
304 2 : break;
305 922 : default:
306 922 : if (tp->precision != UNAFFECTED)
307 6 : rec->sql_desc_precision = tp->precision;
308 922 : if (tp->scale != UNAFFECTED)
309 0 : rec->sql_desc_scale = tp->scale;
310 : break;
311 : }
312 924 : if (tp->datetime_interval_precision != UNAFFECTED)
313 0 : rec->sql_desc_datetime_interval_precision = tp->datetime_interval_precision;
314 924 : rec->sql_desc_fixed_prec_scale = tp->fixed;
315 924 : rec->sql_desc_num_prec_radix = tp->radix;
316 924 : rec->sql_desc_unsigned = tp->radix == 0 ? SQL_TRUE : SQL_FALSE;
317 :
318 924 : if (rec->sql_desc_concise_type == SQL_CHAR ||
319 : rec->sql_desc_concise_type == SQL_VARCHAR ||
320 : rec->sql_desc_concise_type == SQL_LONGVARCHAR ||
321 : rec->sql_desc_concise_type == SQL_WCHAR ||
322 : rec->sql_desc_concise_type == SQL_WVARCHAR ||
323 : rec->sql_desc_concise_type == SQL_WLONGVARCHAR)
324 535 : rec->sql_desc_case_sensitive = SQL_TRUE;
325 : else
326 389 : rec->sql_desc_case_sensitive = SQL_FALSE;
327 :
328 924 : s = mapi_get_table(hdl, i);
329 924 : if (s) {
330 924 : char *p = strchr(s, '.');
331 924 : if (rec->sql_desc_table_name)
332 22 : free(rec->sql_desc_table_name);
333 924 : if (p) {
334 924 : if (rec->sql_desc_schema_name)
335 2 : free(rec->sql_desc_schema_name);
336 924 : rec->sql_desc_schema_name = (SQLCHAR *) dupODBCstring((SQLCHAR *) s, p - s);
337 924 : if (rec->sql_desc_schema_name == NULL)
338 0 : goto nomem;
339 924 : rec->sql_desc_table_name = (SQLCHAR *) strdup(p + 1);
340 924 : if (rec->sql_desc_table_name == NULL)
341 0 : goto nomem;
342 924 : if (p != s) {
343 : /* base table name and base
344 : * column name exist if there
345 : * is a schema name */
346 87 : if (rec->sql_desc_base_table_name)
347 0 : free(rec->sql_desc_base_table_name);
348 87 : rec->sql_desc_base_table_name = (SQLCHAR *) strdup(p + 1);
349 87 : if (rec->sql_desc_base_table_name == NULL)
350 0 : goto nomem;
351 87 : if (rec->sql_desc_name) {
352 87 : rec->sql_desc_base_column_name = (SQLCHAR *) strdup((char *) rec->sql_desc_name);
353 87 : if (rec->sql_desc_base_column_name == NULL)
354 0 : goto nomem;
355 : }
356 : }
357 : } else {
358 0 : rec->sql_desc_table_name = (SQLCHAR *) strdup(s);
359 0 : if (rec->sql_desc_table_name == NULL)
360 0 : goto nomem;
361 : }
362 : }
363 :
364 924 : if (rec->sql_desc_type != SQL_INTERVAL &&
365 924 : (rec->sql_desc_length = mapi_get_digits(hdl, i)) == 0)
366 25 : rec->sql_desc_length = mapi_get_len(hdl, i);
367 :
368 924 : rec->sql_desc_local_type_name = NULL;
369 924 : rec->sql_desc_catalog_name = NULL;
370 924 : rec->sql_desc_literal_prefix = NULL;
371 924 : rec->sql_desc_literal_suffix = NULL;
372 :
373 : /* unused fields */
374 924 : rec->sql_desc_data_ptr = NULL;
375 924 : rec->sql_desc_indicator_ptr = NULL;
376 924 : rec->sql_desc_octet_length_ptr = NULL;
377 924 : rec->sql_desc_parameter_type = 0;
378 :
379 : /* this must come after other fields have been
380 : * initialized */
381 924 : if (rec->sql_desc_concise_type == SQL_CHAR ||
382 : rec->sql_desc_concise_type == SQL_VARCHAR ||
383 : rec->sql_desc_concise_type == SQL_LONGVARCHAR ||
384 : rec->sql_desc_concise_type == SQL_WCHAR ||
385 : rec->sql_desc_concise_type == SQL_WVARCHAR ||
386 : rec->sql_desc_concise_type == SQL_WLONGVARCHAR) {
387 : /* for strings, get the display size from what
388 : * the server told us the size is for this
389 : * column, and derive the octet length from
390 : * that */
391 535 : rec->sql_desc_display_size = mapi_get_len(hdl, i);
392 535 : rec->sql_desc_octet_length = 4 * rec->sql_desc_display_size;
393 :
394 : /* For large varchar column definitions conditionally
395 : * change type to SQL_WLONGVARCHAR when mapToLongVarchar is set (e.g. to 4000)
396 : * This is a workaround for MS SQL Server linked server
397 : * which can not handle large varchars (ref: SUPPORT-747) */
398 535 : if (rec->sql_desc_concise_type == SQL_WVARCHAR
399 520 : && stmt->Dbc->mapToLongVarchar > 0
400 2 : && rec->sql_desc_length > (SQLULEN) stmt->Dbc->mapToLongVarchar)
401 1 : rec->sql_desc_concise_type = SQL_WLONGVARCHAR;
402 : } else {
403 389 : rec->sql_desc_length = ODBCLength(rec, SQL_DESC_LENGTH);
404 389 : rec->sql_desc_display_size = ODBCLength(rec, SQL_DESC_DISPLAY_SIZE);
405 389 : rec->sql_desc_octet_length = ODBCLength(rec, SQL_DESC_OCTET_LENGTH);
406 : }
407 924 : if (rec->sql_desc_length == 0) {
408 6 : rec->sql_desc_length = SQL_NO_TOTAL;
409 6 : rec->sql_desc_display_size = SQL_NO_TOTAL;
410 6 : rec->sql_desc_octet_length = SQL_NO_TOTAL;
411 : }
412 :
413 924 : rec++;
414 : }
415 :
416 99 : return stmt->Error ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS;
417 :
418 0 : nomem:
419 : /* Memory allocation error */
420 0 : addStmtError(stmt, "HY001", NULL, 0);
421 0 : return SQL_ERROR;
422 : }
423 :
424 : SQLRETURN
425 2004 : MNDBExecute(ODBCStmt *stmt)
426 : {
427 2004 : MapiHdl hdl;
428 2004 : MapiMsg msg;
429 2004 : char *query;
430 2004 : const char *errstr;
431 2004 : char *sep;
432 2004 : size_t querylen;
433 2004 : size_t querypos;
434 2004 : int i;
435 2004 : ODBCDesc *desc;
436 2004 : SQLLEN offset;
437 2004 : long timeout;
438 :
439 : /* check statement cursor state, query should be prepared */
440 2004 : if (stmt->State == INITED ||
441 1999 : (stmt->State >= EXECUTED0 && stmt->queryid < 0)) {
442 : /* Function sequence error */
443 0 : addStmtError(stmt, "HY010", NULL, 0);
444 0 : return SQL_ERROR;
445 : }
446 2004 : if (stmt->State >= EXECUTED1 ||
447 1999 : (stmt->State == EXECUTED0 && mapi_more_results(stmt->hdl))) {
448 : /* Invalid cursor state */
449 0 : addStmtError(stmt, "24000", NULL, 0);
450 0 : return SQL_ERROR;
451 : }
452 :
453 2004 : assert(stmt->Dbc);
454 2004 : assert(stmt->Dbc->mid);
455 2004 : hdl = stmt->hdl;
456 :
457 2004 : assert(hdl);
458 :
459 2004 : desc = stmt->ApplParamDescr;
460 :
461 2004 : if (desc->sql_desc_count < stmt->nparams ||
462 2004 : stmt->ImplParamDescr->sql_desc_count < stmt->nparams) {
463 : /* COUNT field incorrect */
464 0 : addStmtError(stmt, "07002", NULL, 0);
465 0 : return SQL_ERROR;
466 : }
467 :
468 2004 : querylen = 1024;
469 2004 : query = malloc(querylen); /* XXX allocate space for parameters */
470 2004 : if (query == NULL) {
471 : /* Memory allocation error */
472 0 : addStmtError(stmt, "HY001", NULL, 0);
473 0 : return SQL_ERROR;
474 : }
475 2004 : if (stmt->qtimeout != stmt->Dbc->qtimeout) {
476 0 : snprintf(query, querylen, "call sys.%s(%" PRIu64 ")",
477 0 : (stmt->Dbc->major == 11 && stmt->Dbc->minor >= 37)
478 : ? "setquerytimeout" : "settimeout",
479 : (uint64_t) stmt->qtimeout);
480 0 : if (mapi_query_handle(hdl, query) == MOK)
481 0 : stmt->Dbc->qtimeout = stmt->qtimeout;
482 : }
483 2004 : querypos = snprintf(query, querylen, "execute %d (", stmt->queryid);
484 : /* XXX fill in parameter values */
485 2004 : if (desc->sql_desc_bind_offset_ptr)
486 0 : offset = *desc->sql_desc_bind_offset_ptr;
487 : else
488 : offset = 0;
489 2004 : sep = "";
490 12008 : for (i = 1; i <= stmt->nparams; i++) {
491 10004 : if (ODBCStore(stmt, i, offset, 0, &query, &querypos, &querylen, sep) == SQL_ERROR) {
492 0 : if (query)
493 0 : free(query);
494 0 : return SQL_ERROR;
495 : }
496 10004 : sep = ",";
497 : }
498 2004 : if (querypos + 1 >= querylen) {
499 0 : char *q = realloc(query, querylen += 10);
500 0 : if (q == NULL) {
501 0 : free(query);
502 0 : addStmtError(stmt, "HY001", NULL, 0);
503 0 : return SQL_ERROR;
504 : }
505 0 : query = q;
506 : }
507 2004 : query[querypos++] = ')';
508 2004 : query[querypos] = 0;
509 :
510 : #ifdef ODBCDEBUG
511 2004 : ODBCLOG("SQLExecute %p %s\n", stmt, query);
512 : #endif
513 :
514 : /* Have the server execute the query */
515 2004 : if (stmt->next == NULL && stmt->Dbc->FirstStmt == stmt &&
516 2003 : stmt->cursorType == SQL_CURSOR_FORWARD_ONLY) {
517 : /* we're the only Stmt handle, and we're only going forward */
518 2003 : if (stmt->Dbc->cachelimit != 10000)
519 3 : mapi_cache_limit(stmt->Dbc->mid, 10000);
520 2003 : stmt->Dbc->cachelimit = 10000;
521 : } else {
522 1 : if (stmt->Dbc->cachelimit != 100)
523 1 : mapi_cache_limit(stmt->Dbc->mid, 100);
524 1 : stmt->Dbc->cachelimit = 100;
525 : }
526 2004 : msg = mapi_query_handle(hdl, query);
527 2004 : free(query);
528 2004 : switch (msg) {
529 : case MOK:
530 2004 : break;
531 0 : case MTIMEOUT:
532 : /* Connection timeout expired / Communication link failure */
533 0 : timeout = msetting_long(stmt->Dbc->settings, MP_REPLY_TIMEOUT);
534 0 : addStmtError(stmt, timeout > 0 ? "HYT01" : "08S01", mapi_error_str(stmt->Dbc->mid), 0);
535 0 : return SQL_ERROR;
536 0 : default:
537 0 : errstr = mapi_result_error(hdl);
538 0 : if (errstr == NULL)
539 0 : errstr = mapi_error_str(stmt->Dbc->mid);
540 0 : if (errstr) {
541 0 : const char *sqlstate;
542 :
543 0 : if ((sqlstate = mapi_result_errorcode(hdl)) != NULL) {
544 0 : addStmtError(stmt, sqlstate, errstr, 0);
545 0 : return SQL_ERROR;
546 : }
547 : }
548 : /* General error */
549 0 : addStmtError(stmt, "HY000", errstr, 0);
550 0 : return SQL_ERROR;
551 : }
552 :
553 : /* now get the result data and store it to our internal data structure */
554 :
555 2004 : return ODBCInitResult(stmt);
556 : }
557 :
558 : SQLRETURN SQL_API
559 : SQLExecute(SQLHSTMT StatementHandle)
560 : {
561 : #ifdef ODBCDEBUG
562 2004 : ODBCLOG("SQLExecute %p\n", StatementHandle);
563 : #endif
564 :
565 2004 : if (!isValidStmt((ODBCStmt *) StatementHandle))
566 : return SQL_INVALID_HANDLE;
567 :
568 2004 : clearStmtErrors((ODBCStmt *) StatementHandle);
569 :
570 2004 : return MNDBExecute((ODBCStmt *) StatementHandle);
571 : }
|