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 10 : TEST_MAIN()
263 : {
264 : const char *s;
265 :
266 10 : odbc_set_conn_attr = set_attr;
267 10 : odbc_conn_additional_params = "ClientCharset=ISO-8859-1;";
268 10 : odbc_connect();
269 :
270 10 : tds_version = odbc_tds_version();
271 :
272 10 : init();
273 :
274 10 : normal_inserts(0);
275 10 : if (tds_version >= 0x703)
276 4 : special_inserts();
277 10 : normal_select();
278 10 : if (tds_version >= 0x703)
279 4 : special_select();
280 :
281 10 : odbc_command("delete from all_types_bcp_unittest");
282 10 : normal_inserts(8);
283 10 : normal_select();
284 :
285 10 : if ((s = getenv("BCP")) != NULL && 0 == strcmp(s, "nodrop")) {
286 0 : printf("BCP=nodrop: tables kept\n");
287 : } else {
288 10 : printf("Dropping tables\n");
289 : cleanup();
290 : }
291 :
292 10 : odbc_disconnect();
293 :
294 10 : printf("Done.\n");
295 10 : return 0;
296 : }
297 :
298 : static void
299 20 : normal_inserts(int prefixlen)
300 : {
301 : int i;
302 : int rows_sent;
303 :
304 : /* set up and send the bcp */
305 20 : printf("preparing to insert into %s ... ", table_name);
306 40 : if (bcp_init(odbc_conn, (bcp_init_char_t *) T(table_name), NULL, NULL, BCP_DIRECTION_IN) == FAIL)
307 : report_bcp_error("bcp_init", __LINE__, __FILE__);
308 20 : printf("OK\n");
309 :
310 20 : test_bind(prefixlen);
311 :
312 20 : printf("Sending same row 10 times... \n");
313 220 : for (i=0; i<10; i++)
314 400 : if (bcp_sendrow(odbc_conn) == FAIL)
315 : report_bcp_error("bcp_sendrow", __LINE__, __FILE__);
316 :
317 : #if 1
318 20 : rows_sent = bcp_batch(odbc_conn);
319 20 : if (rows_sent == -1)
320 : report_bcp_error("bcp_batch", __LINE__, __FILE__);
321 : #endif
322 :
323 20 : printf("OK\n");
324 :
325 : /* end bcp. */
326 20 : rows_sent = bcp_done(odbc_conn);
327 20 : if (rows_sent != 0)
328 : report_bcp_error("bcp_done", __LINE__, __FILE__);
329 : else
330 20 : printf("%d rows copied.\n", rows_sent);
331 :
332 20 : printf("done\n");
333 20 : }
334 :
335 : static void
336 4 : special_inserts(void)
337 : {
338 : int rows_sent;
339 : SQL_TIMESTAMP_STRUCT timestamp;
340 : DBDATETIME datetime;
341 : SQL_NUMERIC_STRUCT numeric;
342 : SQLWCHAR hello[6], oo[3];
343 :
344 4 : printf("sending special types\n");
345 4 : rows_sent = 0;
346 :
347 8 : if (bcp_init(odbc_conn, (bcp_init_char_t *) T("special_types_bcp_unittest"), NULL, NULL, BCP_DIRECTION_IN) == FAIL)
348 : report_bcp_error("bcp_init", __LINE__, __FILE__);
349 4 : printf("OK\n");
350 :
351 8 : if (bcp_control(odbc_conn, BCPHINTSA, (void *) "TABLOCK") != SUCCEED)
352 : report_bcp_error("bcp_init", __LINE__, __FILE__);
353 :
354 4 : datetime.dtdays = 42075;
355 4 : datetime.dttime = 16683900;
356 4 : timestamp.year = 2015;
357 4 : timestamp.month = 3;
358 4 : timestamp.day = 14;
359 4 : timestamp.hour = 15;
360 4 : timestamp.minute = 26;
361 4 : timestamp.second = 53;
362 4 : timestamp.fraction = 589793000;
363 4 : memset(&numeric, 0, sizeof(numeric));
364 4 : numeric.precision = 19;
365 4 : numeric.scale = 6;
366 4 : numeric.sign = 1;
367 4 : numeric.val[0] = 0xd9;
368 4 : numeric.val[1] = 0xef;
369 4 : numeric.val[2] = 0x2f;
370 4 : odbc_to_sqlwchar(hello, "hello", 6);
371 4 : odbc_to_sqlwchar(oo, "\xd3\xd4", 3);
372 8 : bcp_bind(odbc_conn, (unsigned char *) &datetime, 0, sizeof(datetime), NULL, 0, BCP_TYPE_SQLDATETIME, 1);
373 8 : bcp_bind(odbc_conn, (unsigned char *) ×tamp, 0, sizeof(timestamp), NULL, 0, BCP_TYPE_SQLDATETIME2, 2);
374 8 : bcp_bind(odbc_conn, (unsigned char *) &numeric, 0, sizeof(numeric), NULL, 0, BCP_TYPE_SQLDECIMAL, 3);
375 8 : bcp_bind(odbc_conn, (unsigned char *) &numeric, 0, sizeof(numeric), NULL, 0, BCP_TYPE_SQLDECIMAL, 4);
376 8 : bcp_bind(odbc_conn, (unsigned char *) hello, 0, 10, NULL, 0, BCP_TYPE_SQLNVARCHAR, 5);
377 8 : bcp_bind(odbc_conn, (unsigned char *) "", 0, 0, NULL, 0, BCP_TYPE_SQLVARCHAR, 6);
378 8 : bcp_bind(odbc_conn, (unsigned char *) oo, 0, 4, NULL, 0, BCP_TYPE_SQLNVARCHAR, 7);
379 8 : bcp_bind(odbc_conn, (unsigned char *) ¬_null_bit, 0, SQL_NULL_DATA, NULL, 0, BCP_TYPE_SQLINT4, 8);
380 :
381 8 : if (bcp_sendrow(odbc_conn) == FAIL)
382 : report_bcp_error("bcp_sendrow", __LINE__, __FILE__);
383 :
384 4 : rows_sent = bcp_batch(odbc_conn);
385 4 : if (rows_sent != 1)
386 : report_bcp_error("bcp_batch", __LINE__, __FILE__);
387 :
388 4 : printf("OK\n");
389 :
390 : /* end bcp. */
391 :
392 4 : rows_sent = bcp_done(odbc_conn);
393 4 : if (rows_sent != 0)
394 : report_bcp_error("bcp_done", __LINE__, __FILE__);
395 : else
396 4 : printf("%d rows copied.\n", rows_sent);
397 :
398 4 : printf("done\n");
399 4 : }
400 :
401 : static void
402 20 : normal_select(void)
403 : {
404 20 : int ok = 1, i;
405 :
406 20 : odbc_command("select * from all_types_bcp_unittest");
407 20 : CHKFetch("SI");
408 :
409 : /* first columns have values */
410 320 : for (i = 0; i < TDS_VECTOR_SIZE(expected); ++i) {
411 : char output[128];
412 : SQLLEN dataSize;
413 300 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
414 300 : if (strcmp(output, expected[i]) || dataSize <= 0) {
415 0 : fprintf(stderr, "Invalid returned col %d: '%s'!='%s'\n", i, expected[i], output);
416 0 : ok = 0;
417 : }
418 : }
419 : /* others are NULL */
420 280 : for (; i < total_cols; ++i) {
421 : char output[128];
422 : SQLLEN dataSize;
423 280 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
424 280 : if (dataSize != SQL_NULL_DATA) {
425 0 : fprintf(stderr, "Invalid returned col %d: should be NULL'\n", i);
426 0 : ok = 0;
427 : }
428 : }
429 20 : if (!ok)
430 0 : exit(1);
431 20 : CHKCloseCursor("SI");
432 20 : }
433 :
434 : static void
435 4 : special_select(void)
436 : {
437 4 : int ok = 1, i;
438 :
439 4 : odbc_command("select top 1 * from special_types_bcp_unittest");
440 4 : CHKFetch("SI");
441 32 : for (i = 0; i < TDS_VECTOR_SIZE(expected_special); ++i) {
442 : char output[128];
443 : SQLLEN dataSize;
444 28 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
445 28 : if (strcmp(output, expected_special[i]) || (dataSize <= 0 && expected_special[i][0] != '\0')) {
446 0 : fprintf(stderr, "Invalid returned col %d: '%s'!='%s'\n", i, expected_special[i], output);
447 0 : ok = 0;
448 : }
449 : }
450 4 : if (!ok)
451 0 : exit(1);
452 4 : CHKCloseCursor("SI");
453 4 : }
|