NCBI C++ ToolKit
bulk.c
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2  * Copyright (C) 2008-2010 Frediano Ziglio
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 /**
21  * \file
22  * \brief Handle bulk copy
23  */
24 
25 #include <config.h>
26 
27 #if HAVE_STRING_H
28 #include <string.h>
29 #endif /* HAVE_STRING_H */
30 
31 #if HAVE_ERRNO_H
32 #include <errno.h>
33 #endif /* HAVE_ERRNO_H */
34 
35 #if HAVE_STDLIB_H
36 #include <stdlib.h>
37 #endif /* HAVE_STDLIB_H */
38 
39 #include <assert.h>
40 
41 #include <freetds/tds.h>
42 #include <freetds/checks.h>
43 #include <freetds/bytes.h>
44 #include <freetds/iconv.h>
45 #include <freetds/stream.h>
46 #include <freetds/string.h>
47 #include "replacements.h"
48 
49 /** \cond HIDDEN_SYMBOLS */
50 #ifndef MAX
51 #define MAX(a,b) ( (a) > (b) ? (a) : (b) )
52 #endif
53 
54 #ifndef MIN
55 #define MIN(a,b) ( (a) < (b) ? (a) : (b) )
56 #endif
57 /** \endcond */
58 
59 /**
60  * Holds clause buffer
61  */
62 typedef struct tds_pbcb
63 {
64  /** buffer */
65  char *pb;
66  /** buffer length */
67  unsigned int cb;
68  /** true is buffer came from malloc */
69  unsigned int from_malloc;
71 
74 static int tds_bcp_add_fixed_columns(TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset, unsigned char * rowbuffer, int start);
75 static int tds_bcp_add_variable_columns(TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset, TDS_UCHAR *rowbuffer, int start, int *pncols);
76 static void tds_bcp_row_free(TDSRESULTINFO* result, unsigned char *row);
77 static int tds_bcp_is_bound(TDSBCPINFO *bcpinfo, TDSCOLUMN *colinfo);
79 
80 /**
81  * Initialize BCP information.
82  * Query structure of the table to server.
83  * \tds
84  * \param bcpinfo BCP information to initialize. Structure should be allocate
85  * and table name and direction should be already set.
86  */
87 TDSRET
89 {
90  TDSRESULTINFO *resinfo;
91  TDSRESULTINFO *bindinfo = NULL;
92  TDSCOLUMN *curcol;
93  TDS_INT result_type;
94  int i;
95  TDSRET rc;
96  const char *fmt;
97 
98  /* FIXME don't leave state in processing state */
99 
100  /* TODO quote tablename if needed */
101  if (bcpinfo->direction != TDS_BCP_QUERYOUT)
102  fmt = "SET FMTONLY ON select * from %s SET FMTONLY OFF";
103  else
104  fmt = "SET FMTONLY ON %s SET FMTONLY OFF";
105 
106  if (TDS_FAILED(rc=tds_submit_queryf(tds, fmt, tds_dstr_cstr(&bcpinfo->tablename))))
107  /* TODO return an error ?? */
108  /* Attempt to use Bulk Copy with a non-existent Server table (might be why ...) */
109  return rc;
110 
111  /* TODO possibly stop at ROWFMT and copy before going to idle */
112  /* TODO check what happen if table is not present, cleanup on error */
113  while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS))
114  == TDS_SUCCESS)
115  continue;
116  if (TDS_FAILED(rc))
117  return rc;
118 
119  /* copy the results info from the TDS socket */
120  if (!tds->res_info)
121  return TDS_FAIL;
122 
123  resinfo = tds->res_info;
124  if ((bindinfo = tds_alloc_results(resinfo->num_cols)) == NULL) {
125  rc = TDS_FAIL;
126  goto cleanup;
127  }
128 
129  bindinfo->row_size = resinfo->row_size;
130 
131  /* Copy the column metadata */
132  rc = TDS_FAIL;
133  for (i = 0; i < bindinfo->num_cols; i++) {
134 
135  curcol = bindinfo->columns[i];
136 
137  /*
138  * TODO use memcpy ??
139  * curcol and resinfo->columns[i] are both TDSCOLUMN.
140  * Why not "curcol = resinfo->columns[i];"? Because the rest of TDSCOLUMN (below column_timestamp)
141  * isn't being used. Perhaps this "upper" part of TDSCOLUMN should be a substructure.
142  * Or, see if the "lower" part is unused (and zeroed out) at this point, and just do one assignment.
143  */
144  curcol->funcs = resinfo->columns[i]->funcs;
145  curcol->column_type = resinfo->columns[i]->column_type;
146  curcol->column_usertype = resinfo->columns[i]->column_usertype;
147  curcol->column_flags = resinfo->columns[i]->column_flags;
148  if (curcol->column_varint_size == 0)
149  curcol->column_cur_size = resinfo->columns[i]->column_cur_size;
150  else
151  curcol->column_cur_size = -1;
152  curcol->column_size = resinfo->columns[i]->column_size;
153  curcol->column_varint_size = resinfo->columns[i]->column_varint_size;
154  curcol->column_prec = resinfo->columns[i]->column_prec;
155  curcol->column_scale = resinfo->columns[i]->column_scale;
156  curcol->on_server.column_type = resinfo->columns[i]->on_server.column_type;
157  curcol->on_server.column_size = resinfo->columns[i]->on_server.column_size;
158  curcol->char_conv = resinfo->columns[i]->char_conv;
159  if (!tds_dstr_dup(&curcol->column_name, &resinfo->columns[i]->column_name))
160  goto cleanup;
161  if (!tds_dstr_dup(&curcol->table_column_name, &resinfo->columns[i]->table_column_name))
162  goto cleanup;
163  curcol->column_nullable = resinfo->columns[i]->column_nullable;
164  curcol->column_identity = resinfo->columns[i]->column_identity;
165  curcol->column_timestamp = resinfo->columns[i]->column_timestamp;
166 
167  memcpy(curcol->column_collation, resinfo->columns[i]->column_collation, 5);
168 
169  if (is_numeric_type(curcol->column_type)) {
171  ((TDS_NUMERIC *) curcol->bcp_column_data->data)->precision = curcol->column_prec;
172  ((TDS_NUMERIC *) curcol->bcp_column_data->data)->scale = curcol->column_scale;
173  } else if (bcpinfo->bind_count != 0 /* ctlib */
174  && is_blob_col(curcol)) {
176  } else {
177  curcol->bcp_column_data =
179  }
180  if (!curcol->bcp_column_data)
181  goto cleanup;
182  }
183 
184  if (!IS_TDS7_PLUS(tds->conn)) {
185  bindinfo->current_row = tds_new(unsigned char, bindinfo->row_size);
186  if (!bindinfo->current_row)
187  goto cleanup;
188  bindinfo->row_free = tds_bcp_row_free;
189  }
190 
191  if (bcpinfo->identity_insert_on) {
192 
193  rc = tds_submit_queryf(tds, "set identity_insert %s on", tds_dstr_cstr(&bcpinfo->tablename));
194  if (TDS_FAILED(rc))
195  goto cleanup;
196 
197  /* TODO use tds_process_simple_query */
198  while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS))
199  == TDS_SUCCESS) {
200  }
201  if (rc != TDS_NO_MORE_RESULTS)
202  goto cleanup;
203  }
204 
205  bcpinfo->bindinfo = bindinfo;
206  bcpinfo->bind_count = 0;
207  return TDS_SUCCESS;
208 
209 cleanup:
210  tds_free_results(bindinfo);
211  return rc;
212 }
213 
214 /**
215  * Help to build query to be sent to server.
216  * Append column declaration to the query.
217  * Only for TDS 7.0+.
218  * \tds
219  * \param[out] clause output string
220  * \param bcpcol column to append
221  * \param first true if column is the first
222  * \return TDS_SUCCESS or TDS_FAIL.
223  */
224 static TDSRET
226 {
227  char column_type[40];
228 
229  tdsdump_log(TDS_DBG_FUNC, "tds7_build_bulk_insert_stmt(%p, %p, %p, %d)\n", tds, clause, bcpcol, first);
230 
231  if (TDS_FAILED(tds_get_column_declaration(tds, bcpcol, column_type))) {
233  tdsdump_log(TDS_DBG_FUNC, "error: cannot build bulk insert statement. unrecognized server datatype %d\n",
234  bcpcol->on_server.column_type);
235  return TDS_FAIL;
236  }
237 
238  if (clause->cb < strlen(clause->pb)
240  + strlen(column_type)
241  + ((first) ? 2u : 4u)) {
242  char *temp = tds_new(char, 2 * clause->cb);
243 
244  if (!temp) {
245  tdserror(tds_get_ctx(tds), tds, TDSEMEM, errno);
246  return TDS_FAIL;
247  }
248  strcpy(temp, clause->pb);
249  if (clause->from_malloc)
250  free(clause->pb);
251  clause->from_malloc = 1;
252  clause->pb = temp;
253  clause->cb *= 2;
254  }
255 
256  if (!first)
257  strcat(clause->pb, ", ");
258 
259  tds_quote_id(tds, strchr(clause->pb, 0), tds_dstr_cstr(&bcpcol->column_name), tds_dstr_len(&bcpcol->column_name));
260  strcat(clause->pb, " ");
261  strcat(clause->pb, column_type);
262 
263  return TDS_SUCCESS;
264 }
265 
266 /**
267  * Prepare the query to be sent to server to request BCP information
268  * \tds
269  * \param bcpinfo BCP information
270  */
271 static TDSRET
273 {
274  char *query;
275 
276  if (IS_TDS7_PLUS(tds->conn)) {
277  int i, firstcol, erc;
278  char *hint;
279  TDSCOLUMN *bcpcol;
280  TDSPBCB colclause;
281  char clause_buffer[4096] = { 0 };
282 
283  colclause.pb = clause_buffer;
284  colclause.cb = sizeof(clause_buffer);
285  colclause.from_malloc = 0;
286 
287  /* TODO avoid asprintf, use always malloc-ed buffer */
288  firstcol = 1;
289 
290  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
291  bcpcol = bcpinfo->bindinfo->columns[i];
292 
293  if (bcpcol->column_timestamp
294  || !tds_bcp_is_bound(bcpinfo, bcpcol))
295  continue;
296  if (!bcpinfo->identity_insert_on && bcpcol->column_identity)
297  continue;
298  tds7_build_bulk_insert_stmt(tds, &colclause, bcpcol, firstcol);
299  firstcol = 0;
300  }
301 
302  if (bcpinfo->hint) {
303  if (asprintf(&hint, " with (%s)", bcpinfo->hint) < 0)
304  hint = NULL;
305  } else {
306  hint = strdup("");
307  }
308  if (!hint) {
309  if (colclause.from_malloc)
310  TDS_ZERO_FREE(colclause.pb);
311  return TDS_FAIL;
312  }
313 
314  erc = asprintf(&query, "insert bulk %s (%s)%s", tds_dstr_cstr(&bcpinfo->tablename), colclause.pb, hint);
315 
316  free(hint);
317  if (colclause.from_malloc)
318  TDS_ZERO_FREE(colclause.pb); /* just for good measure; not used beyond this point */
319 
320  if (erc < 0)
321  return TDS_FAIL;
322  } else {
323  /* NOTE: if we use "with nodescribe" for following inserts server do not send describe */
324  if (asprintf(&query, "insert bulk %s", tds_dstr_cstr(&bcpinfo->tablename)) < 0)
325  return TDS_FAIL;
326  }
327 
328  /* save the statement for later... */
329  bcpinfo->insert_stmt = query;
330 
331  return TDS_SUCCESS;
332 }
333 
334 /**
335  * Send one row of data to server
336  * \tds
337  * \param bcpinfo BCP information
338  * \param get_col_data function to call to retrieve data to be sent
339  * \param ignored function to call if we try to send NULL if not allowed (not used)
340  * \param offset passed to get_col_data and null_error to specify the row to get
341  * \return TDS_SUCCESS or TDS_FAIL.
342  */
343 TDSRET
345  tds_bcp_get_col_data get_col_data,
346  tds_bcp_null_error null_error, int offset)
347 {
348  TDSCOLUMN *bindcol;
349  int i, start_col = bcpinfo->next_col;
350  TDSRET rc;
351 
353  "tds_bcp_send_bcp_record(%p, %p, %p, %p, %d)\n",
354  tds, bcpinfo, get_col_data, null_error, offset);
355 
357  return TDS_FAIL;
358 
359  if (start_col > 0) {
360  bindcol = bcpinfo->bindinfo->columns[start_col - 1];
361  *bindcol->column_lenbind
362  = MIN((TDS_INT) bindcol->column_bindlen
363  - bcpinfo->text_sent,
364  *bindcol->column_lenbind);
365  tds_put_n(tds, bindcol->column_varaddr,
366  *bindcol->column_lenbind);
367  bcpinfo->text_sent += *bindcol->column_lenbind;
368  if ((TDS_UINT) bcpinfo->text_sent < bindcol->column_bindlen) {
369  return TDS_SUCCESS; /* That's all for now. */
370  } else if (!IS_TDS7_PLUS(tds->conn)) {
371  bcpinfo->blob_cols++;
372  }
373  bcpinfo->next_col = 0;
374  bcpinfo->text_sent = 0;
375  }
376 
377  if (IS_TDS7_PLUS(tds->conn)) {
378 
379  if (start_col == 0) {
380  tds_put_byte(tds, TDS_ROW_TOKEN); /* 0xd1 */
381  }
382  for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) {
383 
384  TDS_INT save_size;
385  unsigned char *save_data;
386  TDSBLOB blob;
387  int /* bool */ has_text = 0;
388  bindcol = bcpinfo->bindinfo->columns[i];
389 
390  /*
391  * Don't send the (meta)data for timestamp columns or
392  * identity columns unless indentity_insert is enabled.
393  */
394 
395  if ((!bcpinfo->identity_insert_on && bindcol->column_identity) ||
396  bindcol->column_timestamp ||
397  !tds_bcp_is_bound(bcpinfo, bindcol)) {
398  continue;
399  }
400 
401  rc = get_col_data(bcpinfo, bindcol, offset);
402  if (rc == TDS_FAIL) {
403  tdsdump_log(TDS_DBG_INFO1, "get_col_data (column %d) failed\n", i + 1);
404  goto cleanup;
405  } else if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */
406  has_text = 1;
407  }
408  tdsdump_log(TDS_DBG_INFO1, "gotten column %d length %d null %d\n",
409  i + 1, bindcol->bcp_column_data->datalen, bindcol->bcp_column_data->is_null);
410 
411  save_size = bindcol->column_cur_size;
412  save_data = bindcol->column_data;
413  assert(bindcol->column_data == NULL);
414  if (bindcol->bcp_column_data->is_null) {
415  if ( !bindcol->column_nullable
416  && !is_nullable_type(bindcol->on_server
417  .column_type) ) {
418  return TDS_FAIL;
419  }
420  bindcol->column_cur_size = -1;
421  } else if (has_text) {
422  bindcol->column_cur_size
423  = bindcol->bcp_column_data->datalen;
424  } else if (is_blob_col(bindcol)) {
425  bindcol->column_cur_size = bindcol->bcp_column_data->datalen;
426  memset(&blob, 0, sizeof(blob));
427  blob.textvalue = (TDS_CHAR *) bindcol->bcp_column_data->data;
428  bindcol->column_data = (unsigned char *) &blob;
429  } else {
430  bindcol->column_cur_size = bindcol->bcp_column_data->datalen;
431  bindcol->column_data = bindcol->bcp_column_data->data;
432  }
433  rc = bindcol->funcs->put_data(tds, bindcol, 1);
434  bindcol->column_cur_size = save_size;
435  bindcol->column_data = save_data;
436 
437  if (TDS_FAILED(rc))
438  goto cleanup;
439  else if (has_text) {
440  bcpinfo->next_col = i + 1;
441  /* bcpinfo->text_sent = 0; */
442  break;
443  }
444  }
445  } /* IS_TDS7_PLUS */
446  else {
447  if (start_col == 0) {
448  int row_pos;
449  int row_sz_pos;
450  int var_cols_written = 0;
451  TDS_INT old_record_size = bcpinfo->bindinfo->row_size;
452  unsigned char *record = bcpinfo->bindinfo->current_row;
453 
454  memset(record, '\0', old_record_size); /* zero the rowbuffer */
455 
456  /*
457  * offset 0 = number of var columns
458  * offset 1 = row number. zeroed (datasever assigns)
459  */
460  row_pos = 2;
461 
462  rc = TDS_FAIL;
463  if ((row_pos = tds_bcp_add_fixed_columns(bcpinfo, get_col_data,
464  null_error, offset,
465  record, row_pos))
466  < 0)
467  goto cleanup;
468 
469  row_sz_pos = row_pos;
470 
471  /* potential variable columns to write */
472 
473  if ((row_pos = tds_bcp_add_variable_columns(bcpinfo,
474  get_col_data,
475  null_error, offset,
476  record, row_pos,
477  &var_cols_written))
478  < 0)
479  goto cleanup;
480 
481 
482  if (var_cols_written) {
483  TDS_PUT_UA2(&record[row_sz_pos], row_pos);
484  record[0] = var_cols_written;
485  }
486 
487  tdsdump_log(TDS_DBG_INFO1, "old_record_size = %d new size = %d \n", old_record_size, row_pos);
488 
489  tds_put_smallint(tds, row_pos);
490  tds_put_n(tds, record, row_pos);
491 
492  /* row is done, now handle any text/image data */
493 
494  bcpinfo->blob_cols = 0;
495  }
496 
497  for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) {
498  bindcol = bcpinfo->bindinfo->columns[i];
499  if (is_blob_type(bindcol->column_type)) {
500  /* Elide trailing NULLs */
501  if (bindcol->bcp_column_data->is_null) {
502  int j;
503  for (j = i + 1;
504  j < bcpinfo->bindinfo->num_cols;
505  ++j) {
506  TDSCOLUMN *bindcol2
507  = bcpinfo->bindinfo
508  ->columns[j];
509  if (is_blob_type(bindcol2
510  ->column_type)
511  && !(bindcol2
512  ->bcp_column_data
513  ->is_null)) {
514  break;
515  }
516  }
517  if (j == bcpinfo->bindinfo->num_cols) {
518  i = j;
519  break;
520  }
521  }
522 
523  rc = get_col_data(bcpinfo, bindcol, offset);
524  if (rc == TDS_FAIL)
525  goto cleanup;
526  /* unknown but zero */
527  tds_put_smallint(tds, 0);
529  (unsigned char)
530  bindcol->column_type);
531  tds_put_byte(tds, 0xff - bcpinfo->blob_cols);
532  /*
533  * offset of txptr we stashed during variable
534  * column processing
535  */
538  if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */
539  bcpinfo->next_col = i + 1;
540  /* bcpinfo->text_sent = 0; */
541  break;
542  }
543  tds_put_n(tds, bindcol->bcp_column_data->data, bindcol->bcp_column_data->datalen);
544  bcpinfo->blob_cols++;
545 
546  }
547  }
548  }
549 
550  if (i == bcpinfo->bindinfo->num_cols) {
552  bcpinfo->next_col = 0;
553  }
554  return TDS_SUCCESS;
555 
556 cleanup:
558  return rc;
559 }
560 
561 /**
562  * Add fixed size columns to the row
563  * \param bcpinfo BCP information
564  * \param get_col_data function to call to retrieve data to be sent
565  * \param ignored function to call if we try to send NULL if not allowed (not used)
566  * \param offset passed to get_col_data and null_error to specify the row to get
567  * \param rowbuffer row buffer to write to
568  * \param start row buffer last end position
569  * \returns new row length or -1 on error.
570  */
571 static int
573  tds_bcp_get_col_data get_col_data,
574  tds_bcp_null_error null_error,
575  int offset, unsigned char * rowbuffer, int start)
576 {
577  TDS_NUMERIC *num;
578  int row_pos = start;
579  TDSCOLUMN *bcpcol;
580  int cpbytes;
581  int i, j;
582  int bitleft = 0, bitpos;
583 
584  assert(bcpinfo);
585  assert(rowbuffer);
586 
587  tdsdump_log(TDS_DBG_FUNC, "tds_bcp_add_fixed_columns(%p, %p, ignored, %d, %p, %d)\n", bcpinfo, get_col_data, offset, rowbuffer, start);
588 
589  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
590 
591  bcpcol = bcpinfo->bindinfo->columns[i];
592 
594  || bcpcol->column_nullable)
595  continue;
596 
597  tdsdump_log(TDS_DBG_FUNC, "tds_bcp_add_fixed_columns column %d is a fixed column\n", i + 1);
598 
599  if (TDS_FAILED(get_col_data(bcpinfo, bcpcol, offset))) {
600  tdsdump_log(TDS_DBG_INFO1, "get_col_data (column %d) failed\n", i + 1);
601  return -1;
602  }
603 
604  if (bcpcol->bcp_column_data->is_null && null_error) {
605  /* No value or default value available and NULL not allowed. */
606  null_error(bcpinfo, i, offset);
607  return -1;
608  }
609 
610  if (is_numeric_type(bcpcol->column_type)) {
611  num = (TDS_NUMERIC *) bcpcol->bcp_column_data->data;
612  cpbytes = tds_numeric_bytes_per_prec[num->precision];
613  memcpy(&rowbuffer[row_pos], num->array, cpbytes);
614  } else if (bcpcol->column_type == SYBBIT) {
615  /* all bit are collapsed together */
616  if (!bitleft) {
617  bitpos = row_pos++;
618  bitleft = 8;
619  rowbuffer[bitpos] = 0;
620  }
621  if (bcpcol->bcp_column_data->data[0])
622  rowbuffer[bitpos] |= 256 >> bitleft;
623  --bitleft;
624  continue;
625  } else {
626  cpbytes = bcpcol->bcp_column_data->datalen > bcpcol->column_size ?
627  bcpcol->column_size : bcpcol->bcp_column_data->datalen;
628  memcpy(&rowbuffer[row_pos], bcpcol->bcp_column_data->data, cpbytes);
629 
630  /* CHAR data may need padding out to the database length with blanks */
631  /* TODO check binary !!! */
632  if (bcpcol->column_type == SYBCHAR && cpbytes < bcpcol->column_size) {
633  for (j = cpbytes; j < bcpcol->column_size; j++)
634  rowbuffer[row_pos + j] = ' ';
635  }
636  }
637 
638  row_pos += bcpcol->column_size;
639  }
640  return row_pos;
641 }
642 
643 /**
644  * Add variable size columns to the row
645  *
646  * \param bcpinfo BCP information already prepared
647  * \param get_col_data function to call to retrieve data to be sent
648  * \param null_error function to call if we try to send NULL if not allowed
649  * \param offset passed to get_col_data and null_error to specify the row to get
650  * \param rowbuffer The row image that will be sent to the server.
651  * \param start Where to begin copying data into the rowbuffer.
652  * \param pncols Address of output variable holding the count of columns added to the rowbuffer.
653  *
654  * \return length of (potentially modified) rowbuffer, or -1.
655  */
656 static int
657 tds_bcp_add_variable_columns(TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset, TDS_UCHAR* rowbuffer, int start, int *pncols)
658 {
659  TDS_USMALLINT offsets[256];
660  unsigned int i, row_pos;
661  unsigned int ncols = 0;
662 
663  assert(bcpinfo);
664  assert(rowbuffer);
665  assert(pncols);
666 
667  tdsdump_log(TDS_DBG_FUNC, "%4s %8s %18s %18s %8s\n", "col",
668  "type",
669  "is_nullable_type",
670  "column_nullable",
671  "is null" );
672  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
673  TDSCOLUMN *bcpcol = bcpinfo->bindinfo->columns[i];
674  tdsdump_log(TDS_DBG_FUNC, "%4d %8d %18s %18s %8s\n", i,
675  bcpcol->column_type,
676  is_nullable_type(bcpcol->on_server.column_type) ? "yes" : "no",
677  bcpcol->column_nullable? "yes" : "no",
678  bcpcol->bcp_column_data->is_null? "yes" : "no" );
679  }
680 
681  /* the first two bytes of the rowbuffer are reserved to hold the entire record length */
682  row_pos = start + 2;
683  offsets[0] = row_pos;
684 
685  tdsdump_log(TDS_DBG_FUNC, "%4s %8s %8s %8s\n", "col", "ncols", "row_pos", "cpbytes");
686 
687  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
688  unsigned int cpbytes = 0;
689  TDSCOLUMN *bcpcol = bcpinfo->bindinfo->columns[i];
690 
691  /*
692  * Is this column of "variable" type, i.e. NULLable
693  * or naturally variable length e.g. VARCHAR
694  */
696  && !bcpcol->column_nullable)
697  continue;
698 
699  tdsdump_log(TDS_DBG_FUNC, "%4d %8d %8d %8d\n", i, ncols, row_pos, cpbytes);
700 
701  if (get_col_data(bcpinfo, bcpcol, offset) == TDS_FAIL)
702  return -1;
703 
704  /* If it's a NOT NULL column, and we have no data, throw an error. */
705  if (!(bcpcol->column_nullable)
706  && bcpcol->bcp_column_data->is_null && null_error) {
707  /* No value or default value available and NULL not allowed. */
708  null_error(bcpinfo, i, offset);
709  return -1;
710  }
711 
712  /* move the column buffer into the rowbuffer */
713  if (!bcpcol->bcp_column_data->is_null) {
714  if (is_blob_type(bcpcol->column_type)) {
715  cpbytes = 16;
716  bcpcol->column_textpos = row_pos; /* save for data write */
717  } else if (is_numeric_type(bcpcol->column_type)) {
718  TDS_NUMERIC *num = (TDS_NUMERIC *) bcpcol->bcp_column_data->data;
719  cpbytes = tds_numeric_bytes_per_prec[num->precision];
720  memcpy(&rowbuffer[row_pos], num->array, cpbytes);
721  } else if ((bcpcol->column_type == SYBVARCHAR
722  || bcpcol->column_type == SYBCHAR)
723  && bcpcol->bcp_column_data->datalen == 0) {
724  cpbytes = 1;
725  rowbuffer[row_pos] = ' ';
726  } else {
727  cpbytes = bcpcol->bcp_column_data->datalen > bcpcol->column_size ?
728  bcpcol->column_size : bcpcol->bcp_column_data->datalen;
729  memcpy(&rowbuffer[row_pos], bcpcol->bcp_column_data->data, cpbytes);
730  }
731  } else if (is_blob_type(bcpcol->column_type)) {
732  bcpcol->column_textpos = row_pos;
733  }
734 
735  row_pos += cpbytes;
736  offsets[++ncols] = row_pos;
737  tdsdump_dump_buf(TDS_DBG_NETWORK, "BCP row buffer so far", rowbuffer, row_pos);
738  }
739 
740  tdsdump_log(TDS_DBG_FUNC, "%4d %8d %8d\n", i, ncols, row_pos);
741 
742  /*
743  * The rowbuffer ends with an offset table and, optionally, an adjustment table.
744  * The offset table has 1-byte elements that describe the locations of the start of each column in
745  * the rowbuffer. If the largest offset is greater than 255, another table -- the adjustment table --
746  * is inserted just before the offset table. It holds the high bytes.
747  *
748  * Both tables are laid out in reverse:
749  * #elements, offset N+1, offset N, offset N-1, ... offset 0
750  * E.g. for 2 columns you have 4 data points:
751  * 1. How many elements (4)
752  * 2. Start of column 3 (non-existent, "one off the end")
753  * 3. Start of column 2
754  * 4. Start of column 1
755  * The length of each column is computed by subtracting its start from the its successor's start.
756  *
757  * The algorithm below computes both tables. If the adjustment table isn't needed, the
758  * effect is to overwrite it with the offset table.
759  */
760  while (ncols && offsets[ncols] == offsets[ncols-1])
761  ncols--; /* trailing NULL columns are not sent and are not included in the offset table */
762 
763  if (ncols) {
764  TDS_UCHAR *poff = rowbuffer + row_pos;
765  unsigned int pfx_top = offsets[ncols] / 256;
766 
767  tdsdump_log(TDS_DBG_FUNC, "ncols=%u poff=%p [%u]\n", ncols, poff, offsets[ncols]);
768 
769  if (offsets[ncols] / 256 == offsets[ncols-1] / 256) {
770  *poff++ = ncols + 1;
771  }
772  /* this is some kind of run-length-prefix encoding */
773  while (pfx_top) {
774  unsigned int n_pfx = 1;
775 
776  for (i = 0; i <= ncols ; ++i)
777  if ((offsets[i] / 256u) < pfx_top)
778  ++n_pfx;
779  *poff++ = n_pfx;
780  --pfx_top;
781  }
782 
783  tdsdump_log(TDS_DBG_FUNC, "poff=%p\n", poff);
784 
785  for (i=0; i <= ncols; i++)
786  *poff++ = offsets[ncols-i] & 0xFF;
787  row_pos = (unsigned int)(poff - rowbuffer);
788  }
789 
790  tdsdump_log(TDS_DBG_FUNC, "%4d %8d %8d\n", i, ncols, row_pos);
791  tdsdump_dump_buf(TDS_DBG_NETWORK, "BCP row buffer", rowbuffer, row_pos);
792 
793  *pncols = ncols;
794 
795  return ncols == 0? start : row_pos;
796 }
797 
798 /**
799  * Send BCP metadata to server.
800  * Only for TDS 7.0+.
801  * \tds
802  * \param bcpinfo BCP information
803  * \return TDS_SUCCESS or TDS_FAIL.
804  */
805 static TDSRET
807 {
808  TDSCOLUMN *bcpcol;
809  int i, num_cols;
810 
811  tdsdump_log(TDS_DBG_FUNC, "tds7_bcp_send_colmetadata(%p, %p)\n", tds, bcpinfo);
812  assert(tds && bcpinfo);
813 
815  return TDS_FAIL;
816 
817  /*
818  * Deep joy! For TDS 7 we have to send a colmetadata message followed by row data
819  */
820  tds_put_byte(tds, TDS7_RESULT_TOKEN); /* 0x81 */
821 
822  num_cols = 0;
823  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
824  bcpcol = bcpinfo->bindinfo->columns[i];
825  if ((!bcpinfo->identity_insert_on && bcpcol->column_identity) ||
826  bcpcol->column_timestamp ||
827  !tds_bcp_is_bound(bcpinfo, bcpcol)) {
828  continue;
829  }
830  num_cols++;
831  }
832 
833  tds_put_smallint(tds, num_cols);
834 
835  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
836  size_t len;
837 
838  bcpcol = bcpinfo->bindinfo->columns[i];
839 
840  /*
841  * dont send the (meta)data for timestamp columns, or
842  * identity columns (unless indentity_insert is enabled
843  */
844 
845  if ((!bcpinfo->identity_insert_on && bcpcol->column_identity) ||
846  bcpcol->column_timestamp ||
847  !tds_bcp_is_bound(bcpinfo, bcpcol)) {
848  continue;
849  }
850 
851  if (IS_TDS72_PLUS(tds->conn))
852  tds_put_int(tds, bcpcol->column_usertype);
853  else
857  (unsigned char) bcpcol->on_server.column_type);
858 
859  assert(bcpcol->funcs);
860  bcpcol->funcs->put_info(tds, bcpcol);
861 
862  /* TODO put this in put_info. It seems that parameter format is
863  * different from BCP format
864  */
865  if (is_blob_type(bcpcol->on_server.column_type)) {
866  /* FIXME support multibyte string */
867  len = tds_dstr_len(&bcpinfo->tablename);
870  (int) len);
871  }
872  /* FIXME support multibyte string */
873  len = tds_dstr_len(&bcpcol->column_name);
874  tds_put_byte(tds, (unsigned char) len);
876  (int) len);
877 
878  }
879 
881  return TDS_SUCCESS;
882 }
883 
884 /**
885  * Tell we finished sending BCP data to server
886  * \tds
887  * \param[out] rows_copied number of rows copied to server
888  */
889 TDSRET
890 tds_bcp_done(TDSSOCKET *tds, int *rows_copied)
891 {
892  TDSRET rc;
893 
894  tdsdump_log(TDS_DBG_FUNC, "tds_bcp_done(%p, %p)\n", tds, rows_copied);
895 
897  return TDS_FAIL;
898 
900 
902 
904  if (TDS_FAILED(rc))
905  return rc;
906 
907  if (rows_copied)
908  *rows_copied = tds->rows_affected;
909 
910  return TDS_SUCCESS;
911 }
912 
913 /**
914  * Start sending BCP data to server.
915  * Initialize stream to accept data.
916  * \tds
917  * \param bcpinfo BCP information already prepared
918  */
919 TDSRET
921 {
922  TDSRET rc;
923 
924  tdsdump_log(TDS_DBG_FUNC, "tds_bcp_start(%p, %p)\n", tds, bcpinfo);
925 
926  rc = tds_submit_query(tds, bcpinfo->insert_stmt);
927  if (TDS_FAILED(rc))
928  return rc;
929 
930  /* set we want to switch to bulk state */
931  tds->bulk_query = 1;
932 
933  if (IS_TDS7_PLUS(tds->conn)) {
935  } else {
936  /*
937  * In TDS 5 we get the column information as a result
938  * set from the "insert bulk" command. We need to get
939  * information about default values from it.
940  */
941  rc = tds_bcp_read_column_defaults(tds, bcpinfo);
942  }
943  if (TDS_FAILED(rc))
944  return rc;
945 
946  tds->out_flag = TDS_BULK;
948  return TDS_FAIL;
949 
950  if (IS_TDS7_PLUS(tds->conn))
951  tds7_bcp_send_colmetadata(tds, bcpinfo);
952 
953  return TDS_SUCCESS;
954 }
955 
956 /**
957  * Free row data allocated in the result set.
958  */
959 static void
960 tds_bcp_row_free(TDSRESULTINFO* result, unsigned char *row)
961 {
962  result->row_size = 0;
963  TDS_ZERO_FREE(result->current_row);
964 }
965 
966 /**
967  * Start bulk copy to server
968  * \tds
969  * \param bcpinfo BCP information already prepared
970  */
971 TDSRET
973 {
974  TDSCOLUMN *bcpcol;
975  int i;
976  int fixed_col_len_tot = 0;
977  int variable_col_len_tot = 0;
978  int column_bcp_data_size = 0;
979  int bcp_record_size = 0;
980  TDSRET rc;
981  TDS_INT var_cols;
982 
983  tdsdump_log(TDS_DBG_FUNC, "tds_bcp_start_copy_in(%p, %p)\n", tds, bcpinfo);
984 
985  rc = tds_bcp_start_insert_stmt(tds, bcpinfo);
986  if (TDS_FAILED(rc))
987  return rc;
988 
989  rc = tds_bcp_start(tds, bcpinfo);
990  if (TDS_FAILED(rc)) {
991  /* TODO, in CTLib was _ctclient_msg(blkdesc->con, "blk_rowxfer", 2, 5, 1, 140, ""); */
992  return rc;
993  }
994 
995  /*
996  * Work out the number of "variable" columns. These are either nullable or of
997  * varying length type e.g. varchar.
998  */
999  var_cols = 0;
1000 
1001  if (IS_TDS50(tds->conn)) {
1002  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
1003 
1004  bcpcol = bcpinfo->bindinfo->columns[i];
1005 
1006  /*
1007  * work out storage required for this datatype
1008  * blobs always require 16, numerics vary, the
1009  * rest can be taken from the server
1010  */
1011 
1012  if (is_blob_type(bcpcol->on_server.column_type))
1013  column_bcp_data_size = 16;
1014  else if (is_numeric_type(bcpcol->on_server.column_type))
1015  column_bcp_data_size = tds_numeric_bytes_per_prec[bcpcol->column_prec];
1016  else
1017  column_bcp_data_size = bcpcol->column_size;
1018 
1019  /*
1020  * now add that size into either fixed or variable
1021  * column totals...
1022  */
1023 
1024  if (is_nullable_type(bcpcol->on_server.column_type) || bcpcol->column_nullable) {
1025  var_cols++;
1026  variable_col_len_tot += column_bcp_data_size;
1027  }
1028  else {
1029  fixed_col_len_tot += column_bcp_data_size;
1030  }
1031  }
1032 
1033  /* this formula taken from sybase manual... */
1034 
1035  bcp_record_size = 4 +
1036  fixed_col_len_tot +
1037  variable_col_len_tot +
1038  ( (int)(variable_col_len_tot / 256 ) + 1 ) +
1039  (var_cols + 1) +
1040  2;
1041 
1042  tdsdump_log(TDS_DBG_FUNC, "current_record_size = %d\n", bcpinfo->bindinfo->row_size);
1043  tdsdump_log(TDS_DBG_FUNC, "bcp_record_size = %d\n", bcp_record_size);
1044 
1045  if (bcp_record_size > bcpinfo->bindinfo->row_size) {
1046  if (!TDS_RESIZE(bcpinfo->bindinfo->current_row, bcp_record_size)) {
1047  tdsdump_log(TDS_DBG_FUNC, "could not realloc current_row\n");
1048  return TDS_FAIL;
1049  }
1050  bcpinfo->bindinfo->row_free = tds_bcp_row_free;
1051  bcpinfo->bindinfo->row_size = bcp_record_size;
1052  }
1053  }
1054 
1055  return TDS_SUCCESS;
1056 }
1057 
1058 /** input stream to read a file */
1059 typedef struct tds_file_stream {
1060  /** common fields, must be the first field */
1062  /** file to read from */
1063  FILE *f;
1064 
1065  /** terminator */
1066  const char *terminator;
1067  /** terminator length in bytes */
1068  size_t term_len;
1069 
1070  /** buffer for store bytes readed that could be the terminator */
1071  char *left;
1072  size_t left_pos;
1074 
1075 /** \cond HIDDEN_SYMBOLS */
1076 #if defined(_WIN32) && defined(HAVE__LOCK_FILE) && defined(HAVE__UNLOCK_FILE)
1077 #define TDS_HAVE_STDIO_LOCKED 1
1078 #define flockfile(s) _lock_file(s)
1079 #define funlockfile(s) _unlock_file(s)
1080 #define getc_unlocked(s) _getc_nolock(s)
1081 #define feof_unlocked(s) _feof_nolock(s)
1082 #endif
1083 
1084 #ifndef TDS_HAVE_STDIO_LOCKED
1085 #undef getc_unlocked
1086 #undef feof_unlocked
1087 #undef flockfile
1088 #undef funlockfile
1089 #define getc_unlocked(s) getc(s)
1090 #define feof_unlocked(s) feof(s)
1091 #define flockfile(s) do { } while(0)
1092 #define funlockfile(s) do { } while(0)
1093 #endif
1094 /** \endcond */
1095 
1096 /**
1097  * Reads a chunk of data from file stream checking for terminator
1098  * \param stream file stream
1099  * \param ptr buffer where to read data
1100  * \param len length of buffer
1101  */
1102 static int
1103 tds_file_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
1104 {
1105  TDSFILESTREAM *s = (TDSFILESTREAM *) stream;
1106  int c;
1107  char *p = (char *) ptr;
1108 
1109  while (len) {
1110  if (memcmp(s->left, s->terminator - s->left_pos, s->term_len) == 0)
1111  return p - (char *) ptr;
1112 
1113  c = getc_unlocked(s->f);
1114  if (c == EOF)
1115  return -1;
1116 
1117  *p++ = s->left[s->left_pos];
1118  --len;
1119 
1120  s->left[s->left_pos++] = c;
1121  s->left_pos %= s->term_len;
1122  }
1123  return p - (char *) ptr;
1124 }
1125 
1126 /**
1127  * Read a data file, passing the data through iconv().
1128  * \retval TDS_SUCCESS success
1129  * \retval TDS_FAIL error reading the column
1130  * \retval TDS_NO_MORE_RESULTS end of file detected
1131  */
1132 TDSRET
1133 tds_bcp_fread(TDSSOCKET * tds, TDSICONV * char_conv, FILE * stream, const char *terminator, size_t term_len, char **outbuf, size_t * outbytes)
1134 {
1135  TDSRET res;
1136  TDSFILESTREAM r;
1137  TDSDYNAMICSTREAM w;
1138  size_t readed;
1139 
1140  /* prepare streams */
1141  r.stream.read = tds_file_stream_read;
1142  r.f = stream;
1143  r.term_len = term_len;
1144  r.left = tds_new0(char, term_len*3);
1145  r.left_pos = 0;
1146  if (!r.left) return TDS_FAIL;
1147 
1148  /* copy terminator twice, let terminator points to second copy */
1149  memcpy(r.left + term_len, terminator, term_len);
1150  memcpy(r.left + term_len*2u, terminator, term_len);
1151  r.terminator = r.left + term_len*2u;
1152 
1153  /* read initial buffer to test with terminator */
1154  readed = fread(r.left, 1, term_len, stream);
1155  if (readed != term_len) {
1156  free(r.left);
1157  if (readed == 0 && feof(stream))
1158  return TDS_NO_MORE_RESULTS;
1159  return TDS_FAIL;
1160  }
1161 
1162  res = tds_dynamic_stream_init(&w, (void**) outbuf, 0);
1163  if (TDS_FAILED(res)) {
1164  free(r.left);
1165  return res;
1166  }
1167 
1168  /* convert/copy from input stream to output one */
1169  flockfile(stream);
1170  if (char_conv == NULL)
1171  res = tds_copy_stream(tds, &r.stream, &w.stream);
1172  else
1173  res = tds_convert_stream(tds, char_conv, to_server, &r.stream, &w.stream);
1174  funlockfile(stream);
1175  free(r.left);
1176 
1177  if (TDS_FAILED(res))
1178  return res;
1179 
1180  *outbytes = w.size;
1181 
1182  /* terminate buffer */
1183  if (!w.stream.buf_len)
1184  return TDS_FAIL;
1185 
1186  ((char *) w.stream.buffer)[0] = 0;
1187  w.stream.write(&w.stream, 1);
1188 
1189  return res;
1190 }
1191 
1192 /**
1193  * Start writing writetext request.
1194  * This request start a bulk session.
1195  * \tds
1196  * \param objname table name
1197  * \param textptr TEXTPTR (see sql documentation)
1198  * \param timestamp data timestamp
1199  * \param with_log is log is enabled during insert
1200  * \param size bytes to be inserted
1201  */
1202 TDSRET
1203 tds_writetext_start(TDSSOCKET *tds, const char *objname, const char *textptr, const char *timestamp, int with_log, TDS_UINT size)
1204 {
1205  TDSRET rc;
1206 
1207  /* TODO mssql does not like timestamp */
1208  rc = tds_submit_queryf(tds,
1209  "writetext bulk %s 0x%s timestamp = 0x%s%s",
1210  objname, textptr, timestamp, with_log ? " with log" : "");
1211  if (TDS_FAILED(rc))
1212  return rc;
1213 
1214  /* set we want to switch to bulk state */
1215  tds->bulk_query = 1;
1216 
1217  /* read the end token */
1219  if (TDS_FAILED(rc))
1220  return rc;
1221 
1222  tds->out_flag = TDS_BULK;
1224  return TDS_FAIL;
1225 
1226  tds_put_int(tds, size);
1227 
1229  return TDS_SUCCESS;
1230 }
1231 
1232 /**
1233  * Send some data in the writetext request started by tds_writetext_start.
1234  * You should write in total (with multiple calls to this function) all
1235  * bytes declared calling tds_writetext_start.
1236  * \tds
1237  * \param text data to write
1238  * \param size data size in bytes
1239  */
1240 TDSRET
1242 {
1244  return TDS_FAIL;
1245 
1246  /* TODO check size left */
1247  tds_put_n(tds, text, size);
1248 
1250  return TDS_SUCCESS;
1251 }
1252 
1253 /**
1254  * Finish sending writetext data.
1255  * \tds
1256  */
1257 TDSRET
1259 {
1261  return TDS_FAIL;
1262 
1265  return TDS_SUCCESS;
1266 }
1267 
1268 
1269 static int tds_bcp_is_bound(TDSBCPINFO *bcpinfo, TDSCOLUMN *colinfo)
1270 {
1271  return (bcpinfo && colinfo &&
1272  /* Don't interfere with dblib bulk insertion from files. */
1273  (bcpinfo->xfer_init == 0
1274  || colinfo->column_varaddr != NULL
1275  || (colinfo->column_lenbind != NULL
1276  && (*colinfo->column_lenbind != 0
1277  || (colinfo->column_nullbind != NULL
1278  /* null-value for blk_textxfer ... */
1279  /* && *colinfo->column_nullbind == -1 */)))));
1280 }
1281 
1282 
1284 {
1285  TDS_INT res_type;
1286  TDS_INT done_flags;
1287  int rc;
1288  int ret = TDS_SUCCESS;
1289 
1290  int num_defs = 0;
1291  int colnum, res_colnum;
1292  TDS_TINYINT byte_val;
1293  unsigned char* src;
1294  TDSRESULTINFO* resinfo;
1295  TDSRESULTINFO* bindinfo = bcpinfo->bindinfo;
1296  TDSCOLUMN* curcol;
1297  TDSCOLUMN* bindcol;
1298 
1299  /* Read metainformation about all columns */
1300  for (colnum = 0; ; ++colnum) {
1301  rc = tds_process_tokens(tds, &res_type, &done_flags,
1303  if (TDS_FAILED(rc)) {
1304  ret = TDS_FAIL;
1305  break;
1306  }
1307 
1308  if (res_type == TDS_DONE_RESULT) {
1309  if ((done_flags & TDS_DONE_ERROR) != 0)
1310  ret = TDS_FAIL;
1311  /*
1312  * First recordset with columnsmetainfo is done
1313  * - go to the next
1314  */
1315  break;
1316  }
1317 
1318  if (res_type != TDS_ROW_RESULT)
1319  continue;
1320 
1321  resinfo = tds->current_results;
1322 
1323 #if ENABLE_EXTRA_CHECKS
1324  /*
1325  * Most probably the format of result is fixed, but
1326  * couple of asserts are still needed to be sure
1327  */
1328  assert(resinfo->num_cols == 12);
1329  assert(colnum < bindinfo->num_cols);
1330 #endif
1331 
1332  bindcol = bindinfo->columns[colnum];
1333 
1334  /* Read "default" flag of the column */
1335  curcol = resinfo->columns[9];
1336 
1337 #if ENABLE_EXTRA_CHECKS
1338  assert(curcol->column_type == SYBINT1);
1339 #endif
1340 
1341  src = curcol->column_data;
1342  byte_val = *((TDS_TINYINT*) src);
1343 
1344  if (byte_val == 1) {
1345  ++num_defs;
1346  bindcol->column_hasdefault = 1;
1347  }
1348  }
1349 
1350  if (TDS_SUCCEED(ret) && num_defs > 0
1351  && TDS_SUCCEED(tds_process_tokens(tds, &res_type, &done_flags,
1352  TDS_RETURN_ROW)))
1353  {
1354  resinfo = tds->current_results;
1355 
1356 #if ENABLE_EXTRA_CHECKS
1357  assert(resinfo->num_cols == num_defs);
1358 #endif
1359 
1360  /* Now read all default values */
1361  res_colnum = 0;
1362  for (colnum = 0; colnum < bindinfo->num_cols;
1363  ++colnum) {
1364  bindcol = bindinfo->columns[colnum];
1365  if ( !bindcol->column_hasdefault ) {
1366  continue;
1367  }
1368 
1369  curcol = resinfo->columns[res_colnum];
1370  ++res_colnum;
1371  src = curcol->column_data;
1372  if (is_blob_type(curcol->column_type)) {
1373  src = (unsigned char*)
1374  ((TDSBLOB*)src)->textvalue;
1376  (bindcol->bcp_column_data);
1377  bindcol->bcp_column_data
1379  (curcol->column_cur_size);
1380  }
1381  /*
1382  * Maybe it's better to ignore all requests
1383  * of defaults after first request
1384  */
1385  if (bindcol->column_default) {
1386  bindcol->column_default
1387  = realloc(bindcol->column_default,
1388  curcol->column_cur_size);
1389  }
1390  else {
1391  bindcol->column_default
1392  = malloc(curcol->column_cur_size);
1393  }
1394  if (bindcol->column_default == NULL) {
1395  ret = TDS_FAIL;
1396  break;
1397  }
1398 
1399  bindcol->column_def_size
1400  = curcol->column_cur_size;
1401  memcpy(bindcol->column_default, src,
1402  bindcol->column_def_size);
1403  }
1404  }
1405 
1406  if (TDS_SUCCEED(ret)) {
1408  }
1409 
1410  return ret;
1411 }
static int tds_bcp_add_variable_columns(TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset, TDS_UCHAR *rowbuffer, int start, int *pncols)
Add variable size columns to the row.
Definition: bulk.c:657
static int tds_bcp_add_fixed_columns(TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset, unsigned char *rowbuffer, int start)
Add fixed size columns to the row.
Definition: bulk.c:572
static int tds_file_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
Reads a chunk of data from file stream checking for terminator.
Definition: bulk.c:1103
static TDSRET tds7_build_bulk_insert_stmt(TDSSOCKET *tds, TDSPBCB *clause, TDSCOLUMN *bcpcol, int first)
Help to build query to be sent to server.
Definition: bulk.c:225
static TDSRET tds7_bcp_send_colmetadata(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
Send BCP metadata to server.
Definition: bulk.c:806
TDSRET tds_bcp_start_copy_in(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
Start bulk copy to server \tds.
Definition: bulk.c:972
TDSRET tds_writetext_start(TDSSOCKET *tds, const char *objname, const char *textptr, const char *timestamp, int with_log, TDS_UINT size)
Start writing writetext request.
Definition: bulk.c:1203
static void tds_bcp_row_free(TDSRESULTINFO *result, unsigned char *row)
Free row data allocated in the result set.
Definition: bulk.c:960
TDSRET tds_bcp_start(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
Start sending BCP data to server.
Definition: bulk.c:920
TDSRET tds_bcp_fread(TDSSOCKET *tds, TDSICONV *char_conv, FILE *stream, const char *terminator, size_t term_len, char **outbuf, size_t *outbytes)
Read a data file, passing the data through iconv().
Definition: bulk.c:1133
static int tds_bcp_is_bound(TDSBCPINFO *bcpinfo, TDSCOLUMN *colinfo)
Definition: bulk.c:1269
static TDSRET tds_bcp_start_insert_stmt(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
Prepare the query to be sent to server to request BCP information \tds.
Definition: bulk.c:272
TDSRET tds_writetext_continue(TDSSOCKET *tds, const TDS_UCHAR *text, TDS_UINT size)
Send some data in the writetext request started by tds_writetext_start.
Definition: bulk.c:1241
static int tds_bcp_read_column_defaults(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
Definition: bulk.c:1283
TDSRET tds_writetext_end(TDSSOCKET *tds)
Finish sending writetext data.
Definition: bulk.c:1258
struct tds_file_stream TDSFILESTREAM
input stream to read a file
TDSRET tds_bcp_init(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
Initialize BCP information.
Definition: bulk.c:88
TDSRET tds_bcp_send_record(TDSSOCKET *tds, TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset)
Send one row of data to server \tds.
Definition: bulk.c:344
TDSRET tds_bcp_done(TDSSOCKET *tds, int *rows_copied)
Tell we finished sending BCP data to server \tds.
Definition: bulk.c:890
struct tds_pbcb TDSPBCB
Holds clause buffer.
#define TDS_PUT_UA2(ptr, val)
Definition: bytes.h:146
static TDSSOCKET * tds
Definition: collations.c:37
static void cleanup(void)
Definition: ct_dynamic.c:30
static DLIST_TYPE *DLIST_NAME() first(DLIST_LIST_TYPE *list)
Definition: dlist.tmpl.h:46
#define NULL
Definition: ncbistd.hpp:225
static const char * tds_dstr_cstr(DSTR *s)
Returns a C version (NUL terminated string) of dstr.
Definition: string.h:66
DSTR * tds_dstr_dup(DSTR *s, const DSTR *src) TDS_WUR
Duplicate a string from another dynamic string.
Definition: tdsstring.c:135
static size_t tds_dstr_len(DSTR *s)
Returns the length of the string in bytes.
Definition: string.h:73
unsigned int
A callback function used to compare two keys in a database.
Definition: types.hpp:1210
@ to_server
Definition: iconv.h:70
int i
int len
static void text(MDB_val *v)
Definition: mdb_dump.c:62
const struct ncbi::grid::netcache::search::fields::SIZE size
#define strdup
Definition: ncbi_ansi_ext.h:70
#define MIN(a, b)
returns smaller of a and b.
Definition: ncbi_std.h:112
#define MAX(a, b)
returns larger of a and b.
Definition: ncbi_std.h:117
static char terminator
Definition: njn_ioutil.cpp:56
double r(size_t dimension_, const Int4 *score_, const double *prob_, double theta_)
@ TDS_BULK
Definition: proto.h:338
#define TDS7_RESULT_TOKEN
Definition: proto.h:83
#define TDS_ROW_TOKEN
Definition: proto.h:100
#define tds_put_string
#define tds_alloc_results
#define tds_put_n
#define tds_convert_stream
#define tds_put_int
#define tds_free_bcp_column_data
#define tds_put_smallint
#define tds_quote_id
#define tds_dynamic_stream_init
#define tds_process_simple_query
#define tds_get_column_declaration
#define tds_flush_packet
#define tds_numeric_bytes_per_prec
#define tds_submit_query
#define tds_set_state
#define tds_alloc_bcp_column_data
#define tds_put_byte
#define tds_submit_queryf
#define tdserror
#define tds_copy_stream
#define tds_process_tokens
#define tds_free_results
#define asprintf
Definition: replacements.h:54
int offset
Definition: replacements.h:160
#define strcat(s, k)
#define assert(x)
Definition: srv_diag.hpp:58
static string query
TDS_UCHAR * data
Definition: tds.h:694
TDS_INT is_null
Definition: tds.h:696
TDS_INT datalen
Definition: tds.h:695
TDS_INT blob_cols
Definition: tds.h:1672
const char * hint
Definition: tds.h:1661
TDS_CHAR * insert_stmt
Definition: tds.h:1664
TDS_INT text_sent
Definition: tds.h:1670
TDS_INT identity_insert_on
Definition: tds.h:1666
TDSRESULTINFO * bindinfo
Definition: tds.h:1669
TDS_INT xfer_init
Definition: tds.h:1667
TDS_INT next_col
Definition: tds.h:1671
DSTR tablename
Definition: tds.h:1663
TDS_INT direction
Definition: tds.h:1665
TDS_INT bind_count
Definition: tds.h:1668
Information about blobs (e.g.
Definition: tds.h:658
TDS_CHAR * textvalue
Definition: tds.h:659
tds_func_put_data * put_data
Send column data to server.
Definition: tds.h:734
tds_func_put_info * put_info
Send metadata column information to server.
Definition: tds.h:724
Metadata about columns in regular and compute rows.
Definition: tds.h:761
TDS_TINYINT column_varint_size
size of length when reading from wire (0, 1, 2 or 4)
Definition: tds.h:773
TDS_SMALLINT * column_nullbind
Definition: tds.h:818
TDS_INT column_textpos
Definition: tds.h:821
TDS_INT column_size
maximun size of data.
Definition: tds.h:766
BCPCOLDATA * bcp_column_data
Definition: tds.h:825
unsigned int column_hasdefault
Definition: tds.h:802
DSTR column_name
Definition: tds.h:787
unsigned int column_timestamp
Definition: tds.h:801
TDS_UCHAR column_collation[5]
Definition: tds.h:803
TDS_UINT column_bindlen
Definition: tds.h:817
const TDSCOLUMNFUNCS * funcs
Definition: tds.h:762
TDS_INT * column_lenbind
Definition: tds.h:820
unsigned char * column_data
Definition: tds.h:793
DSTR table_column_name
Definition: tds.h:788
TDS_TINYINT column_prec
precision for decimal/numeric
Definition: tds.h:775
TDS_SERVER_TYPE column_type
This type can be different from wire type because conversion (e.g.
Definition: tds.h:768
unsigned int column_identity
Definition: tds.h:797
unsigned int column_nullable
Definition: tds.h:795
TDSICONV * char_conv
refers to previously allocated iconv information
Definition: tds.h:784
TDS_INT column_def_size
Definition: tds.h:790
TDS_TINYINT column_scale
scale for decimal/numeric
Definition: tds.h:776
TDS_CHAR * column_varaddr
Definition: tds.h:819
struct tds_column::@124 on_server
unsigned char * column_default
Definition: tds.h:791
TDS_INT column_flags
Definition: tds.h:764
TDS_INT column_cur_size
size written in variable (ie: char, text, binary).
Definition: tds.h:811
TDS_INT column_usertype
Definition: tds.h:763
output stream to write data to a dynamic buffer
Definition: stream.h:99
TDSOUTSTREAM stream
Definition: stream.h:100
size_t size
size of data inside buffer
Definition: stream.h:106
input stream to read a file
Definition: bulk.c:1059
size_t left_pos
Definition: bulk.c:1072
char * left
buffer for store bytes readed that could be the terminator
Definition: bulk.c:1071
FILE * f
file to read from
Definition: bulk.c:1063
const char * terminator
terminator
Definition: bulk.c:1066
size_t term_len
terminator length in bytes
Definition: bulk.c:1068
TDSINSTREAM stream
common fields, must be the first field
Definition: bulk.c:1061
define a stream of data used for input
Definition: stream.h:30
size_t buf_len
Definition: stream.h:51
char * buffer
write buffer.
Definition: stream.h:50
int(* write)(struct tds_output_stream *stream, size_t len)
write len bytes from buffer, return <0 if error or len
Definition: stream.h:41
Holds clause buffer.
Definition: bulk.c:63
char * pb
buffer
Definition: bulk.c:65
unsigned int from_malloc
true is buffer came from malloc
Definition: bulk.c:69
unsigned int cb
buffer length
Definition: bulk.c:67
Hold information for any results.
Definition: tds.h:842
unsigned char * current_row
Definition: tds.h:849
void(* row_free)(struct tds_result_info *result, unsigned char *row)
Definition: tds.h:850
TDS_USMALLINT num_cols
Definition: tds.h:845
TDSCOLUMN ** columns
Definition: tds.h:844
TDS_INT row_size
Definition: tds.h:851
Information for a server connection.
Definition: tds.h:1211
TDSRESULTINFO * current_results
Current query information.
Definition: tds.h:1263
TDSRESULTINFO * res_info
Definition: tds.h:1264
TDS_INT8 rows_affected
rows updated/deleted/inserted/selected, TDS_NO_COUNT if not valid
Definition: tds.h:1278
TDS_TINYINT bulk_query
true is query sent was a bulk query so we need to switch state to QUERYING
Definition: tds.h:1269
unsigned char out_flag
output buffer type
Definition: tds.h:1241
TDSCONNECTION conn[1]
Definition: tds.h:1215
unsigned char precision
Definition: proto.h:27
unsigned char array[33]
Definition: proto.h:29
#define SYBVARCHAR
Definition: sybdb.h:162
#define SYBINT1
Definition: sybdb.h:166
#define SYBCHAR
Definition: sybdb.h:160
#define SYBBIT
Definition: sybdb.h:178
Main include file for libtds.
#define TDS_FAIL
Definition: tds.h:204
#define tds_new(type, n)
Definition: tds.h:1392
#define is_numeric_type(x)
Definition: tds.h:454
#define TDS_FAILED(rc)
Definition: tds.h:206
tds_sysdep_int32_type TDS_INT
Definition: tds.h:149
@ TDS_RETURN_ROW
Definition: tds.h:256
@ TDS_TOKEN_RESULTS
Definition: tds.h:261
@ TDS_RETURN_DONE
Definition: tds.h:255
#define TDS_DONE_RESULT
Definition: tds.h:227
#define tdsdump_log
Definition: tds.h:1561
#define TDS_DBG_INFO1
Definition: tds.h:900
#define TDS_NO_MORE_RESULTS
Definition: tds.h:202
#define IS_TDS50(x)
Definition: tds.h:1701
#define is_blob_type(x)
Definition: tds.h:443
void(* tds_bcp_null_error)(TDSBCPINFO *bulk, int index, int offset)
Definition: tds.h:1677
unsigned char TDS_UCHAR
Definition: tds.h:145
#define is_blob_col(x)
Definition: tds.h:445
#define IS_TDS72_PLUS(x)
Definition: tds.h:1710
unsigned char TDS_TINYINT
Definition: tds.h:146
@ TDS_PENDING
cilent is waiting for data
Definition: tds.h:866
@ TDS_SENDING
client would send data
Definition: tds.h:865
@ TDS_WRITING
client is writing data
Definition: tds.h:864
#define tdsdump_dump_buf
Definition: tds.h:1564
@ TDSEBPROBADTYP
Definition: tds.h:327
@ TDSEMEM
Definition: tds.h:310
#define IS_TDS7_PLUS(x)
Definition: tds.h:1708
TDSRET(* tds_bcp_get_col_data)(TDSBCPINFO *bulk, TDSCOLUMN *bcpcol, int offset)
Definition: tds.h:1676
#define tds_new0(type, n)
Definition: tds.h:1393
char TDS_CHAR
Definition: tds.h:144
#define is_nullable_type(x)
Definition: tds.h:439
#define TDS_ROW_RESULT
Definition: tds.h:216
@ TDS_DONE_ERROR
error occurred
Definition: tds.h:272
int TDSRET
Definition: tds.h:201
tds_sysdep_uint16_type TDS_USMALLINT
Definition: tds.h:148
#define TDS_SUCCESS
Definition: tds.h:203
#define TDS_RESIZE(p, n_elem)
Definition: tds.h:1390
@ TDS_BCP_QUERYOUT
Definition: tds.h:1656
#define TDS_SUCCEED(rc)
Definition: tds.h:207
#define TDS_ZERO_FREE(x)
Definition: tds.h:359
tds_sysdep_uint32_type TDS_UINT
Definition: tds.h:150
#define tds_get_ctx(tds)
Definition: tds.h:1294
#define TDS_PUT_SMALLINT(tds, v)
Definition: tds.h:1743
#define TDS_DBG_FUNC
Definition: tds.h:898
#define TDS_DBG_NETWORK
Definition: tds.h:901
else result
Definition: token2.c:20
uchar outbuf[(1000000+1000000)]
Definition: unzcrash.c:41
void free(voidpf ptr)
voidp malloc(uInt size)
Modified on Fri Dec 01 04:45:39 2023 by modify_doxy.py rev. 669887