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