GCC Code Coverage Report


Directory: src/gate/
File: src/gate/data/odbc_adapter.c
Date: 2026-10-19 17:06:30
Exec Total Coverage
Lines: 360 528 68.2%
Functions: 26 29 89.7%
Branches: 106 217 48.8%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2026, 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* SetEnvAttr)(SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
78 SQLRETURN(SQL_API* Prepare)(SQLHSTMT StatementHandle, SQLTCHAR* StatementText, SQLINTEGER TextLength);
79 SQLRETURN(SQL_API* Execute)(SQLHSTMT StatementHandle);
80 SQLRETURN(SQL_API* GetDiagRec)(SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLTCHAR* Sqlstate,
81 SQLINTEGER* NativeError, SQLTCHAR* MessageText, SQLSMALLINT BufferLength, SQLSMALLINT* TextLength);
82 SQLRETURN(SQL_API* NumResultCols)(SQLHSTMT StatementHandle, SQLSMALLINT* ColumnCount);
83 SQLRETURN(SQL_API* BindParameter)(SQLHSTMT hstmt, SQLUSMALLINT ipar, SQLSMALLINT fParamType, SQLSMALLINT fCType,
84 SQLSMALLINT fSqlType, SQLULEN cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLLEN cbValueMax, SQLLEN* pcbValue);
85 SQLRETURN(SQL_API* NumParams)(SQLHSTMT StatementHandle, SQLSMALLINT* ParameterCountPtr);
86 SQLRETURN(SQL_API* RowCount)(SQLHSTMT StatementHandle, SQLLEN* RowCount);
87 } gate_odbc_functions_t;
88
89 static gate_odbc_functions_t SQL = GATE_INIT_EMPTY;
90
91 #if defined(GATE_SYS_WIN)
92 static const gate_string_t lib_name = GATE_STRING_INIT_STATIC("odbc32");
93 #else
94 static const gate_string_t lib_name = GATE_STRING_INIT_STATIC("libodbc.so");
95 #endif
96
97 #define Name_AllocConnect "SQLAllocConnect"
98 #define Name_FreeEnv "SQLFreeEnv"
99 #define Name_FreeConnect "SQLFreeConnect"
100 #define Name_Disconnect "SQLDisconnect"
101 #define Name_AllocStmt "SQLAllocStmt"
102 #define Name_Fetch "SQLFetch"
103 #define Name_FreeStmt "SQLFreeStmt"
104 #define Name_GetData "SQLGetData"
105 #define Name_AllocEnv "SQLAllocEnv"
106 #define Name_Execute "SQLExecute"
107 #define Name_NumResultCols "SQLNumResultCols"
108 #define Name_BindParameter "SQLBindParameter"
109 #define Name_NumParams "SQLNumParams"
110 #define Name_RowCount "SQLRowCount"
111 #define Name_SetEnvAttr "SQLSetEnvAttr"
112
113
114 #if defined(GATE_WIN32_UNICODE)
115 # define Name_Connect "SQLConnectW"
116 # define Name_DescribeCol "SQLDescribeColW"
117 # define Name_Prepare "SQLPrepareW"
118 # define Name_GetDiagRec "SQLGetDiagRecW"
119 #else
120 # define Name_Connect "SQLConnect"
121 # define Name_DescribeCol "SQLDescribeCol"
122 # define Name_Prepare "SQLPrepare"
123 # define Name_GetDiagRec "SQLGetDiagRec"
124 #endif
125
126 4 static gate_result_t load_odbc_functions()
127 {
128 static gate_library_t odbc_lib = NULL;
129 gate_result_t ret;
130 4 gate_library_t lib = NULL;
131
132 do
133 {
134
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if (odbc_lib != NULL)
135 {
136 3 ret = GATE_RESULT_OK;
137 3 break;
138 }
139
140 1 ret = gate_library_open(&lib_name, &lib, 0);
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
142
143 1 ret = gate_library_get_function_name(lib, Name_AllocConnect, &SQL.AllocConnect);
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
145 1 ret = gate_library_get_function_name(lib, Name_FreeEnv, &SQL.FreeEnv);
146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
147 1 ret = gate_library_get_function_name(lib, Name_FreeConnect, &SQL.FreeConnect);
148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
149 1 ret = gate_library_get_function_name(lib, Name_Disconnect, &SQL.Disconnect);
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
151 1 ret = gate_library_get_function_name(lib, Name_AllocStmt, &SQL.AllocStmt);
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
153 1 ret = gate_library_get_function_name(lib, Name_Fetch, &SQL.Fetch);
154
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
155 1 ret = gate_library_get_function_name(lib, Name_FreeStmt, &SQL.FreeStmt);
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
157 1 ret = gate_library_get_function_name(lib, Name_GetData, &SQL.GetData);
158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
159 1 ret = gate_library_get_function_name(lib, Name_AllocEnv, &SQL.AllocEnv);
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
161 1 ret = gate_library_get_function_name(lib, Name_Execute, &SQL.Execute);
162
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
163 1 ret = gate_library_get_function_name(lib, Name_NumResultCols, &SQL.NumResultCols);
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
165 1 ret = gate_library_get_function_name(lib, Name_Connect, &SQL.Connect);
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
167 1 ret = gate_library_get_function_name(lib, Name_DescribeCol, &SQL.DescribeCol);
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
169 1 ret = gate_library_get_function_name(lib, Name_Prepare, &SQL.Prepare);
170
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
171 1 ret = gate_library_get_function_name(lib, Name_GetDiagRec, &SQL.GetDiagRec);
172
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
173 1 ret = gate_library_get_function_name(lib, Name_BindParameter, &SQL.BindParameter);
174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
175 1 ret = gate_library_get_function_name(lib, Name_NumParams, &SQL.NumParams);
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
177 1 ret = gate_library_get_function_name(lib, Name_RowCount, &SQL.RowCount);
178
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
179
180 /* optional functions */
181 1 ret = gate_library_get_function_name(lib, Name_SetEnvAttr, &SQL.SetEnvAttr);
182
183 1 odbc_lib = lib;
184 1 lib = NULL;
185 1 ret = GATE_RESULT_OK;
186 } while (0);
187
188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (lib != NULL)
189 {
190 ✗ gate_library_close(lib);
191 }
192
193 4 return ret;
194 }
195
196
197
198 41 static gate_bool_t gate_data_reader_odbc_is_valid(gate_data_reader_base_t* reader)
199 {
200 41 SQLHSTMT stmt = (SQLHSTMT)reader->native_handles[0];
201 41 return stmt != NULL;
202 }
203
204 6 static gate_result_t gate_data_reader_odbc_close(gate_data_reader_base_t* reader)
205 {
206 6 reader->native_handles[0] = NULL;
207 6 return GATE_RESULT_OK;
208 }
209
210 13 static SQLSMALLINT gate_data_reader_odbc_patch_sql_type(SQLSMALLINT defined_type)
211 {
212 /* some types are defined but in SQLGetData another type must be used */
213
2/3
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
13 switch (defined_type)
214 {
215 6 case SQL_DECIMAL:
216 case SQL_NUMERIC:
217 case SQL_CHAR:
218 case SQL_VARCHAR:
219 6 case SQL_LONGVARCHAR: return SQL_CHAR;
220 ✗ case SQL_WVARCHAR:
221 ✗ case SQL_WLONGVARCHAR: return SQL_WCHAR;
222 7 default: break;
223 }
224 7 return defined_type;
225 }
226
227 13 static gate_value_t* gate_data_reader_odbc_get_value(SQLHSTMT hstmt, SQLUSMALLINT index, SQLSMALLINT sql_type, gate_value_t* value)
228 {
229 13 gate_value_t* ret = NULL;
230 13 char buffer[8192] = GATE_INIT_EMPTY;
231 13 SQLLEN buffer_used = 0;
232 SQLRETURN sql_res;
233 gate_bool_t bool_val;
234 13 gate_strbuilder_t text_builder = GATE_INIT_EMPTY;
235 13 gate_string_t text = GATE_STRING_INIT_EMPTY;
236 13 gate_array_t arr = GATE_INIT_EMPTY;
237 13 SQLGUID const* ptr_sql_guid = NULL;
238 gate_guid_t guid;
239 13 DATE_STRUCT const* ptr_date_struct = NULL;
240 13 TIME_STRUCT const* ptr_time_struct = NULL;
241 13 TIMESTAMP_STRUCT const* ptr_timestamp_struct = NULL;
242 gate_datetime_t dt;
243
244 13 sql_type = gate_data_reader_odbc_patch_sql_type(sql_type);
245
246 13 sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
247
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 switch (sql_res)
248 {
249 13 case SQL_SUCCESS:
250 case SQL_NO_DATA:
251 case SQL_SUCCESS_WITH_INFO:
252 {
253 /* OK, continue */
254 13 break;
255 }
256 ✗ default:
257 {
258 /* error case */
259 ✗ return ret;
260 }
261 }
262
263
4/20
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 6 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
13 switch (sql_type)
264 {
265 ✗ case SQL_C_STINYINT:
266 ✗ case SQL_TINYINT: { ret = gate_value_create(GATE_TYPE_I8, buffer, value); break; }
267 ✗ case SQL_C_SSHORT:
268 ✗ case SQL_SMALLINT: { ret = gate_value_create(GATE_TYPE_I16, buffer, value); break; }
269 5 case SQL_C_SLONG:
270 5 case SQL_INTEGER: { ret = gate_value_create(GATE_TYPE_I32, buffer, value); break; }
271 ✗ case SQL_C_SBIGINT:
272 ✗ case SQL_BIGINT: { ret = gate_value_create(GATE_TYPE_I64, buffer, value); break; }
273 ✗ case SQL_REAL: { ret = gate_value_create(GATE_TYPE_R32, buffer, value); break; }
274 ✗ case SQL_FLOAT: { ret = gate_value_create(GATE_TYPE_R32, buffer, value); break; }
275 1 case SQL_DOUBLE: { ret = gate_value_create(GATE_TYPE_R64, buffer, value); break; }
276 ✗ case SQL_C_UTINYINT: { ret = gate_value_create(GATE_TYPE_UI8, buffer, value); break; }
277 ✗ case SQL_C_USHORT: { ret = gate_value_create(GATE_TYPE_UI16, buffer, value); break; }
278 ✗ case SQL_C_ULONG: { ret = gate_value_create(GATE_TYPE_UI32, buffer, value); break; }
279 ✗ case SQL_C_UBIGINT: { ret = gate_value_create(GATE_TYPE_UI64, buffer, value); break; }
280 ✗ case SQL_BIT:
281 {
282 ✗ bool_val = buffer[0] == 0 ? false : true;
283 ✗ ret = gate_value_create(GATE_TYPE_BOOL, &bool_val, value);
284 ✗ break;
285 }
286
287 6 case SQL_DECIMAL:
288 case SQL_NUMERIC:
289 case SQL_CHAR:
290 case SQL_VARCHAR:
291 case SQL_LONGVARCHAR:
292 {
293 /* extract byte data */
294 6 gate_strbuilder_create(&text_builder, 0);
295 6 gate_strbuilder_append_text(&text_builder, &buffer[0], buffer_used);
296
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 while (sql_res == SQL_SUCCESS_WITH_INFO)
297 {
298 ✗ sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
299 ✗ gate_strbuilder_append_text(&text_builder, &buffer[0], buffer_used);
300 }
301 6 gate_strbuilder_to_string(&text_builder, &text);
302 6 ret = gate_value_create(GATE_TYPE_STRING, &text, value);
303 6 gate_string_release(&text);
304 6 gate_strbuilder_release(&text_builder);
305 6 break;
306 }
307 ✗ case SQL_WCHAR:
308 case SQL_WVARCHAR:
309 case SQL_WLONGVARCHAR:
310 {
311 /* extract UTF16 text */
312 ✗ gate_strbuilder_create(&text_builder, 0);
313 ✗ gate_strbuilder_append_text16(&text_builder, (gate_char16_t const*)&buffer[0], buffer_used / sizeof(gate_char16_t));
314 ✗ while (sql_res == SQL_SUCCESS_WITH_INFO)
315 {
316 ✗ sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
317 ✗ gate_strbuilder_append_text16(&text_builder, (gate_char16_t const*)&buffer[0], buffer_used / sizeof(gate_char16_t));
318 }
319 ✗ gate_strbuilder_to_string(&text_builder, &text);
320 ✗ ret = gate_value_create(GATE_TYPE_STRING, &text, value);
321 ✗ gate_string_release(&text);
322 ✗ gate_strbuilder_release(&text_builder);
323 ✗ break;
324 }
325 1 case SQL_BINARY:
326 case SQL_VARBINARY:
327 case SQL_LONGVARBINARY:
328 {
329 1 gate_arraylist_t arrlst = gate_arraylist_create(1, buffer, buffer_used, NULL, NULL);
330
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (!gate_arraylist_is_null(arrlst))
331 {
332
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 while (sql_res == SQL_SUCCESS_WITH_INFO)
333 {
334 ✗ sql_res = SQL.GetData(hstmt, index, sql_type, buffer, sizeof(buffer), &buffer_used);
335 ✗ gate_arraylist_add_n(arrlst, buffer, buffer_used);
336 }
337
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (NULL != gate_array_create(&arr, arrlst))
338 {
339 1 ret = gate_value_create(GATE_TYPE_ARRAY, &arr, value);
340 1 gate_array_release(&arr);
341 }
342 1 gate_arraylist_release(&arrlst);
343 }
344 1 break;
345 }
346 ✗ case SQL_GUID:
347 {
348 ✗ ptr_sql_guid = (SQLGUID const*)&buffer[0];
349 ✗ guid.item1 = ptr_sql_guid->Data1;
350 ✗ guid.item2 = ptr_sql_guid->Data2;
351 ✗ guid.item3 = ptr_sql_guid->Data3;
352 ✗ gate_mem_copy(guid.item4, ptr_sql_guid->Data4, 8);
353 ✗ ret = gate_value_create(GATE_TYPE_GUID, &guid, value);
354 ✗ break;
355 }
356 ✗ case SQL_TYPE_DATE:
357 {
358 ✗ ptr_date_struct = (DATE_STRUCT const*)&buffer[0];
359 ✗ dt.date.year = ptr_date_struct->year;
360 ✗ dt.date.month = (gate_uint8_t)ptr_date_struct->month;
361 ✗ dt.date.day = (gate_uint8_t)ptr_date_struct->day;
362 ✗ ret = gate_value_create(GATE_TYPE_DATE, &dt.date, value);
363 ✗ break;
364 }
365 ✗ case SQL_TYPE_TIME:
366 {
367 ✗ ptr_time_struct = (TIME_STRUCT const*)&buffer[0];
368 ✗ dt.time.hour = (gate_uint8_t)ptr_time_struct->hour;
369 ✗ dt.time.minute = (gate_uint8_t)ptr_time_struct->minute;
370 ✗ dt.time.second = (gate_uint8_t)ptr_time_struct->second;
371 ✗ dt.time.microsecond = 0;
372 ✗ ret = gate_value_create(GATE_TYPE_DAYTIME, &dt.time, value);
373 ✗ break;
374 }
375 ✗ case SQL_TYPE_TIMESTAMP:
376 {
377 ✗ ptr_timestamp_struct = (TIMESTAMP_STRUCT const*)&buffer[0];
378 ✗ dt.date.year = ptr_timestamp_struct->year;
379 ✗ dt.date.month = (gate_uint8_t)ptr_timestamp_struct->month;
380 ✗ dt.date.day = (gate_uint8_t)ptr_timestamp_struct->day;
381 ✗ dt.time.hour = (gate_uint8_t)ptr_timestamp_struct->hour;
382 ✗ dt.time.minute = (gate_uint8_t)ptr_timestamp_struct->minute;
383 ✗ dt.time.second = (gate_uint8_t)ptr_timestamp_struct->second;
384 ✗ dt.time.microsecond = ptr_timestamp_struct->fraction / 1000; /*fraction == nanoseconds */
385 ✗ ret = gate_value_create(GATE_TYPE_DAYTIME, &dt.time, value);
386 ✗ break;
387 }
388 ✗ default:
389 {
390 /* not supported */
391 ✗ break;
392 }
393 }
394
395 13 return ret;
396 }
397
398 4 static gate_result_t gate_data_reader_odbc_next(gate_data_reader_base_t* reader)
399 {
400 4 gate_result_t ret = GATE_RESULT_FAILED;
401 4 SQLHSTMT hstmt = (SQLHSTMT)reader->native_handles[0];
402 SQLRETURN sql_res;
403 gate_size_t ndx;
404
405 do
406 {
407
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (hstmt == NULL)
408 {
409 ✗ ret = GATE_RESULT_INVALIDSTATE;
410 ✗ break;
411 }
412
413 4 sql_res = SQL.Fetch(hstmt);
414
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (SQL_NO_DATA == sql_res)
415 {
416 2 gate_data_reader_base_close(reader);
417 2 ret = GATE_RESULT_ENDOFSTREAM;
418 2 break;
419 }
420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (SQL_SUCCESS != sql_res)
421 {
422 ✗ ret = GATE_RESULT_FAILED;
423 ✗ break;
424 }
425
426 2 ret = GATE_RESULT_OK;
427
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 2 times.
15 for (ndx = 0; ndx != reader->field_count; ++ndx)
428 {
429 13 gate_value_release(&reader->values[ndx]);
430 13 gate_data_reader_odbc_get_value(hstmt, (SQLUSMALLINT)(ndx + 1), (SQLUSMALLINT)reader->types[ndx], &reader->values[ndx]);
431 }
432
433 } while (0);
434
435 4 return ret;
436 }
437
438
439 2 static gate_result_t gate_data_reader_odbc_init(gate_data_reader_base_t* reader)
440 {
441 2 gate_result_t ret = GATE_RESULT_OK;
442 2 SQLSMALLINT col_count = 0;
443 SQLSMALLINT col_index;
444 SQLRETURN sql_ret;
445 2 SQLHSTMT hstmt = (SQLHSTMT)reader->native_handles[0];
446 SQLTCHAR colname[1024];
447 SQLSMALLINT colname_len;
448 SQLSMALLINT coltype;
449 SQLULEN colvalue_len;
450 SQLSMALLINT colvalue_digits;
451 SQLSMALLINT colvalue_nullable;
452
453 do
454 {
455 2 sql_ret = SQL.NumResultCols(hstmt, &col_count);
456
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (SQL_SUCCESS != sql_ret)
457 {
458 ✗ ret = GATE_RESULT_FAILED;
459 ✗ break;
460 }
461 2 reader->field_count = col_count;
462
463
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 2 times.
15 for (col_index = 0; col_index != col_count; ++col_index)
464 {
465 13 colname_len = sizeof(colname) / sizeof(colname[0]);
466 13 coltype = 0;
467 13 colvalue_len = 0;
468 13 colvalue_digits = 0;
469 13 colvalue_nullable = 0;
470 13 sql_ret = SQL.DescribeCol(hstmt, (SQLUSMALLINT)(col_index + 1), colname, colname_len, &colname_len,
471 &coltype, &colvalue_len, &colvalue_digits, &colvalue_nullable);
472 #if defined(GATE_SYS_WIN)
473 gate_win32_winstr_2_utf8_string((LPCTSTR)&colname[0], colname_len, &reader->names[col_index]);
474 #else
475 13 gate_string_create(&reader->names[col_index], (char const*)&colname[0], colname_len);
476 #endif
477 13 reader->types[col_index] = coltype;
478 }
479 } while (0);
480
481 2 return ret;
482 }
483
484
485 2 static gate_data_reader_t* gate_data_reader_odbc_create(gate_data_statement_base_t* ptr_statement, SQLHSTMT stmt)
486 {
487 2 gate_data_reader_t* ret = NULL;
488 2 gate_data_handles_t handles = GATE_INIT_EMPTY;
489
490 2 handles[0] = (void*)stmt;
491
492 2 ret = gate_data_reader_base_create(
493 (gate_data_statement_t*)ptr_statement, handles,
494 &gate_data_reader_odbc_init,
495 &gate_data_reader_odbc_is_valid,
496 &gate_data_reader_odbc_next,
497 &gate_data_reader_odbc_close);
498
499 2 return ret;
500 }
501
502 typedef union odbc_param_value {
503 SQLINTEGER sql_int;
504 SQLBIGINT sql_bigint;
505 SQLDOUBLE sql_double;
506 SQL_TIMESTAMP_STRUCT sql_timestamp;
507 gate_string_t text;
508 gate_blob_t blob;
509 } odbc_param_value_t;
510
511 typedef struct odbc_param {
512 SQLSMALLINT index; /* parameter index*/
513 odbc_param_value_t value; /* assigned value to parameter */
514 gate_mem_dtor_t dtor; /* destructor of value */
515 SQLLEN length; /* bind-length of value */
516 } odbc_param_t;
517
518 12 static void GATE_CALL odbc_param_dtor(void* dest)
519 {
520 12 odbc_param_t* const ptr = (odbc_param_t*)dest;
521
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
12 if (ptr->dtor)
522 {
523 4 ptr->dtor(&ptr->value);
524 4 ptr->dtor = NULL;
525 }
526 12 gate_mem_clear(ptr, sizeof(odbc_param_t));
527 12 }
528
529 12 static odbc_param_t* odbc_param_get(gate_slotlist_t* param_cache, SQLSMALLINT paramIndex)
530 {
531 gate_slotlist_iterator_t iter;
532 void* cache_item;
533 odbc_param_t* ptr_param;
534 12 cache_item = gate_slotlist_first(param_cache, &iter);
535 /* search already existing paramIndex */
536
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 12 times.
37 while (cache_item != NULL)
537 {
538 25 ptr_param = (odbc_param_t*)cache_item;
539
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if (ptr_param->index == paramIndex)
540 {
541 /* clean-up previous value fields */
542 ✗ odbc_param_dtor(ptr_param);
543 /* re-init empty value with index*/
544 ✗ ptr_param->index = paramIndex;
545 ✗ return ptr_param;
546 }
547 25 cache_item = gate_slotlist_next(param_cache, &iter);
548 }
549 /* paramIndex does not exist -> create new entry */
550 12 ptr_param = (odbc_param_t*)gate_mem_alloc(sizeof(odbc_param_t));
551
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (NULL != ptr_param)
552 {
553 12 gate_mem_clear(ptr_param, sizeof(odbc_param_t));
554 12 ptr_param->index = paramIndex;
555 12 gate_slotlist_import(param_cache, ptr_param);
556 }
557 12 return ptr_param;
558 }
559
560 11 static gate_result_t odbcstmt_init(gate_data_statement_base_t* self)
561 {
562 11 gate_slotlist_t* ptr_slots = (gate_slotlist_t*)gate_mem_alloc(sizeof(gate_slotlist_t));
563
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (ptr_slots == NULL)
564 {
565 ✗ return GATE_RESULT_OUTOFMEMORY;
566 }
567
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 if (NULL == gate_slotlist_create(ptr_slots, sizeof(odbc_param_t), NULL, &odbc_param_dtor))
568 {
569 ✗ gate_mem_dealloc(ptr_slots);
570 ✗ return GATE_RESULT_OUTOFMEMORY;
571 }
572 11 self->native_handles[2] = (void*)ptr_slots;
573 11 return GATE_RESULT_OK;
574 }
575
576 11 static void odbcstmt_close(gate_data_statement_base_t* self)
577 {
578 11 SQLHSTMT const hstmt = (SQLHSTMT)self->native_handles[1];
579 11 gate_slotlist_t* const ptr_slots = (gate_slotlist_t*)self->native_handles[2];
580
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (hstmt != NULL)
581 {
582 11 SQL.FreeStmt(hstmt, SQL_CLOSE);
583 11 SQL.FreeStmt(hstmt, SQL_DROP);
584 11 self->native_handles[1] = NULL;
585 }
586
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (ptr_slots != NULL)
587 {
588 11 gate_slotlist_destroy(ptr_slots);
589 11 gate_mem_dealloc(ptr_slots);
590 11 self->native_handles[2] = NULL;
591 }
592 11 }
593
594 12 static gate_result_t odbcstmt_convert_result(SQLRETURN sql_result, SQLHANDLE sql_handle)
595 {
596
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (SQL_SUCCEEDED(sql_result))
597 {
598 12 return GATE_RESULT_OK;
599 }
600 else
601 {
602 ✗ if (sql_handle != NULL)
603 {
604 ✗ SQLTCHAR sqlState[32] = GATE_INIT_EMPTY;
605 ✗ SQLINTEGER native_error_code = 0;
606 ✗ SQLTCHAR sqlMsg[1024] = GATE_INIT_EMPTY;
607 ✗ SQLSMALLINT sqlMsgLen = 0;
608 ✗ SQL.GetDiagRec(SQL_HANDLE_STMT, sql_handle, 1, sqlState, &native_error_code, sqlMsg, sizeof(sqlMsg) / sizeof(sqlMsg[0]), &sqlMsgLen);
609 ✗ return GATE_RESULT_FAILED;
610 }
611 /* TODO: detailed error analysis */
612 ✗ return GATE_RESULT_FAILED;
613 }
614 }
615
616 ✗ static gate_result_t odbcstmt_set_null_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_slotlist_t* param_cache)
617 {
618 ✗ odbc_param_t* ptr_param = odbc_param_get(param_cache, odbc_index);
619 ✗ if (NULL == ptr_param)
620 {
621 ✗ return GATE_RESULT_OUTOFMEMORY;
622 }
623 else
624 {
625 SQLRETURN sql_result;
626 ✗ ptr_param->length = SQL_NULL_DATA;
627 ✗ sql_result = SQL.BindParameter(
628 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 0, 0, NULL, 0, &ptr_param->length);
629 ✗ return odbcstmt_convert_result(sql_result, hstmt);
630 }
631 }
632
633 ✗ static gate_result_t odbcstmt_set_bigint_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_int64_t value, gate_slotlist_t* param_cache)
634 {
635 ✗ odbc_param_t* ptr_param = odbc_param_get(param_cache, odbc_index);
636 ✗ if (NULL == ptr_param)
637 {
638 ✗ return GATE_RESULT_OUTOFMEMORY;
639 }
640 else
641 {
642 SQLRETURN sql_result;
643 ✗ ptr_param->length = 0;
644 ✗ ptr_param->value.sql_bigint = value;
645 ✗ sql_result = SQL.BindParameter(
646 ✗ hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &ptr_param->value.sql_bigint, 0, &ptr_param->length);
647 ✗ return odbcstmt_convert_result(sql_result, hstmt);
648 }
649 }
650
651 4 static gate_result_t odbcstmt_set_int_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_int32_t value, gate_slotlist_t* param_cache)
652 {
653 4 odbc_param_t* ptr_param = odbc_param_get(param_cache, odbc_index);
654
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (NULL == ptr_param)
655 {
656 ✗ return GATE_RESULT_OUTOFMEMORY;
657 }
658 else
659 {
660 SQLRETURN sql_result;
661 4 ptr_param->length = 0;
662 4 ptr_param->value.sql_int = value;
663 8 sql_result = SQL.BindParameter(
664 4 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &ptr_param->value.sql_int, 0, &ptr_param->length);
665 4 return odbcstmt_convert_result(sql_result, hstmt);
666 }
667 }
668
669 1 static gate_result_t odbcstmt_set_real_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_real64_t value, gate_slotlist_t* param_cache)
670 {
671 1 odbc_param_t* ptr_param = odbc_param_get(param_cache, odbc_index);
672
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == ptr_param)
673 {
674 ✗ return GATE_RESULT_OUTOFMEMORY;
675 }
676 else
677 {
678 SQLRETURN sql_result;
679 1 ptr_param->length = 0;
680 1 ptr_param->value.sql_double = value;
681 2 sql_result = SQL.BindParameter(
682 1 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, 0, 0, &ptr_param->value.sql_double, 0, &ptr_param->length);
683 1 return odbcstmt_convert_result(sql_result, hstmt);
684 }
685 }
686
687 3 static gate_result_t odbcstmt_set_text_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, char const* text, gate_size_t textlen, gate_slotlist_t* param_cache)
688 {
689 3 odbc_param_t* ptr_param = odbc_param_get(param_cache, odbc_index);
690
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (NULL == ptr_param)
691 {
692 ✗ return GATE_RESULT_OUTOFMEMORY;
693 }
694 else
695 {
696 SQLRETURN sql_result;
697
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (NULL == gate_string_create(&ptr_param->value.text, text, textlen))
698 {
699 ✗ return GATE_RESULT_OUTOFMEMORY;
700 }
701 3 ptr_param->dtor = &gate_string_destructor;
702 3 ptr_param->length = textlen;
703 6 sql_result = SQL.BindParameter(
704 3 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, (SQLULEN)textlen, 0, (char*)ptr_param->value.text.str, 0, &ptr_param->length);
705 3 return odbcstmt_convert_result(sql_result, hstmt);
706 }
707 }
708
709 1 static gate_result_t odbcstmt_set_binary_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, void const* data, gate_size_t datalen, gate_slotlist_t* param_cache)
710 {
711 1 odbc_param_t* ptr_param = odbc_param_get(param_cache, odbc_index);
712
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == ptr_param)
713 {
714 ✗ return GATE_RESULT_OUTOFMEMORY;
715 }
716 else
717 {
718 SQLRETURN sql_result;
719
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_blob_create(&ptr_param->value.blob, data, datalen))
720 {
721 ✗ return GATE_RESULT_OUTOFMEMORY;
722 }
723 1 ptr_param->dtor = &gate_blob_destructor;
724 1 ptr_param->length = datalen;
725 2 sql_result = SQL.BindParameter(
726 1 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, 0, 0, (SQLPOINTER)ptr_param->value.blob.data, 0, &ptr_param->length);
727 1 return odbcstmt_convert_result(sql_result, hstmt);
728 }
729 }
730
731 3 static gate_result_t odbcstmt_set_datetime_param(SQLHSTMT hstmt, SQLUSMALLINT odbc_index, gate_datetime_t const* dt, gate_slotlist_t* param_cache)
732 {
733 3 odbc_param_t* ptr_param = odbc_param_get(param_cache, odbc_index);
734
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (NULL == ptr_param)
735 {
736 ✗ return GATE_RESULT_OUTOFMEMORY;
737 }
738 else
739 {
740 SQLRETURN sql_result;
741 3 SQL_TIMESTAMP_STRUCT* sql_dt = &ptr_param->value.sql_timestamp;
742 3 sql_dt->year = dt->date.year;
743 3 sql_dt->month = dt->date.month;
744 3 sql_dt->day = dt->date.day;
745 3 sql_dt->hour = dt->time.hour;
746 3 sql_dt->minute = dt->time.minute;
747 3 sql_dt->second = dt->time.second;
748 3 sql_dt->fraction = 0;
749 3 ptr_param->length = sizeof(SQL_TIMESTAMP_STRUCT);
750
751
752 3 sql_result = SQL.BindParameter(
753 hstmt, odbc_index, SQL_PARAM_INPUT, SQL_C_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP, 0, 0, sql_dt, ptr_param->length, &ptr_param->length);
754
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!SQL_SUCCEEDED(sql_result))
755 {
756 /* ODBC Date-time structs are not supported by all drivers, try text-representation: */
757 gate_result_t result;
758 ✗ char str[32] = GATE_INIT_EMPTY;
759 ✗ gate_size_t len = sizeof(str);
760 ✗ result = gate_date_to_string(dt, 0, "{YYYY}-{MM}-{DD} {hh}:{mm}:{ss}", str, &len);
761 ✗ if (GATE_SUCCEEDED(result))
762 {
763 ✗ return odbcstmt_set_text_param(hstmt, odbc_index, str, len, param_cache);
764 }
765 }
766 3 return odbcstmt_convert_result(sql_result, hstmt);
767 }
768 }
769
770
771 12 static gate_result_t odbcstmt_set_param(gate_data_statement_base_t* self, gate_size_t index, gate_value_t const* value)
772 {
773 12 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
774 12 SQLUSMALLINT odbc_index = (SQLUSMALLINT)(index);
775 12 gate_slotlist_t* ptr_slots = (gate_slotlist_t*)self->native_handles[2];
776 gate_cstrbuffer8_t buffer;
777 12 gate_datetime_t dt = GATE_INIT_EMPTY;
778 12 gate_result_t ret = GATE_RESULT_FAILED;
779
780
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if ((value == NULL) || (value->value_type == GATE_TYPE_EMPTY))
781 {
782 ✗ return odbcstmt_set_null_param(hstmt, odbc_index, ptr_slots);
783 }
784
785
11/20
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 1 times.
✓ Branch 17 taken 1 times.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
12 switch (value->value_type)
786 {
787 ✗ case GATE_TYPE_BOOL: return odbcstmt_set_int_param(hstmt, odbc_index, value->content.bool_value ? 1 : 0, ptr_slots);
788 1 case GATE_TYPE_I8: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.i8_value, ptr_slots);
789 1 case GATE_TYPE_I16: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.i16_value, ptr_slots);
790 2 case GATE_TYPE_I32: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.i32_value, ptr_slots);
791 ✗ case GATE_TYPE_I64: return odbcstmt_set_bigint_param(hstmt, odbc_index, (gate_int32_t)value->content.i64_value, ptr_slots);
792 ✗ case GATE_TYPE_UI8: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.ui8_value, ptr_slots);
793 ✗ case GATE_TYPE_UI16: return odbcstmt_set_int_param(hstmt, odbc_index, (gate_int32_t)value->content.ui16_value, ptr_slots);
794 ✗ case GATE_TYPE_UI32: return odbcstmt_set_bigint_param(hstmt, odbc_index, (gate_int64_t)value->content.ui32_value, ptr_slots);
795 ✗ case GATE_TYPE_UI64: return odbcstmt_set_bigint_param(hstmt, odbc_index, (gate_int64_t)value->content.ui64_value, ptr_slots);
796
797 1 case GATE_TYPE_R32: return odbcstmt_set_real_param(hstmt, odbc_index, (gate_real64_t)value->content.r32_value, ptr_slots);
798 ✗ case GATE_TYPE_R64: return odbcstmt_set_real_param(hstmt, odbc_index, (gate_real64_t)value->content.r64_value, ptr_slots);
799
800 1 case GATE_TYPE_BLOB: return odbcstmt_set_binary_param(hstmt, odbc_index, value->content.blob_value.data, value->content.blob_value.length, ptr_slots);
801
802 ✗ case GATE_TYPE_CSTR: return odbcstmt_set_text_param(hstmt, odbc_index, value->content.cstring_value, gate_str_length(value->content.cstring_value), ptr_slots);
803 1 case GATE_TYPE_STRING: return odbcstmt_set_text_param(hstmt, odbc_index, value->content.string_value.str, value->content.string_value.length, ptr_slots);
804
805 1 case GATE_TYPE_WSTR:
806 {
807
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (NULL == gate_cstrbuffer_create_wstr(&buffer, value->content.wstring_value, gate_wstr_length(value->content.wstring_value)))
808 {
809 ✗ return GATE_RESULT_OUTOFMEMORY;
810 }
811 1 ret = odbcstmt_set_text_param(hstmt, odbc_index, gate_cstrbuffer_get(&buffer), gate_cstrbuffer_length(&buffer), ptr_slots);
812 1 gate_cstrbuffer_destroy(&buffer);
813 1 return ret;
814 }
815 1 case GATE_TYPE_GUID:
816 {
817 1 ret = gate_guid_to_string(&value->content.guid_value, buffer.local_buffer);
818
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_RETURN_IF_FAILED(ret);
819 1 return odbcstmt_set_text_param(hstmt, odbc_index, buffer.local_buffer, gate_str_length(buffer.local_buffer), ptr_slots);
820 }
821 1 case GATE_TYPE_DATE:
822 {
823 1 dt.date = value->content.date_value;
824 1 return odbcstmt_set_datetime_param(hstmt, odbc_index, &dt, ptr_slots);
825 }
826 1 case GATE_TYPE_DATETIME:
827 {
828 1 return odbcstmt_set_datetime_param(hstmt, odbc_index, &value->content.datetime_value, ptr_slots);
829 }
830 1 case GATE_TYPE_TIME:
831 {
832 1 ret = gate_time_to_datetime(&value->content.time_value, &dt);
833
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_RETURN_IF_FAILED(ret);
834 1 return odbcstmt_set_datetime_param(hstmt, odbc_index, &dt, ptr_slots);
835 }
836 ✗ case GATE_TYPE_PROPERTY:
837 default:
838 ✗ return GATE_RESULT_INCORRECTTYPE;
839 }
840 }
841
842 ✗ static gate_result_t odbcstmt_set_named_param(gate_data_statement_base_t* self, gate_string_t const* name, gate_value_t const* value)
843 {
844 /* named parameters are not supported by odbc */
845 ✗ return GATE_RESULT_NOTSUPPORTED;
846 }
847
848 3 static gate_result_t odbcstmt_reset(gate_data_statement_base_t* self)
849 {
850 SQLRETURN sql_ret;
851 3 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
852
853
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (hstmt == NULL)
854 {
855 ✗ return GATE_RESULT_INVALIDSTATE;
856 }
857
858 3 sql_ret = SQL.FreeStmt(hstmt, SQL_RESET_PARAMS);
859
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 switch (sql_ret)
860 {
861 3 case SQL_SUCCESS:
862 case SQL_SUCCESS_WITH_INFO:
863 3 return GATE_RESULT_OK;
864 ✗ default:
865 ✗ return GATE_RESULT_FAILED;
866 }
867 }
868
869 9 static gate_result_t odbcstmt_execute(gate_data_statement_base_t* self, gate_int32_t* affected_rows)
870 {
871 SQLRETURN sql_ret;
872 9 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
873 9 SQLLEN len_rows = 0;
874
875
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (hstmt == NULL)
876 {
877 ✗ return GATE_RESULT_INVALIDSTATE;
878 }
879
880 9 sql_ret = SQL.Execute(hstmt);
881
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
9 if ((SQL_SUCCESS != sql_ret) && (SQL_SUCCESS_WITH_INFO != sql_ret))
882 {
883 ✗ SQLTCHAR sqlState[32] = GATE_INIT_EMPTY;
884 ✗ SQLINTEGER native_error_code = 0;
885 ✗ SQLTCHAR sqlMsg[1024] = GATE_INIT_EMPTY;
886 ✗ SQLSMALLINT sqlMsgLen = 0;
887 ✗ SQL.GetDiagRec(SQL_HANDLE_STMT, hstmt, 1, sqlState, &native_error_code, sqlMsg, sizeof(sqlMsg) / sizeof(sqlMsg[0]), &sqlMsgLen);
888 ✗ return GATE_RESULT_FAILED;
889 }
890
891 /* success case */
892
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 if (affected_rows)
893 {
894 9 SQL.RowCount(hstmt, &len_rows);
895 9 *affected_rows = (gate_int32_t)len_rows;
896 }
897 9 return GATE_RESULT_OK;
898 }
899
900 2 static gate_result_t odbcstmt_query(gate_data_statement_base_t* self, gate_data_reader_t** ptr_reader)
901 {
902 SQLRETURN sql_ret;
903 2 SQLHSTMT hstmt = (SQLHSTMT)self->native_handles[1];
904 2 SQLLEN len_rows = 0;
905
906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (hstmt == NULL)
907 {
908 ✗ return GATE_RESULT_INVALIDSTATE;
909 }
910
911 2 sql_ret = SQL.Execute(hstmt);
912
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2 if ((SQL_SUCCESS != sql_ret) && (SQL_SUCCESS_WITH_INFO != sql_ret))
913 {
914 ✗ SQLTCHAR sqlState[32] = GATE_INIT_EMPTY;
915 ✗ SQLINTEGER native_error_code = 0;
916 ✗ SQLTCHAR sqlMsg[1024] = GATE_INIT_EMPTY;
917 ✗ SQLSMALLINT sqlMsgLen = 0;
918 ✗ SQL.GetDiagRec(SQL_HANDLE_STMT, hstmt, 1, sqlState, &native_error_code, sqlMsg, sizeof(sqlMsg) / sizeof(sqlMsg[0]), &sqlMsgLen);
919 ✗ return GATE_RESULT_FAILED;
920 }
921
922
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ptr_reader == NULL)
923 {
924 /* success, reader not required, free unused resources */
925 ✗ SQL.FreeStmt(hstmt, SQL_CLOSE);
926 ✗ SQL.FreeStmt(hstmt, SQL_DROP);
927 ✗ return GATE_RESULT_OK;
928 }
929
930 2 *ptr_reader = gate_data_reader_odbc_create(self, hstmt);
931
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (*ptr_reader == NULL)
932 {
933 /* failed to allocate data reader */
934 ✗ SQL.FreeStmt(hstmt, SQL_CLOSE);
935 ✗ SQL.FreeStmt(hstmt, SQL_DROP);
936 ✗ return GATE_RESULT_FAILED;
937 }
938 2 return GATE_RESULT_OK;
939
940 }
941
942
943
944
945
946
947
948
949 11 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)
950 {
951 11 gate_result_t ret = GATE_RESULT_FAILED;
952 11 SQLHSTMT hstmt = NULL;
953 11 SQLHDBC hdbc = self->native_handles[0];
954 SQLRETURN sql_ret;
955 SQLTCHAR cmd[4096 + 2048 + 1024];
956 11 SQLSMALLINT param_count = 0;
957 gate_size_t cmd_len;
958 gate_data_handles_t handles;
959 11 gate_data_statement_base_t* ptr_stmt_base = NULL;
960
961 do
962 {
963 #if defined(GATE_SYS_WIN)
964 cmd_len = gate_win32_utf8_2_winstr(sql->str, sql->length, (LPTSTR)&cmd[0], sizeof(cmd) / sizeof(cmd[0]));
965 #else
966 11 cmd_len = gate_string_to_buffer(sql, (char*)&cmd[0], sizeof(cmd) / sizeof(cmd[0]));
967 #endif
968
969 11 sql_ret = SQL.AllocStmt(hdbc, &hstmt);
970
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (SQL_SUCCESS != sql_ret)
971 {
972 ✗ ret = GATE_RESULT_OUTOFRESOURCES;
973 ✗ break;
974 }
975
976 11 sql_ret = SQL.Prepare(hstmt, cmd, (SQLINTEGER)cmd_len);
977
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (SQL_SUCCESS != sql_ret)
978 {
979 ✗ ret = GATE_RESULT_FAILED;
980 ✗ break;
981 }
982
983 11 gate_mem_clear(&handles[0], sizeof(handles));
984 11 handles[0] = (void*)hdbc;
985 11 handles[1] = (void*)hstmt;
986
987 11 ret = gate_data_statement_base_create(
988 self, handles, sql,
989 &odbcstmt_init,
990 &odbcstmt_close,
991 &odbcstmt_set_param,
992 &odbcstmt_set_named_param,
993 &odbcstmt_reset,
994 &odbcstmt_execute,
995 &odbcstmt_query,
996 &ptr_stmt_base);
997
998
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 GATE_BREAK_IF_FAILED(ret);
999
1000
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 if (SQL_ERROR != SQL.NumParams(hstmt, &param_count))
1001 {
1002 11 ptr_stmt_base->param_count = param_count;
1003 }
1004
1005 /* success case: */
1006
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (ptr_stmt != NULL)
1007 {
1008 11 *ptr_stmt = (gate_data_statement_t*)ptr_stmt_base;
1009 11 ptr_stmt_base = NULL;
1010 }
1011 11 hstmt = NULL;
1012 11 ret = GATE_RESULT_OK;
1013 } while (0);
1014
1015
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (ptr_stmt_base != NULL)
1016 {
1017 ✗ gate_object_release(ptr_stmt_base);
1018 }
1019
1020
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (hstmt != NULL)
1021 {
1022 ✗ SQL.FreeStmt(hstmt, SQL_CLOSE);
1023 ✗ SQL.FreeStmt(hstmt, SQL_DROP);
1024 }
1025 11 return ret;
1026 }
1027
1028 2 static gate_result_t gate_data_connection_odbc_init(gate_data_connection_base_t* self)
1029 {
1030 GATE_UNUSED_ARG(self);
1031 2 return GATE_RESULT_OK;
1032 }
1033
1034 2 static void gate_data_connection_odbc_close(gate_data_connection_base_t* self)
1035 {
1036 2 SQLHDBC hdbc = self->native_handles[0];
1037 2 SQLHENV henv = self->native_handles[1];
1038
1039
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (hdbc != NULL)
1040 {
1041 2 SQL.Disconnect(hdbc);
1042 2 SQL.FreeConnect(hdbc);
1043 }
1044
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (henv != NULL)
1045 {
1046 #if !defined(GATE_SYS_WIN)
1047 /* NOTICE: Windows ODBC 3.x drives produce errors in Application Verifier
1048 due to pooled connections or another object hierarchy.
1049 Explicit freeing is not required and disabled therefor.
1050 */
1051 2 SQL.FreeEnv(henv);
1052 #endif
1053 }
1054 2 gate_mem_clear(self->native_handles, sizeof(self->native_handles));
1055 2 }
1056
1057
1058
1059 4 gate_result_t gate_data_adapter_odbc_create(gate_string_t const* path, gate_enumint_t flags, gate_data_connection_t** ptr_connection)
1060 {
1061 4 gate_result_t ret = GATE_RESULT_OK;
1062 SQLTCHAR str_connection[4096];
1063 gate_size_t str_connection_len;
1064 4 SQLTCHAR* str_user = NULL;
1065 4 gate_size_t str_user_len = 0;
1066 4 SQLTCHAR* str_pass = NULL;
1067 4 gate_size_t str_pass_len = 0;
1068 4 SQLHENV henv = NULL;
1069 4 SQLHDBC hdbc = NULL;
1070 SQLRETURN sql_ret;
1071 4 gate_data_handles_t handles = GATE_INIT_EMPTY;
1072
1073 do
1074 {
1075 4 ret = load_odbc_functions();
1076
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 GATE_BREAK_IF_FAILED(ret);
1077
1078 #if defined(GATE_SYS_WIN)
1079 str_connection_len = gate_win32_utf8_2_winstr(path->str, path->length, (LPTSTR)&str_connection[0], sizeof(str_connection) / sizeof(str_connection[0]));
1080 #else
1081 4 str_connection_len = gate_string_to_buffer(path, (char*)&str_connection[0], sizeof(str_connection) / sizeof(str_connection[0]));
1082 #endif
1083 4 sql_ret = SQL.AllocEnv(&henv);
1084
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (SQL_SUCCESS != sql_ret)
1085 {
1086 ✗ ret = GATE_RESULT_OUTOFRESOURCES;
1087 ✗ break;
1088 }
1089
1090 4 sql_ret = SQL.AllocConnect(henv, &hdbc);
1091
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (SQL_SUCCESS != sql_ret)
1092 {
1093 ✗ ret = GATE_RESULT_OUTOFRESOURCES;
1094 ✗ break;
1095 }
1096
1097 12 sql_ret = SQL.Connect(hdbc, str_connection, (SQLSMALLINT)str_connection_len,
1098 4 str_user, (SQLSMALLINT)str_user_len, str_pass, (SQLSMALLINT)str_pass_len);
1099
1100
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 if ((SQL_SUCCESS != sql_ret) && (SQL_SUCCESS_WITH_INFO != sql_ret))
1101 {
1102 SQLTCHAR state_msg[1024];
1103 2 SQLINTEGER native_error = 0;
1104 SQLTCHAR error_msg[1024];
1105 2 SQLSMALLINT error_msg_used = 0;
1106 2 SQL.GetDiagRec(SQL_HANDLE_DBC, hdbc, 1, state_msg, &native_error, error_msg, 1024, &error_msg_used);
1107 2 ret = GATE_RESULT_FAILED;
1108 2 break;
1109 }
1110
1111 2 handles[0] = (void*)hdbc;
1112 2 handles[1] = (void*)henv;
1113 2 ret = gate_data_connection_base_create(
1114 handles,
1115 &gate_data_connection_odbc_init,
1116 &gate_data_connection_odbc_close,
1117 &gate_data_connection_odbc_create_statement,
1118 ptr_connection);
1119
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (GATE_SUCCEEDED(ret))
1120 {
1121 2 hdbc = NULL;
1122 2 henv = NULL;
1123 }
1124 } while (0);
1125
1126
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (hdbc)
1127 {
1128 2 SQL.Disconnect(hdbc);
1129 2 SQL.FreeConnect(hdbc);
1130 }
1131
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (henv)
1132 {
1133 2 SQL.FreeEnv(henv);
1134 }
1135 4 return ret;
1136 }
1137
1138 #else /* GATE_DATA_ODBC_IMPL */
1139
1140 gate_result_t gate_data_adapter_odbc_create(gate_string_t const* path, gate_enumint_t flags, gate_data_connection_t** ptr_connection)
1141 {
1142 (void)path;
1143 (void)flags;
1144 (void)ptr_connection;
1145 return GATE_RESULT_NOTIMPLEMENTED;
1146 }
1147
1148 #endif
1149