LCOV - code coverage report
Current view: top level - src/tds - bulk.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 619 680 91.0 %
Date: 2026-05-31 10:59:18 Functions: 31 31 100.0 %

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

Generated by: LCOV version 1.13