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 : "",
56 : };
57 :
58 : static int tds_version;
59 :
60 : static void
61 20 : cleanup(void)
62 : {
63 20 : odbc_command("if exists (select 1 from sysobjects where type = 'U' and name = 'all_types_bcp_unittest') drop table all_types_bcp_unittest");
64 20 : odbc_command("if exists (select 1 from sysobjects where type = 'U' and name = 'special_types_bcp_unittest') drop table special_types_bcp_unittest");
65 20 : }
66 :
67 : static void
68 10 : init(void)
69 : {
70 10 : cleanup();
71 :
72 10 : odbc_command("CREATE TABLE all_types_bcp_unittest ("
73 : " not_null_bit bit NOT NULL"
74 : ""
75 : ", not_null_char char(10) NOT NULL"
76 : ", not_null_varchar varchar(10) NOT NULL"
77 : ""
78 : ", not_null_datetime datetime NOT NULL"
79 : ", not_null_smalldatetime smalldatetime NOT NULL"
80 : ""
81 : ", not_null_money money NOT NULL"
82 : ", not_null_smallmoney smallmoney NOT NULL"
83 : ""
84 : ", not_null_float float NOT NULL"
85 : ", not_null_real real NOT NULL"
86 : ""
87 : ", not_null_decimal decimal(5,2) NOT NULL"
88 : ", not_null_numeric numeric(5,2) NOT NULL"
89 : ""
90 : ", not_null_int int NOT NULL"
91 : ", not_null_smallint smallint NOT NULL"
92 : ", not_null_tinyint tinyint NOT NULL"
93 : ", not_null_nvarchar nvarchar(10) NOT NULL"
94 : ""
95 : ", nullable_char char(10) NULL"
96 : ", nullable_varchar varchar(10) NULL"
97 : ""
98 : ", nullable_datetime datetime NULL"
99 : ", nullable_smalldatetime smalldatetime NULL"
100 : ""
101 : ", nullable_money money NULL"
102 : ", nullable_smallmoney smallmoney NULL"
103 : ""
104 : ", nullable_float float NULL"
105 : ", nullable_real real NULL"
106 : ""
107 : ", nullable_decimal decimal(5,2) NULL"
108 : ", nullable_numeric numeric(5,2) NULL"
109 : ""
110 : ", nullable_int int NULL"
111 : ", nullable_smallint smallint NULL"
112 : ", nullable_tinyint tinyint NULL"
113 : ", nullable_nvarchar nvarchar(10) NULL"
114 : ")");
115 :
116 10 : if (tds_version < 0x703)
117 : return;
118 :
119 : /* Excludes:
120 : * binary
121 : * image
122 : * uniqueidentifier
123 : * varbinary
124 : * text
125 : * timestamp
126 : * nchar
127 : * ntext
128 : * nvarchar
129 : */
130 4 : odbc_command("CREATE TABLE special_types_bcp_unittest ("
131 : "dt datetime not null,"
132 : "dt2 datetime2(6) not null,"
133 : "num decimal(19,9) not null,"
134 : "numstr varchar(64) not null,"
135 : "empty varchar(64) not null,"
136 : "bitnull bit null"
137 : ")");
138 : }
139 :
140 : #define VARCHAR_BIND(x) \
141 : bcp_bind( odbc_conn, (prefixlen == 0 ? (void*)&x.value : (void*)&x.prefix), prefixlen, \
142 : strlen(x.value), NULL, termlen, BCP_TYPE_SQLVARCHAR, col++ )
143 :
144 : #define INT_BIND(x) \
145 : bcp_bind( odbc_conn, (prefixlen == 0 ? (void*)&x.value : (void*)&x.prefix), prefixlen, \
146 : SQL_VARLEN_DATA, NULL, termlen, BCP_TYPE_SQLINT4, col++ )
147 :
148 : #define NULL_BIND(x, type) \
149 : bcp_bind( odbc_conn, (prefixlen == 0 ? (void*)&x.value : (void*)&null_prefix), prefixlen, \
150 : prefixlen == 0 ? SQL_NULL_DATA : SQL_VARLEN_DATA, NULL, termlen, type, col++ )
151 :
152 : static void
153 20 : test_bind(int prefixlen)
154 : {
155 : enum { termlen = 0 };
156 :
157 : RETCODE fOK;
158 20 : int col=1;
159 :
160 : /* non nulls */
161 40 : fOK = INT_BIND(not_null_bit);
162 20 : assert(fOK == SUCCEED);
163 :
164 40 : fOK = VARCHAR_BIND(not_null_char);
165 20 : assert(fOK == SUCCEED);
166 40 : fOK = VARCHAR_BIND(not_null_varchar);
167 20 : assert(fOK == SUCCEED);
168 :
169 40 : fOK = VARCHAR_BIND(not_null_datetime);
170 20 : assert(fOK == SUCCEED);
171 40 : fOK = VARCHAR_BIND(not_null_smalldatetime);
172 20 : assert(fOK == SUCCEED);
173 :
174 40 : fOK = VARCHAR_BIND(not_null_money);
175 20 : assert(fOK == SUCCEED);
176 40 : fOK = VARCHAR_BIND(not_null_smallmoney);
177 20 : assert(fOK == SUCCEED);
178 :
179 40 : fOK = VARCHAR_BIND(not_null_float);
180 20 : assert(fOK == SUCCEED);
181 40 : fOK = VARCHAR_BIND(not_null_real);
182 20 : assert(fOK == SUCCEED);
183 :
184 40 : fOK = VARCHAR_BIND(not_null_decimal);
185 20 : assert(fOK == SUCCEED);
186 40 : fOK = VARCHAR_BIND(not_null_numeric);
187 20 : assert(fOK == SUCCEED);
188 :
189 40 : fOK = INT_BIND(not_null_int);
190 20 : assert(fOK == SUCCEED);
191 40 : fOK = INT_BIND(not_null_smallint);
192 20 : assert(fOK == SUCCEED);
193 40 : fOK = INT_BIND(not_null_tinyint);
194 20 : assert(fOK == SUCCEED);
195 40 : fOK = VARCHAR_BIND(not_null_nvarchar);
196 20 : assert(fOK == SUCCEED);
197 :
198 : /* nulls */
199 : assert(fOK == SUCCEED);
200 40 : fOK = NULL_BIND(not_null_char, BCP_TYPE_SQLVARCHAR);
201 20 : assert(fOK == SUCCEED);
202 40 : fOK = NULL_BIND(not_null_varchar, BCP_TYPE_SQLVARCHAR);
203 20 : assert(fOK == SUCCEED);
204 :
205 40 : fOK = NULL_BIND(not_null_datetime, BCP_TYPE_SQLVARCHAR);
206 20 : assert(fOK == SUCCEED);
207 40 : fOK = NULL_BIND(not_null_smalldatetime, BCP_TYPE_SQLVARCHAR);
208 20 : assert(fOK == SUCCEED);
209 :
210 40 : fOK = NULL_BIND(not_null_money, BCP_TYPE_SQLVARCHAR);
211 20 : assert(fOK == SUCCEED);
212 40 : fOK = NULL_BIND(not_null_smallmoney, BCP_TYPE_SQLVARCHAR);
213 20 : assert(fOK == SUCCEED);
214 :
215 40 : fOK = NULL_BIND(not_null_float, BCP_TYPE_SQLVARCHAR);
216 20 : assert(fOK == SUCCEED);
217 40 : fOK = NULL_BIND(not_null_real, BCP_TYPE_SQLVARCHAR);
218 20 : assert(fOK == SUCCEED);
219 :
220 40 : fOK = NULL_BIND(not_null_decimal, BCP_TYPE_SQLVARCHAR);
221 20 : assert(fOK == SUCCEED);
222 40 : fOK = NULL_BIND(not_null_numeric, BCP_TYPE_SQLVARCHAR);
223 20 : assert(fOK == SUCCEED);
224 :
225 40 : fOK = NULL_BIND(not_null_int, BCP_TYPE_SQLINT4);
226 20 : assert(fOK == SUCCEED);
227 40 : fOK = NULL_BIND(not_null_smallint, BCP_TYPE_SQLINT4);
228 20 : assert(fOK == SUCCEED);
229 40 : fOK = NULL_BIND(not_null_tinyint, BCP_TYPE_SQLINT4);
230 20 : assert(fOK == SUCCEED);
231 40 : fOK = NULL_BIND(not_null_nvarchar, BCP_TYPE_SQLVARCHAR);
232 20 : assert(fOK == SUCCEED);
233 :
234 20 : }
235 :
236 : static void
237 10 : set_attr(void)
238 : {
239 10 : SQLSetConnectAttr(odbc_conn, SQL_COPT_SS_BCP, (SQLPOINTER)SQL_BCP_ON, 0);
240 10 : }
241 :
242 : static void
243 : report_bcp_error(const char *errmsg, int line, const char *file)
244 : {
245 0 : odbc_stmt = NULL;
246 0 : odbc_report_error(errmsg, line, file);
247 : }
248 :
249 : static void normal_inserts(int prefixlen);
250 : static void normal_select(void);
251 : static void special_inserts(void);
252 : static void special_select(void);
253 :
254 : static const char table_name[] = "all_types_bcp_unittest";
255 :
256 : int
257 10 : main(void)
258 : {
259 : const char *s;
260 :
261 10 : odbc_set_conn_attr = set_attr;
262 10 : odbc_connect();
263 :
264 10 : tds_version = odbc_tds_version();
265 :
266 10 : init();
267 :
268 10 : normal_inserts(0);
269 10 : if (tds_version >= 0x703)
270 4 : special_inserts();
271 10 : normal_select();
272 10 : if (tds_version >= 0x703)
273 4 : special_select();
274 :
275 10 : odbc_command("delete from all_types_bcp_unittest");
276 10 : normal_inserts(8);
277 10 : normal_select();
278 :
279 10 : if ((s = getenv("BCP")) != NULL && 0 == strcmp(s, "nodrop")) {
280 0 : printf("BCP=nodrop: '%s' kept\n", table_name);
281 : } else {
282 10 : printf("Dropping table %s\n", table_name);
283 10 : odbc_command("drop table all_types_bcp_unittest");
284 10 : if (tds_version >= 0x703)
285 4 : odbc_command("drop table special_types_bcp_unittest");
286 : }
287 :
288 10 : cleanup();
289 :
290 10 : odbc_disconnect();
291 :
292 10 : printf("Done.\n");
293 : return 0;
294 : }
295 :
296 : static void
297 20 : normal_inserts(int prefixlen)
298 : {
299 : int i;
300 : int rows_sent;
301 :
302 : /* set up and send the bcp */
303 20 : printf("preparing to insert into %s ... ", table_name);
304 40 : if (bcp_init(odbc_conn, (bcp_init_char_t *) T(table_name), NULL, NULL, BCP_DIRECTION_IN) == FAIL)
305 : report_bcp_error("bcp_init", __LINE__, __FILE__);
306 20 : printf("OK\n");
307 :
308 20 : test_bind(prefixlen);
309 :
310 20 : printf("Sending same row 10 times... \n");
311 220 : for (i=0; i<10; i++)
312 400 : if (bcp_sendrow(odbc_conn) == FAIL)
313 : report_bcp_error("bcp_sendrow", __LINE__, __FILE__);
314 :
315 : #if 1
316 20 : rows_sent = bcp_batch(odbc_conn);
317 20 : if (rows_sent == -1)
318 : report_bcp_error("bcp_batch", __LINE__, __FILE__);
319 : #endif
320 :
321 20 : printf("OK\n");
322 :
323 : /* end bcp. */
324 20 : rows_sent = bcp_done(odbc_conn);
325 20 : if (rows_sent != 0)
326 : report_bcp_error("bcp_done", __LINE__, __FILE__);
327 : else
328 20 : printf("%d rows copied.\n", rows_sent);
329 :
330 20 : printf("done\n");
331 20 : }
332 :
333 : static void
334 4 : special_inserts(void)
335 : {
336 : int rows_sent;
337 : SQL_TIMESTAMP_STRUCT timestamp;
338 : DBDATETIME datetime;
339 : SQL_NUMERIC_STRUCT numeric;
340 :
341 4 : printf("sending special types\n");
342 4 : rows_sent = 0;
343 :
344 8 : if (bcp_init(odbc_conn, (bcp_init_char_t *) T("special_types_bcp_unittest"), NULL, NULL, BCP_DIRECTION_IN) == FAIL)
345 : report_bcp_error("bcp_init", __LINE__, __FILE__);
346 4 : printf("OK\n");
347 :
348 8 : if (bcp_control(odbc_conn, BCPHINTSA, (void *) "TABLOCK") != SUCCEED)
349 : report_bcp_error("bcp_init", __LINE__, __FILE__);
350 :
351 4 : datetime.dtdays = 42075;
352 4 : datetime.dttime = 16683900;
353 4 : timestamp.year = 2015;
354 4 : timestamp.month = 3;
355 4 : timestamp.day = 14;
356 4 : timestamp.hour = 15;
357 4 : timestamp.minute = 26;
358 4 : timestamp.second = 53;
359 4 : timestamp.fraction = 589793238;
360 4 : memset(&numeric, 0, sizeof(numeric));
361 4 : numeric.precision = 19;
362 4 : numeric.scale = 6;
363 4 : numeric.sign = 1;
364 4 : numeric.val[0] = 0xd9;
365 4 : numeric.val[1] = 0xef;
366 4 : numeric.val[2] = 0x2f;
367 8 : bcp_bind(odbc_conn, (unsigned char *) &datetime, 0, sizeof(datetime), NULL, 0, BCP_TYPE_SQLDATETIME, 1);
368 8 : bcp_bind(odbc_conn, (unsigned char *) ×tamp, 0, sizeof(timestamp), NULL, 0, BCP_TYPE_SQLDATETIME2, 2);
369 8 : bcp_bind(odbc_conn, (unsigned char *) &numeric, 0, sizeof(numeric), NULL, 0, BCP_TYPE_SQLDECIMAL, 3);
370 8 : bcp_bind(odbc_conn, (unsigned char *) &numeric, 0, sizeof(numeric), NULL, 0, BCP_TYPE_SQLDECIMAL, 4);
371 8 : bcp_bind(odbc_conn, (unsigned char *) "", 0, 0, NULL, 0, BCP_TYPE_SQLVARCHAR, 5);
372 8 : bcp_bind(odbc_conn, (unsigned char *) ¬_null_bit, 0, SQL_NULL_DATA, NULL, 0, BCP_TYPE_SQLINT4, 6);
373 :
374 8 : if (bcp_sendrow(odbc_conn) == FAIL)
375 : report_bcp_error("bcp_sendrow", __LINE__, __FILE__);
376 :
377 4 : rows_sent = bcp_batch(odbc_conn);
378 4 : if (rows_sent != 1)
379 : report_bcp_error("bcp_batch", __LINE__, __FILE__);
380 :
381 4 : printf("OK\n");
382 :
383 : /* end bcp. */
384 :
385 4 : rows_sent = bcp_done(odbc_conn);
386 4 : if (rows_sent != 0)
387 : report_bcp_error("bcp_done", __LINE__, __FILE__);
388 : else
389 4 : printf("%d rows copied.\n", rows_sent);
390 :
391 4 : printf("done\n");
392 4 : }
393 :
394 : static void
395 20 : normal_select(void)
396 : {
397 20 : int ok = 1, i;
398 :
399 20 : odbc_command("select * from all_types_bcp_unittest");
400 20 : CHKFetch("SI");
401 :
402 : /* first columns have values */
403 320 : for (i = 0; i < TDS_VECTOR_SIZE(expected); ++i) {
404 : char output[128];
405 : SQLLEN dataSize;
406 300 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
407 300 : if (strcmp(output, expected[i]) || dataSize <= 0) {
408 0 : fprintf(stderr, "Invalid returned col %d: '%s'!='%s'\n", i, expected[i], output);
409 0 : ok = 0;
410 : }
411 : }
412 : /* others are NULL */
413 280 : for (; i < total_cols; ++i) {
414 : char output[128];
415 : SQLLEN dataSize;
416 280 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
417 280 : if (dataSize != SQL_NULL_DATA) {
418 0 : fprintf(stderr, "Invalid returned col %d: should be NULL'\n", i);
419 0 : ok = 0;
420 : }
421 : }
422 20 : if (!ok)
423 0 : exit(1);
424 20 : CHKCloseCursor("SI");
425 20 : }
426 :
427 : static void
428 4 : special_select(void)
429 : {
430 4 : int ok = 1, i;
431 :
432 4 : odbc_command("select top 1 * from special_types_bcp_unittest");
433 4 : CHKFetch("SI");
434 24 : for (i = 0; i < TDS_VECTOR_SIZE(expected_special); ++i) {
435 : char output[128];
436 : SQLLEN dataSize;
437 20 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
438 20 : if (strcmp(output, expected_special[i]) || (dataSize <= 0 && expected_special[i][0] != '\0')) {
439 0 : fprintf(stderr, "Invalid returned col %d: '%s'!='%s'\n", i, expected_special[i], output);
440 0 : ok = 0;
441 : }
442 : }
443 4 : if (!ok)
444 0 : exit(1);
445 4 : CHKCloseCursor("SI");
446 4 : }
|