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