Line data Source code
1 : /* Test binding and calling of TVPs */
2 :
3 : #include "common.h"
4 : #include <assert.h>
5 : #include <odbcss.h>
6 :
7 : #undef MEMORY_TESTS
8 : #if defined(HAVE_MALLOC_H)
9 : # include <malloc.h>
10 : # if defined(HAVE_MALLINFO2) || defined(HAVE_MALLINFO) || defined(HAVE__HEAPWALK)
11 : # define MEMORY_TESTS 1
12 : # endif
13 : #endif
14 :
15 : #if defined(HAVE_VALGRIND_MEMCHECK_H)
16 : # include <valgrind/valgrind.h>
17 : #else
18 : # define RUNNING_ON_VALGRIND 0
19 : #endif
20 :
21 : #include <freetds/bool.h>
22 :
23 : #define MAX_ROWS 5
24 : #define MAX_STRING_LENGTH 20
25 :
26 : static SQLINTEGER intCol[MAX_ROWS];
27 : static SQLCHAR strCol[MAX_ROWS][MAX_STRING_LENGTH], binCol[MAX_ROWS][MAX_STRING_LENGTH];
28 : static SQL_DATE_STRUCT dateCol[MAX_ROWS];
29 : static SQL_NUMERIC_STRUCT numericCol[MAX_ROWS];
30 :
31 : static SQLLEN lIntCol[MAX_ROWS];
32 : static SQLLEN lStrCol[MAX_ROWS];
33 : static SQLLEN lDateCol[MAX_ROWS];
34 : static SQLLEN lNumericCol[MAX_ROWS];
35 : static SQLLEN lBinCol[MAX_ROWS];
36 :
37 : static SQLCHAR outputBuffer[256];
38 : static SQLLEN lenBuffer;
39 :
40 : typedef union {
41 : SQLPOINTER fldSQLPOINTER;
42 : SQLSMALLINT fldSQLSMALLINT;
43 : SQLUSMALLINT fldSQLUSMALLINT;
44 : SQLINTEGER fldSQLINTEGER;
45 : SQLUINTEGER fldSQLUINTEGER;
46 : SQLLEN fldSQLLEN;
47 : SQLULEN fldSQLULEN;
48 : } field_output;
49 :
50 : /* utility to get a field from descriptor */
51 : static field_output
52 108 : get_desc_field(SQLINTEGER desc_type, SQLSMALLINT icol, SQLSMALLINT fDescType, size_t size)
53 : {
54 : SQLHDESC desc;
55 : SQLINTEGER ind;
56 : field_output buf;
57 :
58 108 : assert(size <= sizeof(buf));
59 108 : CHKGetStmtAttr(desc_type, &desc, sizeof(desc), &ind, "S");
60 :
61 108 : memset(&buf, 0x5a, sizeof(buf));
62 108 : ind = 1234;
63 108 : CHKGetDescField(desc, icol, fDescType, &buf, (SQLINTEGER) size, &ind,
64 : "S");
65 108 : assert(ind == size);
66 :
67 108 : return buf;
68 : }
69 :
70 : /* Utility to get a field from descriptor.
71 : * desc_type APP or IMP.
72 : * col column number (or 0 if does not matter).
73 : * field descriptor field (SQL_DESC_xxx).
74 : * type SQL type to be returned, strings not supported.
75 : */
76 : #define GET_DESC_FIELD(desc_type, col, field, type) \
77 : (get_desc_field(SQL_ATTR_ ## desc_type ## _PARAM_DESC, col, field, sizeof(type)).fld ## type)
78 :
79 : /* utility to check condition and returns error string */
80 : static char*
81 104 : check_cond(bool condition, const char *fmt, ...)
82 : {
83 : va_list ap;
84 104 : char *ret = NULL;
85 :
86 104 : if (condition)
87 : return ret;
88 :
89 0 : va_start(ap, fmt);
90 0 : assert(vasprintf(&ret, fmt, ap) >= 0);
91 0 : va_end(ap);
92 0 : return ret;
93 : }
94 :
95 : #define CHECK_COND(args) do { \
96 : char *err = check_cond args; \
97 : if (err) { \
98 : failed = true; \
99 : fprintf(stderr, "Wrong condition at line %d: %s\n", __LINE__, err); \
100 : free(err); \
101 : } \
102 : } while(0)
103 :
104 : /*
105 : * Generate some data as the columns of our TVPs
106 : */
107 : static void
108 10 : setup(void)
109 : {
110 : int i;
111 :
112 60 : for (i = 0; i < MAX_ROWS; i++) {
113 : /* Setup integer column */
114 50 : intCol[i] = i * 10;
115 50 : lIntCol[i] = sizeof(SQLINTEGER);
116 :
117 : /* Setup string column */
118 50 : sprintf((char *) strCol[i], "Dummy value %d", i * 3);
119 50 : lStrCol[i] = strlen((char *) strCol[i]);
120 :
121 : /* Setup date column */
122 50 : dateCol[i].day = (i % 28) + 1;
123 50 : dateCol[i].month = (i * 5) % 12 + 1;
124 50 : dateCol[i].year = i + 2000;
125 50 : lDateCol[i] = sizeof(SQL_DATE_STRUCT);
126 :
127 : /* Setup numeric values column */
128 50 : numericCol[i].precision = 10;
129 50 : numericCol[i].scale = 0;
130 50 : numericCol[i].sign = i % 2;
131 50 : memset(numericCol[i].val, 0, SQL_MAX_NUMERIC_LEN);
132 50 : sprintf((char *) numericCol[i].val, "%x", i * 10);
133 50 : lNumericCol[i] = sizeof(SQL_NUMERIC_STRUCT);
134 :
135 : /* Setup binary values column */
136 50 : sprintf((char *) binCol[i], "%d", i * 11);
137 50 : lBinCol[i] = strlen((char *) binCol[i]);
138 : }
139 10 : }
140 :
141 : static void
142 : dirty_name(SQLWCHAR *name)
143 : {
144 16 : const SQLWCHAR f = name[0];
145 16 : name[0] = (f == 'X' || f == 'x') ? 'Y' : 'X';
146 : }
147 :
148 : /*
149 : * Test calling a RPC with a TVP containing 3 columns and 4 rows
150 : */
151 : static void
152 4 : TestTVPInsert(void)
153 : {
154 : SQLWCHAR *tableName;
155 : SQLLEN numRows;
156 : SQLHDESC apd;
157 4 : bool failed = false;
158 : SQLPOINTER ptr;
159 :
160 : /* here we append some dummy string to check binding with a length */
161 4 : tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType_and_garbage");
162 :
163 4 : odbc_command("IF OBJECT_ID('TestTVPProc') IS NOT NULL DROP PROC TestTVPProc");
164 4 : odbc_command("IF TYPE_ID('TVPType') IS NOT NULL DROP TYPE TVPType");
165 4 : odbc_command("IF OBJECT_ID('TVPTable') IS NOT NULL DROP TABLE TVPTable");
166 :
167 4 : odbc_command("CREATE TABLE TVPTable (PersonID INT PRIMARY KEY, Name VARCHAR(50))");
168 4 : odbc_command("CREATE TYPE TVPType " "AS TABLE (vPersonID INT PRIMARY KEY, vName VARCHAR(50))");
169 4 : odbc_command("CREATE PROCEDURE TestTVPProc (@TVPParam TVPType READONLY) "
170 : "AS INSERT INTO TVPTable SELECT * FROM @TVPParam");
171 :
172 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
173 4 : ptr = GET_DESC_FIELD(APP, 1, SQL_DESC_DATA_PTR, SQLPOINTER);
174 4 : CHECK_COND((ptr == intCol, "SQL_DESC_DATA_PTR expected %p got %p", intCol, ptr));
175 4 : CHKGetStmtAttr(SQL_ATTR_APP_PARAM_DESC, &apd, sizeof(apd), NULL, "S");
176 4 : CHKSetDescField(apd, 1, SQL_DESC_CONCISE_TYPE, TDS_INT2PTR(SQL_C_DOUBLE), sizeof(SQLSMALLINT), "S");
177 4 : ptr = GET_DESC_FIELD(APP, 1, SQL_DESC_DATA_PTR, SQLPOINTER);
178 4 : CHECK_COND((ptr == NULL, "SQL_DESC_DATA_PTR expected %p got %p", NULL, ptr));
179 4 : assert(!failed);
180 :
181 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, 7 * sizeof(SQLWCHAR), NULL, "S");
182 4 : dirty_name(tableName);
183 :
184 4 : CHKGetStmtAttr(SQL_ATTR_APP_PARAM_DESC, &apd, sizeof(apd), NULL, "S");
185 4 : CHKSetDescField(apd, 1, SQL_DESC_OCTET_LENGTH_PTR, (SQLPOINTER) &numRows, sizeof(void*), "S");
186 :
187 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
188 :
189 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
190 4 : CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, MAX_STRING_LENGTH, 0, strCol, MAX_STRING_LENGTH, lStrCol, "S");
191 :
192 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
193 :
194 : /* Population of the StrLen_or_IndPtr buffer can be deferred */
195 : /* We use one rows less than the maximum to check if code is using the right value */
196 4 : numRows = MAX_ROWS - 1;
197 :
198 4 : CHKExecDirect(T("{CALL TestTVPProc(?)}"), SQL_NTS, "S");
199 :
200 : /* Ensure that we successfully add 5 rows */
201 4 : odbc_command("SELECT COUNT(*) FROM TVPTable");
202 :
203 4 : CHKFetch("SI");
204 :
205 4 : CHKGetData(1, SQL_C_CHAR, outputBuffer, sizeof(outputBuffer), &lenBuffer, "S");
206 4 : if (atoi((char *) outputBuffer) != numRows) {
207 0 : fprintf(stderr, "Wrong number of rows inserted, expected %ld, got %s\n", (long) numRows, outputBuffer);
208 0 : exit(1);
209 : }
210 :
211 4 : CHKFetch("No");
212 4 : CHKCloseCursor("SI");
213 :
214 : /* check all rows are present */
215 4 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable WHERE PersonID = 0 AND Name = 'Dummy value 0') SELECT 1");
216 4 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable WHERE PersonID = 10 AND Name = 'Dummy value 3') SELECT 1");
217 4 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable WHERE PersonID = 20 AND Name = 'Dummy value 6') SELECT 1");
218 4 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable WHERE PersonID = 30 AND Name = 'Dummy value 9') SELECT 1");
219 4 : odbc_check_no_row("IF EXISTS(SELECT * FROM TVPTable WHERE PersonID = 40 AND Name = 'Dummy value 12') SELECT 1");
220 :
221 4 : odbc_command("DROP PROC TestTVPProc");
222 4 : odbc_command("DROP TYPE TVPType");
223 4 : odbc_command("DROP TABLE TVPTable");
224 :
225 4 : CHKFreeStmt(SQL_RESET_PARAMS, "S");
226 4 : }
227 :
228 : /*
229 : * Test TVP usage with more parameter types, using a TVP of 2 columns and 5 rows
230 : */
231 : static void
232 4 : TestTVPInsert2(void)
233 : {
234 : SQLWCHAR *tableName; /* Test explicit schema declaration */
235 : SQLLEN numRows;
236 :
237 4 : tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType2");
238 :
239 4 : odbc_command("IF OBJECT_ID('TestTVPProc2') IS NOT NULL DROP PROC TestTVPProc2");
240 4 : odbc_command("IF TYPE_ID('TVPType2') IS NOT NULL DROP TYPE TVPType2");
241 4 : odbc_command("IF OBJECT_ID('TVPTable2') IS NOT NULL DROP TABLE TVPTable2");
242 :
243 4 : odbc_command("CREATE TABLE TVPTable2 (Num NUMERIC(10, 5), Bin BINARY(10))");
244 4 : odbc_command("CREATE TYPE TVPType2 " "AS TABLE (vNum NUMERIC(10, 5), vBin VARBINARY(10))");
245 4 : odbc_command("CREATE PROCEDURE TestTVPProc2 (@TVPParam TVPType2 READONLY) "
246 : "AS INSERT INTO TVPTable2 SELECT vNum, vBin FROM @TVPParam");
247 :
248 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
249 4 : dirty_name(tableName);
250 :
251 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
252 :
253 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_NUMERIC, SQL_NUMERIC,
254 : 10, 5, numericCol, sizeof(SQL_NUMERIC_STRUCT), lNumericCol, "S");
255 4 : CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY,
256 : MAX_STRING_LENGTH, 4, binCol, MAX_STRING_LENGTH, lBinCol, "S");
257 :
258 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
259 :
260 : /* Population of the StrLen_or_IndPtr buffer can be deferred */
261 4 : numRows = MAX_ROWS;
262 :
263 4 : CHKExecDirect(T("{CALL TestTVPProc2(?)}"), SQL_NTS, "S");
264 :
265 : /* Ensure that we successfully add 5 rows */
266 4 : odbc_command("SELECT COUNT(*) FROM TVPTable2");
267 :
268 4 : CHKFetch("SI");
269 :
270 4 : CHKGetData(1, SQL_C_CHAR, outputBuffer, sizeof(outputBuffer), &lenBuffer, "S");
271 4 : if (strcmp((char *) outputBuffer, "5") != 0) {
272 0 : fprintf(stderr, "Wrong number of columns inserted, expected %ld, got %s\n", (long) numRows, outputBuffer);
273 0 : exit(1);
274 : }
275 :
276 4 : CHKFetch("No");
277 4 : CHKFetch("No");
278 4 : CHKCloseCursor("SI");
279 :
280 4 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable2 WHERE Bin = 0x30 AND Num = -48) SELECT 1");
281 4 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable2 WHERE Bin = 0x3131 AND Num = 97) SELECT 1");
282 4 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable2 WHERE Bin = 0x3232 AND Num = -13361) SELECT 1");
283 4 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable2 WHERE Bin = 0x3333 AND Num = 25905) SELECT 1");
284 4 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM TVPTable2 WHERE Bin = 0x3434 AND Num = -14386) SELECT 1");
285 :
286 4 : odbc_command("DROP PROC TestTVPProc2");
287 4 : odbc_command("DROP TYPE TVPType2");
288 4 : odbc_command("DROP TABLE TVPTable2");
289 :
290 4 : CHKFreeStmt(SQL_RESET_PARAMS, "S");
291 4 : }
292 :
293 : /*
294 : * Test freeing a TVP without executing it - simulates the case
295 : * where we encounter an error before calling SQLExecDirect
296 : */
297 : static void
298 4 : TestTVPMemoryManagement(void)
299 : {
300 : SQLWCHAR *tableName;
301 : SQLLEN numRows;
302 :
303 4 : tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType");
304 :
305 4 : odbc_command("IF OBJECT_ID('TestTVPProc') IS NOT NULL DROP PROC TestTVPProc");
306 4 : odbc_command("IF TYPE_ID('TVPType') IS NOT NULL DROP TYPE TVPType");
307 4 : odbc_command("IF OBJECT_ID('TVPTable') IS NOT NULL DROP TABLE TVPTable");
308 :
309 4 : odbc_command("CREATE TABLE TVPTable (PersonID INT PRIMARY KEY, Name VARCHAR(50))");
310 4 : odbc_command("CREATE TYPE TVPType " "AS TABLE (vPersonID INT PRIMARY KEY, vName VARCHAR(50))");
311 4 : odbc_command("CREATE PROCEDURE TestTVPProc (@TVPParam TVPType READONLY) "
312 : "AS INSERT INTO TVPTable SELECT * FROM @TVPParam");
313 :
314 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
315 4 : dirty_name(tableName);
316 :
317 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
318 :
319 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
320 4 : CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, MAX_STRING_LENGTH, 0, strCol, MAX_STRING_LENGTH, lStrCol, "S");
321 :
322 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
323 :
324 4 : odbc_command("DROP PROC TestTVPProc");
325 4 : odbc_command("DROP TYPE TVPType");
326 4 : odbc_command("DROP TABLE TVPTable");
327 :
328 4 : CHKFreeStmt(SQL_RESET_PARAMS, "S");
329 4 : }
330 :
331 : /* Test some errors happens when we expect them */
332 : static void
333 4 : TestErrors(void)
334 : {
335 : SQLWCHAR *tableName;
336 : SQLLEN numRows;
337 :
338 4 : tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType");
339 :
340 : /* SQL error 07006 -- [Microsoft][ODBC Driver 17 for SQL Server]Restricted data type attribute violation */
341 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
342 4 : odbc_read_error();
343 4 : assert(strcmp(odbc_sqlstate, "07006") == 0);
344 :
345 : /* SQL error HY105 -- [Microsoft][ODBC Driver 17 for SQL Server]Invalid parameter type */
346 4 : CHKBindParameter(1, SQL_PARAM_OUTPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
347 4 : odbc_read_error();
348 4 : assert(strcmp(odbc_sqlstate, "HY105") == 0);
349 :
350 : /* SQL error HY105 -- [Microsoft][ODBC Driver 17 for SQL Server]Invalid parameter type */
351 4 : CHKBindParameter(1, SQL_PARAM_OUTPUT, SQL_C_LONG, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
352 4 : odbc_read_error();
353 4 : assert(strcmp(odbc_sqlstate, "HY105") == 0);
354 :
355 : /* SQL error HY105 -- [Microsoft][ODBC Driver 17 for SQL Server]Invalid parameter type */
356 4 : CHKBindParameter(1, SQL_PARAM_INPUT_OUTPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
357 4 : odbc_read_error();
358 4 : assert(strcmp(odbc_sqlstate, "HY105") == 0);
359 :
360 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
361 4 : tableName[0] = 'A';
362 :
363 4 : CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
364 :
365 : /* SQL error IM020 -- [Microsoft][ODBC Driver 17 for SQL Server]Parameter focus does not refer to a table-valued parameter */
366 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 2, SQL_IS_INTEGER, "E");
367 4 : odbc_read_error();
368 4 : assert(strcmp(odbc_sqlstate, "IM020") == 0);
369 :
370 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
371 :
372 : /* SQL error HY004 -- [Microsoft][ODBC Driver 17 for SQL Server]Invalid SQL data type */
373 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
374 4 : odbc_read_error();
375 4 : assert(strcmp(odbc_sqlstate, "HY004") == 0);
376 :
377 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
378 4 : CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, MAX_STRING_LENGTH, 0, strCol, MAX_STRING_LENGTH, lStrCol, "S");
379 :
380 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
381 :
382 4 : odbc_reset_statement();
383 4 : }
384 :
385 : static void
386 4 : TestDescriptorValues(void)
387 : {
388 : SQLWCHAR *tableName;
389 : SQLLEN numRows;
390 : SQLPOINTER ptr;
391 : SQLSMALLINT type;
392 : SQLLEN count, len;
393 4 : bool failed = false;
394 :
395 4 : tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType");
396 :
397 4 : count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
398 4 : CHECK_COND((count == 0, "count %d == 0", (int) count));
399 4 : count = GET_DESC_FIELD(IMP, 0, SQL_DESC_COUNT, SQLSMALLINT);
400 4 : CHECK_COND((count == 0, "count %d == 0", (int) count));
401 :
402 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
403 4 : dirty_name(tableName);
404 :
405 4 : count = GET_DESC_FIELD(APP, 1, SQL_DESC_LENGTH, SQLULEN);
406 4 : CHECK_COND((count == 1, "count %d == 1", (int) count));
407 4 : count = GET_DESC_FIELD(IMP, 1, SQL_DESC_LENGTH, SQLULEN);
408 4 : CHECK_COND((count == 0, "count %d == 0", (int) count));
409 :
410 4 : count = GET_DESC_FIELD(APP, 0, SQL_DESC_ARRAY_SIZE, SQLULEN);
411 4 : CHECK_COND((count == 1, "count %d == 1", (int) count));
412 :
413 4 : count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
414 4 : CHECK_COND((count == 1, "count %d == 1", (int) count));
415 4 : count = GET_DESC_FIELD(IMP, 0, SQL_DESC_COUNT, SQLSMALLINT);
416 4 : CHECK_COND((count == 1, "count %d == 1", (int) count));
417 :
418 : /* data pointer should point to table name */
419 4 : ptr = GET_DESC_FIELD(APP, 1, SQL_DESC_DATA_PTR, SQLPOINTER);
420 4 : CHECK_COND((ptr == tableName, "SQL_DESC_DATA_PTR expected %p got %p", tableName, ptr));
421 4 : if (odbc_driver_is_freetds()) {
422 : /* MS driver cannot read this, used internally by FreeTDS */
423 4 : ptr = GET_DESC_FIELD(IMP, 1, SQL_DESC_DATA_PTR, SQLPOINTER);
424 4 : printf("Internal pointer %p\n", ptr);
425 : }
426 :
427 : /* indicator should be the pointer to number of rows */
428 4 : ptr = GET_DESC_FIELD(APP, 1, SQL_DESC_INDICATOR_PTR, SQLPOINTER);
429 4 : CHECK_COND((ptr == &numRows, "SQL_DESC_INDICATOR_PTR expected %p got %p", &numRows, ptr));
430 4 : if (odbc_driver_is_freetds()) {
431 4 : ptr = GET_DESC_FIELD(IMP, 1, SQL_DESC_INDICATOR_PTR, SQLPOINTER);
432 4 : CHECK_COND((ptr == NULL, "SQL_DESC_INDICATOR_PTR expected %p got %p", NULL, ptr));
433 : }
434 :
435 : /* octect length pointer should be the pointer to number of rows */
436 4 : ptr = GET_DESC_FIELD(APP, 1, SQL_DESC_OCTET_LENGTH_PTR, SQLPOINTER);
437 4 : CHECK_COND((ptr == &numRows, "SQL_DESC_OCTET_LENGTH_PTR expected %p got %p", &numRows, ptr));
438 4 : if (odbc_driver_is_freetds()) {
439 4 : ptr = GET_DESC_FIELD(IMP, 1, SQL_DESC_OCTET_LENGTH_PTR, SQLPOINTER);
440 4 : CHECK_COND((ptr == NULL, "SQL_DESC_OCTET_LENGTH_PTR expected %p got %p", NULL, ptr));
441 : }
442 :
443 : /* this should be then length of tableName passed to SQLBindParameter */
444 4 : len = GET_DESC_FIELD(APP, 1, SQL_DESC_OCTET_LENGTH, SQLLEN);
445 4 : CHECK_COND((len == SQL_NTS, "SQL_DESC_OCTET_LENGTH expected %ld got %ld", (long) SQL_NTS, (long) len));
446 4 : len = GET_DESC_FIELD(IMP, 1, SQL_DESC_OCTET_LENGTH, SQLLEN);
447 4 : CHECK_COND((len == 0, "SQL_DESC_OCTET_LENGTH expected %ld got %ld", (long) 0, (long) len));
448 :
449 4 : type = GET_DESC_FIELD(APP, 1, SQL_DESC_CONCISE_TYPE, SQLSMALLINT);
450 4 : CHECK_COND((type == SQL_C_BINARY, "SQL_DESC_CONCISE_TYPE expected %d got %d", SQL_C_BINARY, type));
451 4 : type = GET_DESC_FIELD(IMP, 1, SQL_DESC_CONCISE_TYPE, SQLSMALLINT);
452 4 : CHECK_COND((type == SQL_SS_TABLE, "SQL_DESC_CONCISE_TYPE expected %d got %d", SQL_SS_TABLE, type));
453 :
454 : /* setting parameter focus should move to different descriptors */
455 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
456 :
457 4 : count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
458 4 : CHECK_COND((count == 0, "count %d == 0", (int) count));
459 4 : count = GET_DESC_FIELD(IMP, 0, SQL_DESC_COUNT, SQLSMALLINT);
460 4 : CHECK_COND((count == 0, "count %d == 0", (int) count));
461 :
462 4 : count = GET_DESC_FIELD(APP, 0, SQL_DESC_ARRAY_SIZE, SQLULEN);
463 4 : CHECK_COND((count == 5, "count %d == 5", (int) count));
464 :
465 : /* modify descriptors */
466 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
467 4 : CHKBindParameter(2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, MAX_STRING_LENGTH, 0, strCol, MAX_STRING_LENGTH, lStrCol, "S");
468 :
469 4 : count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
470 4 : CHECK_COND((count == 2, "count %d == 2", (int) count));
471 4 : count = GET_DESC_FIELD(IMP, 0, SQL_DESC_COUNT, SQLSMALLINT);
472 4 : CHECK_COND((count == 2, "count %d == 2", (int) count));
473 :
474 : /* switch back to main descriptors */
475 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
476 :
477 4 : count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
478 4 : CHECK_COND((count == 1, "count %d == 1", (int) count));
479 4 : count = GET_DESC_FIELD(IMP, 0, SQL_DESC_COUNT, SQLSMALLINT);
480 4 : CHECK_COND((count == 1, "count %d == 1", (int) count));
481 :
482 : /* switch back to table */
483 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
484 :
485 : /* this should fail, cannot set table inside a table */
486 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "E");
487 :
488 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER, "S");
489 :
490 : /* reset parameters, we should reset TVP */
491 4 : CHKFreeStmt(SQL_RESET_PARAMS, "S");
492 :
493 4 : count = GET_DESC_FIELD(APP, 0, SQL_DESC_COUNT, SQLSMALLINT);
494 4 : CHECK_COND((count == 0, "count %d == 0", (int) count));
495 :
496 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
497 :
498 4 : assert(!failed);
499 :
500 4 : odbc_reset_statement();
501 4 : }
502 :
503 : #ifdef MEMORY_TESTS
504 : static size_t
505 16 : memory_usage(void)
506 : {
507 16 : size_t ret = 0;
508 :
509 : /* mallinfo does not work on Valgrind, ignore */
510 16 : if (RUNNING_ON_VALGRIND > 0)
511 : return ret;
512 :
513 : #if defined(HAVE__HEAPWALK)
514 : {
515 : _HEAPINFO hinfo;
516 : int heapstatus;
517 :
518 : hinfo._pentry = NULL;
519 : while ((heapstatus = _heapwalk(&hinfo)) == _HEAPOK) {
520 : if (hinfo._useflag == _USEDENTRY)
521 : ret += hinfo._size;
522 : }
523 : assert(heapstatus == _HEAPEMPTY || heapstatus == _HEAPEND);
524 : }
525 : #elif defined(HAVE_MALLINFO2)
526 : ret = mallinfo2().uordblks;
527 : #else
528 0 : ret = (size_t) (mallinfo().uordblks);
529 : #endif
530 0 : return ret;
531 : }
532 :
533 : static void
534 4 : TestInitializeLeak(void)
535 : {
536 : SQLWCHAR *tableName;
537 : SQLLEN numRows;
538 : size_t initial_memory;
539 : int i;
540 :
541 4 : tableName = odbc_get_sqlwchar(&odbc_buf, "TVPType");
542 :
543 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
544 :
545 4 : CHKSetStmtAttr(SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER, "S");
546 :
547 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
548 :
549 : /* try to repeat binding column */
550 4 : initial_memory = memory_usage();
551 4100 : for (i = 0; i < 1024; ++i)
552 4096 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, intCol, sizeof(SQLINTEGER), lIntCol, "S");
553 :
554 : /* memory should not increase a lot */
555 4 : assert(memory_usage() < initial_memory + 10240);
556 :
557 4 : odbc_reset_statement();
558 :
559 : /* check we don't leak binding table multiple times */
560 : /* this leak memory on MS driver */
561 4 : if (odbc_driver_is_freetds()) {
562 4 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
563 :
564 : /* try to repeat set of the table */
565 4 : initial_memory = memory_usage();
566 4100 : for (i = 0; i < 1024; ++i)
567 4096 : CHKBindParameter(1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, MAX_ROWS, 0, tableName, SQL_NTS, &numRows, "S");
568 :
569 : /* memory should not increase a lot */
570 4 : assert(memory_usage() < initial_memory + 10240);
571 :
572 4 : odbc_reset_statement();
573 : }
574 4 : }
575 : #endif
576 :
577 10 : TEST_MAIN()
578 : {
579 10 : odbc_use_version3 = 1;
580 :
581 10 : setup();
582 :
583 10 : odbc_connect();
584 :
585 10 : if (odbc_tds_version() < 0x703) {
586 6 : odbc_disconnect();
587 6 : printf("TVP data is supported since protocol 7.3, MSSQL only.\n");
588 6 : odbc_test_skipped();
589 0 : return 0;
590 : }
591 :
592 4 : TestTVPInsert();
593 4 : TestTVPInsert2();
594 4 : TestTVPMemoryManagement();
595 4 : TestErrors();
596 4 : TestDescriptorValues();
597 :
598 : #ifdef MEMORY_TESTS
599 4 : TestInitializeLeak();
600 : #endif
601 :
602 4 : odbc_disconnect();
603 :
604 4 : return 0;
605 : }
|