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