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