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, \
142 : (prefixlen == 0 ? (void*)&x.value : (void*)&x.prefix), \
143 : prefixlen, (SQLINTEGER) strlen(x.value), NULL, termlen, \
144 : BCP_TYPE_SQLVARCHAR, col++ )
145 :
146 : #define INT_BIND(x) \
147 : bcp_bind( odbc_conn, (prefixlen == 0 ? (void*)&x.value : (void*)&x.prefix), prefixlen, \
148 : SQL_VARLEN_DATA, NULL, termlen, BCP_TYPE_SQLINT4, col++ )
149 :
150 : #define NULL_BIND(x, type) \
151 : bcp_bind( odbc_conn, (prefixlen == 0 ? (void*)&x.value : (void*)&null_prefix), prefixlen, \
152 : prefixlen == 0 ? SQL_NULL_DATA : SQL_VARLEN_DATA, NULL, termlen, type, col++ )
153 :
154 : static void
155 20 : test_bind(int prefixlen)
156 : {
157 : enum { termlen = 0 };
158 :
159 : RETCODE fOK;
160 20 : int col=1;
161 :
162 : /* non nulls */
163 40 : fOK = INT_BIND(not_null_bit);
164 20 : assert(fOK == SUCCEED);
165 :
166 40 : fOK = VARCHAR_BIND(not_null_char);
167 20 : assert(fOK == SUCCEED);
168 40 : fOK = VARCHAR_BIND(not_null_varchar);
169 20 : assert(fOK == SUCCEED);
170 :
171 40 : fOK = VARCHAR_BIND(not_null_datetime);
172 20 : assert(fOK == SUCCEED);
173 40 : fOK = VARCHAR_BIND(not_null_smalldatetime);
174 20 : assert(fOK == SUCCEED);
175 :
176 40 : fOK = VARCHAR_BIND(not_null_money);
177 20 : assert(fOK == SUCCEED);
178 40 : fOK = VARCHAR_BIND(not_null_smallmoney);
179 20 : assert(fOK == SUCCEED);
180 :
181 40 : fOK = VARCHAR_BIND(not_null_float);
182 20 : assert(fOK == SUCCEED);
183 40 : fOK = VARCHAR_BIND(not_null_real);
184 20 : assert(fOK == SUCCEED);
185 :
186 40 : fOK = VARCHAR_BIND(not_null_decimal);
187 20 : assert(fOK == SUCCEED);
188 40 : fOK = VARCHAR_BIND(not_null_numeric);
189 20 : assert(fOK == SUCCEED);
190 :
191 40 : fOK = INT_BIND(not_null_int);
192 20 : assert(fOK == SUCCEED);
193 40 : fOK = INT_BIND(not_null_smallint);
194 20 : assert(fOK == SUCCEED);
195 40 : fOK = INT_BIND(not_null_tinyint);
196 20 : assert(fOK == SUCCEED);
197 40 : fOK = VARCHAR_BIND(not_null_nvarchar);
198 20 : assert(fOK == SUCCEED);
199 :
200 : /* nulls */
201 : assert(fOK == SUCCEED);
202 40 : fOK = NULL_BIND(not_null_char, BCP_TYPE_SQLVARCHAR);
203 20 : assert(fOK == SUCCEED);
204 40 : fOK = NULL_BIND(not_null_varchar, BCP_TYPE_SQLVARCHAR);
205 20 : assert(fOK == SUCCEED);
206 :
207 40 : fOK = NULL_BIND(not_null_datetime, BCP_TYPE_SQLVARCHAR);
208 20 : assert(fOK == SUCCEED);
209 40 : fOK = NULL_BIND(not_null_smalldatetime, BCP_TYPE_SQLVARCHAR);
210 20 : assert(fOK == SUCCEED);
211 :
212 40 : fOK = NULL_BIND(not_null_money, BCP_TYPE_SQLVARCHAR);
213 20 : assert(fOK == SUCCEED);
214 40 : fOK = NULL_BIND(not_null_smallmoney, BCP_TYPE_SQLVARCHAR);
215 20 : assert(fOK == SUCCEED);
216 :
217 40 : fOK = NULL_BIND(not_null_float, BCP_TYPE_SQLVARCHAR);
218 20 : assert(fOK == SUCCEED);
219 40 : fOK = NULL_BIND(not_null_real, BCP_TYPE_SQLVARCHAR);
220 20 : assert(fOK == SUCCEED);
221 :
222 40 : fOK = NULL_BIND(not_null_decimal, BCP_TYPE_SQLVARCHAR);
223 20 : assert(fOK == SUCCEED);
224 40 : fOK = NULL_BIND(not_null_numeric, BCP_TYPE_SQLVARCHAR);
225 20 : assert(fOK == SUCCEED);
226 :
227 40 : fOK = NULL_BIND(not_null_int, BCP_TYPE_SQLINT4);
228 20 : assert(fOK == SUCCEED);
229 40 : fOK = NULL_BIND(not_null_smallint, BCP_TYPE_SQLINT4);
230 20 : assert(fOK == SUCCEED);
231 40 : fOK = NULL_BIND(not_null_tinyint, BCP_TYPE_SQLINT4);
232 20 : assert(fOK == SUCCEED);
233 40 : fOK = NULL_BIND(not_null_nvarchar, BCP_TYPE_SQLVARCHAR);
234 20 : assert(fOK == SUCCEED);
235 :
236 20 : }
237 :
238 : static void
239 10 : set_attr(void)
240 : {
241 10 : SQLSetConnectAttr(odbc_conn, SQL_COPT_SS_BCP, (SQLPOINTER)SQL_BCP_ON, 0);
242 10 : }
243 :
244 : static void
245 : report_bcp_error(const char *errmsg, int line, const char *file)
246 : {
247 0 : odbc_stmt = NULL;
248 0 : odbc_report_error(errmsg, line, file);
249 : }
250 :
251 : static void normal_inserts(int prefixlen);
252 : static void normal_select(void);
253 : static void special_inserts(void);
254 : static void special_select(void);
255 :
256 : static const char table_name[] = "all_types_bcp_unittest";
257 :
258 10 : TEST_MAIN()
259 : {
260 : const char *s;
261 :
262 10 : odbc_set_conn_attr = set_attr;
263 10 : odbc_connect();
264 :
265 10 : tds_version = odbc_tds_version();
266 :
267 10 : init();
268 :
269 10 : normal_inserts(0);
270 10 : if (tds_version >= 0x703)
271 4 : special_inserts();
272 10 : normal_select();
273 10 : if (tds_version >= 0x703)
274 4 : special_select();
275 :
276 10 : odbc_command("delete from all_types_bcp_unittest");
277 10 : normal_inserts(8);
278 10 : normal_select();
279 :
280 10 : if ((s = getenv("BCP")) != NULL && 0 == strcmp(s, "nodrop")) {
281 0 : printf("BCP=nodrop: '%s' kept\n", table_name);
282 : } else {
283 10 : printf("Dropping table %s\n", table_name);
284 10 : odbc_command("drop table all_types_bcp_unittest");
285 10 : if (tds_version >= 0x703)
286 4 : odbc_command("drop table special_types_bcp_unittest");
287 : }
288 :
289 10 : cleanup();
290 :
291 10 : odbc_disconnect();
292 :
293 10 : printf("Done.\n");
294 10 : return 0;
295 : }
296 :
297 : static void
298 20 : normal_inserts(int prefixlen)
299 : {
300 : int i;
301 : int rows_sent;
302 :
303 : /* set up and send the bcp */
304 20 : printf("preparing to insert into %s ... ", table_name);
305 40 : if (bcp_init(odbc_conn, (bcp_init_char_t *) T(table_name), NULL, NULL, BCP_DIRECTION_IN) == FAIL)
306 : report_bcp_error("bcp_init", __LINE__, __FILE__);
307 20 : printf("OK\n");
308 :
309 20 : test_bind(prefixlen);
310 :
311 20 : printf("Sending same row 10 times... \n");
312 220 : for (i=0; i<10; i++)
313 400 : if (bcp_sendrow(odbc_conn) == FAIL)
314 : report_bcp_error("bcp_sendrow", __LINE__, __FILE__);
315 :
316 : #if 1
317 20 : rows_sent = bcp_batch(odbc_conn);
318 20 : if (rows_sent == -1)
319 : report_bcp_error("bcp_batch", __LINE__, __FILE__);
320 : #endif
321 :
322 20 : printf("OK\n");
323 :
324 : /* end bcp. */
325 20 : rows_sent = bcp_done(odbc_conn);
326 20 : if (rows_sent != 0)
327 : report_bcp_error("bcp_done", __LINE__, __FILE__);
328 : else
329 20 : printf("%d rows copied.\n", rows_sent);
330 :
331 20 : printf("done\n");
332 20 : }
333 :
334 : static void
335 4 : special_inserts(void)
336 : {
337 : int rows_sent;
338 : SQL_TIMESTAMP_STRUCT timestamp;
339 : DBDATETIME datetime;
340 : SQL_NUMERIC_STRUCT numeric;
341 :
342 4 : printf("sending special types\n");
343 4 : rows_sent = 0;
344 :
345 8 : if (bcp_init(odbc_conn, (bcp_init_char_t *) T("special_types_bcp_unittest"), NULL, NULL, BCP_DIRECTION_IN) == FAIL)
346 : report_bcp_error("bcp_init", __LINE__, __FILE__);
347 4 : printf("OK\n");
348 :
349 8 : if (bcp_control(odbc_conn, BCPHINTSA, (void *) "TABLOCK") != SUCCEED)
350 : report_bcp_error("bcp_init", __LINE__, __FILE__);
351 :
352 4 : datetime.dtdays = 42075;
353 4 : datetime.dttime = 16683900;
354 4 : timestamp.year = 2015;
355 4 : timestamp.month = 3;
356 4 : timestamp.day = 14;
357 4 : timestamp.hour = 15;
358 4 : timestamp.minute = 26;
359 4 : timestamp.second = 53;
360 4 : timestamp.fraction = 589793238;
361 4 : memset(&numeric, 0, sizeof(numeric));
362 4 : numeric.precision = 19;
363 4 : numeric.scale = 6;
364 4 : numeric.sign = 1;
365 4 : numeric.val[0] = 0xd9;
366 4 : numeric.val[1] = 0xef;
367 4 : numeric.val[2] = 0x2f;
368 8 : bcp_bind(odbc_conn, (unsigned char *) &datetime, 0, sizeof(datetime), NULL, 0, BCP_TYPE_SQLDATETIME, 1);
369 8 : bcp_bind(odbc_conn, (unsigned char *) ×tamp, 0, sizeof(timestamp), NULL, 0, BCP_TYPE_SQLDATETIME2, 2);
370 8 : bcp_bind(odbc_conn, (unsigned char *) &numeric, 0, sizeof(numeric), NULL, 0, BCP_TYPE_SQLDECIMAL, 3);
371 8 : bcp_bind(odbc_conn, (unsigned char *) &numeric, 0, sizeof(numeric), NULL, 0, BCP_TYPE_SQLDECIMAL, 4);
372 8 : bcp_bind(odbc_conn, (unsigned char *) "", 0, 0, NULL, 0, BCP_TYPE_SQLVARCHAR, 5);
373 8 : bcp_bind(odbc_conn, (unsigned char *) ¬_null_bit, 0, SQL_NULL_DATA, NULL, 0, BCP_TYPE_SQLINT4, 6);
374 :
375 8 : if (bcp_sendrow(odbc_conn) == FAIL)
376 : report_bcp_error("bcp_sendrow", __LINE__, __FILE__);
377 :
378 4 : rows_sent = bcp_batch(odbc_conn);
379 4 : if (rows_sent != 1)
380 : report_bcp_error("bcp_batch", __LINE__, __FILE__);
381 :
382 4 : printf("OK\n");
383 :
384 : /* end bcp. */
385 :
386 4 : rows_sent = bcp_done(odbc_conn);
387 4 : if (rows_sent != 0)
388 : report_bcp_error("bcp_done", __LINE__, __FILE__);
389 : else
390 4 : printf("%d rows copied.\n", rows_sent);
391 :
392 4 : printf("done\n");
393 4 : }
394 :
395 : static void
396 20 : normal_select(void)
397 : {
398 20 : int ok = 1, i;
399 :
400 20 : odbc_command("select * from all_types_bcp_unittest");
401 20 : CHKFetch("SI");
402 :
403 : /* first columns have values */
404 320 : for (i = 0; i < TDS_VECTOR_SIZE(expected); ++i) {
405 : char output[128];
406 : SQLLEN dataSize;
407 300 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
408 300 : if (strcmp(output, expected[i]) || dataSize <= 0) {
409 0 : fprintf(stderr, "Invalid returned col %d: '%s'!='%s'\n", i, expected[i], output);
410 0 : ok = 0;
411 : }
412 : }
413 : /* others are NULL */
414 280 : for (; i < total_cols; ++i) {
415 : char output[128];
416 : SQLLEN dataSize;
417 280 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
418 280 : if (dataSize != SQL_NULL_DATA) {
419 0 : fprintf(stderr, "Invalid returned col %d: should be NULL'\n", i);
420 0 : ok = 0;
421 : }
422 : }
423 20 : if (!ok)
424 0 : exit(1);
425 20 : CHKCloseCursor("SI");
426 20 : }
427 :
428 : static void
429 4 : special_select(void)
430 : {
431 4 : int ok = 1, i;
432 :
433 4 : odbc_command("select top 1 * from special_types_bcp_unittest");
434 4 : CHKFetch("SI");
435 24 : for (i = 0; i < TDS_VECTOR_SIZE(expected_special); ++i) {
436 : char output[128];
437 : SQLLEN dataSize;
438 20 : CHKGetData(i + 1, SQL_C_CHAR, output, sizeof(output), &dataSize, "S");
439 20 : if (strcmp(output, expected_special[i]) || (dataSize <= 0 && expected_special[i][0] != '\0')) {
440 0 : fprintf(stderr, "Invalid returned col %d: '%s'!='%s'\n", i, expected_special[i], output);
441 0 : ok = 0;
442 : }
443 : }
444 4 : if (!ok)
445 0 : exit(1);
446 4 : CHKCloseCursor("SI");
447 4 : }
|