LCOV - code coverage report
Current view: top level - src/tds - bulk.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 587 653 89.9 %
Date: 2026-03-24 22:22:09 Functions: 25 27 92.6 %

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

Generated by: LCOV version 1.13