GCC Code Coverage Report


Directory: src/gate/
File: src/gate/data/odbc_adapter.c
Date: 2025-12-12 23:40:09
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;
123 gate_library_t lib = NULL;
124
125 do
126 {
127 if (odbc_lib != NULL)
128 {
129 ret = GATE_RESULT_OK;
130 break;
131 }
132
133 ret = gate_library_open(&lib_name, &lib, 0);
134 GATE_BREAK_IF_FAILED(ret);
135
136 ret = gate_library_get_function_name(lib, Name_AllocConnect, &SQL.AllocConnect);
137 GATE_BREAK_IF_FAILED(ret);
138 ret = gate_library_get_function_name(lib, Name_FreeEnv, &SQL.FreeEnv);
139 GATE_BREAK_IF_FAILED(ret);
140 ret = gate_library_get_function_name(lib, Name_FreeConnect, &SQL.FreeConnect);
141 GATE_BREAK_IF_FAILED(ret);
142 ret = gate_library_get_function_name(lib, Name_Disconnect, &SQL.Disconnect);
143 GATE_BREAK_IF_FAILED(ret);
144 ret = gate_library_get_function_name(lib, Name_AllocStmt, &SQL.AllocStmt);
145 GATE_BREAK_IF_FAILED(ret);
146 ret = gate_library_get_function_name(lib, Name_Fetch, &SQL.Fetch);
147 GATE_BREAK_IF_FAILED(ret);
148 ret = gate_library_get_function_name(lib, Name_FreeStmt, &SQL.FreeStmt);
149 GATE_BREAK_IF_FAILED(ret);
150 ret = gate_library_get_function_name(lib, Name_GetData, &SQL.GetData);
151 GATE_BREAK_IF_FAILED(ret);
152 ret = gate_library_get_function_name(lib, Name_AllocEnv, &SQL.AllocEnv);
153 GATE_BREAK_IF_FAILED(ret);
154 ret = gate_library_get_function_name(lib, Name_Execute, &SQL.Execute);
155 GATE_BREAK_IF_FAILED(ret);
156 ret = gate_library_get_function_name(lib, Name_NumResultCols, &SQL.NumResultCols);
157 GATE_BREAK_IF_FAILED(ret);
158 ret = gate_library_get_function_name(lib, Name_Connect, &SQL.Connect);
159 GATE_BREAK_IF_FAILED(ret);
160 ret = gate_library_get_function_name(lib, Name_DescribeCol, &SQL.DescribeCol);
161 GATE_BREAK_IF_FAILED(ret);
162 ret = gate_library_get_function_name(lib, Name_Prepare, &SQL.Prepare);
163 GATE_BREAK_IF_FAILED(ret);
164 ret = gate_library_get_function_name(lib, Name_GetDiagRec, &SQL.GetDiagRec);
165 GATE_BREAK_IF_FAILED(ret);
166 ret = gate_library_get_function_name(lib, Name_BindParameter, &SQL.BindParameter);
167 GATE_BREAK_IF_FAILED(ret);
168 ret = gate_library_get_function_name(lib, Name_NumParams, &SQL.NumParams);
169 GATE_BREAK_IF_FAILED(ret);
170 ret = gate_library_get_function_name(lib, Name_RowCount, &SQL.RowCount);
171 GATE_BREAK_IF_FAILED(ret);
172
173 odbc_lib = lib;
174 lib = NULL;
175 ret = GATE_RESULT_OK;
176 } while (0);
177
178 if (lib != NULL)
179 {
180 gate_library_close(lib);
181 }
182
183 return ret;
184 }
185
186
187
188 static gate_bool_t gate_data_reader_odbc_is_valid(gate_data_reader_base_t* reader)
189 {
190 SQLHSTMT stmt = (SQLHSTMT)reader->native_handles[0];
191 return stmt != NULL;
192 }
193
194 static gate_result_t gate_data_reader_odbc_close(gate_data_reader_base_t* reader)
195 {
196 reader->native_handles[0] = NULL;
197 return GATE_RESULT_OK;
198 }
199
200 static SQLSMALLINT gate_data_reader_odbc_patch_sql_type(SQLSMALLINT defined_type)
201 {
202 /* some types are defined but in SQLGetData another type must be used */
203 switch (defined_type)
204 {
205 case SQL_DECIMAL:
206 case SQL_NUMERIC:
207 case SQL_CHAR:
208 case SQL_VARCHAR:
209 case SQL_LONGVARCHAR: return SQL_CHAR;
210 case SQL_WVARCHAR:
211 case SQL_WLONGVARCHAR: return SQL_WCHAR;
212 default: break;
213 }
214 return defined_type;
215 }
216
217 static gate_value_t* gate_data_reader_odbc_get_value(SQLHSTMT hstmt, SQLUSMALLINT index, SQLSMALLINT sql_type, gate_value_t* value)
218 {
219 gate_value_t* ret = NULL;
220 char buffer[8192] = GATE_INIT_EMPTY;
221 SQLLEN buffer_used = 0;
222 SQLRETURN sql_res;
223 gate_bool_t bool_val;
224 gate_strbuilder_t text_builder = GATE_INIT_EMPTY;
225 gate_string_t text = GATE_STRING_INIT_EMPTY;
226 gate_arraylist_t arrlst = NULL;
227 gate_array_t arr = GATE_INIT_EMPTY;
228 SQLGUID const* ptr_sql_guid = NULL;
229 gate_guid_t guid;
230 DATE_STRUCT const* ptr_date_struct = NULL;
231 TIME_STRUCT const* ptr_time_struct = NULL;
232 TIMESTAMP_STRUCT const* ptr_timestamp_struct = NULL;
233 gate_datetime_t dt;
234
235 sql_type = gate_data_reader_odbc_patch_sql_type(sql_type);
236
237 sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
238 switch (sql_res)
239 {
240 case SQL_SUCCESS:
241 case SQL_NO_DATA:
242 case SQL_SUCCESS_WITH_INFO:
243 {
244 /* OK, continue */
245 break;
246 }
247 default:
248 {
249 /* error case */
250 return ret;
251 }
252 }
253
254 switch (sql_type)
255 {
256 case SQL_C_STINYINT:
257 case SQL_TINYINT: { ret = gate_value_create(GATE_TYPE_I8, buffer, value); break; }
258 case SQL_C_SSHORT:
259 case SQL_SMALLINT: { ret = gate_value_create(GATE_TYPE_I16, buffer, value); break; }
260 case SQL_C_SLONG:
261 case SQL_INTEGER: { ret = gate_value_create(GATE_TYPE_I32, buffer, value); break; }
262 case SQL_C_SBIGINT:
263 case SQL_BIGINT: { ret = gate_value_create(GATE_TYPE_I64, buffer, value); break; }
264 case SQL_REAL: { ret = gate_value_create(GATE_TYPE_R32, buffer, value); break; }
265 case SQL_FLOAT: { ret = gate_value_create(GATE_TYPE_R32, buffer, value); break; }
266 case SQL_DOUBLE: { ret = gate_value_create(GATE_TYPE_R64, buffer, value); break; }
267 case SQL_C_UTINYINT: { ret = gate_value_create(GATE_TYPE_UI8, buffer, value); break; }
268 case SQL_C_USHORT: { ret = gate_value_create(GATE_TYPE_UI16, buffer, value); break; }
269 case SQL_C_ULONG: { ret = gate_value_create(GATE_TYPE_UI32, buffer, value); break; }
270 case SQL_C_UBIGINT: { ret = gate_value_create(GATE_TYPE_UI64, buffer, value); break; }
271 case SQL_BIT:
272 {
273 bool_val = buffer[0] == 0 ? false : true;
274 ret = gate_value_create(GATE_TYPE_BOOL, &bool_val, value);
275 break;
276 }
277
278 case SQL_DECIMAL:
279 case SQL_NUMERIC:
280 case SQL_CHAR:
281 case SQL_VARCHAR:
282 case SQL_LONGVARCHAR:
283 {
284 /* extract byte data */
285 gate_strbuilder_create(&text_builder, 0);
286 gate_strbuilder_append_text(&text_builder, &buffer[0], buffer_used);
287 while (sql_res == SQL_SUCCESS_WITH_INFO)
288 {
289 sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
290 gate_strbuilder_append_text(&text_builder, &buffer[0], buffer_used);
291 }
292 gate_strbuilder_to_string(&text_builder, &text);
293 ret = gate_value_create(GATE_TYPE_STRING, &text, value);
294 gate_string_release(&text);
295 gate_strbuilder_release(&text_builder);
296 break;
297 }
298 case SQL_WCHAR:
299 case SQL_WVARCHAR:
300 case SQL_WLONGVARCHAR:
301 {
302 /* extract UTF16 text */
303 gate_strbuilder_create(&text_builder, 0);
304 gate_strbuilder_append_text16(&text_builder, (gate_char16_t const*)&buffer[0], buffer_used / sizeof(gate_char16_t));
305 while (sql_res == SQL_SUCCESS_WITH_INFO)
306 {
307 sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
308 gate_strbuilder_append_text16(&text_builder, (gate_char16_t const*)&buffer[0], buffer_used / sizeof(gate_char16_t));
309 }
310 gate_strbuilder_to_string(&text_builder, &text);
311 ret = gate_value_create(GATE_TYPE_STRING, &text, value);
312 gate_string_release(&text);
313 gate_strbuilder_release(&text_builder);
314 break;
315 }
316 case SQL_BINARY:
317 case SQL_VARBINARY:
318 case SQL_LONGVARBINARY:
319 {
320 arrlst = gate_arraylist_create(1, buffer, buffer_used, NULL, NULL);
321 if (arrlst)
322 {
323 while (sql_res == SQL_SUCCESS_WITH_INFO)
324 {
325 sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
326 gate_arraylist_add_n(arrlst, buffer, buffer_used);
327 }
328 if (NULL != gate_array_create(&arr, arrlst))
329 {
330 ret = gate_value_create(GATE_TYPE_ARRAY, &arr, value);
331 gate_array_release(&arr);
332 }
333 gate_arraylist_release(arrlst);
334 }
335 break;
336 }
337 case SQL_GUID:
338 {
339 ptr_sql_guid = (SQLGUID const*)&buffer[0];
340 guid.item1 = ptr_sql_guid->Data1;
341 guid.item2 = ptr_sql_guid->Data2;
342 guid.item3 = ptr_sql_guid->Data3;
343 gate_mem_copy(guid.item4, ptr_sql_guid->Data4, 8);
344 ret = gate_value_create(GATE_TYPE_GUID, &guid, value);
345 break;
346 }
347 case SQL_TYPE_DATE:
348 {
349 ptr_date_struct = (DATE_STRUCT const*)&buffer[0];
350 dt.date.year = ptr_date_struct->year;
351 dt.date.month = (gate_uint8_t)ptr_date_struct->month;
352 dt.date.day = (gate_uint8_t)ptr_date_struct->day;
353 ret = gate_value_create(GATE_TYPE_DATE, &dt.date, value);
354 break;
355 }
356 case SQL_TYPE_TIME:
357 {
358 ptr_time_struct = (TIME_STRUCT const*)&buffer[0];
359 dt.time.hour = (gate_uint8_t)ptr_time_struct->hour;
360 dt.time.minute = (gate_uint8_t)ptr_time_struct->minute;
361 dt.time.second = (gate_uint8_t)ptr_time_struct->second;
362 dt.time.microsecond = 0;
363 ret = gate_value_create(GATE_TYPE_DAYTIME, &dt.time, value);
364 break;
365 }
366 case SQL_TYPE_TIMESTAMP:
367 {
368 ptr_timestamp_struct = (TIMESTAMP_STRUCT const*)&buffer[0];
369 dt.date.year = ptr_timestamp_struct->year;
370 dt.date.month = (gate_uint8_t)ptr_timestamp_struct->month;
371 dt.date.day = (gate_uint8_t)ptr_timestamp_struct->day;
372 dt.time.hour = (gate_uint8_t)ptr_timestamp_struct->hour;
373 dt.time.minute = (gate_uint8_t)ptr_timestamp_struct->minute;
374 dt.time.second = (gate_uint8_t)ptr_timestamp_struct->second;
375 dt.time.microsecond = ptr_timestamp_struct->fraction / 1000; /*fraction == nanoseconds */
376 ret = gate_value_create(GATE_TYPE_DAYTIME, &dt.time, value);
377 break;
378 }
379 default:
380 {
381 /* not supported */
382 break;
383 }
384 }
385
386 return ret;
387 }
388
389 static gate_result_t gate_data_reader_odbc_next(gate_data_reader_base_t* reader)
390 {
391 gate_result_t ret = GATE_RESULT_FAILED;
392 SQLHSTMT hstmt = (SQLHSTMT)reader->native_handles[0];
393 SQLRETURN sql_res;
394 gate_size_t ndx;
395
396 do
397 {
398 if (hstmt == NULL)
399 {
400 ret = GATE_RESULT_INVALIDSTATE;
401 break;
402 }
403
404 sql_res = SQL.Fetch(hstmt);
405 if (SQL_SUCCESS != sql_res)
406 {
407 ret = GATE_RESULT_FAILED;
408 break;
409 }
410
411 ret = GATE_RESULT_OK;
412 for (ndx = 0; ndx != reader->field_count; ++ndx)
413 {
414 gate_value_release(&reader->values[ndx]);
415 gate_data_reader_odbc_get_value(hstmt, (SQLUSMALLINT)(ndx + 1), (SQLUSMALLINT)reader->types[ndx], &reader->values[ndx]);
416 }
417
418 } while (0);
419
420 return ret;
421 }
422
423
424 static gate_result_t gate_data_reader_odbc_init(gate_data_reader_base_t* reader)
425 {
426 gate_result_t ret = GATE_RESULT_OK;
427 SQLSMALLINT col_count = 0;
428 SQLSMALLINT col_index;
429 SQLRETURN sql_ret;
430 SQLHSTMT hstmt = (SQLHSTMT)reader->native_handles[0];
431 SQLTCHAR colname[1024];
432 SQLSMALLINT colname_len;
433 SQLSMALLINT coltype;
434 SQLULEN colvalue_len;
435 SQLSMALLINT colvalue_digits;
436 SQLSMALLINT colvalue_nullable;
437
438 do
439 {
440 sql_ret = SQL.NumResultCols(hstmt, &col_count);
441 if (SQL_SUCCESS != sql_ret)
442 {
443 ret = GATE_RESULT_FAILED;
444 break;
445 }
446 reader->field_count = col_count;
447
448 for (col_index = 0; col_index != col_count; ++col_index)
449 {
450 colname_len = sizeof(colname) / sizeof(colname[0]);
451 coltype = 0;
452 colvalue_len = 0;
453 colvalue_digits = 0;
454 colvalue_nullable = 0;
455 sql_ret = SQL.DescribeCol(hstmt, (SQLUSMALLINT)(col_index + 1), colname, colname_len, &colname_len,
456 &coltype, &colvalue_len, &colvalue_digits, &colvalue_nullable);
457 #if defined(GATE_SYS_WIN)
458 gate_win32_winstr_2_utf8_string((LPCTSTR)&colname[0], colname_len, &reader->names[col_index]);
459 #else
460 gate_string_create(&reader->names[col_index], (char const*)&colname[0], colname_len);
461 #endif
462 reader->types[col_index] = coltype;
463 }
464 } while (0);
465
466 return ret;
467 }
468
469
470 static gate_data_reader_t* gate_data_reader_odbc_create(gate_data_statement_base_t* ptr_statement, SQLHSTMT stmt)
471 {
472 gate_data_reader_t* ret = NULL;
473 gate_data_handles_t handles = GATE_INIT_EMPTY;
474
475 handles[0] = (void*)stmt;
476
477 ret = gate_data_reader_base_create(
478 (gate_data_statement_t*)ptr_statement, handles,
479 &gate_data_reader_odbc_init,
480 &gate_data_reader_odbc_is_valid,
481 &gate_data_reader_odbc_next,
482 &gate_data_reader_odbc_close);
483
484 return ret;
485 }
486
487
488 static gate_result_t odbcstmt_init(gate_data_statement_base_t* self)
489 {
490 return GATE_RESULT_OK;
491 }
492
493 static void odbcstmt_close(gate_data_statement_base_t* self)
494 {
495 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
496 if (hstmt != NULL)
497 {
498 SQL.FreeStmt(hstmt, SQL_CLOSE);
499 SQL.FreeStmt(hstmt, SQL_DROP);
500 self->native_handles[1] = NULL;
501 }
502 }
503
504 static gate_result_t odbcstmt_convert_result(SQLRETURN sql_result)
505 {
506 if (SQL_SUCCEEDED(sql_result))
507 {
508 return GATE_RESULT_OK;
509 }
510 else
511 {
512 /* TODO: detailed error analysis */
513 return GATE_RESULT_FAILED;
514 }
515 }
516
517 static gate_result_t odbcstmt_set_null_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index)
518 {
519 SQLINTEGER param1 = 123;
520 SQLLEN param2Ind = SQL_NULL_DATA;
521 SQLRETURN sql_result = SQL.BindParameter(
522 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 0, 0, NULL, 0, &param2Ind);
523 return odbcstmt_convert_result(sql_result);
524 }
525
526 static gate_result_t odbcstmt_set_bigint_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_int64_t value)
527 {
528 SQLBIGINT bigint = value;
529 SQLRETURN sql_result = SQL.BindParameter(
530 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &bigint, 0, NULL);
531 return odbcstmt_convert_result(sql_result);
532 }
533 static gate_result_t odbcstmt_set_int_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_int32_t value)
534 {
535 SQLINTEGER n = value;
536 SQLRETURN sql_result = SQL.BindParameter(
537 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &n, 0, NULL);
538 return odbcstmt_convert_result(sql_result);
539 }
540 static gate_result_t odbcstmt_set_real_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_real64_t value)
541 {
542 SQLDOUBLE n = value;
543 SQLRETURN sql_result = SQL.BindParameter(
544 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, 0, 0, &n, 0, NULL);
545 return odbcstmt_convert_result(sql_result);
546 }
547
548 static gate_result_t odbcstmt_set_text_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, char const* text, gate_size_t textlen)
549 {
550 SQLPOINTER ptr_text = (SQLPOINTER)text;
551 SQLULEN len = (SQLULEN)textlen;
552 SQLRETURN sql_result = SQL.BindParameter(
553 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, len, 0, ptr_text, 0, NULL);
554 return odbcstmt_convert_result(sql_result);
555 }
556
557 static gate_result_t odbcstmt_set_binary_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, void const* data, gate_size_t datalen)
558 {
559 SQLPOINTER ptr_text = (SQLPOINTER)data;
560 SQLULEN len = (SQLULEN)datalen;
561 SQLLEN lenret = (SQLLEN)datalen;
562 SQLRETURN sql_result = SQL.BindParameter(
563 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, len, 0, ptr_text, 0, &lenret);
564 return odbcstmt_convert_result(sql_result);
565 }
566
567 static gate_result_t odbcstmt_set_datetime_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_datetime_t const* dt)
568 {
569 SQLRETURN sql_result;
570 SQL_TIMESTAMP_STRUCT sql_dt;
571 sql_dt.year = dt->date.year;
572 sql_dt.month = dt->date.month;
573 sql_dt.day = dt->date.day;
574 sql_dt.hour = dt->time.hour;
575 sql_dt.minute = dt->time.minute;
576 sql_dt.second = dt->time.second;
577 sql_dt.fraction = 0;
578
579 sql_result = SQL.BindParameter(
580 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, 0, 0, &sql_dt, 0, NULL);
581 return odbcstmt_convert_result(sql_result);
582 }
583
584
585
586 static gate_result_t odbcstmt_set_param(gate_data_statement_base_t* self, gate_size_t index, gate_value_t const* value)
587 {
588 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
589 SQLUSMALLINT odbc_index = (SQLUSMALLINT)(index + 1);
590 gate_cstrbuffer8_t buffer;
591 gate_datetime_t dt = GATE_INIT_EMPTY;
592 gate_result_t ret = GATE_RESULT_FAILED;
593
594 if ((value == NULL) || (value->value_type == GATE_TYPE_EMPTY))
595 {
596 return odbcstmt_set_null_param(hstmt, odbc_index);
597 }
598
599 switch (value->value_type)
600 {
601 case GATE_TYPE_BOOL: return odbcstmt_set_int_param(hstmt, odbc_index, value->content.bool_value ? 1 : 0);
602 case GATE_TYPE_I8: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.i8_value);
603 case GATE_TYPE_I16: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.i16_value);
604 case GATE_TYPE_I32: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.i32_value);
605 case GATE_TYPE_I64: return odbcstmt_set_bigint_param(hstmt, odbc_index, (gate_int32_t)value->content.i64_value);
606 case GATE_TYPE_UI8: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.ui8_value);
607 case GATE_TYPE_UI16: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.ui16_value);
608 case GATE_TYPE_UI32: return odbcstmt_set_bigint_param(hstmt, odbc_index, (gate_int64_t)value->content.ui32_value);
609 case GATE_TYPE_UI64: return odbcstmt_set_bigint_param(hstmt, odbc_index, (gate_int64_t)value->content.ui64_value);
610
611 case GATE_TYPE_R32: return odbcstmt_set_real_param(hstmt, odbc_index, (gate_real64_t)value->content.r32_value);
612 case GATE_TYPE_R64: return odbcstmt_set_real_param(hstmt, odbc_index, (gate_real64_t)value->content.r64_value);
613
614 case GATE_TYPE_BLOB: return odbcstmt_set_binary_param(hstmt, odbc_index, value->content.blob_value.data, value->content.blob_value.length);
615
616 case GATE_TYPE_CSTR: return odbcstmt_set_text_param(hstmt, odbc_index, value->content.cstring_value, gate_str_length(value->content.cstring_value));
617 case GATE_TYPE_STRING: return odbcstmt_set_text_param(hstmt, odbc_index, value->content.string_value.str, value->content.string_value.length);
618
619 case GATE_TYPE_WSTR:
620 {
621 if (NULL == gate_cstrbuffer_create_wstr(&buffer, value->content.wstring_value, gate_wstr_length(value->content.wstring_value)))
622 {
623 return GATE_RESULT_OUTOFMEMORY;
624 }
625 ret = odbcstmt_set_text_param(hstmt, odbc_index, gate_cstrbuffer_get(&buffer), gate_cstrbuffer_length(&buffer));
626 gate_cstrbuffer_destroy(&buffer);
627 return ret;
628 }
629 case GATE_TYPE_GUID:
630 {
631 ret = gate_guid_to_string(&value->content.guid_value, buffer.local_buffer);
632 GATE_RETURN_IF_FAILED(ret);
633 return odbcstmt_set_text_param(hstmt, odbc_index, buffer.local_buffer, gate_str_length(buffer.local_buffer));
634 }
635 case GATE_TYPE_DATE:
636 {
637 dt.date = value->content.date_value;
638 return odbcstmt_set_datetime_param(hstmt, odbc_index, &dt);
639 }
640 case GATE_TYPE_DATETIME:
641 {
642 return odbcstmt_set_datetime_param(hstmt, odbc_index, &value->content.datetime_value);
643 }
644 case GATE_TYPE_TIME:
645 {
646 ret = gate_time_to_datetime(&value->content.time_value, &dt);
647 GATE_RETURN_IF_FAILED(ret);
648 return odbcstmt_set_datetime_param(hstmt, odbc_index, &dt);
649 }
650 case GATE_TYPE_PROPERTY:
651 default:
652 return GATE_RESULT_INCORRECTTYPE;
653 }
654 }
655
656 static gate_result_t odbcstmt_set_named_param(gate_data_statement_base_t* self, gate_string_t const* name, gate_value_t const* value)
657 {
658 /* named parameters are not supported by odbc */
659 return GATE_RESULT_NOTSUPPORTED;
660 }
661
662 static gate_result_t odbcstmt_reset(gate_data_statement_base_t* self)
663 {
664 SQLRETURN sql_ret;
665 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
666
667 if (hstmt == NULL)
668 {
669 return GATE_RESULT_INVALIDSTATE;
670 }
671
672 sql_ret = SQL.FreeStmt(hstmt, SQL_RESET_PARAMS);
673 switch (sql_ret)
674 {
675 case SQL_SUCCESS:
676 case SQL_SUCCESS_WITH_INFO:
677 return GATE_RESULT_OK;
678 default:
679 return GATE_RESULT_FAILED;
680 }
681 }
682
683 static gate_result_t odbcstmt_execute(gate_data_statement_base_t* self, gate_int32_t* affected_rows)
684 {
685 SQLRETURN sql_ret;
686 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
687 SQLLEN len_rows = 0;
688
689 if (hstmt == NULL)
690 {
691 return GATE_RESULT_INVALIDSTATE;
692 }
693
694 sql_ret = SQL.Execute(hstmt);
695 if (SQL_SUCCESS != sql_ret)
696 {
697 return GATE_RESULT_FAILED;
698 }
699
700 /* success case */
701 if (affected_rows)
702 {
703 SQL.RowCount(hstmt, &len_rows);
704 *affected_rows = (gate_int32_t)len_rows;
705 }
706 return GATE_RESULT_OK;
707 }
708
709 static gate_result_t odbcstmt_query(gate_data_statement_base_t* self, gate_data_reader_t** ptr_reader)
710 {
711 SQLRETURN sql_ret;
712 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
713 SQLLEN len_rows = 0;
714
715 if (hstmt == NULL)
716 {
717 return GATE_RESULT_INVALIDSTATE;
718 }
719
720 sql_ret = SQL.Execute(hstmt);
721 if (SQL_SUCCESS != sql_ret)
722 {
723 return GATE_RESULT_FAILED;
724 }
725
726 if (ptr_reader == NULL)
727 {
728 /* success, reader not required, free unused resources */
729 SQL.FreeStmt(hstmt, SQL_CLOSE);
730 SQL.FreeStmt(hstmt, SQL_DROP);
731 return GATE_RESULT_OK;
732 }
733
734 *ptr_reader = gate_data_reader_odbc_create(self, hstmt);
735 if (*ptr_reader == NULL)
736 {
737 /* failed to allocate data reader */
738 SQL.FreeStmt(hstmt, SQL_CLOSE);
739 SQL.FreeStmt(hstmt, SQL_DROP);
740 return GATE_RESULT_FAILED;
741 }
742 return GATE_RESULT_OK;
743
744 }
745
746
747
748
749
750
751
752
753 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)
754 {
755 gate_result_t ret = GATE_RESULT_FAILED;
756 SQLHSTMT hstmt = NULL;
757 SQLHDBC hdbc = self->native_handles[0];
758 SQLRETURN sql_ret;
759 SQLTCHAR cmd[4096 + 2048 + 1024];
760 SQLSMALLINT param_count = 0;
761 gate_size_t cmd_len;
762 gate_data_handles_t handles;
763 gate_data_statement_base_t* ptr_stmt_base = NULL;
764
765 do
766 {
767 #if defined(GATE_SYS_WIN)
768 cmd_len = gate_win32_utf8_2_winstr(sql->str, sql->length, (LPTSTR)&cmd[0], sizeof(cmd) / sizeof(cmd[0]));
769 #else
770 cmd_len = gate_string_to_buffer(sql, (char*)&cmd[0], sizeof(cmd) / sizeof(cmd[0]));
771 #endif
772
773 sql_ret = SQL.AllocStmt(hdbc, &hstmt);
774 if (SQL_SUCCESS != sql_ret)
775 {
776 ret = GATE_RESULT_OUTOFRESOURCES;
777 break;
778 }
779
780 sql_ret = SQL.Prepare(hstmt, cmd, (SQLINTEGER)cmd_len);
781 if (SQL_SUCCESS != sql_ret)
782 {
783 ret = GATE_RESULT_FAILED;
784 break;
785 }
786
787 gate_mem_clear(&handles[0], sizeof(handles));
788 handles[0] = (void*)hdbc;
789 handles[1] = (void*)hstmt;
790
791 ret = gate_data_statement_base_create(
792 self, handles, sql,
793 &odbcstmt_init,
794 &odbcstmt_close,
795 &odbcstmt_set_param,
796 &odbcstmt_set_named_param,
797 &odbcstmt_reset,
798 &odbcstmt_execute,
799 &odbcstmt_query,
800 &ptr_stmt_base);
801
802 GATE_BREAK_IF_FAILED(ret);
803
804 if (SQL_ERROR != SQL.NumParams(hstmt, &param_count))
805 {
806 ptr_stmt_base->param_count = param_count;
807 }
808
809 /* success case: */
810 if (ptr_stmt != NULL)
811 {
812 *ptr_stmt = (gate_data_statement_t*)ptr_stmt_base;
813 ptr_stmt_base = NULL;
814 }
815 hstmt = NULL;
816 ret = GATE_RESULT_OK;
817 } while (0);
818
819 if (ptr_stmt_base != NULL)
820 {
821 gate_object_release(ptr_stmt_base);
822 }
823
824 if (hstmt != NULL)
825 {
826 SQL.FreeStmt(hstmt, SQL_CLOSE);
827 SQL.FreeStmt(hstmt, SQL_DROP);
828 }
829 return ret;
830 }
831
832 static gate_result_t gate_data_connection_odbc_init(gate_data_connection_base_t* self)
833 {
834 GATE_UNUSED_ARG(self);
835 return GATE_RESULT_OK;
836 }
837
838 static void gate_data_connection_odbc_close(gate_data_connection_base_t* self)
839 {
840 SQLHDBC hdbc = self->native_handles[0];
841 SQLHENV henv = self->native_handles[1];
842
843 if (hdbc != NULL)
844 {
845 SQL.Disconnect(hdbc);
846 SQL.FreeConnect(hdbc);
847 }
848 if (henv != NULL)
849 {
850 SQL.FreeEnv(henv);
851 }
852 gate_mem_clear(self->native_handles, sizeof(self->native_handles));
853 }
854
855
856
857 gate_result_t gate_data_adapter_odbc_create(gate_string_t const* path, gate_enumint_t flags, gate_data_connection_t** ptr_connection)
858 {
859 gate_result_t ret = GATE_RESULT_OK;
860 SQLTCHAR str_connection[4096];
861 gate_size_t str_connection_len;
862 SQLTCHAR* str_user = NULL;
863 gate_size_t str_user_len = 0;
864 SQLTCHAR* str_pass = NULL;
865 gate_size_t str_pass_len = 0;
866 SQLHENV henv = NULL;
867 SQLHDBC hdbc = NULL;
868 SQLRETURN sql_ret;
869 gate_data_handles_t handles = GATE_INIT_EMPTY;
870
871 do
872 {
873 ret = load_odbc_functions();
874 GATE_BREAK_IF_FAILED(ret);
875
876 #if defined(GATE_SYS_WIN)
877 str_connection_len = gate_win32_utf8_2_winstr(path->str, path->length, (LPTSTR)&str_connection[0], sizeof(str_connection) / sizeof(str_connection[0]));
878 #else
879 str_connection_len = gate_string_to_buffer(path, (char*)&str_connection[0], sizeof(str_connection) / sizeof(str_connection[0]));
880 #endif
881
882
883 sql_ret = SQL.AllocEnv(&henv);
884 if (SQL_SUCCESS != sql_ret)
885 {
886 ret = GATE_RESULT_OUTOFRESOURCES;
887 break;
888 }
889
890 sql_ret = SQL.AllocConnect(henv, &hdbc);
891 if (SQL_SUCCESS != sql_ret)
892 {
893 ret = GATE_RESULT_OUTOFRESOURCES;
894 break;
895 }
896
897 sql_ret = SQL.Connect(hdbc, str_connection, (SQLSMALLINT)str_connection_len,
898 str_user, (SQLSMALLINT)str_user_len, str_pass, (SQLSMALLINT)str_pass_len);
899
900 if ((SQL_SUCCESS != sql_ret) && (SQL_SUCCESS_WITH_INFO != sql_ret))
901 {
902 SQLTCHAR state_msg[1024];
903 SQLINTEGER native_error = 0;
904 SQLTCHAR error_msg[1024];
905 SQLSMALLINT error_msg_used = 0;
906 SQL.GetDiagRec(SQL_HANDLE_DBC, hdbc, 1, state_msg, &native_error, error_msg, 1024, &error_msg_used);
907 ret = GATE_RESULT_FAILED;
908 break;
909 }
910
911 handles[0] = (void*)hdbc;
912 handles[1] = (void*)henv;
913 ret = gate_data_connection_base_create(
914 handles,
915 &gate_data_connection_odbc_init,
916 &gate_data_connection_odbc_close,
917 &gate_data_connection_odbc_create_statement,
918 ptr_connection);
919 if (GATE_SUCCEEDED(ret))
920 {
921 hdbc = NULL;
922 henv = NULL;
923 }
924 } while (0);
925
926 if (hdbc)
927 {
928 SQL.Disconnect(hdbc);
929 SQL.FreeConnect(hdbc);
930 }
931 if (henv)
932 {
933 SQL.FreeEnv(henv);
934 }
935 return ret;
936 }
937
938 #else /* GATE_DATA_ODBC_IMPL */
939
940 gate_result_t gate_data_adapter_odbc_create(gate_string_t const* path, gate_enumint_t flags, gate_data_connection_t** ptr_connection)
941 {
942 (void)path;
943 (void)flags;
944 (void)ptr_connection;
945 return GATE_RESULT_NOTIMPLEMENTED;
946 }
947
948 #endif
949