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