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