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