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