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