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