LCOV - code coverage report
Current view: top level - src/tds - bulk.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 471 529 89.0 %
Date: 2025-01-18 12:13:41 Functions: 20 20 100.0 %

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

Generated by: LCOV version 1.13