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