GCC Code Coverage Report


Directory: src/gate/
File: src/gate/data/odbc_adapter.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 0 2 0.0%
Functions: 0 1 0.0%
Branches: 0 0 -%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28 #include "gate/data/odbc_adapter.h"
29 #include "gate/results.h"
30 #include "gate/libraries.h"
31 #include "gate/arrays.h"
32 #include "gate/debugging.h"
33 #include "gate/guids.h"
34 #include "gate/times.h"
35 #include "gate/data/adapter_base.h"
36
37 #if defined(GATE_DATA_ODBC_ENABLED)
38 # if !defined(GATE_SYS_WINCE) && !defined(GATE_SYS_WINSTORE) && !defined(GATE_SYS_ANDROID) && !defined(GATE_SYS_DOS) && !defined(GATE_SYS_EFI) && !defined(GATE_SYS_OPENBSD) && !defined(GATE_SYS_WASM) && !defined(GATE_SYS_BEOS)
39 # define GATE_DATA_ODBC_IMPL
40 # endif
41 #endif
42
43
44 #ifdef GATE_DATA_ODBC_IMPL
45
46 #include "gate/platforms.h"
47 #include <sql.h>
48 #include <sqlext.h>
49
50 #if defined(GATE_COMPILER_MSVC98)
51 # ifdef _WIN64
52 typedef INT64 SQLLEN;
53 typedef UINT64 SQLULEN;
54 # else
55 # define SQLLEN SQLINTEGER
56 # define SQLULEN SQLUINTEGER
57 # endif
58 #endif
59
60 typedef struct gate_odbc_functions
61 {
62 SQLRETURN(SQL_API* AllocConnect)(SQLHENV EnvironmentHandle, SQLHDBC* ConnectionHandle);
63 SQLRETURN(SQL_API* FreeEnv)(SQLHENV EnvironmentHandle);
64 SQLRETURN(SQL_API* Connect)(SQLHDBC ConnectionHandle, SQLTCHAR* ServerName, SQLSMALLINT NameLength1,
65 SQLTCHAR* UserName, SQLSMALLINT NameLength2, SQLTCHAR* Authentication, SQLSMALLINT NameLength3);
66 SQLRETURN(SQL_API* FreeConnect)(SQLHDBC ConnectionHandle);
67 SQLRETURN(SQL_API* Disconnect)(SQLHDBC ConnectionHandle);
68 SQLRETURN(SQL_API* AllocStmt)(SQLHDBC ConnectionHandle, SQLHSTMT* StatementHandle);
69 SQLRETURN(SQL_API* Fetch)(SQLHSTMT StatementHandle);
70 SQLRETURN(SQL_API* FreeStmt)(SQLHSTMT StatementHandle, SQLUSMALLINT Option);
71 SQLRETURN(SQL_API* GetData)(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber,
72 SQLSMALLINT TargetType, SQLPOINTER TargetValue, SQLLEN BufferLength, SQLLEN* StrLen_or_Ind);
73 SQLRETURN(SQL_API* DescribeCol)(SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLTCHAR* ColumnName,
74 SQLSMALLINT BufferLength, SQLSMALLINT* NameLength, SQLSMALLINT* DataType,
75 SQLULEN* ColumnSize, SQLSMALLINT* DecimalDigits, SQLSMALLINT* Nullable);
76 SQLRETURN(SQL_API* AllocEnv)(SQLHENV* EnvironmentHandle);
77 SQLRETURN(SQL_API* Prepare)(SQLHSTMT StatementHandle, SQLTCHAR* StatementText, SQLINTEGER TextLength);
78 SQLRETURN(SQL_API* Execute)(SQLHSTMT StatementHandle);
79 SQLRETURN(SQL_API* GetDiagRec)(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLTCHAR* Sqlstate,
80 SQLINTEGER* NativeError, SQLTCHAR* MessageText, SQLSMALLINT BufferLength, SQLSMALLINT* TextLength);
81 SQLRETURN(SQL_API* NumResultCols)(SQLHSTMT StatementHandle, SQLSMALLINT* ColumnCount);
82 SQLRETURN(SQL_API* BindParameter)(SQLHSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT fParamType, SQLSMALLINT fCType,
83 SQLSMALLINT fSqlType, SQLULEN cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLLEN cbValueMax, SQLLEN* pcbValue);
84 SQLRETURN(SQL_API* NumParams)(SQLHSTMT StatementHandle, SQLSMALLINT* ParameterCountPtr);
85 SQLRETURN(SQL_API* RowCount)(SQLHSTMT StatementHandle, SQLLEN* RowCount);
86 } gate_odbc_functions_t;
87
88 static gate_odbc_functions_t SQL = GATE_INIT_EMPTY;
89
90 static const gate_string_t lib_name = GATE_STRING_INIT_STATIC("odbc32");
91
92 #define Name_AllocConnect "SQLAllocConnect"
93 #define Name_FreeEnv "SQLFreeEnv"
94 #define Name_FreeConnect "SQLFreeConnect"
95 #define Name_Disconnect "SQLDisconnect"
96 #define Name_AllocStmt "SQLAllocStmt"
97 #define Name_Fetch "SQLFetch"
98 #define Name_FreeStmt "SQLFreeStmt"
99 #define Name_GetData "SQLGetData"
100 #define Name_AllocEnv "SQLAllocEnv"
101 #define Name_Execute "SQLExecute"
102 #define Name_NumResultCols "SQLNumResultCols"
103 #define Name_BindParameter "SQLBindParameter"
104 #define Name_NumParams "SQLNumParams"
105 #define Name_RowCount "SQLRowCount"
106
107 #if defined(GATE_WIN32_UNICODE)
108 # define Name_Connect "SQLConnectW"
109 # define Name_DescribeCol "SQLDescribeColW"
110 # define Name_Prepare "SQLPrepareW"
111 # define Name_GetDiagRec "SQLGetDiagRecW"
112 #else
113 # define Name_Connect "SQLConnect"
114 # define Name_DescribeCol "SQLDescribeCol"
115 # define Name_Prepare "SQLPrepare"
116 # define Name_GetDiagRec "SQLGetDiagRec"
117 #endif
118
119 static gate_result_t load_odbc_functions()
120 {
121 static gate_library_t odbc_lib = NULL;
122 gate_result_t ret = GATE_RESULT_FAILED;
123 gate_library_t lib = NULL;
124
125 do
126 {
127 if (odbc_lib != NULL)
128 {
129 ret = GATE_RESULT_OK;
130 }
131
132 ret = gate_library_open(&lib_name, &lib, 0);
133 GATE_BREAK_IF_FAILED(ret);
134
135 ret = gate_library_get_function_name(lib, Name_AllocConnect, &SQL.AllocConnect);
136 GATE_BREAK_IF_FAILED(ret);
137 ret = gate_library_get_function_name(lib, Name_FreeEnv, &SQL.FreeEnv);
138 GATE_BREAK_IF_FAILED(ret);
139 ret = gate_library_get_function_name(lib, Name_FreeConnect, &SQL.FreeConnect);
140 GATE_BREAK_IF_FAILED(ret);
141 ret = gate_library_get_function_name(lib, Name_Disconnect, &SQL.Disconnect);
142 GATE_BREAK_IF_FAILED(ret);
143 ret = gate_library_get_function_name(lib, Name_AllocStmt, &SQL.AllocStmt);
144 GATE_BREAK_IF_FAILED(ret);
145 ret = gate_library_get_function_name(lib, Name_Fetch, &SQL.Fetch);
146 GATE_BREAK_IF_FAILED(ret);
147 ret = gate_library_get_function_name(lib, Name_FreeStmt, &SQL.FreeStmt);
148 GATE_BREAK_IF_FAILED(ret);
149 ret = gate_library_get_function_name(lib, Name_GetData, &SQL.GetData);
150 GATE_BREAK_IF_FAILED(ret);
151 ret = gate_library_get_function_name(lib, Name_AllocEnv, &SQL.AllocEnv);
152 GATE_BREAK_IF_FAILED(ret);
153 ret = gate_library_get_function_name(lib, Name_Execute, &SQL.Execute);
154 GATE_BREAK_IF_FAILED(ret);
155 ret = gate_library_get_function_name(lib, Name_NumResultCols, &SQL.NumResultCols);
156 GATE_BREAK_IF_FAILED(ret);
157 ret = gate_library_get_function_name(lib, Name_Connect, &SQL.Connect);
158 GATE_BREAK_IF_FAILED(ret);
159 ret = gate_library_get_function_name(lib, Name_DescribeCol, &SQL.DescribeCol);
160 GATE_BREAK_IF_FAILED(ret);
161 ret = gate_library_get_function_name(lib, Name_Prepare, &SQL.Prepare);
162 GATE_BREAK_IF_FAILED(ret);
163 ret = gate_library_get_function_name(lib, Name_GetDiagRec, &SQL.GetDiagRec);
164 GATE_BREAK_IF_FAILED(ret);
165 ret = gate_library_get_function_name(lib, Name_BindParameter, &SQL.BindParameter);
166 GATE_BREAK_IF_FAILED(ret);
167 ret = gate_library_get_function_name(lib, Name_NumParams, &SQL.NumParams);
168 GATE_BREAK_IF_FAILED(ret);
169 ret = gate_library_get_function_name(lib, Name_RowCount, &SQL.RowCount);
170 GATE_BREAK_IF_FAILED(ret);
171
172 odbc_lib = lib;
173 lib = NULL;
174 ret = GATE_RESULT_OK;
175 } while (0);
176
177 if (lib != NULL)
178 {
179 gate_library_close(lib);
180 }
181
182 return ret;
183 }
184
185
186
187 static gate_bool_t gate_data_reader_odbc_is_valid(gate_data_reader_base_t* reader)
188 {
189 SQLHSTMT stmt = (SQLHSTMT)reader->native_handles[0];
190 return stmt != NULL;
191 }
192
193 static gate_result_t gate_data_reader_odbc_close(gate_data_reader_base_t* reader)
194 {
195 reader->native_handles[0] = NULL;
196 return GATE_RESULT_OK;
197 }
198
199 static SQLSMALLINT gate_data_reader_odbc_patch_sql_type(SQLSMALLINT defined_type)
200 {
201 /* some types are defined but in SQLGetData another type must be used */
202 switch (defined_type)
203 {
204 case SQL_DECIMAL:
205 case SQL_NUMERIC:
206 case SQL_CHAR:
207 case SQL_VARCHAR:
208 case SQL_LONGVARCHAR: return SQL_CHAR;
209 case SQL_WVARCHAR:
210 case SQL_WLONGVARCHAR: return SQL_WCHAR;
211 default: break;
212 }
213 return defined_type;
214 }
215
216 static gate_value_t* gate_data_reader_odbc_get_value(SQLHSTMT hstmt, SQLUSMALLINT index, SQLSMALLINT sql_type, gate_value_t* value)
217 {
218 gate_value_t* ret = NULL;
219 char buffer[8192] = GATE_INIT_EMPTY;
220 SQLLEN buffer_used = 0;
221 SQLRETURN sql_res;
222 gate_bool_t bool_val;
223 gate_strbuilder_t text_builder = GATE_INIT_EMPTY;
224 gate_string_t text = GATE_STRING_INIT_EMPTY;
225 gate_arraylist_t arrlst = NULL;
226 gate_array_t arr = GATE_INIT_EMPTY;
227 SQLGUID const* ptr_sql_guid = NULL;
228 gate_guid_t guid;
229 DATE_STRUCT const* ptr_date_struct = NULL;
230 TIME_STRUCT const* ptr_time_struct = NULL;
231 TIMESTAMP_STRUCT const* ptr_timestamp_struct = NULL;
232 gate_datetime_t dt;
233
234 sql_type = gate_data_reader_odbc_patch_sql_type(sql_type);
235
236 sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
237 switch (sql_res)
238 {
239 case SQL_SUCCESS:
240 case SQL_NO_DATA:
241 case SQL_SUCCESS_WITH_INFO:
242 {
243 /* OK, continue */
244 break;
245 }
246 default:
247 {
248 /* error case */
249 return ret;
250 }
251 }
252
253 switch (sql_type)
254 {
255 case SQL_C_STINYINT:
256 case SQL_TINYINT: { ret = gate_value_create(GATE_TYPE_I8, buffer, value); break; }
257 case SQL_C_SSHORT:
258 case SQL_SMALLINT: { ret = gate_value_create(GATE_TYPE_I16, buffer, value); break; }
259 case SQL_C_SLONG:
260 case SQL_INTEGER: { ret = gate_value_create(GATE_TYPE_I32, buffer, value); break; }
261 case SQL_C_SBIGINT:
262 case SQL_BIGINT: { ret = gate_value_create(GATE_TYPE_I64, buffer, value); break; }
263 case SQL_REAL: { ret = gate_value_create(GATE_TYPE_R32, buffer, value); break; }
264 case SQL_FLOAT: { ret = gate_value_create(GATE_TYPE_R32, buffer, value); break; }
265 case SQL_DOUBLE: { ret = gate_value_create(GATE_TYPE_R64, buffer, value); break; }
266 case SQL_C_UTINYINT: { ret = gate_value_create(GATE_TYPE_UI8, buffer, value); break; }
267 case SQL_C_USHORT: { ret = gate_value_create(GATE_TYPE_UI16, buffer, value); break; }
268 case SQL_C_ULONG: { ret = gate_value_create(GATE_TYPE_UI32, buffer, value); break; }
269 case SQL_C_UBIGINT: { ret = gate_value_create(GATE_TYPE_UI64, buffer, value); break; }
270 case SQL_BIT:
271 {
272 bool_val = buffer[0] == 0 ? false : true;
273 ret = gate_value_create(GATE_TYPE_BOOL, &bool_val, value);
274 break;
275 }
276
277 case SQL_DECIMAL:
278 case SQL_NUMERIC:
279 case SQL_CHAR:
280 case SQL_VARCHAR:
281 case SQL_LONGVARCHAR:
282 {
283 /* extract byte data */
284 gate_strbuilder_create(&text_builder, 0);
285 gate_strbuilder_append_text(&text_builder, &buffer[0], buffer_used);
286 while (sql_res == SQL_SUCCESS_WITH_INFO)
287 {
288 sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
289 gate_strbuilder_append_text(&text_builder, &buffer[0], buffer_used);
290 }
291 gate_strbuilder_to_string(&text_builder, &text);
292 ret = gate_value_create(GATE_TYPE_STRING, &text, value);
293 gate_string_release(&text);
294 gate_strbuilder_release(&text_builder);
295 break;
296 }
297 case SQL_WCHAR:
298 case SQL_WVARCHAR:
299 case SQL_WLONGVARCHAR:
300 {
301 /* extract UTF16 text */
302 gate_strbuilder_create(&text_builder, 0);
303 gate_strbuilder_append_text16(&text_builder, (gate_char16_t const*)&buffer[0], buffer_used / sizeof(gate_char16_t));
304 while (sql_res == SQL_SUCCESS_WITH_INFO)
305 {
306 sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
307 gate_strbuilder_append_text16(&text_builder, (gate_char16_t const*)&buffer[0], buffer_used / sizeof(gate_char16_t));
308 }
309 gate_strbuilder_to_string(&text_builder, &text);
310 ret = gate_value_create(GATE_TYPE_STRING, &text, value);
311 gate_string_release(&text);
312 gate_strbuilder_release(&text_builder);
313 break;
314 }
315 case SQL_BINARY:
316 case SQL_VARBINARY:
317 case SQL_LONGVARBINARY:
318 {
319 arrlst = gate_arraylist_create(1, buffer, buffer_used, NULL, NULL);
320 if (arrlst)
321 {
322 while (sql_res == SQL_SUCCESS_WITH_INFO)
323 {
324 sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
325 gate_arraylist_add_n(arrlst, buffer, buffer_used);
326 }
327 if (NULL != gate_array_create(&arr, arrlst))
328 {
329 ret = gate_value_create(GATE_TYPE_ARRAY, &arr, value);
330 gate_array_release(&arr);
331 }
332 gate_arraylist_release(arrlst);
333 }
334 break;
335 }
336 case SQL_GUID:
337 {
338 ptr_sql_guid = (SQLGUID const*)&buffer[0];
339 guid.item1 = ptr_sql_guid->Data1;
340 guid.item2 = ptr_sql_guid->Data2;
341 guid.item3 = ptr_sql_guid->Data3;
342 gate_mem_copy(guid.item4, ptr_sql_guid->Data4, 8);
343 ret = gate_value_create(GATE_TYPE_GUID, &guid, value);
344 break;
345 }
346 case SQL_TYPE_DATE:
347 {
348 ptr_date_struct = (DATE_STRUCT const*)&buffer[0];
349 dt.date.year = ptr_date_struct->year;
350 dt.date.month = (gate_uint8_t)ptr_date_struct->month;
351 dt.date.day = (gate_uint8_t)ptr_date_struct->day;
352 ret = gate_value_create(GATE_TYPE_DATE, &dt.date, value);
353 break;
354 }
355 case SQL_TYPE_TIME:
356 {
357 ptr_time_struct = (TIME_STRUCT const*)&buffer[0];
358 dt.time.hour = (gate_uint8_t)ptr_time_struct->hour;
359 dt.time.minute = (gate_uint8_t)ptr_time_struct->minute;
360 dt.time.second = (gate_uint8_t)ptr_time_struct->second;
361 dt.time.microsecond = 0;
362 ret = gate_value_create(GATE_TYPE_DAYTIME, &dt.time, value);
363 break;
364 }
365 case SQL_TYPE_TIMESTAMP:
366 {
367 ptr_timestamp_struct = (TIMESTAMP_STRUCT const*)&buffer[0];
368 dt.date.year = ptr_timestamp_struct->year;
369 dt.date.month = (gate_uint8_t)ptr_timestamp_struct->month;
370 dt.date.day = (gate_uint8_t)ptr_timestamp_struct->day;
371 dt.time.hour = (gate_uint8_t)ptr_timestamp_struct->hour;
372 dt.time.minute = (gate_uint8_t)ptr_timestamp_struct->minute;
373 dt.time.second = (gate_uint8_t)ptr_timestamp_struct->second;
374 dt.time.microsecond = ptr_timestamp_struct->fraction / 1000; /*fraction == nanoseconds */
375 ret = gate_value_create(GATE_TYPE_DAYTIME, &dt.time, value);
376 break;
377 }
378 default:
379 {
380 /* not supported */
381 break;
382 }
383 }
384
385 return ret;
386 }
387
388 static gate_result_t gate_data_reader_odbc_next(gate_data_reader_base_t* reader)
389 {
390 gate_result_t ret = GATE_RESULT_FAILED;
391 SQLHSTMT hstmt = (SQLHSTMT)reader->native_handles[0];
392 SQLRETURN sql_res;
393 gate_size_t ndx;
394
395 do
396 {
397 if (hstmt == NULL)
398 {
399 ret = GATE_RESULT_INVALIDSTATE;
400 break;
401 }
402
403 sql_res = SQL.Fetch(hstmt);
404 if (SQL_SUCCESS != sql_res)
405 {
406 ret = GATE_RESULT_FAILED;
407 break;
408 }
409
410 ret = GATE_RESULT_OK;
411 for (ndx = 0; ndx != reader->field_count; ++ndx)
412 {
413 gate_value_release(&reader->values[ndx]);
414 gate_data_reader_odbc_get_value(hstmt, (SQLUSMALLINT)(ndx + 1), (SQLUSMALLINT)reader->types[ndx], &reader->values[ndx]);
415 }
416
417 } while (0);
418
419 return ret;
420 }
421
422
423 static gate_result_t gate_data_reader_odbc_init(gate_data_reader_base_t* reader)
424 {
425 gate_result_t ret = GATE_RESULT_OK;
426 SQLSMALLINT col_count = 0;
427 SQLSMALLINT col_index;
428 SQLRETURN sql_ret;
429 SQLHSTMT hstmt = (SQLHSTMT)reader->native_handles[0];
430 SQLTCHAR colname[1024];
431 SQLSMALLINT colname_len;
432 SQLSMALLINT coltype;
433 SQLULEN colvalue_len;
434 SQLSMALLINT colvalue_digits;
435 SQLSMALLINT colvalue_nullable;
436
437 do
438 {
439 sql_ret = SQL.NumResultCols(hstmt, &col_count);
440 if (SQL_SUCCESS != sql_ret)
441 {
442 ret = GATE_RESULT_FAILED;
443 break;
444 }
445 reader->field_count = col_count;
446
447 for (col_index = 0; col_index != col_count; ++col_index)
448 {
449 colname_len = sizeof(colname) / sizeof(colname[0]);
450 coltype = 0;
451 colvalue_len = 0;
452 colvalue_digits = 0;
453 colvalue_nullable = 0;
454 sql_ret = SQL.DescribeCol(hstmt, (SQLUSMALLINT)(col_index + 1), colname, colname_len, &colname_len,
455 &coltype, &colvalue_len, &colvalue_digits, &colvalue_nullable);
456 #if defined(GATE_SYS_WIN)
457 gate_win32_winstr_2_utf8_string((LPCTSTR)&colname[0], colname_len, &reader->names[col_index]);
458 #else
459 gate_string_create(&reader->names[col_index], (char const*)&colname[0], colname_len);
460 #endif
461 reader->types[col_index] = coltype;
462 }
463 } while (0);
464
465 return ret;
466 }
467
468
469 static gate_data_reader_t* gate_data_reader_odbc_create(gate_data_statement_base_t* ptr_statement, SQLHSTMT stmt)
470 {
471 gate_data_reader_t* ret = NULL;
472 gate_data_handles_t handles = GATE_INIT_EMPTY;
473
474 handles[0] = (void*)stmt;
475
476 ret = gate_data_reader_base_create(
477 (gate_data_statement_t*)ptr_statement, handles,
478 &gate_data_reader_odbc_init,
479 &gate_data_reader_odbc_is_valid,
480 &gate_data_reader_odbc_next,
481 &gate_data_reader_odbc_close);
482
483 return ret;
484 }
485
486
487 static gate_result_t odbcstmt_init(gate_data_statement_base_t* self)
488 {
489 return GATE_RESULT_OK;
490 }
491
492 static void odbcstmt_close(gate_data_statement_base_t* self)
493 {
494 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
495 if (hstmt != NULL)
496 {
497 SQL.FreeStmt(hstmt, SQL_CLOSE);
498 SQL.FreeStmt(hstmt, SQL_DROP);
499 self->native_handles[1] = NULL;
500 }
501 }
502
503 static gate_result_t odbcstmt_convert_result(SQLRETURN sql_result)
504 {
505 if (SQL_SUCCEEDED(sql_result))
506 {
507 return GATE_RESULT_OK;
508 }
509 else
510 {
511 /* TODO: detailed error analysis */
512 return GATE_RESULT_FAILED;
513 }
514 }
515
516 static gate_result_t odbcstmt_set_null_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index)
517 {
518 SQLINTEGER param1 = 123;
519 SQLLEN param2Ind = SQL_NULL_DATA;
520 SQLRETURN sql_result = SQL.BindParameter(
521 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 0, 0, NULL, 0, &param2Ind);
522 return odbcstmt_convert_result(sql_result);
523 }
524
525 static gate_result_t odbcstmt_set_bigint_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_int64_t value)
526 {
527 SQLBIGINT bigint = value;
528 SQLRETURN sql_result = SQL.BindParameter(
529 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &bigint, 0, NULL);
530 return odbcstmt_convert_result(sql_result);
531 }
532 static gate_result_t odbcstmt_set_int_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_int32_t value)
533 {
534 SQLINTEGER n = value;
535 SQLRETURN sql_result = SQL.BindParameter(
536 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &n, 0, NULL);
537 return odbcstmt_convert_result(sql_result);
538 }
539 static gate_result_t odbcstmt_set_real_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_real64_t value)
540 {
541 SQLDOUBLE n = value;
542 SQLRETURN sql_result = SQL.BindParameter(
543 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, 0, 0, &n, 0, NULL);
544 return odbcstmt_convert_result(sql_result);
545 }
546
547 static gate_result_t odbcstmt_set_text_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, char const* text, gate_size_t textlen)
548 {
549 SQLPOINTER ptr_text = (SQLPOINTER)text;
550 SQLULEN len = (SQLULEN)textlen;
551 SQLRETURN sql_result = SQL.BindParameter(
552 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, len, 0, ptr_text, 0, NULL);
553 return odbcstmt_convert_result(sql_result);
554 }
555
556 static gate_result_t odbcstmt_set_binary_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, void const* data, gate_size_t datalen)
557 {
558 SQLPOINTER ptr_text = (SQLPOINTER)data;
559 SQLULEN len = (SQLULEN)datalen;
560 SQLLEN lenret = (SQLLEN)datalen;
561 SQLRETURN sql_result = SQL.BindParameter(
562 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, len, 0, ptr_text, 0, &lenret);
563 return odbcstmt_convert_result(sql_result);
564 }
565
566 static gate_result_t odbcstmt_set_datetime_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_datetime_t const* dt)
567 {
568 SQLRETURN sql_result;
569 SQL_TIMESTAMP_STRUCT sql_dt;
570 sql_dt.year = dt->date.year;
571 sql_dt.month = dt->date.month;
572 sql_dt.day = dt->date.day;
573 sql_dt.hour = dt->time.hour;
574 sql_dt.minute = dt->time.minute;
575 sql_dt.second = dt->time.second;
576 sql_dt.fraction = 0;
577
578 sql_result = SQL.BindParameter(
579 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, 0, 0, &sql_dt, 0, NULL);
580 return odbcstmt_convert_result(sql_result);
581 }
582
583
584
585 static gate_result_t odbcstmt_set_param(gate_data_statement_base_t* self, gate_size_t index, gate_value_t const* value)
586 {
587 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
588 SQLUSMALLINT odbc_index = (SQLUSMALLINT)(index + 1);
589 gate_cstrbuffer8_t buffer;
590 gate_datetime_t dt = GATE_INIT_EMPTY;
591 gate_result_t ret = GATE_RESULT_FAILED;
592
593 if ((value == NULL) || (value->value_type == GATE_TYPE_EMPTY))
594 {
595 return odbcstmt_set_null_param(hstmt, odbc_index);
596 }
597
598 switch (value->value_type)
599 {
600 case GATE_TYPE_BOOL: return odbcstmt_set_int_param(hstmt, odbc_index, value->content.bool_value ? 1 : 0);
601 case GATE_TYPE_I8: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.i8_value);
602 case GATE_TYPE_I16: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.i16_value);
603 case GATE_TYPE_I32: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.i32_value);
604 case GATE_TYPE_I64: return odbcstmt_set_bigint_param(hstmt, odbc_index, (gate_int32_t)value->content.i64_value);
605 case GATE_TYPE_UI8: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.ui8_value);
606 case GATE_TYPE_UI16: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.ui16_value);
607 case GATE_TYPE_UI32: return odbcstmt_set_bigint_param(hstmt, odbc_index, (gate_int64_t)value->content.ui32_value);
608 case GATE_TYPE_UI64: return odbcstmt_set_bigint_param(hstmt, odbc_index, (gate_int64_t)value->content.ui64_value);
609
610 case GATE_TYPE_R32: return odbcstmt_set_real_param(hstmt, odbc_index, (gate_real64_t)value->content.r32_value);
611 case GATE_TYPE_R64: return odbcstmt_set_real_param(hstmt, odbc_index, (gate_real64_t)value->content.r64_value);
612
613 case GATE_TYPE_BLOB: return odbcstmt_set_binary_param(hstmt, odbc_index, value->content.blob_value.data, value->content.blob_value.length);
614
615 case GATE_TYPE_CSTR: return odbcstmt_set_text_param(hstmt, odbc_index, value->content.cstring_value, gate_str_length(value->content.cstring_value));
616 case GATE_TYPE_STRING: return odbcstmt_set_text_param(hstmt, odbc_index, value->content.string_value.str, value->content.string_value.length);
617
618 case GATE_TYPE_WSTR:
619 {
620 if (NULL == gate_cstrbuffer_create_wstr(&buffer, value->content.wstring_value, gate_wstr_length(value->content.wstring_value)))
621 {
622 return GATE_RESULT_OUTOFMEMORY;
623 }
624 ret = odbcstmt_set_text_param(hstmt, odbc_index, gate_cstrbuffer_get(&buffer), gate_cstrbuffer_length(&buffer));
625 gate_cstrbuffer_destroy(&buffer);
626 return ret;
627 }
628 case GATE_TYPE_GUID:
629 {
630 ret = gate_guid_to_string(&value->content.guid_value, buffer.local_buffer);
631 GATE_RETURN_IF_FAILED(ret);
632 return odbcstmt_set_text_param(hstmt, odbc_index, buffer.local_buffer, gate_str_length(buffer.local_buffer));
633 }
634 case GATE_TYPE_DATE:
635 {
636 dt.date = value->content.date_value;
637 return odbcstmt_set_datetime_param(hstmt, odbc_index, &dt);
638 }
639 case GATE_TYPE_DATETIME:
640 {
641 return odbcstmt_set_datetime_param(hstmt, odbc_index, &value->content.datetime_value);
642 }
643 case GATE_TYPE_TIME:
644 {
645 ret = gate_time_to_datetime(&value->content.time_value, &dt);
646 GATE_RETURN_IF_FAILED(ret);
647 return odbcstmt_set_datetime_param(hstmt, odbc_index, &dt);
648 }
649 case GATE_TYPE_PROPERTY:
650 default:
651 return GATE_RESULT_INCORRECTTYPE;
652 }
653 }
654
655 static gate_result_t odbcstmt_set_named_param(gate_data_statement_base_t* self, gate_string_t const* name, gate_value_t const* value)
656 {
657 /* named parameters are not supported by odbc */
658 return GATE_RESULT_NOTSUPPORTED;
659 }
660
661 static gate_result_t odbcstmt_reset(gate_data_statement_base_t* self)
662 {
663 SQLRETURN sql_ret;
664 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
665
666 if (hstmt == NULL)
667 {
668 return GATE_RESULT_INVALIDSTATE;
669 }
670
671 sql_ret = SQL.FreeStmt(hstmt, SQL_RESET_PARAMS);
672 switch (sql_ret)
673 {
674 case SQL_SUCCESS:
675 case SQL_SUCCESS_WITH_INFO:
676 return GATE_RESULT_OK;
677 default:
678 return GATE_RESULT_FAILED;
679 }
680 }
681
682 static gate_result_t odbcstmt_execute(gate_data_statement_base_t* self, gate_int32_t* affected_rows)
683 {
684 SQLRETURN sql_ret;
685 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
686 SQLLEN len_rows = 0;
687
688 if (hstmt == NULL)
689 {
690 return GATE_RESULT_INVALIDSTATE;
691 }
692
693 sql_ret = SQL.Execute(hstmt);
694 if (SQL_SUCCESS != sql_ret)
695 {
696 return GATE_RESULT_FAILED;
697 }
698
699 /* success case */
700 if (affected_rows)
701 {
702 SQL.RowCount(hstmt, &len_rows);
703 *affected_rows = (gate_int32_t)len_rows;
704 }
705 return GATE_RESULT_OK;
706 }
707
708 static gate_result_t odbcstmt_query(gate_data_statement_base_t* self, gate_data_reader_t** ptr_reader)
709 {
710 SQLRETURN sql_ret;
711 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
712 SQLLEN len_rows = 0;
713
714 if (hstmt == NULL)
715 {
716 return GATE_RESULT_INVALIDSTATE;
717 }
718
719 sql_ret = SQL.Execute(hstmt);
720 if (SQL_SUCCESS != sql_ret)
721 {
722 return GATE_RESULT_FAILED;
723 }
724
725 if (ptr_reader == NULL)
726 {
727 /* success, reader not required, free unused resources */
728 SQL.FreeStmt(hstmt, SQL_CLOSE);
729 SQL.FreeStmt(hstmt, SQL_DROP);
730 return GATE_RESULT_OK;
731 }
732
733 *ptr_reader = gate_data_reader_odbc_create(self, hstmt);
734 if (*ptr_reader == NULL)
735 {
736 /* failed to allocate data reader */
737 SQL.FreeStmt(hstmt, SQL_CLOSE);
738 SQL.FreeStmt(hstmt, SQL_DROP);
739 return GATE_RESULT_FAILED;
740 }
741 return GATE_RESULT_OK;
742
743 }
744
745
746
747
748
749
750
751
752 gate_result_t gate_data_connection_odbc_create_statement(gate_data_connection_base_t* self, gate_string_t const* sql, gate_data_statement_t** ptr_stmt)
753 {
754 gate_result_t ret = GATE_RESULT_FAILED;
755 SQLHSTMT hstmt = NULL;
756 SQLHDBC hdbc = self->native_handles[0];
757 SQLRETURN sql_ret;
758 SQLTCHAR cmd[4096 + 2048 + 1024];
759 SQLSMALLINT param_count = 0;
760 gate_size_t cmd_len;
761 gate_data_handles_t handles;
762 gate_data_statement_base_t* ptr_stmt_base = NULL;
763
764 do
765 {
766 #if defined(GATE_SYS_WIN)
767 cmd_len = gate_win32_utf8_2_winstr(sql->str, sql->length, (LPTSTR)&cmd[0], sizeof(cmd) / sizeof(cmd[0]));
768 #else
769 cmd_len = gate_string_to_buffer(sql, (char*)&cmd[0], sizeof(cmd) / sizeof(cmd[0]));
770 #endif
771
772 sql_ret = SQL.AllocStmt(hdbc, &hstmt);
773 if (SQL_SUCCESS != sql_ret)
774 {
775 ret = GATE_RESULT_OUTOFRESOURCES;
776 break;
777 }
778
779 sql_ret = SQL.Prepare(hstmt, cmd, (SQLINTEGER)cmd_len);
780 if (SQL_SUCCESS != sql_ret)
781 {
782 ret = GATE_RESULT_FAILED;
783 break;
784 }
785
786 gate_mem_clear(&handles[0], sizeof(handles));
787 handles[0] = (void*)hdbc;
788 handles[1] = (void*)hstmt;
789
790 ret = gate_data_statement_base_create(
791 self, handles, sql,
792 &odbcstmt_init,
793 &odbcstmt_close,
794 &odbcstmt_set_param,
795 &odbcstmt_set_named_param,
796 &odbcstmt_reset,
797 &odbcstmt_execute,
798 &odbcstmt_query,
799 &ptr_stmt_base);
800
801 GATE_BREAK_IF_FAILED(ret);
802
803 if (SQL_ERROR != SQL.NumParams(hstmt, &param_count))
804 {
805 ptr_stmt_base->param_count = param_count;
806 }
807
808 /* success case: */
809 if (ptr_stmt != NULL)
810 {
811 *ptr_stmt = (gate_data_statement_t*)ptr_stmt_base;
812 ptr_stmt_base = NULL;
813 }
814 hstmt = NULL;
815 ret = GATE_RESULT_OK;
816 } while (0);
817
818 if (ptr_stmt_base != NULL)
819 {
820 gate_object_release(ptr_stmt_base);
821 }
822
823 if (hstmt != NULL)
824 {
825 SQL.FreeStmt(hstmt, SQL_CLOSE);
826 SQL.FreeStmt(hstmt, SQL_DROP);
827 }
828 return ret;
829 }
830
831 static gate_result_t gate_data_connection_odbc_init(gate_data_connection_base_t* self)
832 {
833 GATE_UNUSED_ARG(self);
834 return GATE_RESULT_OK;
835 }
836
837 static void gate_data_connection_odbc_close(gate_data_connection_base_t* self)
838 {
839 SQLHDBC hdbc = self->native_handles[0];
840 SQLHENV henv = self->native_handles[1];
841
842 if (hdbc != NULL)
843 {
844 SQL.Disconnect(hdbc);
845 SQL.FreeConnect(hdbc);
846 }
847 if (henv != NULL)
848 {
849 SQL.FreeEnv(henv);
850 }
851 gate_mem_clear(self->native_handles, sizeof(self->native_handles));
852 }
853
854
855
856 gate_result_t gate_data_adapter_odbc_create(gate_string_t const* path, gate_enumint_t flags, gate_data_connection_t** ptr_connection)
857 {
858 gate_result_t ret = GATE_RESULT_OK;
859 SQLTCHAR str_connection[4096];
860 gate_size_t str_connection_len;
861 SQLTCHAR* str_user = NULL;
862 gate_size_t str_user_len = 0;
863 SQLTCHAR* str_pass = NULL;
864 gate_size_t str_pass_len = 0;
865 SQLHENV henv = NULL;
866 SQLHDBC hdbc = NULL;
867 SQLRETURN sql_ret;
868 gate_data_handles_t handles = GATE_INIT_EMPTY;
869
870 do
871 {
872 ret = load_odbc_functions();
873 GATE_BREAK_IF_FAILED(ret);
874
875 #if defined(GATE_SYS_WIN)
876 str_connection_len = gate_win32_utf8_2_winstr(path->str, path->length, (LPTSTR)&str_connection[0], sizeof(str_connection) / sizeof(str_connection[0]));
877 #else
878 str_connection_len = gate_string_to_buffer(path, (char*)&str_connection[0], sizeof(str_connection) / sizeof(str_connection[0]));
879 #endif
880
881
882 sql_ret = SQL.AllocEnv(&henv);
883 if (SQL_SUCCESS != sql_ret)
884 {
885 ret = GATE_RESULT_OUTOFRESOURCES;
886 break;
887 }
888
889 sql_ret = SQL.AllocConnect(henv, &hdbc);
890 if (SQL_SUCCESS != sql_ret)
891 {
892 ret = GATE_RESULT_OUTOFRESOURCES;
893 break;
894 }
895
896 sql_ret = SQL.Connect(hdbc, str_connection, (SQLSMALLINT)str_connection_len,
897 str_user, (SQLSMALLINT)str_user_len, str_pass, (SQLSMALLINT)str_pass_len);
898
899 if ((SQL_SUCCESS != sql_ret) && (SQL_SUCCESS_WITH_INFO != sql_ret))
900 {
901 SQLTCHAR state_msg[1024];
902 SQLINTEGER native_error = 0;
903 SQLTCHAR error_msg[1024];
904 SQLSMALLINT error_msg_used = 0;
905 SQL.GetDiagRec(SQL_HANDLE_DBC, hdbc, 1, state_msg, &native_error, error_msg, 1024, &error_msg_used);
906 ret = GATE_RESULT_FAILED;
907 break;
908 }
909
910 handles[0] = (void*)hdbc;
911 handles[1] = (void*)henv;
912 ret = gate_data_connection_base_create(
913 handles,
914 &gate_data_connection_odbc_init,
915 &gate_data_connection_odbc_close,
916 &gate_data_connection_odbc_create_statement,
917 ptr_connection);
918 if (GATE_SUCCEEDED(ret))
919 {
920 hdbc = NULL;
921 henv = NULL;
922 }
923 } while (0);
924
925 if (hdbc)
926 {
927 SQL.Disconnect(hdbc);
928 SQL.FreeConnect(hdbc);
929 }
930 if (henv)
931 {
932 SQL.FreeEnv(henv);
933 }
934 return ret;
935 }
936
937 #else /* GATE_DATA_ODBC_IMPL */
938
939 gate_result_t gate_data_adapter_odbc_create(gate_string_t const* path, gate_enumint_t flags, gate_data_connection_t** ptr_connection)
940 {
941 (void)path;
942 (void)flags;
943 (void)ptr_connection;
944 return GATE_RESULT_NOTIMPLEMENTED;
945 }
946
947 #endif
948