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