1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 * Copyright (C) 2008-2010 Frediano Ziglio
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20 /**
21 * \file
22 * \brief Handle bulk copy
23 */
24
25 #include <config.h>
26
27 #if HAVE_STRING_H
28 #include <string.h>
29 #endif /* HAVE_STRING_H */
30
31 #if HAVE_ERRNO_H
32 #include <errno.h>
33 #endif /* HAVE_ERRNO_H */
34
35 #if HAVE_STDLIB_H
36 #include <stdlib.h>
37 #endif /* HAVE_STDLIB_H */
38
39 #include <assert.h>
40
41 #include <freetds/tds.h>
42 #include "tds_checks.h"
43 #include <freetds/bytes.h>
44 #include <freetds/iconv.h>
45 #include <freetds/stream.h>
46 #include <freetds/string.h>
47 #include "replacements.h"
48
49 /** \cond HIDDEN_SYMBOLS */
50 #ifndef MAX
51 #define MAX(a,b) ( (a) > (b) ? (a) : (b) )
52 #endif
53
54 #ifndef MIN
55 #define MIN(a,b) ( (a) < (b) ? (a) : (b) )
56 #endif
57 /** \endcond */
58
59 /**
60 * Holds clause buffer
61 */
62 typedef struct tds_pbcb
63 {
64 /** buffer */
65 char *pb;
66 /** buffer length */
67 unsigned int cb;
68 /** true is buffer came from malloc */
69 unsigned int from_malloc;
70 } TDSPBCB;
71
72 static TDSRET tds7_bcp_send_colmetadata(TDSSOCKET *tds, TDSBCPINFO *bcpinfo);
73 static TDSRET tds_bcp_start_insert_stmt(TDSSOCKET *tds, TDSBCPINFO *bcpinfo);
74 static int tds_bcp_add_fixed_columns(TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset, unsigned char * rowbuffer, int start);
75 static int tds_bcp_add_variable_columns(TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset, TDS_UCHAR *rowbuffer, int start, int *pncols);
76 static void tds_bcp_row_free(TDSRESULTINFO* result, unsigned char *row);
77 static int tds_bcp_is_bound(TDSBCPINFO *bcpinfo, TDSCOLUMN *colinfo);
78 static int tds_bcp_read_column_defaults(TDSSOCKET *tds, TDSBCPINFO *bcpinfo);
79
80 /**
81 * Initialize BCP information.
82 * Query structure of the table to server.
83 * \tds
84 * \param bcpinfo BCP information to initialize. Structure should be allocate
85 * and table name and direction should be already set.
86 */
87 TDSRET
tds_bcp_init(TDSSOCKET * tds,TDSBCPINFO * bcpinfo)88 tds_bcp_init(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
89 {
90 TDSRESULTINFO *resinfo;
91 TDSRESULTINFO *bindinfo = NULL;
92 TDSCOLUMN *curcol;
93 TDS_INT result_type;
94 int i;
95 TDSRET rc;
96 const char *fmt;
97
98 /* FIXME don't leave state in processing state */
99
100 /* TODO quote tablename if needed */
101 if (bcpinfo->direction != TDS_BCP_QUERYOUT)
102 fmt = "SET FMTONLY ON select * from %s SET FMTONLY OFF";
103 else
104 fmt = "SET FMTONLY ON %s SET FMTONLY OFF";
105
106 if (TDS_FAILED(rc=tds_submit_queryf(tds, fmt, bcpinfo->tablename)))
107 /* TODO return an error ?? */
108 /* Attempt to use Bulk Copy with a non-existent Server table (might be why ...) */
109 return rc;
110
111 /* TODO possibly stop at ROWFMT and copy before going to idle */
112 /* TODO check what happen if table is not present, cleanup on error */
113 while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS))
114 == TDS_SUCCESS)
115 continue;
116 if (TDS_FAILED(rc))
117 return rc;
118
119 /* copy the results info from the TDS socket */
120 if (!tds->res_info)
121 return TDS_FAIL;
122
123 resinfo = tds->res_info;
124 if ((bindinfo = tds_alloc_results(resinfo->num_cols)) == NULL) {
125 rc = TDS_FAIL;
126 goto cleanup;
127 }
128
129 bindinfo->row_size = resinfo->row_size;
130
131 /* Copy the column metadata */
132 rc = TDS_FAIL;
133 for (i = 0; i < bindinfo->num_cols; i++) {
134
135 curcol = bindinfo->columns[i];
136
137 /*
138 * TODO use memcpy ??
139 * curcol and resinfo->columns[i] are both TDSCOLUMN.
140 * Why not "curcol = resinfo->columns[i];"? Because the rest of TDSCOLUMN (below column_timestamp)
141 * isn't being used. Perhaps this "upper" part of TDSCOLUMN should be a substructure.
142 * Or, see if the "lower" part is unused (and zeroed out) at this point, and just do one assignment.
143 */
144 curcol->funcs = resinfo->columns[i]->funcs;
145 curcol->column_type = resinfo->columns[i]->column_type;
146 curcol->column_usertype = resinfo->columns[i]->column_usertype;
147 curcol->column_flags = resinfo->columns[i]->column_flags;
148 if (curcol->column_varint_size == 0)
149 curcol->column_cur_size = resinfo->columns[i]->column_cur_size;
150 else
151 curcol->column_cur_size = -1;
152 curcol->column_size = resinfo->columns[i]->column_size;
153 curcol->column_varint_size = resinfo->columns[i]->column_varint_size;
154 curcol->column_prec = resinfo->columns[i]->column_prec;
155 curcol->column_scale = resinfo->columns[i]->column_scale;
156 curcol->on_server.column_type = resinfo->columns[i]->on_server.column_type;
157 curcol->on_server.column_size = resinfo->columns[i]->on_server.column_size;
158 curcol->char_conv = resinfo->columns[i]->char_conv;
159 if (!tds_dstr_dup(&curcol->column_name, &resinfo->columns[i]->column_name))
160 goto cleanup;
161 if (!tds_dstr_dup(&curcol->table_column_name, &resinfo->columns[i]->table_column_name))
162 goto cleanup;
163 curcol->column_nullable = resinfo->columns[i]->column_nullable;
164 curcol->column_identity = resinfo->columns[i]->column_identity;
165 curcol->column_timestamp = resinfo->columns[i]->column_timestamp;
166
167 memcpy(curcol->column_collation, resinfo->columns[i]->column_collation, 5);
168
169 if (is_numeric_type(curcol->column_type)) {
170 curcol->bcp_column_data = tds_alloc_bcp_column_data(sizeof(TDS_NUMERIC));
171 ((TDS_NUMERIC *) curcol->bcp_column_data->data)->precision = curcol->column_prec;
172 ((TDS_NUMERIC *) curcol->bcp_column_data->data)->scale = curcol->column_scale;
173 } else if (bcpinfo->bind_count != 0 /* ctlib */
174 && is_blob_col(curcol)) {
175 curcol->bcp_column_data = tds_alloc_bcp_column_data(0);
176 } else {
177 curcol->bcp_column_data =
178 tds_alloc_bcp_column_data(MAX(curcol->column_size,curcol->on_server.column_size));
179 }
180 if (!curcol->bcp_column_data)
181 goto cleanup;
182 }
183
184 if (!IS_TDS7_PLUS(tds->conn)) {
185 bindinfo->current_row = (unsigned char*) malloc(bindinfo->row_size);
186 if (!bindinfo->current_row)
187 goto cleanup;
188 bindinfo->row_free = tds_bcp_row_free;
189 }
190
191 if (bcpinfo->identity_insert_on) {
192
193 rc = tds_submit_queryf(tds, "set identity_insert %s on", bcpinfo->tablename);
194 if (TDS_FAILED(rc))
195 goto cleanup;
196
197 /* TODO use tds_process_simple_query */
198 while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS))
199 == TDS_SUCCESS) {
200 }
201 if (rc != TDS_NO_MORE_RESULTS)
202 goto cleanup;
203 }
204
205 bcpinfo->bindinfo = bindinfo;
206 bcpinfo->bind_count = 0;
207 return TDS_SUCCESS;
208
209 cleanup:
210 tds_free_results(bindinfo);
211 return rc;
212 }
213
214 /**
215 * Help to build query to be sent to server.
216 * Append column declaration to the query.
217 * Only for TDS 7.0+.
218 * \tds
219 * \param[out] clause output string
220 * \param bcpcol column to append
221 * \param first true if column is the first
222 * \return TDS_SUCCESS or TDS_FAIL.
223 */
224 static TDSRET
tds7_build_bulk_insert_stmt(TDSSOCKET * tds,TDSPBCB * clause,TDSCOLUMN * bcpcol,int first)225 tds7_build_bulk_insert_stmt(TDSSOCKET * tds, TDSPBCB * clause, TDSCOLUMN * bcpcol, int first)
226 {
227 char column_type[40];
228
229 tdsdump_log(TDS_DBG_FUNC, "tds7_build_bulk_insert_stmt(%p, %p, %p, %d)\n", tds, clause, bcpcol, first);
230
231 if (TDS_FAILED(tds_get_column_declaration(tds, bcpcol, column_type))) {
232 tdserror(tds_get_ctx(tds), tds, TDSEBPROBADTYP, errno);
233 tdsdump_log(TDS_DBG_FUNC, "error: cannot build bulk insert statement. unrecognized server datatype %d\n",
234 bcpcol->on_server.column_type);
235 return TDS_FAIL;
236 }
237
238 if (clause->cb < strlen(clause->pb)
239 + tds_quote_id(tds, NULL, tds_dstr_cstr(&bcpcol->column_name), tds_dstr_len(&bcpcol->column_name))
240 + strlen(column_type)
241 + ((first) ? 2u : 4u)) {
242 char *temp = (char*) malloc(2 * clause->cb);
243
244 if (!temp) {
245 tdserror(tds_get_ctx(tds), tds, TDSEMEM, errno);
246 return TDS_FAIL;
247 }
248 strcpy(temp, clause->pb);
249 if (clause->from_malloc)
250 free(clause->pb);
251 clause->from_malloc = 1;
252 clause->pb = temp;
253 clause->cb *= 2;
254 }
255
256 if (!first)
257 strcat(clause->pb, ", ");
258
259 tds_quote_id(tds, strchr(clause->pb, 0), tds_dstr_cstr(&bcpcol->column_name), tds_dstr_len(&bcpcol->column_name));
260 strcat(clause->pb, " ");
261 strcat(clause->pb, column_type);
262
263 return TDS_SUCCESS;
264 }
265
266 /**
267 * Prepare the query to be sent to server to request BCP information
268 * \tds
269 * \param bcpinfo BCP information
270 */
271 static TDSRET
tds_bcp_start_insert_stmt(TDSSOCKET * tds,TDSBCPINFO * bcpinfo)272 tds_bcp_start_insert_stmt(TDSSOCKET * tds, TDSBCPINFO * bcpinfo)
273 {
274 char *query;
275
276 if (IS_TDS7_PLUS(tds->conn)) {
277 int i, firstcol, erc;
278 char *hint;
279 TDSCOLUMN *bcpcol;
280 TDSPBCB colclause;
281 char clause_buffer[4096] = { 0 };
282
283 colclause.pb = clause_buffer;
284 colclause.cb = sizeof(clause_buffer);
285 colclause.from_malloc = 0;
286
287 /* TODO avoid asprintf, use always malloc-ed buffer */
288 firstcol = 1;
289
290 for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
291 bcpcol = bcpinfo->bindinfo->columns[i];
292
293 if (bcpcol->column_timestamp || !tds_bcp_is_bound(bcpinfo, bcpcol))
294 continue;
295 if (!bcpinfo->identity_insert_on && bcpcol->column_identity)
296 continue;
297 tds7_build_bulk_insert_stmt(tds, &colclause, bcpcol, firstcol);
298 firstcol = 0;
299 }
300
301 if (bcpinfo->hint) {
302 if (asprintf(&hint, " with (%s)", bcpinfo->hint) < 0)
303 hint = NULL;
304 } else {
305 hint = strdup("");
306 }
307 if (!hint) {
308 if (colclause.from_malloc)
309 TDS_ZERO_FREE(colclause.pb);
310 return TDS_FAIL;
311 }
312
313 erc = asprintf(&query, "insert bulk %s (%s)%s", bcpinfo->tablename, colclause.pb, hint);
314
315 free(hint);
316 if (colclause.from_malloc)
317 TDS_ZERO_FREE(colclause.pb); /* just for good measure; not used beyond this point */
318
319 if (erc < 0)
320 return TDS_FAIL;
321 } else {
322 /* NOTE: if we use "with nodescribe" for following inserts server do not send describe */
323 if (asprintf(&query, "insert bulk %s", bcpinfo->tablename) < 0)
324 return TDS_FAIL;
325 }
326
327 /* save the statement for later... */
328 bcpinfo->insert_stmt = query;
329
330 return TDS_SUCCESS;
331 }
332
333 /**
334 * Send one row of data to server
335 * \tds
336 * \param bcpinfo BCP information
337 * \param get_col_data function to call to retrieve data to be sent
338 * \param ignored function to call if we try to send NULL if not allowed (not used)
339 * \param offset passed to get_col_data and null_error to specify the row to get
340 * \return TDS_SUCCESS or TDS_FAIL.
341 */
342 TDSRET
tds_bcp_send_record(TDSSOCKET * tds,TDSBCPINFO * bcpinfo,tds_bcp_get_col_data get_col_data,tds_bcp_null_error null_error,int offset)343 tds_bcp_send_record(TDSSOCKET *tds, TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset)
344 {
345 TDSCOLUMN *bindcol;
346 int i, start_col = bcpinfo->next_col;
347 TDSRET rc;
348
349 tdsdump_log(TDS_DBG_FUNC, "tds_bcp_send_bcp_record(%p, %p, %p, %p, %d)\n", tds, bcpinfo, get_col_data, null_error, offset);
350
351 if (tds->out_flag != TDS_BULK || tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
352 return TDS_FAIL;
353
354 if (start_col > 0) {
355 bindcol = bcpinfo->bindinfo->columns[start_col - 1];
356 *bindcol->column_lenbind
357 = MIN((TDS_INT) bindcol->column_bindlen - bcpinfo->text_sent,
358 *bindcol->column_lenbind);
359 tds_put_n(tds, bindcol->column_varaddr, *bindcol->column_lenbind);
360 bcpinfo->text_sent += *bindcol->column_lenbind;
361 if ((TDS_UINT) bcpinfo->text_sent < bindcol->column_bindlen) {
362 return TDS_SUCCESS; /* That's all for now. */
363 } else if (!IS_TDS7_PLUS(tds->conn)) {
364 bcpinfo->blob_cols++;
365 }
366 bcpinfo->next_col = 0;
367 bcpinfo->text_sent = 0;
368 }
369
370 if (IS_TDS7_PLUS(tds->conn)) {
371
372 if (start_col == 0) {
373 tds_put_byte(tds, TDS_ROW_TOKEN); /* 0xd1 */
374 }
375 for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) {
376
377 TDS_INT save_size;
378 unsigned char *save_data;
379 TDSBLOB blob;
380 int /* bool */ has_text = 0;
381
382 bindcol = bcpinfo->bindinfo->columns[i];
383
384 /*
385 * Don't send the (meta)data for timestamp columns or
386 * identity columns unless indentity_insert is enabled.
387 */
388
389 if ((!bcpinfo->identity_insert_on && bindcol->column_identity) ||
390 bindcol->column_timestamp ||
391 !tds_bcp_is_bound(bcpinfo, bindcol)) {
392 continue;
393 }
394
395 rc = get_col_data(bcpinfo, bindcol, offset);
396 if (rc == TDS_FAIL) {
397 tdsdump_log(TDS_DBG_INFO1, "get_col_data (column %d) failed\n", i + 1);
398 goto cleanup;
399 } else if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */
400 has_text = 1;
401 }
402 tdsdump_log(TDS_DBG_INFO1, "gotten column %d length %d null %d\n",
403 i + 1, bindcol->bcp_column_data->datalen, bindcol->bcp_column_data->is_null);
404
405 save_size = bindcol->column_cur_size;
406 save_data = bindcol->column_data;
407 assert(bindcol->column_data == NULL);
408 if (bindcol->bcp_column_data->is_null) {
409 if ( !bindcol->column_nullable
410 && !is_nullable_type(bindcol->on_server.column_type) ) {
411 return TDS_FAIL;
412 }
413 bindcol->column_cur_size = -1;
414 } else if (has_text) {
415 bindcol->column_cur_size = bindcol->bcp_column_data->datalen;
416 } else if (is_blob_col(bindcol)) {
417 bindcol->column_cur_size = bindcol->bcp_column_data->datalen;
418 memset(&blob, 0, sizeof(blob));
419 blob.textvalue = (TDS_CHAR *) bindcol->bcp_column_data->data;
420 bindcol->column_data = (unsigned char *) &blob;
421 } else {
422 bindcol->column_cur_size = bindcol->bcp_column_data->datalen;
423 bindcol->column_data = bindcol->bcp_column_data->data;
424 }
425 rc = bindcol->funcs->put_data(tds, bindcol, 1);
426 bindcol->column_cur_size = save_size;
427 bindcol->column_data = save_data;
428
429 if (TDS_FAILED(rc))
430 goto cleanup;
431 else if (has_text) {
432 bcpinfo->next_col = i + 1;
433 /* bcpinfo->text_sent = 0; */
434 break;
435 }
436 }
437 } /* IS_TDS7_PLUS */
438 else {
439 if (start_col == 0) {
440 int row_pos;
441 int row_sz_pos;
442 int var_cols_written = 0;
443 TDS_INT old_record_size = bcpinfo->bindinfo->row_size;
444 unsigned char *record = bcpinfo->bindinfo->current_row;
445
446 memset(record, '\0', old_record_size); /* zero the rowbuffer */
447
448 /*
449 * offset 0 = number of var columns
450 * offset 1 = row number. zeroed (datasever assigns)
451 */
452 row_pos = 2;
453
454 rc = TDS_FAIL;
455 if ((row_pos = tds_bcp_add_fixed_columns(bcpinfo, get_col_data, null_error, offset, record, row_pos)) < 0)
456 goto cleanup;
457
458 row_sz_pos = row_pos;
459
460 /* potential variable columns to write */
461
462 if ((row_pos = tds_bcp_add_variable_columns(bcpinfo, get_col_data, null_error, offset, record, row_pos, &var_cols_written)) < 0)
463 goto cleanup;
464
465
466 if (var_cols_written) {
467 TDS_PUT_UA2(&record[row_sz_pos], row_pos);
468 record[0] = var_cols_written;
469 }
470
471 tdsdump_log(TDS_DBG_INFO1, "old_record_size = %d new size = %d \n", old_record_size, row_pos);
472
473 tds_put_smallint(tds, row_pos);
474 tds_put_n(tds, record, row_pos);
475
476 /* row is done, now handle any text/image data */
477
478 bcpinfo->blob_cols = 0;
479 }
480
481 for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) {
482 bindcol = bcpinfo->bindinfo->columns[i];
483 if (is_blob_type(bindcol->column_type)) {
484 /* Elide trailing NULLs */
485 if (bindcol->bcp_column_data->is_null) {
486 int j;
487 for (j = i + 1; j < bcpinfo->bindinfo->num_cols; ++j) {
488 TDSCOLUMN *bindcol2 = bcpinfo->bindinfo->columns[j];
489 if (is_blob_type(bindcol2->column_type)
490 && !bindcol2->bcp_column_data->is_null) {
491 break;
492 }
493 }
494 if (j == bcpinfo->bindinfo->num_cols) {
495 i = j;
496 break;
497 }
498 }
499
500 rc = get_col_data(bcpinfo, bindcol, offset);
501 if (rc == TDS_FAIL)
502 goto cleanup;
503 /* unknown but zero */
504 tds_put_smallint(tds, 0);
505 tds_put_byte(tds, (unsigned char) bindcol->column_type);
506 tds_put_byte(tds, 0xff - bcpinfo->blob_cols);
507 /*
508 * offset of txptr we stashed during variable
509 * column processing
510 */
511 tds_put_smallint(tds, bindcol->column_textpos);
512 tds_put_int(tds, bindcol->bcp_column_data->datalen);
513 if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */
514 bcpinfo->next_col = i + 1;
515 /* bcpinfo->text_sent = 0; */
516 break;
517 }
518 tds_put_n(tds, bindcol->bcp_column_data->data, bindcol->bcp_column_data->datalen);
519 bcpinfo->blob_cols++;
520
521 }
522 }
523 }
524
525 if (i == bcpinfo->bindinfo->num_cols) {
526 tds_set_state(tds, TDS_SENDING);
527 bcpinfo->next_col = 0;
528 }
529 return TDS_SUCCESS;
530
531 cleanup:
532 tds_set_state(tds, TDS_SENDING);
533 return rc;
534 }
535
536 /**
537 * Add fixed size columns to the row
538 * \param bcpinfo BCP information
539 * \param get_col_data function to call to retrieve data to be sent
540 * \param ignored function to call if we try to send NULL if not allowed (not used)
541 * \param offset passed to get_col_data and null_error to specify the row to get
542 * \param rowbuffer row buffer to write to
543 * \param start row buffer last end position
544 * \returns new row length or -1 on error.
545 */
546 static int
tds_bcp_add_fixed_columns(TDSBCPINFO * bcpinfo,tds_bcp_get_col_data get_col_data,tds_bcp_null_error null_error,int offset,unsigned char * rowbuffer,int start)547 tds_bcp_add_fixed_columns(TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error,
548 int offset, unsigned char * rowbuffer, int start)
549 {
550 TDS_NUMERIC *num;
551 int row_pos = start;
552 TDSCOLUMN *bcpcol;
553 int cpbytes;
554 int i, j;
555 int bitleft = 0, bitpos;
556
557 assert(bcpinfo);
558 assert(rowbuffer);
559
560 tdsdump_log(TDS_DBG_FUNC, "tds_bcp_add_fixed_columns(%p, %p, ignored, %d, %p, %d)\n", bcpinfo, get_col_data, offset, rowbuffer, start);
561
562 for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
563
564 bcpcol = bcpinfo->bindinfo->columns[i];
565
566 if (is_nullable_type(bcpcol->on_server.column_type)
567 || bcpcol->column_nullable)
568 continue;
569
570 tdsdump_log(TDS_DBG_FUNC, "tds_bcp_add_fixed_columns column %d is a fixed column\n", i + 1);
571
572 if (TDS_FAILED(get_col_data(bcpinfo, bcpcol, offset))) {
573 tdsdump_log(TDS_DBG_INFO1, "get_col_data (column %d) failed\n", i + 1);
574 return -1;
575 }
576
577 if (bcpcol->bcp_column_data->is_null && null_error) {
578 /* No value or default value available and NULL not allowed. */
579 null_error(bcpinfo, i, offset);
580 return -1;
581 }
582
583 if (is_numeric_type(bcpcol->column_type)) {
584 num = (TDS_NUMERIC *) bcpcol->bcp_column_data->data;
585 cpbytes = tds_numeric_bytes_per_prec[num->precision];
586 memcpy(&rowbuffer[row_pos], num->array, cpbytes);
587 } else if (bcpcol->column_type == SYBBIT) {
588 /* all bit are collapsed together */
589 if (!bitleft) {
590 bitpos = row_pos++;
591 bitleft = 8;
592 rowbuffer[bitpos] = 0;
593 }
594 if (bcpcol->bcp_column_data->data[0])
595 rowbuffer[bitpos] |= 256 >> bitleft;
596 --bitleft;
597 continue;
598 } else {
599 cpbytes = bcpcol->bcp_column_data->datalen > bcpcol->column_size ?
600 bcpcol->column_size : bcpcol->bcp_column_data->datalen;
601 memcpy(&rowbuffer[row_pos], bcpcol->bcp_column_data->data, cpbytes);
602
603 /* CHAR data may need padding out to the database length with blanks */
604 /* TODO check binary !!! */
605 if (bcpcol->column_type == SYBCHAR && cpbytes < bcpcol->column_size) {
606 for (j = cpbytes; j < bcpcol->column_size; j++)
607 rowbuffer[row_pos + j] = ' ';
608 }
609 }
610
611 row_pos += bcpcol->column_size;
612 }
613 return row_pos;
614 }
615
616 /**
617 * Add variable size columns to the row
618 *
619 * \param bcpinfo BCP information already prepared
620 * \param get_col_data function to call to retrieve data to be sent
621 * \param null_error function to call if we try to send NULL if not allowed
622 * \param offset passed to get_col_data and null_error to specify the row to get
623 * \param rowbuffer The row image that will be sent to the server.
624 * \param start Where to begin copying data into the rowbuffer.
625 * \param pncols Address of output variable holding the count of columns added to the rowbuffer.
626 *
627 * \return length of (potentially modified) rowbuffer, or -1.
628 */
629 static int
tds_bcp_add_variable_columns(TDSBCPINFO * bcpinfo,tds_bcp_get_col_data get_col_data,tds_bcp_null_error null_error,int offset,TDS_UCHAR * rowbuffer,int start,int * pncols)630 tds_bcp_add_variable_columns(TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset, TDS_UCHAR* rowbuffer, int start, int *pncols)
631 {
632 TDS_USMALLINT offsets[256];
633 unsigned int i, row_pos;
634 unsigned int ncols = 0;
635
636 assert(bcpinfo);
637 assert(rowbuffer);
638 assert(pncols);
639
640 tdsdump_log(TDS_DBG_FUNC, "%4s %8s %18s %18s %8s\n", "col",
641 "type",
642 "is_nullable_type",
643 "column_nullable",
644 "is null" );
645 for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
646 TDSCOLUMN *bcpcol = bcpinfo->bindinfo->columns[i];
647 tdsdump_log(TDS_DBG_FUNC, "%4d %8d %18s %18s %8s\n", i,
648 bcpcol->column_type,
649 is_nullable_type(bcpcol->on_server.column_type)? "yes" : "no",
650 bcpcol->column_nullable? "yes" : "no",
651 bcpcol->bcp_column_data->is_null? "yes" : "no" );
652 }
653
654 /* the first two bytes of the rowbuffer are reserved to hold the entire record length */
655 row_pos = start + 2;
656 offsets[0] = row_pos;
657
658 tdsdump_log(TDS_DBG_FUNC, "%4s %8s %8s %8s\n", "col", "ncols", "row_pos", "cpbytes");
659
660 for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
661 unsigned int cpbytes = 0;
662 TDSCOLUMN *bcpcol = bcpinfo->bindinfo->columns[i];
663
664 /*
665 * Is this column of "variable" type, i.e. NULLable
666 * or naturally variable length e.g. VARCHAR
667 */
668 if (!is_nullable_type(bcpcol->on_server.column_type)
669 && !bcpcol->column_nullable)
670 continue;
671
672 tdsdump_log(TDS_DBG_FUNC, "%4d %8d %8d %8d\n", i, ncols, row_pos, cpbytes);
673
674 if (get_col_data(bcpinfo, bcpcol, offset) == TDS_FAIL)
675 return -1;
676
677 /* If it's a NOT NULL column, and we have no data, throw an error. */
678 if (!(bcpcol->column_nullable) && bcpcol->bcp_column_data->is_null
679 && null_error) {
680 /* No value or default value available and NULL not allowed. */
681 null_error(bcpinfo, i, offset);
682 return -1;
683 }
684
685 /* move the column buffer into the rowbuffer */
686 if (!bcpcol->bcp_column_data->is_null) {
687 if (is_blob_type(bcpcol->column_type)) {
688 cpbytes = 16;
689 bcpcol->column_textpos = row_pos; /* save for data write */
690 } else if (is_numeric_type(bcpcol->column_type)) {
691 TDS_NUMERIC *num = (TDS_NUMERIC *) bcpcol->bcp_column_data->data;
692 cpbytes = tds_numeric_bytes_per_prec[num->precision];
693 memcpy(&rowbuffer[row_pos], num->array, cpbytes);
694 } else if ((bcpcol->column_type == SYBVARCHAR
695 || bcpcol->column_type == SYBCHAR)
696 && bcpcol->bcp_column_data->datalen == 0) {
697 cpbytes = 1;
698 rowbuffer[row_pos] = ' ';
699 } else {
700 cpbytes = bcpcol->bcp_column_data->datalen > bcpcol->column_size ?
701 bcpcol->column_size : bcpcol->bcp_column_data->datalen;
702 memcpy(&rowbuffer[row_pos], bcpcol->bcp_column_data->data, cpbytes);
703 }
704 } else if (is_blob_type(bcpcol->column_type)) {
705 bcpcol->column_textpos = row_pos;
706 }
707
708 row_pos += cpbytes;
709 offsets[++ncols] = row_pos;
710 tdsdump_dump_buf(TDS_DBG_NETWORK, "BCP row buffer so far", rowbuffer, row_pos);
711 }
712
713 tdsdump_log(TDS_DBG_FUNC, "%4d %8d %8d\n", i, ncols, row_pos);
714
715 /*
716 * The rowbuffer ends with an offset table and, optionally, an adjustment table.
717 * The offset table has 1-byte elements that describe the locations of the start of each column in
718 * the rowbuffer. If the largest offset is greater than 255, another table -- the adjustment table --
719 * is inserted just before the offset table. It holds the high bytes.
720 *
721 * Both tables are laid out in reverse:
722 * #elements, offset N+1, offset N, offset N-1, ... offset 0
723 * E.g. for 2 columns you have 4 data points:
724 * 1. How many elements (4)
725 * 2. Start of column 3 (non-existent, "one off the end")
726 * 3. Start of column 2
727 * 4. Start of column 1
728 * The length of each column is computed by subtracting its start from the its successor's start.
729 *
730 * The algorithm below computes both tables. If the adjustment table isn't needed, the
731 * effect is to overwrite it with the offset table.
732 */
733 while (ncols && offsets[ncols] == offsets[ncols-1])
734 ncols--; /* trailing NULL columns are not sent and are not included in the offset table */
735
736 if (ncols) {
737 TDS_UCHAR *poff = rowbuffer + row_pos;
738 unsigned int pfx_top = offsets[ncols] / 256;
739
740 tdsdump_log(TDS_DBG_FUNC, "ncols=%u poff=%p [%u]\n", ncols, poff, offsets[ncols]);
741
742 if (offsets[ncols] / 256 == offsets[ncols-1] / 256) {
743 *poff++ = ncols + 1;
744 }
745 /* this is some kind of run-length-prefix encoding */
746 while (pfx_top) {
747 unsigned int n_pfx = 1;
748
749 for (i = 0; i <= ncols ; ++i)
750 if ((offsets[i] / 256u) < pfx_top)
751 ++n_pfx;
752 *poff++ = n_pfx;
753 --pfx_top;
754 }
755
756 tdsdump_log(TDS_DBG_FUNC, "poff=%p\n", poff);
757
758 for (i=0; i <= ncols; i++)
759 *poff++ = offsets[ncols-i] & 0xFF;
760 row_pos = (unsigned int)(poff - rowbuffer);
761 }
762
763 tdsdump_log(TDS_DBG_FUNC, "%4d %8d %8d\n", i, ncols, row_pos);
764 tdsdump_dump_buf(TDS_DBG_NETWORK, "BCP row buffer", rowbuffer, row_pos);
765
766 *pncols = ncols;
767
768 return ncols == 0? start : row_pos;
769 }
770
771 /**
772 * Send BCP metadata to server.
773 * Only for TDS 7.0+.
774 * \tds
775 * \param bcpinfo BCP information
776 * \return TDS_SUCCESS or TDS_FAIL.
777 */
778 static TDSRET
tds7_bcp_send_colmetadata(TDSSOCKET * tds,TDSBCPINFO * bcpinfo)779 tds7_bcp_send_colmetadata(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
780 {
781 TDSCOLUMN *bcpcol;
782 int i, num_cols;
783
784 tdsdump_log(TDS_DBG_FUNC, "tds7_bcp_send_colmetadata(%p, %p)\n", tds, bcpinfo);
785 assert(tds && bcpinfo);
786
787 if (tds->out_flag != TDS_BULK || tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
788 return TDS_FAIL;
789
790 /*
791 * Deep joy! For TDS 7 we have to send a colmetadata message followed by row data
792 */
793 tds_put_byte(tds, TDS7_RESULT_TOKEN); /* 0x81 */
794
795 num_cols = 0;
796 for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
797 bcpcol = bcpinfo->bindinfo->columns[i];
798 if ((!bcpinfo->identity_insert_on && bcpcol->column_identity) ||
799 bcpcol->column_timestamp || !tds_bcp_is_bound(bcpinfo, bcpcol)) {
800 continue;
801 }
802 num_cols++;
803 }
804
805 tds_put_smallint(tds, num_cols);
806
807 for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
808 size_t len;
809
810 bcpcol = bcpinfo->bindinfo->columns[i];
811
812 /*
813 * dont send the (meta)data for timestamp columns, or
814 * identity columns (unless indentity_insert is enabled
815 */
816
817 if ((!bcpinfo->identity_insert_on && bcpcol->column_identity) ||
818 bcpcol->column_timestamp || !tds_bcp_is_bound(bcpinfo, bcpcol)) {
819 continue;
820 }
821
822 if (IS_TDS72_PLUS(tds->conn))
823 tds_put_int(tds, bcpcol->column_usertype);
824 else
825 tds_put_smallint(tds, bcpcol->column_usertype);
826 tds_put_smallint(tds, bcpcol->column_flags);
827 tds_put_byte(tds, (unsigned char) bcpcol->on_server.column_type);
828
829 assert(bcpcol->funcs);
830 bcpcol->funcs->put_info(tds, bcpcol);
831
832 /* TODO put this in put_info. It seems that parameter format is
833 * different from BCP format
834 */
835 if (is_blob_type(bcpcol->on_server.column_type)) {
836 /* FIXME strlen return len in bytes not in characters required here */
837 TDS_PUT_SMALLINT(tds, strlen(bcpinfo->tablename));
838 tds_put_string(tds, bcpinfo->tablename, (int)strlen(bcpinfo->tablename));
839 }
840 /* FIXME support multibyte string */
841 len = tds_dstr_len(&bcpcol->column_name);
842 tds_put_byte(tds, (unsigned char) len);
843 tds_put_string(tds, tds_dstr_cstr(&bcpcol->column_name), len);
844
845 }
846
847 tds_set_state(tds, TDS_SENDING);
848 return TDS_SUCCESS;
849 }
850
851 /**
852 * Tell we finished sending BCP data to server
853 * \tds
854 * \param[out] rows_copied number of rows copied to server
855 */
856 TDSRET
tds_bcp_done(TDSSOCKET * tds,int * rows_copied)857 tds_bcp_done(TDSSOCKET *tds, int *rows_copied)
858 {
859 TDSRET rc;
860
861 tdsdump_log(TDS_DBG_FUNC, "tds_bcp_done(%p, %p)\n", tds, rows_copied);
862
863 if (tds->out_flag != TDS_BULK || tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
864 return TDS_FAIL;
865
866 tds_flush_packet(tds);
867
868 tds_set_state(tds, TDS_PENDING);
869
870 rc = tds_process_simple_query(tds);
871 if (TDS_FAILED(rc))
872 return rc;
873
874 if (rows_copied)
875 *rows_copied = tds->rows_affected;
876
877 return TDS_SUCCESS;
878 }
879
880 /**
881 * Start sending BCP data to server.
882 * Initialize stream to accept data.
883 * \tds
884 * \param bcpinfo BCP information already prepared
885 */
886 TDSRET
tds_bcp_start(TDSSOCKET * tds,TDSBCPINFO * bcpinfo)887 tds_bcp_start(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
888 {
889 TDSRET rc;
890
891 tdsdump_log(TDS_DBG_FUNC, "tds_bcp_start(%p, %p)\n", tds, bcpinfo);
892
893 rc = tds_submit_query(tds, bcpinfo->insert_stmt);
894 if (TDS_FAILED(rc))
895 return rc;
896
897 /* set we want to switch to bulk state */
898 tds->bulk_query = 1;
899
900 if (IS_TDS7_PLUS(tds->conn)) {
901 rc = tds_process_simple_query(tds);
902 } else {
903 /*
904 * In TDS 5 we get the column information as a result set from
905 * the "insert bulk" command. We need to get information
906 * about default values from it.
907 */
908 rc = tds_bcp_read_column_defaults(tds, bcpinfo);
909 }
910 if (TDS_FAILED(rc))
911 return rc;
912
913 tds->out_flag = TDS_BULK;
914 if (tds_set_state(tds, TDS_SENDING) != TDS_SENDING)
915 return TDS_FAIL;
916
917 if (IS_TDS7_PLUS(tds->conn))
918 tds7_bcp_send_colmetadata(tds, bcpinfo);
919
920 return TDS_SUCCESS;
921 }
922
923 /**
924 * Free row data allocated in the result set.
925 */
926 static void
tds_bcp_row_free(TDSRESULTINFO * result,unsigned char * row)927 tds_bcp_row_free(TDSRESULTINFO* result, unsigned char *row)
928 {
929 result->row_size = 0;
930 TDS_ZERO_FREE(result->current_row);
931 }
932
933 /**
934 * Start bulk copy to server
935 * \tds
936 * \param bcpinfo BCP information already prepared
937 */
938 TDSRET
tds_bcp_start_copy_in(TDSSOCKET * tds,TDSBCPINFO * bcpinfo)939 tds_bcp_start_copy_in(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
940 {
941 TDSCOLUMN *bcpcol;
942 int i;
943 int fixed_col_len_tot = 0;
944 int variable_col_len_tot = 0;
945 int column_bcp_data_size = 0;
946 int bcp_record_size = 0;
947 TDSRET rc;
948 TDS_INT var_cols;
949
950 tdsdump_log(TDS_DBG_FUNC, "tds_bcp_start_copy_in(%p, %p)\n", tds, bcpinfo);
951
952 rc = tds_bcp_start_insert_stmt(tds, bcpinfo);
953 if (TDS_FAILED(rc))
954 return rc;
955
956 rc = tds_bcp_start(tds, bcpinfo);
957 if (TDS_FAILED(rc)) {
958 /* TODO, in CTLib was _ctclient_msg(blkdesc->con, "blk_rowxfer", 2, 5, 1, 140, ""); */
959 return rc;
960 }
961
962 /*
963 * Work out the number of "variable" columns. These are either nullable or of
964 * varying length type e.g. varchar.
965 */
966 var_cols = 0;
967
968 if (IS_TDS50(tds->conn)) {
969 for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
970
971 bcpcol = bcpinfo->bindinfo->columns[i];
972
973 /*
974 * work out storage required for this datatype
975 * blobs always require 16, numerics vary, the
976 * rest can be taken from the server
977 */
978
979 if (is_blob_type(bcpcol->on_server.column_type))
980 column_bcp_data_size = 16;
981 else if (is_numeric_type(bcpcol->on_server.column_type))
982 column_bcp_data_size = tds_numeric_bytes_per_prec[bcpcol->column_prec];
983 else
984 column_bcp_data_size = bcpcol->column_size;
985
986 /*
987 * now add that size into either fixed or variable
988 * column totals...
989 */
990
991 if (is_nullable_type(bcpcol->on_server.column_type) || bcpcol->column_nullable) {
992 var_cols++;
993 variable_col_len_tot += column_bcp_data_size;
994 }
995 else {
996 fixed_col_len_tot += column_bcp_data_size;
997 }
998 }
999
1000 /* this formula taken from sybase manual... */
1001
1002 bcp_record_size = 4 +
1003 fixed_col_len_tot +
1004 variable_col_len_tot +
1005 ( (int)(variable_col_len_tot / 256 ) + 1 ) +
1006 (var_cols + 1) +
1007 2;
1008
1009 tdsdump_log(TDS_DBG_FUNC, "current_record_size = %d\n", bcpinfo->bindinfo->row_size);
1010 tdsdump_log(TDS_DBG_FUNC, "bcp_record_size = %d\n", bcp_record_size);
1011
1012 if (bcp_record_size > bcpinfo->bindinfo->row_size) {
1013 if (!TDS_RESIZE(bcpinfo->bindinfo->current_row, bcp_record_size)) {
1014 tdsdump_log(TDS_DBG_FUNC, "could not realloc current_row\n");
1015 return TDS_FAIL;
1016 }
1017 bcpinfo->bindinfo->row_free = tds_bcp_row_free;
1018 bcpinfo->bindinfo->row_size = bcp_record_size;
1019 }
1020 }
1021
1022 return TDS_SUCCESS;
1023 }
1024
1025 /** input stream to read a file */
1026 typedef struct tds_file_stream {
1027 /** common fields, must be the first field */
1028 TDSINSTREAM stream;
1029 /** file to read from */
1030 FILE *f;
1031
1032 /** terminator */
1033 const char *terminator;
1034 /** terminator length in bytes */
1035 size_t term_len;
1036
1037 /** buffer for store bytes readed that could be the terminator */
1038 char *left;
1039 size_t left_pos;
1040 } TDSFILESTREAM;
1041
1042 /** \cond HIDDEN_SYMBOLS */
1043 #if defined(_WIN32) && defined(HAVE__LOCK_FILE) && defined(HAVE__UNLOCK_FILE)
1044 #define TDS_HAVE_STDIO_LOCKED 1
1045 #define flockfile(s) _lock_file(s)
1046 #define funlockfile(s) _unlock_file(s)
1047 #define getc_unlocked(s) _getc_nolock(s)
1048 #define feof_unlocked(s) _feof_nolock(s)
1049 #endif
1050
1051 #ifndef TDS_HAVE_STDIO_LOCKED
1052 #undef getc_unlocked
1053 #undef feof_unlocked
1054 #undef flockfile
1055 #undef funlockfile
1056 #define getc_unlocked(s) getc(s)
1057 #define feof_unlocked(s) feof(s)
1058 #define flockfile(s) do { } while(0)
1059 #define funlockfile(s) do { } while(0)
1060 #endif
1061 /** \endcond */
1062
1063 /**
1064 * Reads a chunk of data from file stream checking for terminator
1065 * \param stream file stream
1066 * \param ptr buffer where to read data
1067 * \param len length of buffer
1068 */
1069 static int
tds_file_stream_read(TDSINSTREAM * stream,void * ptr,size_t len)1070 tds_file_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
1071 {
1072 TDSFILESTREAM *s = (TDSFILESTREAM *) stream;
1073 int c;
1074 char *p = (char *) ptr;
1075
1076 while (len) {
1077 if (memcmp(s->left, s->terminator - s->left_pos, s->term_len) == 0)
1078 return p - (char *) ptr;
1079
1080 c = getc_unlocked(s->f);
1081 if (c == EOF)
1082 return -1;
1083
1084 *p++ = s->left[s->left_pos];
1085 --len;
1086
1087 s->left[s->left_pos++] = c;
1088 s->left_pos %= s->term_len;
1089 }
1090 return p - (char *) ptr;
1091 }
1092
1093 /**
1094 * Read a data file, passing the data through iconv().
1095 * \retval TDS_SUCCESS success
1096 * \retval TDS_FAIL error reading the column
1097 * \retval TDS_NO_MORE_RESULTS end of file detected
1098 */
1099 TDSRET
tds_bcp_fread(TDSSOCKET * tds,TDSICONV * char_conv,FILE * stream,const char * terminator,size_t term_len,char ** outbuf,size_t * outbytes)1100 tds_bcp_fread(TDSSOCKET * tds, TDSICONV * char_conv, FILE * stream, const char *terminator, size_t term_len, char **outbuf, size_t * outbytes)
1101 {
1102 TDSRET res;
1103 TDSFILESTREAM r;
1104 TDSDYNAMICSTREAM w;
1105 size_t readed;
1106
1107 /* prepare streams */
1108 r.stream.read = tds_file_stream_read;
1109 r.f = stream;
1110 r.term_len = term_len;
1111 r.left = (char*) calloc(1, term_len*3);
1112 r.left_pos = 0;
1113 if (!r.left) return TDS_FAIL;
1114
1115 /* copy terminator twice, let terminator points to second copy */
1116 memcpy(r.left + term_len, terminator, term_len);
1117 memcpy(r.left + term_len*2u, terminator, term_len);
1118 r.terminator = r.left + term_len*2u;
1119
1120 /* read initial buffer to test with terminator */
1121 readed = fread(r.left, 1, term_len, stream);
1122 if (readed != term_len) {
1123 free(r.left);
1124 if (readed == 0 && feof(stream))
1125 return TDS_NO_MORE_RESULTS;
1126 return TDS_FAIL;
1127 }
1128
1129 res = tds_dynamic_stream_init(&w, (void**) outbuf, 0);
1130 if (TDS_FAILED(res)) {
1131 free(r.left);
1132 return res;
1133 }
1134
1135 /* convert/copy from input stream to output one */
1136 flockfile(stream);
1137 if (char_conv == NULL)
1138 res = tds_copy_stream(tds, &r.stream, &w.stream);
1139 else
1140 res = tds_convert_stream(tds, char_conv, to_server, &r.stream, &w.stream);
1141 funlockfile(stream);
1142 free(r.left);
1143
1144 if (TDS_FAILED(res))
1145 return res;
1146
1147 *outbytes = w.size;
1148
1149 /* terminate buffer */
1150 if (!w.stream.buf_len)
1151 return TDS_FAIL;
1152
1153 ((char *) w.stream.buffer)[0] = 0;
1154 w.stream.write(&w.stream, 1);
1155
1156 return res;
1157 }
1158
1159 /**
1160 * Start writing writetext request.
1161 * This request start a bulk session.
1162 * \tds
1163 * \param objname table name
1164 * \param textptr TEXTPTR (see sql documentation)
1165 * \param timestamp data timestamp
1166 * \param with_log is log is enabled during insert
1167 * \param size bytes to be inserted
1168 */
1169 TDSRET
tds_writetext_start(TDSSOCKET * tds,const char * objname,const char * textptr,const char * timestamp,int with_log,TDS_UINT size)1170 tds_writetext_start(TDSSOCKET *tds, const char *objname, const char *textptr, const char *timestamp, int with_log, TDS_UINT size)
1171 {
1172 TDSRET rc;
1173
1174 /* TODO mssql does not like timestamp */
1175 rc = tds_submit_queryf(tds,
1176 "writetext bulk %s 0x%s timestamp = 0x%s%s",
1177 objname, textptr, timestamp, with_log ? " with log" : "");
1178 if (TDS_FAILED(rc))
1179 return rc;
1180
1181 /* set we want to switch to bulk state */
1182 tds->bulk_query = 1;
1183
1184 /* read the end token */
1185 rc = tds_process_simple_query(tds);
1186 if (TDS_FAILED(rc))
1187 return rc;
1188
1189 tds->out_flag = TDS_BULK;
1190 if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
1191 return TDS_FAIL;
1192
1193 tds_put_int(tds, size);
1194
1195 tds_set_state(tds, TDS_SENDING);
1196 return TDS_SUCCESS;
1197 }
1198
1199 /**
1200 * Send some data in the writetext request started by tds_writetext_start.
1201 * You should write in total (with multiple calls to this function) all
1202 * bytes declared calling tds_writetext_start.
1203 * \tds
1204 * \param text data to write
1205 * \param size data size in bytes
1206 */
1207 TDSRET
tds_writetext_continue(TDSSOCKET * tds,const TDS_UCHAR * text,TDS_UINT size)1208 tds_writetext_continue(TDSSOCKET *tds, const TDS_UCHAR *text, TDS_UINT size)
1209 {
1210 if (tds->out_flag != TDS_BULK || tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
1211 return TDS_FAIL;
1212
1213 /* TODO check size left */
1214 tds_put_n(tds, text, size);
1215
1216 tds_set_state(tds, TDS_SENDING);
1217 return TDS_SUCCESS;
1218 }
1219
1220 /**
1221 * Finish sending writetext data.
1222 * \tds
1223 */
1224 TDSRET
tds_writetext_end(TDSSOCKET * tds)1225 tds_writetext_end(TDSSOCKET *tds)
1226 {
1227 if (tds->out_flag != TDS_BULK || tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
1228 return TDS_FAIL;
1229
1230 tds_flush_packet(tds);
1231 tds_set_state(tds, TDS_PENDING);
1232 return TDS_SUCCESS;
1233 }
1234
1235
tds_bcp_is_bound(TDSBCPINFO * bcpinfo,TDSCOLUMN * colinfo)1236 static int tds_bcp_is_bound(TDSBCPINFO *bcpinfo, TDSCOLUMN *colinfo)
1237 {
1238 return (bcpinfo && colinfo &&
1239 /* Don't interfere with dblib bulk insertion from files. */
1240 (bcpinfo->xfer_init == 0
1241 || colinfo->column_varaddr != NULL
1242 || (colinfo->column_lenbind != NULL
1243 && (*colinfo->column_lenbind != 0
1244 || (colinfo->column_nullbind != NULL
1245 /* null-value for blk_textxfer ... */
1246 /* && *colinfo->column_nullbind == -1 */)))));
1247 }
1248
tds_bcp_read_column_defaults(TDSSOCKET * tds,TDSBCPINFO * bcpinfo)1249 static int tds_bcp_read_column_defaults(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
1250 {
1251 TDS_INT res_type;
1252 TDS_INT done_flags;
1253 int rc;
1254 int ret = TDS_SUCCESS;
1255
1256 int num_defs = 0;
1257 int colnum, res_colnum;
1258 TDS_TINYINT byte_val;
1259 unsigned char* src;
1260 TDSRESULTINFO* resinfo;
1261 TDSRESULTINFO* bindinfo = bcpinfo->bindinfo;
1262 TDSCOLUMN* curcol;
1263 TDSCOLUMN* bindcol;
1264
1265 /* Read metainformation about all columns */
1266 for (colnum = 0; ; ++colnum) {
1267 rc = tds_process_tokens(tds, &res_type, &done_flags,
1268 TDS_RETURN_ROW | TDS_RETURN_DONE);
1269 if (TDS_FAILED(rc)) {
1270 ret = TDS_FAIL;
1271 break;
1272 }
1273
1274 if (res_type == TDS_DONE_RESULT) {
1275 if ((done_flags & TDS_DONE_ERROR) != 0)
1276 ret = TDS_FAIL;
1277 /* First recordset with columnsmetainfo is done - go to the next */
1278 break;
1279 }
1280
1281 if (res_type != TDS_ROW_RESULT)
1282 continue;
1283
1284 resinfo = tds->current_results;
1285
1286 #if ENABLE_EXTRA_CHECKS
1287 /* Most probably the format of result is fixed, but couple of asserts
1288 are still needed to be sure */
1289 assert(resinfo->num_cols == 12);
1290 assert(colnum < bindinfo->num_cols);
1291 #endif
1292
1293 bindcol = bindinfo->columns[colnum];
1294
1295 /* Read "default" flag of the column */
1296 curcol = resinfo->columns[9];
1297
1298 #if ENABLE_EXTRA_CHECKS
1299 assert(curcol->column_type == SYBINT1);
1300 #endif
1301
1302 src = curcol->column_data;
1303 byte_val = *((TDS_TINYINT*) src);
1304
1305 if (byte_val == 1) {
1306 ++num_defs;
1307 bindcol->column_hasdefault = 1;
1308 }
1309 }
1310
1311 if (TDS_SUCCEED(ret) && num_defs > 0
1312 && TDS_SUCCEED(tds_process_tokens(tds, &res_type, &done_flags,
1313 TDS_RETURN_ROW)))
1314 {
1315 resinfo = tds->current_results;
1316
1317 #if ENABLE_EXTRA_CHECKS
1318 assert(resinfo->num_cols == num_defs);
1319 #endif
1320
1321 /* Now read all default values */
1322 res_colnum = 0;
1323 for (colnum = 0; colnum < bindinfo->num_cols; ++colnum) {
1324 bindcol = bindinfo->columns[colnum];
1325 if (!bindcol->column_hasdefault)
1326 continue;
1327
1328 curcol = resinfo->columns[res_colnum];
1329 ++res_colnum;
1330 src = curcol->column_data;
1331 if (is_blob_type(curcol->column_type)) {
1332 src = (unsigned char*) ((TDSBLOB*)src)->textvalue;
1333 tds_free_bcp_column_data(bindcol->bcp_column_data);
1334 bindcol->bcp_column_data
1335 = tds_alloc_bcp_column_data(curcol->column_cur_size);
1336 }
1337 /*
1338 * Maybe it's better to ignore all requests of defaults
1339 * after first request
1340 */
1341 if (bindcol->column_default) {
1342 bindcol->column_default = realloc(bindcol->column_default,
1343 curcol->column_cur_size);
1344 }
1345 else {
1346 bindcol->column_default = malloc(curcol->column_cur_size);
1347 }
1348 if (!bindcol->column_default) {
1349 ret = TDS_FAIL;
1350 break;
1351 }
1352
1353 bindcol->column_def_size = curcol->column_cur_size;
1354 memcpy(bindcol->column_default, src, bindcol->column_def_size);
1355 }
1356 }
1357
1358 if (TDS_SUCCEED(ret)) {
1359 ret = tds_process_simple_query(tds);
1360 }
1361
1362 return ret;
1363 }
1364