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  if (IS_TDS7_PLUS(tds->conn)
366  && bindcol->column_varint_size == 8
367  && *bindcol->column_lenbind > 0) {
368  /* Put PLP chunk length. */
369  tds_put_int(tds, *bindcol->column_lenbind);
370  }
371  tds_put_n(tds, bindcol->column_varaddr,
372  *bindcol->column_lenbind);
373  bcpinfo->text_sent += *bindcol->column_lenbind;
374  if ((TDS_UINT) bcpinfo->text_sent < bindcol->column_bindlen) {
375  return TDS_SUCCESS; /* That's all for now. */
376  } else if (!IS_TDS7_PLUS(tds->conn)) {
377  bcpinfo->blob_cols++;
378  } else if (bindcol->column_varint_size == 8) {
379  tds_put_int(tds, 0); /* Put PLP terminator. */
380  }
381  bcpinfo->next_col = 0;
382  bcpinfo->text_sent = 0;
383  }
384 
385  if (IS_TDS7_PLUS(tds->conn)) {
386 
387  if (start_col == 0) {
388  tds_put_byte(tds, TDS_ROW_TOKEN); /* 0xd1 */
389  }
390  for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) {
391 
392  TDS_INT save_size;
393  unsigned char *save_data;
394  TDSBLOB blob;
395  int /* bool */ has_text = 0;
396  bindcol = bcpinfo->bindinfo->columns[i];
397 
398  /*
399  * Don't send the (meta)data for timestamp columns or
400  * identity columns unless indentity_insert is enabled.
401  */
402 
403  if ((!bcpinfo->identity_insert_on && bindcol->column_identity) ||
404  bindcol->column_timestamp ||
405  !tds_bcp_is_bound(bcpinfo, bindcol)) {
406  continue;
407  }
408 
409  rc = get_col_data(bcpinfo, bindcol, offset);
410  if (rc == TDS_FAIL) {
411  tdsdump_log(TDS_DBG_INFO1, "get_col_data (column %d) failed\n", i + 1);
412  goto cleanup;
413  } else if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */
414  has_text = 1;
415  }
416  tdsdump_log(TDS_DBG_INFO1, "gotten column %d length %d null %d\n",
417  i + 1, bindcol->bcp_column_data->datalen, bindcol->bcp_column_data->is_null);
418 
419  save_size = bindcol->column_cur_size;
420  save_data = bindcol->column_data;
421  assert(bindcol->column_data == NULL);
422  if (bindcol->bcp_column_data->is_null) {
423  if ( !bindcol->column_nullable
424  && !is_nullable_type(bindcol->on_server
425  .column_type) ) {
426  return TDS_FAIL;
427  }
428  bindcol->column_cur_size = -1;
429  } else if (has_text) {
430  bindcol->column_cur_size
431  = bindcol->bcp_column_data->datalen;
432  } else if (is_blob_col(bindcol)) {
433  bindcol->column_cur_size = bindcol->bcp_column_data->datalen;
434  memset(&blob, 0, sizeof(blob));
435  blob.textvalue = (TDS_CHAR *) bindcol->bcp_column_data->data;
436  bindcol->column_data = (unsigned char *) &blob;
437  } else {
438  bindcol->column_cur_size = bindcol->bcp_column_data->datalen;
439  bindcol->column_data = bindcol->bcp_column_data->data;
440  }
441  rc = bindcol->funcs->put_data(tds, bindcol, 1);
442  bindcol->column_cur_size = save_size;
443  bindcol->column_data = save_data;
444 
445  if (TDS_FAILED(rc))
446  goto cleanup;
447  else if (has_text) {
448  bcpinfo->next_col = i + 1;
449  /* bcpinfo->text_sent = 0; */
450  break;
451  }
452  }
453  } /* IS_TDS7_PLUS */
454  else {
455  if (start_col == 0) {
456  int row_pos;
457  int row_sz_pos;
458  int var_cols_written = 0;
459  TDS_INT old_record_size = bcpinfo->bindinfo->row_size;
460  unsigned char *record = bcpinfo->bindinfo->current_row;
461 
462  memset(record, '\0', old_record_size); /* zero the rowbuffer */
463 
464  /*
465  * offset 0 = number of var columns
466  * offset 1 = row number. zeroed (datasever assigns)
467  */
468  row_pos = 2;
469 
470  rc = TDS_FAIL;
471  if ((row_pos = tds_bcp_add_fixed_columns(bcpinfo, get_col_data,
472  null_error, offset,
473  record, row_pos))
474  < 0)
475  goto cleanup;
476 
477  row_sz_pos = row_pos;
478 
479  /* potential variable columns to write */
480 
481  if ((row_pos = tds_bcp_add_variable_columns(bcpinfo,
482  get_col_data,
483  null_error, offset,
484  record, row_pos,
485  &var_cols_written))
486  < 0)
487  goto cleanup;
488 
489 
490  if (var_cols_written) {
491  TDS_PUT_UA2(&record[row_sz_pos], row_pos);
492  record[0] = var_cols_written;
493  }
494 
495  tdsdump_log(TDS_DBG_INFO1, "old_record_size = %d new size = %d \n", old_record_size, row_pos);
496 
497  tds_put_smallint(tds, row_pos);
498  tds_put_n(tds, record, row_pos);
499 
500  /* row is done, now handle any text/image data */
501 
502  bcpinfo->blob_cols = 0;
503  }
504 
505  for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) {
506  bindcol = bcpinfo->bindinfo->columns[i];
507  if (is_blob_type(bindcol->column_type)) {
508  /* Elide trailing NULLs */
509  if (bindcol->bcp_column_data->is_null) {
510  int j;
511  for (j = i + 1;
512  j < bcpinfo->bindinfo->num_cols;
513  ++j) {
514  TDSCOLUMN *bindcol2
515  = bcpinfo->bindinfo
516  ->columns[j];
517  if (is_blob_type(bindcol2
518  ->column_type)
519  && !(bindcol2
520  ->bcp_column_data
521  ->is_null)) {
522  break;
523  }
524  }
525  if (j == bcpinfo->bindinfo->num_cols) {
526  i = j;
527  break;
528  }
529  }
530 
531  rc = get_col_data(bcpinfo, bindcol, offset);
532  if (rc == TDS_FAIL)
533  goto cleanup;
534  /* unknown but zero */
535  tds_put_smallint(tds, 0);
537  (unsigned char)
538  bindcol->column_type);
539  tds_put_byte(tds, 0xff - bcpinfo->blob_cols);
540  /*
541  * offset of txptr we stashed during variable
542  * column processing
543  */
546  if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */
547  bcpinfo->next_col = i + 1;
548  /* bcpinfo->text_sent = 0; */
549  break;
550  }
551  tds_put_n(tds, bindcol->bcp_column_data->data, bindcol->bcp_column_data->datalen);
552  bcpinfo->blob_cols++;
553 
554  }
555  }
556  }
557 
558  if (i == bcpinfo->bindinfo->num_cols) {
560  bcpinfo->next_col = 0;
561  }
562  return TDS_SUCCESS;
563 
564 cleanup:
566  return rc;
567 }
568 
569 /**
570  * Add fixed size columns to the row
571  * \param bcpinfo BCP information
572  * \param get_col_data function to call to retrieve data to be sent
573  * \param ignored function to call if we try to send NULL if not allowed (not used)
574  * \param offset passed to get_col_data and null_error to specify the row to get
575  * \param rowbuffer row buffer to write to
576  * \param start row buffer last end position
577  * \returns new row length or -1 on error.
578  */
579 static int
581  tds_bcp_get_col_data get_col_data,
582  tds_bcp_null_error null_error,
583  int offset, unsigned char * rowbuffer, int start)
584 {
585  TDS_NUMERIC *num;
586  int row_pos = start;
587  TDSCOLUMN *bcpcol;
588  int cpbytes;
589  int i, j;
590  int bitleft = 0, bitpos;
591 
592  assert(bcpinfo);
593  assert(rowbuffer);
594 
595  tdsdump_log(TDS_DBG_FUNC, "tds_bcp_add_fixed_columns(%p, %p, ignored, %d, %p, %d)\n", bcpinfo, get_col_data, offset, rowbuffer, start);
596 
597  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
598 
599  bcpcol = bcpinfo->bindinfo->columns[i];
600 
602  || bcpcol->column_nullable)
603  continue;
604 
605  tdsdump_log(TDS_DBG_FUNC, "tds_bcp_add_fixed_columns column %d is a fixed column\n", i + 1);
606 
607  if (TDS_FAILED(get_col_data(bcpinfo, bcpcol, offset))) {
608  tdsdump_log(TDS_DBG_INFO1, "get_col_data (column %d) failed\n", i + 1);
609  return -1;
610  }
611 
612  if (bcpcol->bcp_column_data->is_null && null_error) {
613  /* No value or default value available and NULL not allowed. */
614  null_error(bcpinfo, i, offset);
615  return -1;
616  }
617 
618  if (is_numeric_type(bcpcol->column_type)) {
619  num = (TDS_NUMERIC *) bcpcol->bcp_column_data->data;
620  cpbytes = tds_numeric_bytes_per_prec[num->precision];
621  memcpy(&rowbuffer[row_pos], num->array, cpbytes);
622  } else if (bcpcol->column_type == SYBBIT) {
623  /* all bit are collapsed together */
624  if (!bitleft) {
625  bitpos = row_pos++;
626  bitleft = 8;
627  rowbuffer[bitpos] = 0;
628  }
629  if (bcpcol->bcp_column_data->data[0])
630  rowbuffer[bitpos] |= 256 >> bitleft;
631  --bitleft;
632  continue;
633  } else {
634  cpbytes = bcpcol->bcp_column_data->datalen > bcpcol->column_size ?
635  bcpcol->column_size : bcpcol->bcp_column_data->datalen;
636  memcpy(&rowbuffer[row_pos], bcpcol->bcp_column_data->data, cpbytes);
637 
638  /* CHAR data may need padding out to the database length with blanks */
639  /* TODO check binary !!! */
640  if (bcpcol->column_type == SYBCHAR && cpbytes < bcpcol->column_size) {
641  for (j = cpbytes; j < bcpcol->column_size; j++)
642  rowbuffer[row_pos + j] = ' ';
643  }
644  }
645 
646  row_pos += bcpcol->column_size;
647  }
648  return row_pos;
649 }
650 
651 /**
652  * Add variable size columns to the row
653  *
654  * \param bcpinfo BCP information already prepared
655  * \param get_col_data function to call to retrieve data to be sent
656  * \param null_error function to call if we try to send NULL if not allowed
657  * \param offset passed to get_col_data and null_error to specify the row to get
658  * \param rowbuffer The row image that will be sent to the server.
659  * \param start Where to begin copying data into the rowbuffer.
660  * \param pncols Address of output variable holding the count of columns added to the rowbuffer.
661  *
662  * \return length of (potentially modified) rowbuffer, or -1.
663  */
664 static int
665 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)
666 {
667  TDS_USMALLINT offsets[256];
668  unsigned int i, row_pos;
669  unsigned int ncols = 0;
670 
671  assert(bcpinfo);
672  assert(rowbuffer);
673  assert(pncols);
674 
675  tdsdump_log(TDS_DBG_FUNC, "%4s %8s %18s %18s %8s\n", "col",
676  "type",
677  "is_nullable_type",
678  "column_nullable",
679  "is null" );
680  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
681  TDSCOLUMN *bcpcol = bcpinfo->bindinfo->columns[i];
682  tdsdump_log(TDS_DBG_FUNC, "%4d %8d %18s %18s %8s\n", i,
683  bcpcol->column_type,
684  is_nullable_type(bcpcol->on_server.column_type) ? "yes" : "no",
685  bcpcol->column_nullable? "yes" : "no",
686  bcpcol->bcp_column_data->is_null? "yes" : "no" );
687  }
688 
689  /* the first two bytes of the rowbuffer are reserved to hold the entire record length */
690  row_pos = start + 2;
691  offsets[0] = row_pos;
692 
693  tdsdump_log(TDS_DBG_FUNC, "%4s %8s %8s %8s\n", "col", "ncols", "row_pos", "cpbytes");
694 
695  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
696  unsigned int cpbytes = 0;
697  TDSCOLUMN *bcpcol = bcpinfo->bindinfo->columns[i];
698 
699  /*
700  * Is this column of "variable" type, i.e. NULLable
701  * or naturally variable length e.g. VARCHAR
702  */
704  && !bcpcol->column_nullable)
705  continue;
706 
707  tdsdump_log(TDS_DBG_FUNC, "%4d %8d %8d %8d\n", i, ncols, row_pos, cpbytes);
708 
709  if (get_col_data(bcpinfo, bcpcol, offset) == TDS_FAIL)
710  return -1;
711 
712  /* If it's a NOT NULL column, and we have no data, throw an error. */
713  if (!(bcpcol->column_nullable)
714  && bcpcol->bcp_column_data->is_null && null_error) {
715  /* No value or default value available and NULL not allowed. */
716  null_error(bcpinfo, i, offset);
717  return -1;
718  }
719 
720  /* move the column buffer into the rowbuffer */
721  if (!bcpcol->bcp_column_data->is_null) {
722  if (is_blob_type(bcpcol->column_type)) {
723  cpbytes = 16;
724  bcpcol->column_textpos = row_pos; /* save for data write */
725  } else if (is_numeric_type(bcpcol->column_type)) {
726  TDS_NUMERIC *num = (TDS_NUMERIC *) bcpcol->bcp_column_data->data;
727  cpbytes = tds_numeric_bytes_per_prec[num->precision];
728  memcpy(&rowbuffer[row_pos], num->array, cpbytes);
729  } else if ((bcpcol->column_type == SYBVARCHAR
730  || bcpcol->column_type == SYBCHAR)
731  && bcpcol->bcp_column_data->datalen == 0) {
732  cpbytes = 1;
733  rowbuffer[row_pos] = ' ';
734  } else {
735  cpbytes = bcpcol->bcp_column_data->datalen > bcpcol->column_size ?
736  bcpcol->column_size : bcpcol->bcp_column_data->datalen;
737  memcpy(&rowbuffer[row_pos], bcpcol->bcp_column_data->data, cpbytes);
738  }
739  } else if (is_blob_type(bcpcol->column_type)) {
740  bcpcol->column_textpos = row_pos;
741  }
742 
743  row_pos += cpbytes;
744  offsets[++ncols] = row_pos;
745  tdsdump_dump_buf(TDS_DBG_NETWORK, "BCP row buffer so far", rowbuffer, row_pos);
746  }
747 
748  tdsdump_log(TDS_DBG_FUNC, "%4d %8d %8d\n", i, ncols, row_pos);
749 
750  /*
751  * The rowbuffer ends with an offset table and, optionally, an adjustment table.
752  * The offset table has 1-byte elements that describe the locations of the start of each column in
753  * the rowbuffer. If the largest offset is greater than 255, another table -- the adjustment table --
754  * is inserted just before the offset table. It holds the high bytes.
755  *
756  * Both tables are laid out in reverse:
757  * #elements, offset N+1, offset N, offset N-1, ... offset 0
758  * E.g. for 2 columns you have 4 data points:
759  * 1. How many elements (4)
760  * 2. Start of column 3 (non-existent, "one off the end")
761  * 3. Start of column 2
762  * 4. Start of column 1
763  * The length of each column is computed by subtracting its start from the its successor's start.
764  *
765  * The algorithm below computes both tables. If the adjustment table isn't needed, the
766  * effect is to overwrite it with the offset table.
767  */
768  while (ncols && offsets[ncols] == offsets[ncols-1])
769  ncols--; /* trailing NULL columns are not sent and are not included in the offset table */
770 
771  if (ncols) {
772  TDS_UCHAR *poff = rowbuffer + row_pos;
773  unsigned int pfx_top = offsets[ncols] / 256;
774 
775  tdsdump_log(TDS_DBG_FUNC, "ncols=%u poff=%p [%u]\n", ncols, poff, offsets[ncols]);
776 
777  if (offsets[ncols] / 256 == offsets[ncols-1] / 256) {
778  *poff++ = ncols + 1;
779  }
780  /* this is some kind of run-length-prefix encoding */
781  while (pfx_top) {
782  unsigned int n_pfx = 1;
783 
784  for (i = 0; i <= ncols ; ++i)
785  if ((offsets[i] / 256u) < pfx_top)
786  ++n_pfx;
787  *poff++ = n_pfx;
788  --pfx_top;
789  }
790 
791  tdsdump_log(TDS_DBG_FUNC, "poff=%p\n", poff);
792 
793  for (i=0; i <= ncols; i++)
794  *poff++ = offsets[ncols-i] & 0xFF;
795  row_pos = (unsigned int)(poff - rowbuffer);
796  }
797 
798  tdsdump_log(TDS_DBG_FUNC, "%4d %8d %8d\n", i, ncols, row_pos);
799  tdsdump_dump_buf(TDS_DBG_NETWORK, "BCP row buffer", rowbuffer, row_pos);
800 
801  *pncols = ncols;
802 
803  return ncols == 0? start : row_pos;
804 }
805 
806 /**
807  * Send BCP metadata to server.
808  * Only for TDS 7.0+.
809  * \tds
810  * \param bcpinfo BCP information
811  * \return TDS_SUCCESS or TDS_FAIL.
812  */
813 static TDSRET
815 {
816  TDSCOLUMN *bcpcol;
817  int i, num_cols;
818 
819  tdsdump_log(TDS_DBG_FUNC, "tds7_bcp_send_colmetadata(%p, %p)\n", tds, bcpinfo);
820  assert(tds && bcpinfo);
821 
823  return TDS_FAIL;
824 
825  /*
826  * Deep joy! For TDS 7 we have to send a colmetadata message followed by row data
827  */
828  tds_put_byte(tds, TDS7_RESULT_TOKEN); /* 0x81 */
829 
830  num_cols = 0;
831  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
832  bcpcol = bcpinfo->bindinfo->columns[i];
833  if ((!bcpinfo->identity_insert_on && bcpcol->column_identity) ||
834  bcpcol->column_timestamp ||
835  !tds_bcp_is_bound(bcpinfo, bcpcol)) {
836  continue;
837  }
838  num_cols++;
839  }
840 
841  tds_put_smallint(tds, num_cols);
842 
843  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
844  size_t len;
845 
846  bcpcol = bcpinfo->bindinfo->columns[i];
847 
848  /*
849  * dont send the (meta)data for timestamp columns, or
850  * identity columns (unless indentity_insert is enabled
851  */
852 
853  if ((!bcpinfo->identity_insert_on && bcpcol->column_identity) ||
854  bcpcol->column_timestamp ||
855  !tds_bcp_is_bound(bcpinfo, bcpcol)) {
856  continue;
857  }
858 
859  if (IS_TDS72_PLUS(tds->conn))
860  tds_put_int(tds, bcpcol->column_usertype);
861  else
865  (unsigned char) bcpcol->on_server.column_type);
866 
867  assert(bcpcol->funcs);
868  bcpcol->funcs->put_info(tds, bcpcol);
869 
870  /* TODO put this in put_info. It seems that parameter format is
871  * different from BCP format
872  */
873  if (is_blob_type(bcpcol->on_server.column_type)) {
874  /* FIXME support multibyte string */
875  len = tds_dstr_len(&bcpinfo->tablename);
878  (int) len);
879  }
880  /* FIXME support multibyte string */
881  len = tds_dstr_len(&bcpcol->column_name);
882  tds_put_byte(tds, (unsigned char) len);
884  (int) len);
885 
886  }
887 
889  return TDS_SUCCESS;
890 }
891 
892 /**
893  * Tell we finished sending BCP data to server
894  * \tds
895  * \param[out] rows_copied number of rows copied to server
896  */
897 TDSRET
898 tds_bcp_done(TDSSOCKET *tds, int *rows_copied)
899 {
900  TDSRET rc;
901 
902  tdsdump_log(TDS_DBG_FUNC, "tds_bcp_done(%p, %p)\n", tds, rows_copied);
903 
905  return TDS_FAIL;
906 
908 
910 
912  if (TDS_FAILED(rc))
913  return rc;
914 
915  if (rows_copied)
916  *rows_copied = tds->rows_affected;
917 
918  return TDS_SUCCESS;
919 }
920 
921 /**
922  * Start sending BCP data to server.
923  * Initialize stream to accept data.
924  * \tds
925  * \param bcpinfo BCP information already prepared
926  */
927 TDSRET
929 {
930  TDSRET rc;
931 
932  tdsdump_log(TDS_DBG_FUNC, "tds_bcp_start(%p, %p)\n", tds, bcpinfo);
933 
934  rc = tds_submit_query(tds, bcpinfo->insert_stmt);
935  if (TDS_FAILED(rc))
936  return rc;
937 
938  /* set we want to switch to bulk state */
939  tds->bulk_query = 1;
940 
941  if (IS_TDS7_PLUS(tds->conn)) {
943  } else {
944  /*
945  * In TDS 5 we get the column information as a result
946  * set from the "insert bulk" command. We need to get
947  * information about default values from it.
948  */
949  rc = tds_bcp_read_column_defaults(tds, bcpinfo);
950  }
951  if (TDS_FAILED(rc))
952  return rc;
953 
954  tds->out_flag = TDS_BULK;
956  return TDS_FAIL;
957 
958  if (IS_TDS7_PLUS(tds->conn))
959  tds7_bcp_send_colmetadata(tds, bcpinfo);
960 
961  return TDS_SUCCESS;
962 }
963 
964 /**
965  * Free row data allocated in the result set.
966  */
967 static void
969 {
970  result->row_size = 0;
971  TDS_ZERO_FREE(result->current_row);
972 }
973 
974 /**
975  * Start bulk copy to server
976  * \tds
977  * \param bcpinfo BCP information already prepared
978  */
979 TDSRET
981 {
982  TDSCOLUMN *bcpcol;
983  int i;
984  int fixed_col_len_tot = 0;
985  int variable_col_len_tot = 0;
986  int column_bcp_data_size = 0;
987  int bcp_record_size = 0;
988  TDSRET rc;
989  TDS_INT var_cols;
990 
991  tdsdump_log(TDS_DBG_FUNC, "tds_bcp_start_copy_in(%p, %p)\n", tds, bcpinfo);
992 
993  rc = tds_bcp_start_insert_stmt(tds, bcpinfo);
994  if (TDS_FAILED(rc))
995  return rc;
996 
997  rc = tds_bcp_start(tds, bcpinfo);
998  if (TDS_FAILED(rc)) {
999  /* TODO, in CTLib was _ctclient_msg(blkdesc->con, "blk_rowxfer", 2, 5, 1, 140, ""); */
1000  return rc;
1001  }
1002 
1003  /*
1004  * Work out the number of "variable" columns. These are either nullable or of
1005  * varying length type e.g. varchar.
1006  */
1007  var_cols = 0;
1008 
1009  if (IS_TDS50(tds->conn)) {
1010  for (i = 0; i < bcpinfo->bindinfo->num_cols; i++) {
1011 
1012  bcpcol = bcpinfo->bindinfo->columns[i];
1013 
1014  /*
1015  * work out storage required for this datatype
1016  * blobs always require 16, numerics vary, the
1017  * rest can be taken from the server
1018  */
1019 
1020  if (is_blob_type(bcpcol->on_server.column_type))
1021  column_bcp_data_size = 16;
1022  else if (is_numeric_type(bcpcol->on_server.column_type))
1023  column_bcp_data_size = tds_numeric_bytes_per_prec[bcpcol->column_prec];
1024  else
1025  column_bcp_data_size = bcpcol->column_size;
1026 
1027  /*
1028  * now add that size into either fixed or variable
1029  * column totals...
1030  */
1031 
1032  if (is_nullable_type(bcpcol->on_server.column_type) || bcpcol->column_nullable) {
1033  var_cols++;
1034  variable_col_len_tot += column_bcp_data_size;
1035  }
1036  else {
1037  fixed_col_len_tot += column_bcp_data_size;
1038  }
1039  }
1040 
1041  /* this formula taken from sybase manual... */
1042 
1043  bcp_record_size = 4 +
1044  fixed_col_len_tot +
1045  variable_col_len_tot +
1046  ( (int)(variable_col_len_tot / 256 ) + 1 ) +
1047  (var_cols + 1) +
1048  2;
1049 
1050  tdsdump_log(TDS_DBG_FUNC, "current_record_size = %d\n", bcpinfo->bindinfo->row_size);
1051  tdsdump_log(TDS_DBG_FUNC, "bcp_record_size = %d\n", bcp_record_size);
1052 
1053  if (bcp_record_size > bcpinfo->bindinfo->row_size) {
1054  if (!TDS_RESIZE(bcpinfo->bindinfo->current_row, bcp_record_size)) {
1055  tdsdump_log(TDS_DBG_FUNC, "could not realloc current_row\n");
1056  return TDS_FAIL;
1057  }
1058  bcpinfo->bindinfo->row_free = tds_bcp_row_free;
1059  bcpinfo->bindinfo->row_size = bcp_record_size;
1060  }
1061  }
1062 
1063  return TDS_SUCCESS;
1064 }
1065 
1066 /** input stream to read a file */
1067 typedef struct tds_file_stream {
1068  /** common fields, must be the first field */
1070  /** file to read from */
1071  FILE *f;
1072 
1073  /** terminator */
1074  const char *terminator;
1075  /** terminator length in bytes */
1076  size_t term_len;
1077 
1078  /** buffer for store bytes readed that could be the terminator */
1079  char *left;
1080  size_t left_pos;
1082 
1083 /** \cond HIDDEN_SYMBOLS */
1084 #if defined(_WIN32) && defined(HAVE__LOCK_FILE) && defined(HAVE__UNLOCK_FILE)
1085 #define TDS_HAVE_STDIO_LOCKED 1
1086 #define flockfile(s) _lock_file(s)
1087 #define funlockfile(s) _unlock_file(s)
1088 #define getc_unlocked(s) _getc_nolock(s)
1089 #define feof_unlocked(s) _feof_nolock(s)
1090 #endif
1091 
1092 #ifndef TDS_HAVE_STDIO_LOCKED
1093 #undef getc_unlocked
1094 #undef feof_unlocked
1095 #undef flockfile
1096 #undef funlockfile
1097 #define getc_unlocked(s) getc(s)
1098 #define feof_unlocked(s) feof(s)
1099 #define flockfile(s) do { } while(0)
1100 #define funlockfile(s) do { } while(0)
1101 #endif
1102 /** \endcond */
1103 
1104 /**
1105  * Reads a chunk of data from file stream checking for terminator
1106  * \param stream file stream
1107  * \param ptr buffer where to read data
1108  * \param len length of buffer
1109  */
1110 static int
1111 tds_file_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
1112 {
1113  TDSFILESTREAM *s = (TDSFILESTREAM *) stream;
1114  int c;
1115  char *p = (char *) ptr;
1116 
1117  while (len) {
1118  if (memcmp(s->left, s->terminator - s->left_pos, s->term_len) == 0)
1119  return p - (char *) ptr;
1120 
1121  c = getc_unlocked(s->f);
1122  if (c == EOF)
1123  return -1;
1124 
1125  *p++ = s->left[s->left_pos];
1126  --len;
1127 
1128  s->left[s->left_pos++] = c;
1129  s->left_pos %= s->term_len;
1130  }
1131  return p - (char *) ptr;
1132 }
1133 
1134 /**
1135  * Read a data file, passing the data through iconv().
1136  * \retval TDS_SUCCESS success
1137  * \retval TDS_FAIL error reading the column
1138  * \retval TDS_NO_MORE_RESULTS end of file detected
1139  */
1140 TDSRET
1141 tds_bcp_fread(TDSSOCKET * tds, TDSICONV * char_conv, FILE * stream, const char *terminator, size_t term_len, char **outbuf, size_t * outbytes)
1142 {
1143  TDSRET res;
1144  TDSFILESTREAM r;
1145  TDSDYNAMICSTREAM w;
1146  size_t readed;
1147 
1148  /* prepare streams */
1149  r.stream.read = tds_file_stream_read;
1150  r.f = stream;
1151  r.term_len = term_len;
1152  r.left = tds_new0(char, term_len*3);
1153  r.left_pos = 0;
1154  if (!r.left) return TDS_FAIL;
1155 
1156  /* copy terminator twice, let terminator points to second copy */
1157  memcpy(r.left + term_len, terminator, term_len);
1158  memcpy(r.left + term_len*2u, terminator, term_len);
1159  r.terminator = r.left + term_len*2u;
1160 
1161  /* read initial buffer to test with terminator */
1162  readed = fread(r.left, 1, term_len, stream);
1163  if (readed != term_len) {
1164  free(r.left);
1165  if (readed == 0 && feof(stream))
1166  return TDS_NO_MORE_RESULTS;
1167  return TDS_FAIL;
1168  }
1169 
1170  res = tds_dynamic_stream_init(&w, (void**) outbuf, 0);
1171  if (TDS_FAILED(res)) {
1172  free(r.left);
1173  return res;
1174  }
1175 
1176  /* convert/copy from input stream to output one */
1177  flockfile(stream);
1178  if (char_conv == NULL)
1179  res = tds_copy_stream(tds, &r.stream, &w.stream);
1180  else
1181  res = tds_convert_stream(tds, char_conv, to_server, &r.stream, &w.stream);
1182  funlockfile(stream);
1183  free(r.left);
1184 
1185  if (TDS_FAILED(res))
1186  return res;
1187 
1188  *outbytes = w.size;
1189 
1190  /* terminate buffer */
1191  if (!w.stream.buf_len)
1192  return TDS_FAIL;
1193 
1194  ((char *) w.stream.buffer)[0] = 0;
1195  w.stream.write(&w.stream, 1);
1196 
1197  return res;
1198 }
1199 
1200 /**
1201  * Start writing writetext request.
1202  * This request start a bulk session.
1203  * \tds
1204  * \param objname table name
1205  * \param textptr TEXTPTR (see sql documentation)
1206  * \param timestamp data timestamp
1207  * \param with_log is log is enabled during insert
1208  * \param size bytes to be inserted
1209  */
1210 TDSRET
1211 tds_writetext_start(TDSSOCKET *tds, const char *objname, const char *textptr, const char *timestamp, int with_log, TDS_UINT size)
1212 {
1213  TDSRET rc;
1214 
1215  /* TODO mssql does not like timestamp */
1216  rc = tds_submit_queryf(tds,
1217  "writetext bulk %s 0x%s timestamp = 0x%s%s",
1218  objname, textptr, timestamp, with_log ? " with log" : "");
1219  if (TDS_FAILED(rc))
1220  return rc;
1221 
1222  /* set we want to switch to bulk state */
1223  tds->bulk_query = 1;
1224 
1225  /* read the end token */
1227  if (TDS_FAILED(rc))
1228  return rc;
1229 
1230  tds->out_flag = TDS_BULK;
1232  return TDS_FAIL;
1233 
1234  tds_put_int(tds, size);
1235 
1237  return TDS_SUCCESS;
1238 }
1239 
1240 /**
1241  * Send some data in the writetext request started by tds_writetext_start.
1242  * You should write in total (with multiple calls to this function) all
1243  * bytes declared calling tds_writetext_start.
1244  * \tds
1245  * \param text data to write
1246  * \param size data size in bytes
1247  */
1248 TDSRET
1250 {
1252  return TDS_FAIL;
1253 
1254  /* TODO check size left */
1255  tds_put_n(tds, text, size);
1256 
1258  return TDS_SUCCESS;
1259 }
1260 
1261 /**
1262  * Finish sending writetext data.
1263  * \tds
1264  */
1265 TDSRET
1267 {
1269  return TDS_FAIL;
1270 
1273  return TDS_SUCCESS;
1274 }
1275 
1276 
1277 static int tds_bcp_is_bound(TDSBCPINFO *bcpinfo, TDSCOLUMN *colinfo)
1278 {
1279  return (bcpinfo && colinfo &&
1280  /* Don't interfere with dblib bulk insertion from files. */
1281  (bcpinfo->xfer_init == 0
1282  || colinfo->column_varaddr != NULL
1283  || (colinfo->column_lenbind != NULL
1284  && (*colinfo->column_lenbind != 0
1285  || (colinfo->column_nullbind != NULL
1286  /* null-value for blk_textxfer ... */
1287  /* && *colinfo->column_nullbind == -1 */)))));
1288 }
1289 
1290 
1292 {
1293  TDS_INT res_type;
1294  TDS_INT done_flags;
1295  int rc;
1296  int ret = TDS_SUCCESS;
1297 
1298  int num_defs = 0;
1299  int colnum, res_colnum;
1300  TDS_TINYINT byte_val;
1301  unsigned char* src;
1302  TDSRESULTINFO* resinfo;
1303  TDSRESULTINFO* bindinfo = bcpinfo->bindinfo;
1304  TDSCOLUMN* curcol;
1305  TDSCOLUMN* bindcol;
1306 
1307  /* Read metainformation about all columns */
1308  for (colnum = 0; ; ++colnum) {
1309  rc = tds_process_tokens(tds, &res_type, &done_flags,
1311  if (TDS_FAILED(rc)) {
1312  ret = TDS_FAIL;
1313  break;
1314  }
1315 
1316  if (res_type == TDS_DONE_RESULT) {
1317  if ((done_flags & TDS_DONE_ERROR) != 0)
1318  ret = TDS_FAIL;
1319  /*
1320  * First recordset with columnsmetainfo is done
1321  * - go to the next
1322  */
1323  break;
1324  }
1325 
1326  if (res_type != TDS_ROW_RESULT)
1327  continue;
1328 
1329  resinfo = tds->current_results;
1330 
1331 #if ENABLE_EXTRA_CHECKS
1332  /*
1333  * Most probably the format of result is fixed, but
1334  * couple of asserts are still needed to be sure
1335  */
1336  assert(resinfo->num_cols == 12);
1337  assert(colnum < bindinfo->num_cols);
1338 #endif
1339 
1340  bindcol = bindinfo->columns[colnum];
1341 
1342  /* Read "default" flag of the column */
1343  curcol = resinfo->columns[9];
1344 
1345 #if ENABLE_EXTRA_CHECKS
1346  assert(curcol->column_type == SYBINT1);
1347 #endif
1348 
1349  src = curcol->column_data;
1350  byte_val = *((TDS_TINYINT*) src);
1351 
1352  if (byte_val == 1) {
1353  ++num_defs;
1354  bindcol->column_hasdefault = 1;
1355  }
1356  }
1357 
1358  if (TDS_SUCCEED(ret) && num_defs > 0
1359  && TDS_SUCCEED(tds_process_tokens(tds, &res_type, &done_flags,
1360  TDS_RETURN_ROW)))
1361  {
1362  resinfo = tds->current_results;
1363 
1364 #if ENABLE_EXTRA_CHECKS
1365  assert(resinfo->num_cols == num_defs);
1366 #endif
1367 
1368  /* Now read all default values */
1369  res_colnum = 0;
1370  for (colnum = 0; colnum < bindinfo->num_cols;
1371  ++colnum) {
1372  bindcol = bindinfo->columns[colnum];
1373  if ( !bindcol->column_hasdefault ) {
1374  continue;
1375  }
1376 
1377  curcol = resinfo->columns[res_colnum];
1378  ++res_colnum;
1379  src = curcol->column_data;
1380  if (is_blob_type(curcol->column_type)) {
1381  src = (unsigned char*)
1382  ((TDSBLOB*)src)->textvalue;
1384  (bindcol->bcp_column_data);
1385  bindcol->bcp_column_data
1387  (curcol->column_cur_size);
1388  }
1389  /*
1390  * Maybe it's better to ignore all requests
1391  * of defaults after first request
1392  */
1393  if (bindcol->column_default) {
1394  bindcol->column_default
1395  = realloc(bindcol->column_default,
1396  curcol->column_cur_size);
1397  }
1398  else {
1399  bindcol->column_default
1400  = malloc(curcol->column_cur_size);
1401  }
1402  if (bindcol->column_default == NULL) {
1403  ret = TDS_FAIL;
1404  break;
1405  }
1406 
1407  bindcol->column_def_size
1408  = curcol->column_cur_size;
1409  memcpy(bindcol->column_default, src,
1410  bindcol->column_def_size);
1411  }
1412  }
1413 
1414  if (TDS_SUCCEED(ret)) {
1416  }
1417 
1418  return ret;
1419 }
static void cleanup(void)
Definition: ct_dynamic.c:30
#define strcat(s, k)
#define TDS_PUT_UA2(ptr, val)
Definition: bytes.h:146
static DLIST_TYPE *DLIST_NAME() first(DLIST_LIST_TYPE *list)
Definition: dlist.tmpl.h:46
@ to_server
Definition: iconv.h:70
@ TDS_BULK
Definition: proto.h:338
#define TDS7_RESULT_TOKEN
Definition: proto.h:83
#define TDS_ROW_TOKEN
Definition: proto.h:100
#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
#define asprintf
Definition: replacements.h:54
int offset
Definition: replacements.h:160
#define SYBVARCHAR
Definition: sybdb.h:162
#define SYBINT1
Definition: sybdb.h:166
#define SYBCHAR
Definition: sybdb.h:160
#define SYBBIT
Definition: sybdb.h:178
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:665
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:580
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:1111
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:814
TDSRET tds_bcp_start_copy_in(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
Start bulk copy to server \tds.
Definition: bulk.c:980
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:1211
static void tds_bcp_row_free(TDSRESULTINFO *result, unsigned char *row)
Free row data allocated in the result set.
Definition: bulk.c:968
TDSRET tds_bcp_start(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
Start sending BCP data to server.
Definition: bulk.c:928
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:1141
static int tds_bcp_is_bound(TDSBCPINFO *bcpinfo, TDSCOLUMN *colinfo)
Definition: bulk.c:1277
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:1249
static int tds_bcp_read_column_defaults(TDSSOCKET *tds, TDSBCPINFO *bcpinfo)
Definition: bulk.c:1291
TDSRET tds_writetext_end(TDSSOCKET *tds)
Finish sending writetext data.
Definition: bulk.c:1266
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:898
struct tds_pbcb TDSPBCB
Holds clause buffer.
static TDSSOCKET * tds
Definition: collations.c:37
#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 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
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_)
#define assert(x)
Definition: srv_diag.hpp:58
#define row(bind, expected)
Definition: string_bind.c:73
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
const char * hint
Definition: tds.h:1661
TDSRESULTINFO * bindinfo
Definition: tds.h:1669
TDS_INT blob_cols
Definition: tds.h:1672
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
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_info * put_info
Send metadata column information to server.
Definition: tds.h:724
tds_func_put_data * put_data
Send column data to server.
Definition: tds.h:734
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_INT column_textpos
Definition: tds.h:821
TDS_INT column_size
maximun size of data.
Definition: tds.h:766
unsigned int column_hasdefault
Definition: tds.h:802
TDS_SMALLINT * column_nullbind
Definition: tds.h:818
DSTR column_name
Definition: tds.h:787
unsigned int column_timestamp
Definition: tds.h:801
TDS_UINT column_bindlen
Definition: tds.h:817
BCPCOLDATA * bcp_column_data
Definition: tds.h:825
TDS_UCHAR column_collation[5]
Definition: tds.h:803
const TDSCOLUMNFUNCS * funcs
Definition: tds.h:762
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
TDSICONV * char_conv
refers to previously allocated iconv information
Definition: tds.h:784
unsigned int column_nullable
Definition: tds.h:795
TDS_INT column_def_size
Definition: tds.h:790
TDS_TINYINT column_scale
scale for decimal/numeric
Definition: tds.h:776
unsigned char * column_data
Definition: tds.h:793
TDS_INT * column_lenbind
Definition: tds.h:820
struct tds_column::@124 on_server
TDS_CHAR * column_varaddr
Definition: tds.h:819
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:1067
size_t left_pos
Definition: bulk.c:1080
const char * terminator
terminator
Definition: bulk.c:1074
FILE * f
file to read from
Definition: bulk.c:1071
size_t term_len
terminator length in bytes
Definition: bulk.c:1076
TDSINSTREAM stream
common fields, must be the first field
Definition: bulk.c:1069
char * left
buffer for store bytes readed that could be the terminator
Definition: bulk.c:1079
define a stream of data used for input
Definition: stream.h:30
size_t buf_len
Definition: stream.h:51
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
char * buffer
write buffer.
Definition: stream.h:50
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
TDSCOLUMN ** columns
Definition: tds.h:844
TDS_USMALLINT num_cols
Definition: tds.h:845
void(* row_free)(struct tds_result_info *result, unsigned char *row)
Definition: tds.h:850
TDS_INT row_size
Definition: tds.h:851
Information for a server connection.
Definition: tds.h:1211
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
TDSRESULTINFO * current_results
Current query information.
Definition: tds.h:1263
TDSRESULTINFO * res_info
Definition: tds.h:1264
unsigned char array[33]
Definition: proto.h:29
unsigned char precision
Definition: proto.h:27
else result
Definition: token2.c:20
uchar outbuf[(1000000+1000000)]
Definition: unzcrash.c:41
void free(voidpf ptr)
voidp malloc(uInt size)
Modified on Mon Jun 24 05:24:13 2024 by modify_doxy.py rev. 669887