Line data Source code
1 : #include "common.h"
2 : #define TDSODBC_BCP
3 : #include <odbcss.h>
4 : #include <assert.h>
5 :
6 : #ifdef UNICODE
7 : typedef SQLWCHAR bcp_init_char_t;
8 : #else
9 : typedef char bcp_init_char_t;
10 : #endif
11 :
12 : struct prefixed_int {
13 : ODBCINT64 prefix;
14 : int value;
15 : };
16 : struct prefixed_str {
17 : ODBCINT64 prefix;
18 : char value[64];
19 : };
20 :
21 : /*
22 : * Static data for insertion
23 : */
24 : static struct prefixed_int not_null_bit = {4, 1};
25 : static struct prefixed_str not_null_char = {64, "a char"};
26 : static struct prefixed_str not_null_varchar = {64, "a varchar"};
27 : static struct prefixed_str not_null_datetime = {64, "2003-12-17 15:44:00.000"};
28 : static struct prefixed_str not_null_smalldatetime = {64, "2003-12-17 15:44:00"};
29 : static struct prefixed_str not_null_money = {64, "12.341"};
30 : static struct prefixed_str not_null_smallmoney = {64, "12.34"};
31 : static struct prefixed_str not_null_float = {64, "12.34"};
32 : static struct prefixed_str not_null_real = {64, "12.34"};
33 : static struct prefixed_str not_null_decimal = {64, "12.34"};
34 : static struct prefixed_str not_null_numeric = {64, "12.34"};
35 : static struct prefixed_int not_null_int = {4, 1234};
36 : static struct prefixed_int not_null_smallint = {4, 1234};
37 : static struct prefixed_int not_null_tinyint = {4, 123};
38 : static struct prefixed_str not_null_nvarchar = {64, "a wide var"};
39 : static ODBCINT64 null_prefix = -1;
40 :
41 : static const char *expected[] = {
42 : "1",
43 : "a char ","a varchar","2003-12-17 15:44:00.000","2003-12-17 15:44:00",
44 : "12.3410","12.3400","12.34","12.3400002","12.34","12.34",
45 : "1234","1234","123",
46 : "a wide var",
47 : };
48 : static const int total_cols = 29;
49 :
50 : static const char *expected_special[] = {
51 : "2015-03-14 15:26:53.000",
52 : "2015-03-14 15:26:53.589793",
53 : "3.141593000",
54 : "3.141593", /* MS driver has "3141593" here. Bug? Should we be bug-compatible? */
55 : "hello",
56 : "",
57 : "\xd3\xd4"
58 : };
59 :
60 : static int tds_version;
61 :
62 : static void
63 : cleanup(void)
64 : {
65 20 : odbc_command("if object_id('all_types_bcp_unittest') is not null drop table all_types_bcp_unittest\n"
66 : "if object_id('special_types_bcp_unittest') is not null drop table special_types_bcp_unittest");
67 : }
68 :
69 : static void
70 10 : init(void)
71 : {
72 : cleanup();
73 :
74 10 : odbc_command("CREATE TABLE all_types_bcp_unittest ("
75 : " not_null_bit bit NOT NULL"
76 : ""
77 : ", not_null_char char(10) NOT NULL"
78 : ", not_null_varchar varchar(10) NOT NULL"
79 : ""
80 : ", not_null_datetime datetime NOT NULL"
81 : ", not_null_smalldatetime smalldatetime NOT NULL"
82 : ""
83 : ", not_null_money money NOT NULL"
84 : ", not_null_smallmoney smallmoney NOT NULL"
85 : ""
86 : ", not_null_float float NOT NULL"
87 : ", not_null_real real NOT NULL"
88 : ""
89 : ", not_null_decimal decimal(5,2) NOT NULL"
90 : ", not_null_numeric numeric(5,2) NOT NULL"
91 : ""
92 : ", not_null_int int NOT NULL"
93 : ", not_null_smallint smallint NOT NULL"
94 : ", not_null_tinyint tinyint NOT NULL"
95 : ", not_null_nvarchar nvarchar(10) NOT NULL"
96 : ""
97 : ", nullable_char char(10) NULL"
98 : ", nullable_varchar varchar(10) NULL"
99 : ""
100 : ", nullable_datetime datetime NULL"
101 : ", nullable_smalldatetime smalldatetime NULL"
102 : ""
103 : ", nullable_money money NULL"
104 : ", nullable_smallmoney smallmoney NULL"
105 : ""
106 : ", nullable_float float NULL"
107 : ", nullable_real real NULL"
108 : ""
109 : ", nullable_decimal decimal(5,2) NULL"
110 : ", nullable_numeric numeric(5,2) NULL"
111 : ""
112 : ", nullable_int int NULL"
113 : ", nullable_smallint smallint NULL"
114 : ", nullable_tinyint tinyint NULL"
115 : ", nullable_nvarchar nvarchar(10) NULL"
116 : ")");
117 :
118 10 : if (tds_version < 0x703)
119 : return;
120 :
121 : /* Excludes:
122 : * binary
123 : * image
124 : * uniqueidentifier
125 : * varbinary
126 : * text
127 : * timestamp
128 : * nchar
129 : * ntext
130 : * nvarchar
131 : */
132 4 : odbc_command("CREATE TABLE special_types_bcp_unittest ("
133 : "dt datetime not null,"
134 : "dt2 datetime2(6) not null,"
135 : "num decimal(19,9) not null,"
136 : "numstr varchar(64) not null,"
137 : "str nvarchar(6) not null,"
138 : "empty varchar(64) not null,"
139 : "cp varchar(64) collate SQL_Latin1_General_CP850_CI_AS not null,"
140 : "bitnull bit null"
141 : ")");
142 : }
143 :
144 : #define VARCHAR_BIND(x) \
145 : bcp_bind( odbc_conn, \
146 : (prefixlen == 0 ? (void*)&x.value : (void*)&x.prefix), \
147 : prefixlen, (SQLINTEGER) strlen(x.value), NULL, termlen, \
148 : BCP_TYPE_SQLVARCHAR, col++ )
149 :
150 : #define INT_BIND(x) \
151 : bcp_bind( odbc_conn, (prefixlen == 0 ? (void*)&x.value : (void*)&x.prefix), prefixlen, \
152 : SQL_VARLEN_DATA, NULL, termlen, BCP_TYPE_SQLINT4, col++ )
153 :
154 : #define NULL_BIND(x, type) \
155 : bcp_bind( odbc_conn, (prefixlen == 0 ? (void*)&x.value : (void*)&null_prefix), prefixlen, \
156 : prefixlen == 0 ? SQL_NULL_DATA : SQL_VARLEN_DATA, NULL, termlen, type, col++ )
157 :
158 : static void
159 20 : test_bind(int prefixlen)
160 : {
161 : enum { termlen = 0 };
162 :
163 : RETCODE fOK;
164 20 : int col=1;
165 :
166 : /* non nulls */
167 40 : fOK = INT_BIND(not_null_bit);
168 20 : assert(fOK == SUCCEED);
169 :
170 40 : fOK = VARCHAR_BIND(not_null_char);
171 20 : assert(fOK == SUCCEED);
172 40 : fOK = VARCHAR_BIND(not_null_varchar);
173 20 : assert(fOK == SUCCEED);
174 :
175 40 : fOK = VARCHAR_BIND(not_null_datetime);
176 20 : assert(fOK == SUCCEED);
177 40 : fOK = VARCHAR_BIND(not_null_smalldatetime);
178 20 : assert(fOK == SUCCEED);
179 :
180 40 : fOK = VARCHAR_BIND(not_null_money);
181 20 : assert(fOK == SUCCEED);
182 40 : fOK = VARCHAR_BIND(not_null_smallmoney);
183 20 : assert(fOK == SUCCEED);
184 :
185 40 : fOK = VARCHAR_BIND(not_null_float);
186 20 : assert(fOK == SUCCEED);
187 40 : fOK = VARCHAR_BIND(not_null_real);
188 20 : assert(fOK == SUCCEED);
189 :
190 40 : fOK = VARCHAR_BIND(not_null_decimal);
191 20 : assert(fOK == SUCCEED);
192 40 : fOK = VARCHAR_BIND(not_null_numeric);
193 20 : assert(fOK == SUCCEED);
194 :
195 40 : fOK = INT_BIND(not_null_int);
196 20 : assert(fOK == SUCCEED);
197 40 : fOK = INT_BIND(not_null_smallint);
198 20 : assert(fOK == SUCCEED);
199 40 : fOK = INT_BIND(not_null_tinyint);
200 20 : assert(fOK == SUCCEED);
201 40 : fOK = VARCHAR_BIND(not_null_nvarchar);
202 20 : assert(fOK == SUCCEED);
203 :
204 : /* nulls */
205 : assert(fOK == SUCCEED);
206 40 : fOK = NULL_BIND(not_null_char, BCP_TYPE_SQLVARCHAR);
207 20 : assert(fOK == SUCCEED);
208 40 : fOK = NULL_BIND(not_null_varchar, BCP_TYPE_SQLVARCHAR);
209 20 : assert(fOK == SUCCEED);
210 :
211 40 : fOK = NULL_BIND(not_null_datetime, BCP_TYPE_SQLVARCHAR);
212 20 : assert(fOK == SUCCEED);
213 40 : fOK = NULL_BIND(not_null_smalldatetime, BCP_TYPE_SQLVARCHAR);
214 20 : assert(fOK == SUCCEED);
215 :
216 40 : fOK = NULL_BIND(not_null_money, BCP_TYPE_SQLVARCHAR);
217 20 : assert(fOK == SUCCEED);
218 40 : fOK = NULL_BIND(not_null_smallmoney, BCP_TYPE_SQLVARCHAR);
219 20 : assert(fOK == SUCCEED);
220 :
221 40 : fOK = NULL_BIND(not_null_float, BCP_TYPE_SQLVARCHAR);
222 20 : assert(fOK == SUCCEED);
223 40 : fOK = NULL_BIND(not_null_real, BCP_TYPE_SQLVARCHAR);
224 20 : assert(fOK == SUCCEED);
225 :
226 40 : fOK = NULL_BIND(not_null_decimal, BCP_TYPE_SQLVARCHAR);
227 20 : assert(fOK == SUCCEED);
228 40 : fOK = NULL_BIND(not_null_numeric, BCP_TYPE_SQLVARCHAR);
229 20 : assert(fOK == SUCCEED);
230 :
231 40 : fOK = NULL_BIND(not_null_int, BCP_TYPE_SQLINT4);
232 20 : assert(fOK == SUCCEED);
233 40 : fOK = NULL_BIND(not_null_smallint, BCP_TYPE_SQLINT4);
234 20 : assert(fOK == SUCCEED);
235 40 : fOK = NULL_BIND(not_null_tinyint, BCP_TYPE_SQLINT4);
236 20 : assert(fOK == SUCCEED);
237 40 : fOK = NULL_BIND(not_null_nvarchar, BCP_TYPE_SQLVARCHAR);
238 20 : assert(fOK == SUCCEED);
239 :
240 20 : }
241 :
242 : static void
243 10 : set_attr(void)
244 : {
245 10 : CHKSetConnectAttr(SQL_COPT_SS_BCP, (SQLPOINTER)SQL_BCP_ON, 0, "S");
246 10 : }
247 :
248 : static void
249 : report_bcp_error(const char *errmsg, int line, const char *file)
250 : {
251 0 : odbc_stmt = NULL;
252 0 : odbc_report_error(errmsg, line, file);
253 : }
254 :
255 : static void normal_inserts(int prefixlen);
256 : static void normal_select(void);
257 : static void special_inserts(void);
258 : static void special_select(void);
259 :
260 : static const char table_name[] = "all_types_bcp_unittest";
261 :
262 : static void
263 80 : check_bind(int line, const void *varaddr, int varlen, const void *terminator, int termlen, int vartype, int table_column,
264 : const char *sqlstate)
265 : {
266 : RETCODE rc;
267 80 : SQLHSTMT stmt = odbc_stmt;
268 :
269 160 : rc = bcp_bind(odbc_conn, (const unsigned char *) varaddr, 0, varlen, (const unsigned char *) terminator, termlen,
270 : vartype, table_column);
271 80 : if (!sqlstate) {
272 20 : if (rc != SUCCEED)
273 : report_bcp_error("bcp_bind", line, __FILE__);
274 : return;
275 : }
276 60 : if (rc != FAIL) {
277 0 : fprintf(stderr, "line %d: bcp_bind succeeded unexpectedly\n", line);
278 0 : exit(1);
279 : }
280 60 : odbc_stmt = NULL;
281 60 : odbc_read_error();
282 60 : odbc_stmt = stmt;
283 60 : if (strcmp(odbc_sqlstate, sqlstate) != 0) {
284 0 : fprintf(stderr, "line %d: wrong SQLSTATE %s, expected %s\n", line, odbc_sqlstate, sqlstate);
285 0 : exit(1);
286 : }
287 : }
288 :
289 : /* bindings rejected as data size cannot be computed (used to crash) */
290 : static void
291 10 : bind_errors(void)
292 : {
293 : static const char str[] = "hello";
294 : static const int num = 1234;
295 :
296 20 : if (bcp_init(odbc_conn, (bcp_init_char_t *) T(table_name), NULL, NULL, BCP_DIRECTION_IN) == FAIL)
297 : report_bcp_error("bcp_init", __LINE__, __FILE__);
298 :
299 : /* NULL terminator */
300 10 : check_bind(__LINE__, &num, SQL_VARLEN_DATA, NULL, 1, BCP_TYPE_SQLINT4, 12, "HY009");
301 10 : check_bind(__LINE__, str, SQL_VARLEN_DATA, NULL, 1, BCP_TYPE_SQLVARCHAR, 3, "HY009");
302 :
303 : /* no length, prefix or terminator */
304 10 : check_bind(__LINE__, str, SQL_VARLEN_DATA, NULL, 0, BCP_TYPE_SQLVARCHAR, 3, "HY009");
305 10 : check_bind(__LINE__, str, SQL_VARLEN_DATA, "", 0, BCP_TYPE_SQLVARCHAR, 3, "HY009");
306 10 : check_bind(__LINE__, str, SQL_VARLEN_DATA, NULL, -1, BCP_TYPE_SQLVARCHAR, 3, "HY009");
307 10 : check_bind(__LINE__, str, SQL_VARLEN_DATA, NULL, 0, BCP_TYPE_SQLDECIMAL, 10, "HY009");
308 :
309 : /* fixed types do not need it */
310 10 : check_bind(__LINE__, &num, SQL_VARLEN_DATA, NULL, 0, BCP_TYPE_SQLINT4, 12, NULL);
311 10 : check_bind(__LINE__, &num, SQL_VARLEN_DATA, NULL, 0, 0, 12, NULL);
312 :
313 10 : bcp_done(odbc_conn);
314 10 : }
315 :
316 10 : TEST_MAIN()
317 : {
318 : const char *s;
319 :
320 10 : odbc_set_conn_attr = set_attr;
321 10 : odbc_conn_additional_params = "ClientCharset=ISO-8859-1;";
322 10 : odbc_connect();
323 :
324 10 : tds_version = odbc_tds_version();
325 :
326 10 : init();
327 :
328 10 : bind_errors();
329 :
330 10 : normal_inserts(0);
331 10 : if (tds_version >= 0x703)
332 4 : special_inserts();
333 10 : normal_select();
334 10 : if (tds_version >= 0x703)
335 4 : special_select();
336 :
337 10 : odbc_command("delete from all_types_bcp_unittest");
338 10 : normal_inserts(8);
339 10 : normal_select();
340 :
341 10 : if ((s = getenv("BCP")) != NULL && 0 == strcmp(s, "nodrop")) {
342 0 : printf("BCP=nodrop: tables kept\n");
343 : } else {
344 10 : printf("Dropping tables\n");
345 : cleanup();
346 : }
347 :
348 10 : odbc_disconnect();
349 :
350 10 : printf("Done.\n");
351 10 : return 0;
352 : }
353 :
354 : static void
355 20 : normal_inserts(int prefixlen)
356 : {
357 : int i;
358 : int rows_sent;
359 :
360 : /* set up and send the bcp */
361 20 : printf("preparing to insert into %s ... ", table_name);
362 40 : if (bcp_init(odbc_conn, (bcp_init_char_t *) T(table_name), NULL, NULL, BCP_DIRECTION_IN) == FAIL)
363 : report_bcp_error("bcp_init", __LINE__, __FILE__);
364 20 : printf("OK\n");
365 :
366 20 : test_bind(prefixlen);
367 :
368 20 : printf("Sending same row 10 times... \n");
369 220 : for (i=0; i<10; i++)
370 400 : if (bcp_sendrow(odbc_conn) == FAIL)
371 : report_bcp_error("bcp_sendrow", __LINE__, __FILE__);
372 :
373 : #if 1
374 20 : rows_sent = bcp_batch(odbc_conn);
375 20 : if (rows_sent == -1)
376 : report_bcp_error("bcp_batch", __LINE__, __FILE__);
377 : #endif
378 :
379 20 : printf("OK\n");
380 :
381 : /* end bcp. */
382 20 : rows_sent = bcp_done(odbc_conn);
383 20 : if (rows_sent != 0)
384 : report_bcp_error("bcp_done", __LINE__, __FILE__);
385 : else
386 20 : printf("%d rows copied.\n", rows_sent);
387 :
388 20 : printf("done\n");
389 20 : }
390 :
391 : static void
392 4 : special_inserts(void)
393 : {
394 : int rows_sent;
395 : SQL_TIMESTAMP_STRUCT timestamp;
396 : DBDATETIME datetime;
397 : SQL_NUMERIC_STRUCT numeric;
398 : SQLWCHAR hello[6], oo[3];
399 :
400 4 : printf("sending special types\n");
401 4 : rows_sent = 0;
402 :
403 8 : if (bcp_init(odbc_conn, (bcp_init_char_t *) T("special_types_bcp_unittest"), NULL, NULL, BCP_DIRECTION_IN) == FAIL)
404 : report_bcp_error("bcp_init", __LINE__, __FILE__);
405 4 : printf("OK\n");
406 :
407 8 : if (bcp_control(odbc_conn, BCPHINTSA, (void *) "TABLOCK") != SUCCEED)
408 : report_bcp_error("bcp_init", __LINE__, __FILE__);
409 :
410 4 : datetime.dtdays = 42075;
411 4 : datetime.dttime = 16683900;
412 4 : timestamp.year = 2015;
413 4 : timestamp.month = 3;
414 4 : timestamp.day = 14;
415 4 : timestamp.hour = 15;
416 4 : timestamp.minute = 26;
417 4 : timestamp.second = 53;
418 4 : timestamp.fraction = 589793000;
419 4 : memset(&numeric, 0, sizeof(numeric));
420 4 : numeric.precision = 19;
421 4 : numeric.scale = 6;
422 4 : numeric.sign = 1;
423 4 : numeric.val[0] = 0xd9;
424 4 : numeric.val[1] = 0xef;
425 4 : numeric.val[2] = 0x2f;
426 4 : odbc_to_sqlwchar(hello, "hello", 6);
427 4 : odbc_to_sqlwchar(oo, "\xd3\xd4", 3);
428 8 : bcp_bind(odbc_conn, (unsigned char *) &datetime, 0, sizeof(datetime), NULL, 0, BCP_TYPE_SQLDATETIME, 1);
429 8 : bcp_bind(odbc_conn, (unsigned char *) ×tamp, 0, sizeof(timestamp), NULL, 0, BCP_TYPE_SQLDATETIME2, 2);
430 8 : bcp_bind(odbc_conn, (unsigned char *) &numeric, 0, sizeof(numeric), NULL, 0, BCP_TYPE_SQLDECIMAL, 3);
431 8 : bcp_bind(odbc_conn, (unsigned char *) &numeric, 0, sizeof(numeric), NULL, 0, BCP_TYPE_SQLDECIMAL, 4);
432 8 : bcp_bind(odbc_conn, (unsigned char *) hello, 0, 10, NULL, 0, BCP_TYPE_SQLNVARCHAR, 5);
433 8 : bcp_bind(odbc_conn, (unsigned char *) "", 0, 0, NULL, 0, BCP_TYPE_SQLVARCHAR, 6);
434 8 : bcp_bind(odbc_conn, (unsigned char *) oo, 0, 4, NULL, 0, BCP_TYPE_SQLNVARCHAR, 7);
435 8 : bcp_bind(odbc_conn, (unsigned char *) ¬_null_bit, 0, SQL_NULL_DATA, NULL, 0, BCP_TYPE_SQLINT4, 8);
436 :
437 8 : if (bcp_sendrow(odbc_conn) == FAIL)
438 : report_bcp_error("bcp_sendrow", __LINE__, __FILE__);
439 :
440 4 : rows_sent = bcp_batch(odbc_conn);
441 4 : if (rows_sent != 1)
442 : report_bcp_error("bcp_batch", __LINE__, __FILE__);
443 :
444 4 : printf("OK\n");
445 :
446 : /* end bcp. */
447 :
448 4 : rows_sent = bcp_done(odbc_conn);
449 4 : if (rows_sent != 0)
450 : report_bcp_error("bcp_done", __LINE__, __FILE__);
451 : else
452 4 : printf("%d rows copied.\n", rows_sent);
453 :
454 4 : printf("done\n");
455 4 : }
456 :
457 : static void
458 20 : normal_select(void)
459 : {
460 20 : int ok = 1, i;
461 :
462 20 : odbc_command("select * from all_types_bcp_unittest");
463 20 : CHKFetch("SI");
464 :
465 : /* first columns have values */
466 320 : for (i = 0; i < TDS_VECTOR_SIZE(expected); ++i) {
467 : char output[128];
468 : SQLLEN dataSize;
469 300 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
470 300 : if (strcmp(output, expected[i]) || dataSize <= 0) {
471 0 : fprintf(stderr, "Invalid returned col %d: '%s'!='%s'\n", i, expected[i], output);
472 0 : ok = 0;
473 : }
474 : }
475 : /* others are NULL */
476 280 : for (; i < total_cols; ++i) {
477 : char output[128];
478 : SQLLEN dataSize;
479 280 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
480 280 : if (dataSize != SQL_NULL_DATA) {
481 0 : fprintf(stderr, "Invalid returned col %d: should be NULL'\n", i);
482 0 : ok = 0;
483 : }
484 : }
485 20 : if (!ok)
486 0 : exit(1);
487 20 : CHKCloseCursor("SI");
488 20 : }
489 :
490 : static void
491 4 : special_select(void)
492 : {
493 4 : int ok = 1, i;
494 :
495 4 : odbc_command("select top 1 * from special_types_bcp_unittest");
496 4 : CHKFetch("SI");
497 32 : for (i = 0; i < TDS_VECTOR_SIZE(expected_special); ++i) {
498 : char output[128];
499 : SQLLEN dataSize;
500 28 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
501 28 : if (strcmp(output, expected_special[i]) || (dataSize <= 0 && expected_special[i][0] != '\0')) {
502 0 : fprintf(stderr, "Invalid returned col %d: '%s'!='%s'\n", i, expected_special[i], output);
503 0 : ok = 0;
504 : }
505 : }
506 4 : if (!ok)
507 0 : exit(1);
508 4 : CHKCloseCursor("SI");
509 4 : }
|