Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 1998-2004, 2005, 2010 Brian Bruns, Bill Thompson
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 : #include <config.h>
21 :
22 : #include <stdarg.h>
23 : #include <stdio.h>
24 :
25 : #if HAVE_STDLIB_H
26 : #include <stdlib.h>
27 : #endif /* HAVE_STDLIB_H */
28 :
29 : #if HAVE_STRING_H
30 : #include <string.h>
31 : #endif /* HAVE_STRING_H */
32 :
33 : #include <freetds/utils.h>
34 : #include <freetds/replacements.h>
35 :
36 : #include "bkpublic.h"
37 :
38 : #include "ctpublic.h"
39 : #include "ctlib.h"
40 :
41 : static void _blk_null_error(TDSBCPINFO *bcpinfo, int index, int offset);
42 : static TDSRET _blk_get_col_data(TDSBCPINFO * bulk, TDSCOLUMN * bcpcol, int index, int offset);
43 : static CS_RETCODE _blk_rowxfer_in(CS_BLKDESC * blkdesc, CS_INT rows_to_xfer, CS_INT * rows_xferred);
44 : static CS_RETCODE _blk_rowxfer_out(CS_BLKDESC * blkdesc, CS_INT rows_to_xfer, CS_INT * rows_xferred);
45 : static void _blk_clean_desc(CS_BLKDESC * blkdesc);
46 :
47 : #define CONN(bulk) ((CS_CONNECTION *) (bulk)->bcpinfo.parent)
48 :
49 : TDS_COMPILE_CHECK(same_size, sizeof(CS_BLKDESC) == sizeof(TDSBCPINFO));
50 : TDS_COMPILE_CHECK(nested_type, TDS_OFFSET(CS_BLKDESC, bcpinfo) == 0);
51 :
52 : CS_RETCODE
53 276 : blk_alloc(CS_CONNECTION * connection, CS_INT version, CS_BLKDESC ** blk_pointer)
54 : {
55 : CS_BLKDESC *blkdesc;
56 :
57 276 : tdsdump_log(TDS_DBG_FUNC, "blk_alloc(%p, %d, %p)\n", connection, version, blk_pointer);
58 :
59 276 : if (!connection || !connection->tds_socket)
60 : return CS_FAIL;
61 :
62 276 : if (connection->tds_socket->conn->tds_version < 0x500)
63 : return CS_FAIL;
64 :
65 276 : blkdesc = (CS_BLKDESC *) tds_alloc_bcpinfo();
66 276 : if (!blkdesc)
67 : return CS_FAIL;
68 :
69 : /* so we know who we belong to */
70 276 : blkdesc->bcpinfo.parent = connection;
71 :
72 276 : *blk_pointer = blkdesc;
73 276 : return CS_SUCCEED;
74 : }
75 :
76 :
77 : CS_RETCODE
78 1768 : blk_bind(CS_BLKDESC * blkdesc, CS_INT item, CS_DATAFMT * datafmt_arg, CS_VOID * buffer, CS_INT * datalen, CS_SMALLINT * indicator)
79 : {
80 : TDSCOLUMN *colinfo;
81 : CS_CONNECTION *con;
82 : CS_INT bind_count;
83 : const CS_DATAFMT_COMMON * datafmt;
84 : int i;
85 :
86 1768 : tdsdump_log(TDS_DBG_FUNC, "blk_bind(%p, %d, %p, %p, %p, %p)\n", blkdesc, item, datafmt_arg, buffer, datalen, indicator);
87 :
88 1768 : if (!blkdesc)
89 : return CS_FAIL;
90 :
91 1768 : con = CONN(blkdesc);
92 :
93 1768 : datafmt = _ct_datafmt_common(con->ctx, datafmt_arg);
94 :
95 1768 : if (item == CS_UNUSED) {
96 : /* clear all bindings */
97 0 : if (datafmt == NULL && buffer == NULL && datalen == NULL && indicator == NULL ) {
98 0 : blkdesc->bcpinfo.bind_count = CS_UNUSED;
99 0 : for (i = 0; i < blkdesc->bcpinfo.bindinfo->num_cols; i++ ) {
100 0 : colinfo = blkdesc->bcpinfo.bindinfo->columns[i];
101 0 : colinfo->column_varaddr = NULL;
102 0 : colinfo->column_bindtype = 0;
103 0 : colinfo->column_bindfmt = 0;
104 0 : colinfo->column_bindlen = 0;
105 0 : colinfo->column_nullbind = NULL;
106 0 : colinfo->column_lenbind = NULL;
107 : }
108 : }
109 : return CS_SUCCEED;
110 : }
111 :
112 : /* check item value */
113 :
114 1768 : if (item < 1 || item > blkdesc->bcpinfo.bindinfo->num_cols) {
115 0 : _ctclient_msg(NULL, con, "blk_bind", 2, 5, 1, 141, "%s, %d", "colnum", item);
116 0 : return CS_FAIL;
117 : }
118 :
119 : /* clear bindings for this column */
120 :
121 1768 : if (datafmt == NULL && buffer == NULL && datalen == NULL && indicator == NULL ) {
122 :
123 0 : colinfo = blkdesc->bcpinfo.bindinfo->columns[item - 1];
124 0 : colinfo->column_varaddr = NULL;
125 0 : colinfo->column_bindtype = 0;
126 0 : colinfo->column_bindfmt = 0;
127 0 : colinfo->column_bindlen = 0;
128 0 : colinfo->column_nullbind = NULL;
129 0 : colinfo->column_lenbind = NULL;
130 :
131 0 : return CS_SUCCEED;
132 : }
133 :
134 1768 : if (datafmt == NULL)
135 : return CS_FAIL;
136 :
137 : /*
138 : * check whether the request is for array binding and ensure that user
139 : * supplies the same datafmt->count to the subsequent ct_bind calls
140 : */
141 :
142 1768 : bind_count = (datafmt->count == 0) ? 1 : datafmt->count;
143 :
144 : /* first bind for this result set */
145 :
146 1768 : if (blkdesc->bcpinfo.bind_count == CS_UNUSED) {
147 426 : blkdesc->bcpinfo.bind_count = bind_count;
148 : } else {
149 : /* all subsequent binds for this result set - the bind counts must be the same */
150 1342 : if (blkdesc->bcpinfo.bind_count != bind_count) {
151 0 : _ctclient_msg(NULL, con, "blk_bind", 1, 1, 1, 137, "%d, %d", bind_count, blkdesc->bcpinfo.bind_count);
152 0 : return CS_FAIL;
153 : }
154 : }
155 :
156 : /* bind the column_varaddr to the address of the buffer */
157 :
158 1768 : colinfo = blkdesc->bcpinfo.bindinfo->columns[item - 1];
159 :
160 1768 : colinfo->column_varaddr = (char *) buffer;
161 1768 : colinfo->column_bindtype = datafmt->datatype;
162 1768 : colinfo->column_bindfmt = datafmt->format;
163 1768 : colinfo->column_bindlen = datafmt->maxlength;
164 1768 : if (indicator) {
165 1320 : colinfo->column_nullbind = indicator;
166 : }
167 1768 : if (datalen) {
168 1768 : colinfo->column_lenbind = datalen;
169 : }
170 : return CS_SUCCEED;
171 : }
172 :
173 : CS_RETCODE
174 0 : blk_colval(SRV_PROC * srvproc, CS_BLKDESC * blkdescp, CS_BLK_ROW * rowp, CS_INT colnum, CS_VOID * valuep, CS_INT valuelen,
175 : CS_INT * outlenp)
176 : {
177 :
178 0 : tdsdump_log(TDS_DBG_FUNC, "blk_colval(%p, %p, %p, %d, %p, %d, %p)\n",
179 : srvproc, blkdescp, rowp, colnum, valuep, valuelen, outlenp);
180 :
181 0 : tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED blk_colval()\n");
182 0 : return CS_FAIL;
183 : }
184 :
185 : CS_RETCODE
186 0 : blk_default(CS_BLKDESC * blkdesc, CS_INT colnum, CS_VOID * buffer, CS_INT buflen, CS_INT * outlen)
187 : {
188 :
189 0 : tdsdump_log(TDS_DBG_FUNC, "blk_default(%p, %d, %p, %d, %p)\n", blkdesc, colnum, buffer, buflen, outlen);
190 :
191 0 : tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED blk_default()\n");
192 0 : return CS_FAIL;
193 : }
194 :
195 : CS_RETCODE
196 1192 : blk_describe(CS_BLKDESC * blkdesc, CS_INT item, CS_DATAFMT * datafmt_arg)
197 : {
198 : TDSCOLUMN *curcol;
199 : CS_INT status, datatype;
200 : CS_DATAFMT_LARGE *datafmt;
201 : CS_DATAFMT_LARGE datafmt_buf;
202 :
203 1192 : tdsdump_log(TDS_DBG_FUNC, "blk_describe(%p, %d, %p)\n", blkdesc, item, datafmt_arg);
204 :
205 1192 : if (!blkdesc)
206 : return CS_FAIL;
207 :
208 1192 : datafmt = _ct_datafmt_conv_prepare(CONN(blkdesc)->ctx, datafmt_arg, &datafmt_buf);
209 :
210 1192 : if (item < 1 || item > blkdesc->bcpinfo.bindinfo->num_cols) {
211 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_describe", 2, 5, 1, 141, "%s, %d", "colnum", item);
212 0 : return CS_FAIL;
213 : }
214 :
215 1192 : curcol = blkdesc->bcpinfo.bindinfo->columns[item - 1];
216 : /* name is always null terminated */
217 2384 : strlcpy(datafmt->name, tds_dstr_cstr(&curcol->column_name), sizeof(datafmt->name));
218 1192 : datafmt->namelen = (CS_INT) strlen(datafmt->name);
219 : /* need to turn the SYBxxx into a CS_xxx_TYPE */
220 1192 : datatype = _ct_get_client_type(curcol, true);
221 1192 : if (datatype == CS_ILLEGAL_TYPE)
222 : return CS_FAIL;
223 1192 : datafmt->datatype = datatype;
224 1192 : tdsdump_log(TDS_DBG_INFO1, "blk_describe() datafmt->datatype = %d server type %d\n", datatype,
225 : curcol->column_type);
226 : /* FIXME is ok this value for numeric/decimal? */
227 1192 : datafmt->maxlength = curcol->column_size;
228 1192 : datafmt->usertype = curcol->column_usertype;
229 1192 : datafmt->precision = curcol->column_prec;
230 1192 : datafmt->scale = curcol->column_scale;
231 :
232 : /*
233 : * There are other options that can be returned, but these are the
234 : * only two being noted via the TDS layer.
235 : */
236 1192 : status = 0;
237 1192 : if (curcol->column_nullable)
238 656 : status |= CS_CANBENULL;
239 1192 : if (curcol->column_identity)
240 0 : status |= CS_IDENTITY;
241 1192 : datafmt->status = status;
242 :
243 1192 : datafmt->count = 1;
244 1192 : datafmt->locale = NULL;
245 :
246 1192 : _ct_datafmt_conv_back(datafmt_arg, datafmt);
247 1192 : return CS_SUCCEED;
248 : }
249 :
250 : CS_RETCODE
251 508 : blk_done(CS_BLKDESC * blkdesc, CS_INT type, CS_INT * outrow)
252 : {
253 : TDSSOCKET *tds;
254 : int rows_copied;
255 :
256 508 : tdsdump_log(TDS_DBG_FUNC, "blk_done(%p, %d, %p)\n", blkdesc, type, outrow);
257 :
258 508 : tds = CONN(blkdesc)->tds_socket;
259 :
260 : /* text/image data still to be sent with blk_textxfer */
261 508 : if ((type == CS_BLK_BATCH || type == CS_BLK_ALL) && blkdesc->bcpinfo.blob_pending) {
262 16 : _ctclient_msg(NULL, CONN(blkdesc), "blk_done", 1, 1, 1, 1, "%s, %s", "blk_done()", "text data pending");
263 16 : return CS_FAIL;
264 : }
265 :
266 492 : if (blkdesc->bcpinfo.direction == CS_BLK_OUT && type == CS_BLK_BATCH) {
267 10 : _ctclient_msg(NULL, CONN(blkdesc), "blk_done", 1, 1, 1, 16, "");
268 10 : return CS_FAIL;
269 : }
270 :
271 : /* nothing transferred, nothing to complete */
272 482 : if ((type == CS_BLK_BATCH || type == CS_BLK_ALL) && !blkdesc->bcpinfo.xfer_init) {
273 10 : if (outrow)
274 10 : *outrow = 0;
275 10 : if (type == CS_BLK_ALL)
276 : _blk_clean_desc(blkdesc);
277 : return CS_SUCCEED;
278 : }
279 :
280 472 : if (blkdesc->bcpinfo.direction == CS_BLK_OUT && type == CS_BLK_ALL) {
281 : TDSRET rc;
282 : TDS_INT result_type;
283 :
284 : /* rows still to retrieve, client can continue or cancel */
285 20 : if (!blkdesc->bcpinfo.out_done) {
286 10 : _ctclient_msg(NULL, CONN(blkdesc), "blk_done", 1, 1, 1, 163, "");
287 10 : return CS_FAIL;
288 : }
289 :
290 : /* discard the rest of the results */
291 20 : while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS)) == TDS_SUCCESS)
292 10 : continue;
293 10 : if (rc != TDS_NO_MORE_RESULTS) {
294 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_done", 2, 5, 1, 140, "");
295 0 : return CS_FAIL;
296 : }
297 :
298 10 : if (outrow)
299 10 : *outrow = blkdesc->bcpinfo.rows_sent;
300 10 : _blk_clean_desc(blkdesc);
301 10 : return CS_SUCCEED;
302 : }
303 :
304 452 : switch (type) {
305 36 : case CS_BLK_BATCH:
306 36 : if (TDS_FAILED(tds_bcp_done(tds, &rows_copied))) {
307 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_done", 2, 5, 1, 140, "");
308 0 : return CS_FAIL;
309 : }
310 :
311 36 : if (outrow)
312 36 : *outrow = rows_copied;
313 :
314 36 : if (TDS_FAILED(tds_bcp_start(tds, &blkdesc->bcpinfo))) {
315 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_done", 2, 5, 1, 140, "");
316 0 : return CS_FAIL;
317 : }
318 : break;
319 :
320 320 : case CS_BLK_ALL:
321 320 : if (TDS_FAILED(tds_bcp_done(tds, &rows_copied))) {
322 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_done", 2, 5, 1, 140, "");
323 0 : return CS_FAIL;
324 : }
325 :
326 320 : if (outrow)
327 320 : *outrow = rows_copied;
328 :
329 : /* free allocated storage in blkdesc & initialise flags, etc. */
330 : _blk_clean_desc(blkdesc);
331 : break;
332 :
333 96 : case CS_BLK_CANCEL:
334 96 : if (blkdesc->bcpinfo.xfer_init) {
335 : TDSRET rc;
336 :
337 76 : if (blkdesc->bcpinfo.direction == CS_BLK_IN) {
338 66 : rc = tds_bcp_cancel(tds);
339 : } else {
340 10 : rc = tds_send_cancel(tds);
341 10 : if (TDS_SUCCEED(rc))
342 10 : rc = tds_process_cancel(tds);
343 : }
344 76 : if (TDS_FAILED(rc)) {
345 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_done", 2, 5, 1, 140, "");
346 0 : return CS_FAIL;
347 : }
348 : }
349 :
350 96 : if (outrow)
351 80 : *outrow = 0;
352 :
353 : /* free allocated storage in blkdesc & initialise flags, etc. */
354 : _blk_clean_desc(blkdesc);
355 : break;
356 :
357 0 : default:
358 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_done", 1, 1, 1, 5, "%d, type", type);
359 0 : return CS_FAIL;
360 : }
361 :
362 : return CS_SUCCEED;
363 : }
364 :
365 : /**
366 : * Free allocated storage in blkdesc and reset flags to allow a new blk_init.
367 : */
368 : static void
369 : _blk_clean_desc(CS_BLKDESC *blkdesc)
370 : {
371 436 : tds_deinit_bcpinfo(&blkdesc->bcpinfo);
372 :
373 436 : blkdesc->bcpinfo.direction = 0;
374 436 : blkdesc->bcpinfo.bind_count = CS_UNUSED;
375 436 : blkdesc->bcpinfo.xfer_init = false;
376 436 : blkdesc->bcpinfo.out_done = false;
377 : }
378 :
379 : CS_RETCODE
380 276 : blk_drop(CS_BLKDESC *blkdesc)
381 : {
382 276 : tdsdump_log(TDS_DBG_FUNC, "blk_drop(%p)\n", blkdesc);
383 :
384 : /* this is possible as CS_BLKDESC contains just bcpinfo field */
385 276 : tds_free_bcpinfo(&blkdesc->bcpinfo);
386 :
387 276 : return CS_SUCCEED;
388 : }
389 :
390 : CS_RETCODE
391 0 : blk_getrow(SRV_PROC * srvproc, CS_BLKDESC * blkdescp, CS_BLK_ROW * rowp)
392 : {
393 0 : tdsdump_log(TDS_DBG_FUNC, "blk_getrow(%p, %p, %p)\n", srvproc, blkdescp, rowp);
394 :
395 0 : tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED blk_getrow()\n");
396 0 : return CS_FAIL;
397 : }
398 :
399 : CS_RETCODE
400 0 : blk_gettext(SRV_PROC * srvproc, CS_BLKDESC * blkdescp, CS_BLK_ROW * rowp, CS_INT bufsize, CS_INT * outlenp)
401 : {
402 :
403 0 : tdsdump_log(TDS_DBG_FUNC, "blk_gettext(%p, %p, %p, %d, %p)\n", srvproc, blkdescp, rowp, bufsize, outlenp);
404 :
405 0 : tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED blk_gettext()\n");
406 0 : return CS_FAIL;
407 : }
408 :
409 : CS_RETCODE
410 456 : blk_init(CS_BLKDESC * blkdesc, CS_INT direction, CS_CHAR * tablename, CS_INT tnamelen)
411 : {
412 456 : tdsdump_log(TDS_DBG_FUNC, "blk_init(%p, %d, %p, %d)\n", blkdesc, direction, tablename, tnamelen);
413 :
414 456 : if (!blkdesc) {
415 : return CS_FAIL;
416 : }
417 :
418 456 : if (direction != CS_BLK_IN && direction != CS_BLK_OUT ) {
419 10 : _ctclient_msg(NULL, CONN(blkdesc), "blk_init", 1, 1, 1, 15, "");
420 10 : return CS_FAIL;
421 : }
422 :
423 446 : if (!tablename) {
424 10 : _ctclient_msg(NULL, CONN(blkdesc), "blk_init", 1, 1, 1, 6, "tblname");
425 10 : return CS_FAIL;
426 : }
427 436 : tnamelen = _ct_get_string_length(tablename, tnamelen);
428 436 : if (tnamelen < 0) {
429 10 : _ctclient_msg(NULL, CONN(blkdesc), "blk_init", 1, 1, 1, 4, "tblnamelen, %d", tnamelen);
430 10 : return CS_FAIL;
431 : }
432 :
433 : /* free allocated storage in blkdesc & initialise flags, etc. */
434 426 : tds_deinit_bcpinfo(&blkdesc->bcpinfo);
435 :
436 : /* string can be no-nul terminated so copy with memcpy */
437 426 : if (!tds_dstr_copyn(&blkdesc->bcpinfo.tablename, tablename, tnamelen)) {
438 : return CS_FAIL;
439 : }
440 :
441 426 : blkdesc->bcpinfo.direction = direction;
442 426 : blkdesc->bcpinfo.bind_count = CS_UNUSED;
443 426 : blkdesc->bcpinfo.xfer_init = false;
444 426 : blkdesc->bcpinfo.rows_sent = 0;
445 426 : blkdesc->bcpinfo.out_done = false;
446 :
447 426 : if (TDS_FAILED(tds_bcp_init(CONN(blkdesc)->tds_socket, &blkdesc->bcpinfo))) {
448 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_init", 2, 5, 1, 140, "");
449 0 : return CS_FAIL;
450 : }
451 426 : blkdesc->bcpinfo.bind_count = CS_UNUSED;
452 :
453 426 : return CS_SUCCEED;
454 : }
455 :
456 : CS_RETCODE
457 100 : blk_props(CS_BLKDESC * blkdesc, CS_INT action, CS_INT property, CS_VOID * buffer, CS_INT buflen, CS_INT * outlen)
458 : {
459 : int retval, intval;
460 :
461 100 : tdsdump_log(TDS_DBG_FUNC, "blk_props(%p, %d, %d, %p, %d, %p)\n", blkdesc, action, property, buffer, buflen, outlen);
462 :
463 100 : switch (property) {
464 0 : case BLK_IDENTITY:
465 0 : switch (action) {
466 0 : case CS_SET:
467 0 : if (buffer) {
468 0 : memcpy(&intval, buffer, sizeof(intval));
469 0 : if (intval == CS_TRUE)
470 0 : blkdesc->bcpinfo.identity_insert_on = true;
471 0 : if (intval == CS_FALSE)
472 0 : blkdesc->bcpinfo.identity_insert_on = false;
473 : }
474 : return CS_SUCCEED;
475 : break;
476 0 : case CS_GET:
477 0 : retval = blkdesc->bcpinfo.identity_insert_on ? CS_TRUE : CS_FALSE ;
478 0 : if (buffer) {
479 0 : memcpy (buffer, &retval, sizeof(retval));
480 0 : if (outlen)
481 0 : *outlen = sizeof(retval);
482 : }
483 : return CS_SUCCEED;
484 : break;
485 0 : default:
486 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_props", 2, 5, 1, 141, "%s, %d", "action", action);
487 : break;
488 : }
489 0 : break;
490 :
491 100 : case BLK_HINTS:
492 100 : return _ct_props_dstr(CONN(blkdesc), &blkdesc->bcpinfo.hint, action, buffer, buflen, outlen);
493 :
494 0 : default:
495 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_props", 2, 5, 1, 141, "%s, %d", "property", property);
496 0 : break;
497 : }
498 : return CS_FAIL;
499 : }
500 :
501 : CS_RETCODE
502 0 : blk_rowalloc(SRV_PROC * srvproc, CS_BLK_ROW ** row)
503 : {
504 0 : tdsdump_log(TDS_DBG_FUNC, "blk_rowalloc(%p, %p)\n", srvproc, row);
505 :
506 0 : tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED blk_rowalloc()\n");
507 0 : return CS_FAIL;
508 : }
509 :
510 : CS_RETCODE
511 0 : blk_rowdrop(SRV_PROC * srvproc, CS_BLK_ROW * row)
512 : {
513 0 : tdsdump_log(TDS_DBG_FUNC, "blk_rowdrop(%p, %p)\n", srvproc, row);
514 :
515 0 : tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED blk_rowdrop()\n");
516 0 : return CS_FAIL;
517 : }
518 :
519 : CS_RETCODE
520 61198 : blk_rowxfer(CS_BLKDESC * blkdesc)
521 : {
522 61198 : tdsdump_log(TDS_DBG_FUNC, "blk_rowxfer(%p)\n", blkdesc);
523 :
524 61198 : return blk_rowxfer_mult(blkdesc, NULL);
525 : }
526 :
527 : CS_RETCODE
528 61358 : blk_rowxfer_mult(CS_BLKDESC * blkdesc, CS_INT * row_count)
529 : {
530 61358 : CS_INT rows_to_xfer = 0;
531 61358 : CS_INT rows_xferred = 0;
532 : CS_RETCODE ret;
533 :
534 61358 : tdsdump_log(TDS_DBG_FUNC, "blk_rowxfer_mult(%p, %p)\n", blkdesc, row_count);
535 :
536 61358 : rows_to_xfer = blkdesc->bcpinfo.bind_count;
537 :
538 61358 : if (blkdesc->bcpinfo.direction == CS_BLK_IN) {
539 : /* a positive row count limits rows to send, zero or negative values send all rows bound */
540 61268 : if (row_count != NULL && *row_count > 0) {
541 40 : if (*row_count > rows_to_xfer) {
542 10 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 1, 1, 1, 33, "");
543 10 : return CS_FAIL;
544 : }
545 : rows_to_xfer = *row_count;
546 : }
547 : /* text/image data still to be sent with blk_textxfer */
548 61258 : if (blkdesc->bcpinfo.blob_pending) {
549 16 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 1, 1, 1, 1, "%s, %s", "blk_rowxfer()",
550 : "text data pending");
551 16 : return CS_FAIL;
552 : }
553 61242 : ret = _blk_rowxfer_in(blkdesc, rows_to_xfer, &rows_xferred);
554 : } else {
555 90 : ret = _blk_rowxfer_out(blkdesc, rows_to_xfer, &rows_xferred);
556 : }
557 61332 : if (row_count)
558 140 : *row_count = rows_xferred;
559 : return ret;
560 :
561 : }
562 :
563 : CS_RETCODE
564 0 : blk_sendrow(CS_BLKDESC * blkdesc, CS_BLK_ROW * row)
565 : {
566 :
567 0 : tdsdump_log(TDS_DBG_FUNC, "blk_sendrow(%p, %p)\n", blkdesc, row);
568 :
569 0 : tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED blk_sendrow()\n");
570 0 : return CS_FAIL;
571 : }
572 :
573 : CS_RETCODE
574 0 : blk_sendtext(CS_BLKDESC * blkdesc, CS_BLK_ROW * row, CS_BYTE * buffer, CS_INT buflen)
575 : {
576 0 : tdsdump_log(TDS_DBG_FUNC, "blk_sendtext(%p, %p, %p, %d)\n", blkdesc, row, buffer, buflen);
577 :
578 0 : tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED blk_sendtext()\n");
579 0 : return CS_FAIL;
580 : }
581 :
582 : CS_RETCODE
583 0 : blk_srvinit(SRV_PROC * srvproc, CS_BLKDESC * blkdescp)
584 : {
585 0 : tdsdump_log(TDS_DBG_FUNC, "blk_srvinit(%p, %p)\n", srvproc, blkdescp);
586 :
587 0 : tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED blk_srvinit()\n");
588 0 : return CS_FAIL;
589 : }
590 :
591 : CS_RETCODE
592 624 : blk_textxfer(CS_BLKDESC *blkdesc, CS_BYTE *buffer, CS_INT buflen, CS_INT *outlen)
593 : {
594 : TDSBCPINFO *bcpinfo;
595 : bool end;
596 :
597 624 : tdsdump_log(TDS_DBG_FUNC, "blk_textxfer(%p, %p, %d, %p)\n", blkdesc, buffer, buflen, outlen);
598 :
599 624 : if (!blkdesc)
600 : return CS_FAIL;
601 :
602 624 : bcpinfo = &blkdesc->bcpinfo;
603 624 : if (bcpinfo->direction != CS_BLK_IN) {
604 0 : tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED blk_textxfer() for copy out\n");
605 : return CS_FAIL;
606 : }
607 :
608 624 : if (!bcpinfo->blob_pending) {
609 32 : if (!bcpinfo->xfer_init)
610 16 : _ctclient_msg(NULL, CONN(blkdesc), "blk_textxfer", 1, 1, 1, 1, "%s, %s", "blk_textxfer()", "no row sent");
611 : else
612 16 : _ctclient_msg(NULL, CONN(blkdesc), "blk_textxfer", 1, 1, 1, 49, "");
613 : return CS_FAIL;
614 : }
615 :
616 592 : if (buflen < 0 || (buflen > 0 && !buffer)) {
617 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_textxfer", 1, 1, 1, 5, "%d, %s", buflen, "buflen");
618 0 : return CS_FAIL;
619 : }
620 :
621 592 : if (buflen > bcpinfo->blob_left) {
622 16 : _ctclient_msg(NULL, CONN(blkdesc), "blk_textxfer", 1, 1, 1, 7, "%d", buflen);
623 16 : return CS_FAIL;
624 : }
625 :
626 576 : end = (buflen == bcpinfo->blob_left);
627 576 : if (TDS_FAILED(tds_bcp_send_blob_data(CONN(blkdesc)->tds_socket, bcpinfo, buffer, buflen))) {
628 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_textxfer", 2, 5, 1, 140, "");
629 0 : return CS_FAIL;
630 : }
631 :
632 576 : return end ? CS_END_DATA : CS_SUCCEED;
633 : }
634 :
635 : static CS_RETCODE
636 90 : _blk_rowxfer_out(CS_BLKDESC * blkdesc, CS_INT rows_to_xfer, CS_INT * rows_xferred)
637 : {
638 : TDSSOCKET *tds;
639 : TDS_INT result_type;
640 : TDSRET ret;
641 : TDS_INT temp_count;
642 :
643 90 : tdsdump_log(TDS_DBG_FUNC, "_blk_rowxfer_out(%p, %d, %p)\n", blkdesc, rows_to_xfer, rows_xferred);
644 :
645 90 : if (!blkdesc || !CONN(blkdesc))
646 : return CS_FAIL;
647 :
648 90 : tds = CONN(blkdesc)->tds_socket;
649 :
650 : /*
651 : * the first time blk_xfer called after blk_init()
652 : * do the query and get to the row data...
653 : */
654 :
655 90 : if (!blkdesc->bcpinfo.xfer_init) {
656 :
657 40 : if (TDS_FAILED(tds_submit_queryf(tds, "select * from %s", tds_dstr_cstr(&blkdesc->bcpinfo.tablename)))) {
658 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 2, 5, 1, 140, "");
659 0 : return CS_FAIL;
660 : }
661 :
662 40 : while ((ret = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS)) == TDS_SUCCESS) {
663 40 : if (result_type == TDS_ROW_RESULT)
664 : break;
665 : }
666 :
667 20 : if (ret != TDS_SUCCESS || result_type != TDS_ROW_RESULT) {
668 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 2, 5, 1, 140, "");
669 0 : return CS_FAIL;
670 : }
671 :
672 20 : blkdesc->bcpinfo.xfer_init = true;
673 : }
674 :
675 90 : if (rows_xferred)
676 90 : *rows_xferred = 0;
677 :
678 90 : if (blkdesc->bcpinfo.out_done)
679 : return CS_END_DATA;
680 :
681 100 : for (temp_count = 0; temp_count < rows_to_xfer; temp_count++) {
682 :
683 110 : ret = tds_process_tokens(tds, &result_type, NULL, TDS_STOPAT_ROWFMT|TDS_STOPAT_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE);
684 :
685 110 : tdsdump_log(TDS_DBG_FUNC, "blk_rowxfer_out() process_row_tokens returned %d\n", ret);
686 :
687 110 : switch (ret) {
688 110 : case TDS_SUCCESS:
689 110 : if (result_type == TDS_ROW_RESULT || result_type == TDS_COMPUTE_RESULT) {
690 100 : if (result_type == TDS_ROW_RESULT) {
691 100 : blkdesc->bcpinfo.rows_sent++;
692 100 : if (_ct_bind_data( CONN(blkdesc)->ctx, tds->current_results, blkdesc->bcpinfo.bindinfo, temp_count))
693 : return CS_ROW_FAIL;
694 100 : if (rows_xferred)
695 100 : *rows_xferred = *rows_xferred + 1;
696 : }
697 : break;
698 : }
699 : case TDS_NO_MORE_RESULTS:
700 10 : blkdesc->bcpinfo.out_done = true;
701 : /* return rows retrieved, CS_END_DATA is returned by next call */
702 10 : return (rows_xferred && *rows_xferred > 0) ? CS_SUCCEED : CS_END_DATA;
703 :
704 0 : default:
705 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 2, 5, 1, 140, "");
706 0 : return CS_FAIL;
707 : break;
708 : }
709 : }
710 :
711 : return CS_SUCCEED;
712 : }
713 :
714 : static CS_RETCODE
715 61242 : _blk_rowxfer_in(CS_BLKDESC * blkdesc, CS_INT rows_to_xfer, CS_INT * rows_xferred)
716 : {
717 : TDSSOCKET *tds;
718 : TDS_INT each_row;
719 :
720 61242 : tdsdump_log(TDS_DBG_FUNC, "_blk_rowxfer_in(%p, %d, %p)\n", blkdesc, rows_to_xfer, rows_xferred);
721 :
722 61242 : if (!blkdesc)
723 : return CS_FAIL;
724 :
725 61242 : tds = CONN(blkdesc)->tds_socket;
726 :
727 : /*
728 : * the first time blk_xfer called after blk_init()
729 : * do the query and get to the row data...
730 : */
731 :
732 61242 : if (!blkdesc->bcpinfo.xfer_init) {
733 :
734 : /*
735 : * first call the start_copy function, which will
736 : * retrieve details of the database table columns
737 : */
738 :
739 386 : if (TDS_FAILED(tds_bcp_start_copy_in(tds, &blkdesc->bcpinfo))) {
740 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 2, 5, 1, 140, "");
741 0 : return CS_FAIL;
742 : }
743 :
744 386 : blkdesc->bcpinfo.xfer_init = true;
745 : }
746 :
747 61130 : for (each_row = 0; each_row < rows_to_xfer; each_row++) {
748 :
749 61332 : if (tds_bcp_send_record(tds, &blkdesc->bcpinfo, _blk_get_col_data, _blk_null_error, each_row) != TDS_SUCCESS) {
750 : /* FIXME */
751 : return CS_FAIL;
752 : }
753 61242 : if (rows_xferred)
754 61242 : *rows_xferred = each_row + 1;
755 :
756 : /* data to be sent with blk_textxfer, stop here */
757 61242 : if (blkdesc->bcpinfo.blob_pending)
758 : break;
759 : }
760 :
761 : return CS_SUCCEED;
762 : }
763 :
764 : static void
765 0 : _blk_null_error(TDSBCPINFO *bcpinfo, int index, int offset)
766 : {
767 0 : CS_BLKDESC *blkdesc = (CS_BLKDESC *) bcpinfo;
768 :
769 0 : tdsdump_log(TDS_DBG_FUNC, "_blk_null_error(%p, %d, %d)\n", bcpinfo, index, offset);
770 :
771 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 2, 7, 1, 142, "%d, %d", index + 1, offset + 1);
772 0 : }
773 :
774 : /** How to handle data too long for a character or binary column */
775 : typedef enum
776 : {
777 : /** truncate data */
778 : BLK_TRUNC_SILENT,
779 : /** truncate data and report a warning */
780 : BLK_TRUNC_WARN,
781 : /** report an error, row is not sent */
782 : BLK_TRUNC_FAIL,
783 : } blk_truncation_mode;
784 :
785 : static bool
786 : _blk_is_binary_source(CS_INT srctype)
787 : {
788 60 : return srctype == CS_BINARY_TYPE || srctype == CS_LONGBINARY_TYPE || srctype == CS_IMAGE_TYPE;
789 : }
790 :
791 : /**
792 : * Tell how to handle data too long for a character or binary column.
793 : * Follows Sybase/SAP library, which depends on source type.
794 : */
795 : static blk_truncation_mode
796 170 : _blk_truncation_mode(CS_INT srctype, bool binary_dest)
797 : {
798 170 : switch (srctype) {
799 60 : case CS_CHAR_TYPE:
800 : case CS_LONGCHAR_TYPE:
801 : case CS_TEXT_TYPE:
802 : case CS_UNICHAR_TYPE:
803 : case CS_UNITEXT_TYPE:
804 60 : return binary_dest ? BLK_TRUNC_WARN : BLK_TRUNC_SILENT;
805 20 : case CS_VARCHAR_TYPE:
806 20 : return binary_dest ? BLK_TRUNC_FAIL : BLK_TRUNC_WARN;
807 20 : case CS_BINARY_TYPE:
808 : case CS_IMAGE_TYPE:
809 20 : return binary_dest ? BLK_TRUNC_SILENT : BLK_TRUNC_WARN;
810 : case CS_LONGBINARY_TYPE:
811 : return BLK_TRUNC_WARN;
812 20 : case CS_VARBINARY_TYPE:
813 20 : return BLK_TRUNC_FAIL;
814 10 : case CS_DATETIME_TYPE:
815 : case CS_DATETIME4_TYPE:
816 : case CS_DATE_TYPE:
817 : case CS_TIME_TYPE:
818 : case CS_BIGDATETIME_TYPE:
819 : case CS_BIGTIME_TYPE:
820 10 : return binary_dest ? BLK_TRUNC_SILENT : BLK_TRUNC_WARN;
821 20 : default:
822 : /* numeric types */
823 20 : return binary_dest ? BLK_TRUNC_WARN : BLK_TRUNC_FAIL;
824 : }
825 : }
826 :
827 : static TDSRET
828 125784 : _blk_get_col_data(TDSBCPINFO *bulk, TDSCOLUMN *bindcol, int index TDS_UNUSED, int offset)
829 : {
830 125784 : bool null_column = false;
831 125784 : unsigned char *src = NULL;
832 :
833 125784 : CS_INT srctype = 0;
834 125784 : CS_INT srclen = 0;
835 125784 : TDS_INT destlen = 0;
836 125784 : CS_SMALLINT *nullind = NULL;
837 125784 : CS_INT *datalen = NULL;
838 125784 : CS_BLKDESC *blkdesc = (CS_BLKDESC *) bulk;
839 125784 : CS_CONTEXT *ctx = CONN(blkdesc)->ctx;
840 :
841 125784 : tdsdump_log(TDS_DBG_FUNC, "_blk_get_col_data(%p, %p, %d)\n", bulk, bindcol, offset);
842 :
843 : /*
844 : * Retrieve the initial bound column_varaddress
845 : * and increment it if offset specified
846 : */
847 :
848 125784 : if (bindcol->column_nullbind) {
849 4646 : nullind = bindcol->column_nullbind;
850 4646 : nullind += offset;
851 : }
852 125784 : if (bindcol->column_lenbind) {
853 125784 : datalen = bindcol->column_lenbind;
854 125784 : datalen += offset;
855 : }
856 :
857 125784 : src = (unsigned char *) bindcol->column_varaddr;
858 125784 : if (!src) {
859 : /*
860 : * Text/image columns bound with a NULL buffer, data will be
861 : * sent using blk_textxfer, bound length is the total length.
862 : */
863 272 : if (is_blob_col(bindcol) && datalen && *datalen >= 0) {
864 272 : bindcol->bcp_column_data->is_null = (nullind && *nullind == -1);
865 272 : bindcol->bcp_column_data->datalen = bindcol->bcp_column_data->is_null ? 0 : *datalen;
866 : /* empty data, nothing to send later */
867 272 : bindcol->bcp_column_data->deferred = (bindcol->bcp_column_data->datalen > 0);
868 272 : return TDS_SUCCESS;
869 : }
870 0 : tdsdump_log(TDS_DBG_ERROR, "error source field not addressable\n");
871 : return TDS_FAIL;
872 : }
873 :
874 125512 : src += offset * bindcol->column_bindlen;
875 :
876 125512 : srctype = bindcol->column_bindtype; /* passes to cs_convert */
877 :
878 125512 : tdsdump_log(TDS_DBG_INFO1, "blk_get_col_data srctype = %d\n", srctype);
879 125512 : tdsdump_log(TDS_DBG_INFO1, "blk_get_col_data datalen = %d\n", datalen ? *datalen : -1);
880 :
881 125512 : if (datalen) {
882 125512 : srclen = *datalen;
883 125512 : if (srclen == CS_UNUSED) {
884 : /* length of fixed types is implicit */
885 204 : if (srctype == CS_NUMERIC_TYPE || srctype == CS_DECIMAL_TYPE) {
886 : srclen = sizeof(CS_NUMERIC);
887 : } else {
888 184 : TDS_SERVER_TYPE type = _ct_get_server_type(NULL, srctype);
889 :
890 184 : srclen = type == TDS_INVALID_TYPE ? -1 : tds_get_size_by_type(type);
891 184 : if (srclen <= 0) {
892 0 : tdsdump_log(TDS_DBG_ERROR, "Not fixed length type (%d) and datalen not specified\n",
893 : bindcol->column_bindtype);
894 : return TDS_FAIL;
895 : }
896 : }
897 : }
898 125492 : if (srclen < 0) {
899 0 : tdsdump_log(TDS_DBG_ERROR, "Invalid datalen specified (%d)\n", srclen);
900 : return TDS_FAIL;
901 : }
902 : }
903 125512 : if (srclen == 0) {
904 1560 : if (nullind && *nullind == -1)
905 : null_column = true;
906 : }
907 :
908 : if (!null_column) {
909 : CS_DATAFMT_COMMON srcfmt;
910 : TDS_SERVER_TYPE desttype;
911 123952 : CONV_RESULT cres, *p_cres = &cres;
912 123952 : bool replace = false;
913 :
914 123952 : srcfmt.datatype = srctype;
915 123952 : srcfmt.maxlength = srclen;
916 :
917 123952 : desttype = tds_get_conversion_type(bindcol->on_server.column_type, bindcol->on_server.column_size);
918 : /*
919 : * For Unicode columns convert to client character type
920 : * and later convert to server encoding
921 : */
922 123952 : if (is_unicode_type(bindcol->on_server.column_type))
923 16 : desttype = tds_get_conversion_type(bindcol->column_type, bindcol->column_size);
924 :
925 123952 : switch (desttype) {
926 : /* binary types not BLOB (with limited size) */
927 470 : case SYBBINARY:
928 : case SYBVARBINARY:
929 : case XSYBBINARY:
930 : case XSYBVARBINARY:
931 : /* conversion from char to binary are a bit peculiar */
932 470 : switch (srctype) {
933 10 : case CS_VARCHAR_TYPE:
934 : {
935 10 : CS_VARCHAR *vc = (CS_VARCHAR *) src;
936 :
937 10 : srcfmt.maxlength = TDS_CLAMP(vc->len, 0, sizeof(vc->str));
938 : }
939 200 : case CS_CHAR_TYPE:
940 : case CS_LONGCHAR_TYPE:
941 : case CS_TEXT_TYPE:
942 200 : desttype = TDS_CONVERT_BINARY;
943 200 : cres.cb.len = (srcfmt.maxlength + 1) / 2;
944 200 : cres.cb.len = TDS_MIN(cres.cb.len, bindcol->on_server.column_size);
945 200 : cres.cb.ib = malloc(cres.cb.len);
946 200 : if (!cres.cb.ib)
947 90 : return TDS_FAIL;
948 200 : free(bindcol->bcp_column_data->data);
949 200 : bindcol->bcp_column_data->data = (void *) cres.cb.ib;
950 200 : break;
951 : default:
952 : replace = true;
953 : break;
954 : }
955 : break;
956 : case SYBIMAGE:
957 : case SYBLONGBINARY:
958 : case SYBCHAR:
959 : case SYBVARCHAR:
960 : case SYBTEXT:
961 : case XSYBCHAR:
962 : case XSYBVARCHAR:
963 : replace = true;
964 : break;
965 260 : case SYBNUMERIC:
966 : case SYBDECIMAL:
967 260 : p_cres = (CONV_RESULT *) bindcol->bcp_column_data->data;
968 260 : p_cres->n.precision = bindcol->column_prec;
969 260 : p_cres->n.scale = bindcol->column_scale;
970 260 : break;
971 62266 : default:
972 62266 : p_cres = (CONV_RESULT *) bindcol->bcp_column_data->data;
973 62266 : break;
974 : }
975 :
976 : /* if convert return FAIL mark error but process other columns */
977 123952 : destlen = _cs_cs2tds(ctx, &srcfmt, src, desttype, p_cres);
978 123952 : if (destlen < 0) {
979 50 : switch (destlen) {
980 40 : case TDS_CONVERT_SYNTAX:
981 40 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 1, 2, 1, 26, "%d, %d", index + 1,
982 40 : bulk->rows_sent + 1);
983 40 : break;
984 10 : case TDS_CONVERT_OVERFLOW:
985 10 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 1, 2, 1, 25, "%d, %d", index + 1,
986 10 : bulk->rows_sent + 1);
987 10 : break;
988 : }
989 :
990 50 : tdsdump_log(TDS_DBG_ERROR, "conversion from srctype %d to TDS type %d failed\n", srctype, desttype);
991 : return TDS_FAIL;
992 : }
993 :
994 123902 : if (replace) {
995 61226 : free(bindcol->bcp_column_data->data);
996 61226 : bindcol->bcp_column_data->data = (void *) cres.c;
997 : }
998 :
999 : /* convert character data to server encoding */
1000 123902 : if (is_unicode_type(bindcol->on_server.column_type) && is_char_type(desttype) && bindcol->char_conv && destlen > 0) {
1001 : size_t out_len;
1002 : const char *converted;
1003 :
1004 32 : converted = tds_convert_string(CONN(blkdesc)->tds_socket, bindcol->char_conv,
1005 16 : (const char *) bindcol->bcp_column_data->data, destlen, &out_len);
1006 16 : if (!converted) {
1007 0 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 1, 2, 1, 26, "%d, %d", index + 1,
1008 0 : bulk->rows_sent + 1);
1009 0 : return TDS_FAIL;
1010 : }
1011 16 : if (converted != (const char *) bindcol->bcp_column_data->data) {
1012 16 : free(bindcol->bcp_column_data->data);
1013 16 : bindcol->bcp_column_data->data = (TDS_UCHAR *) converted;
1014 : }
1015 16 : destlen = (TDS_INT) out_len;
1016 : }
1017 :
1018 : /* data too long for character or binary column */
1019 123902 : if (!is_blob_col(bindcol) && destlen > bindcol->on_server.column_size
1020 430 : && (desttype == TDS_CONVERT_BINARY || is_binary_type(desttype) || is_char_type(desttype))) {
1021 170 : bool binary_dest = (desttype == TDS_CONVERT_BINARY || is_binary_type(desttype));
1022 :
1023 170 : switch (_blk_truncation_mode(srctype, binary_dest)) {
1024 40 : case BLK_TRUNC_FAIL:
1025 40 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 1, 2, 1, 25, "%d, %d", index + 1,
1026 40 : bulk->rows_sent + 1);
1027 40 : return TDS_FAIL;
1028 100 : case BLK_TRUNC_WARN:
1029 100 : _ctclient_msg(NULL, CONN(blkdesc), "blk_rowxfer", 1, 2, 1, 42, "%d, %d", index + 1,
1030 100 : bulk->rows_sent + 1);
1031 100 : break;
1032 : case BLK_TRUNC_SILENT:
1033 : break;
1034 : }
1035 130 : destlen = bindcol->on_server.column_size;
1036 :
1037 : /* do not split bytes in hexadecimal representation of binary data */
1038 190 : if (!binary_dest && _blk_is_binary_source(srctype)) {
1039 20 : int digit_size = is_unicode_type(bindcol->on_server.column_type) ? 2 : 1;
1040 :
1041 20 : destlen -= destlen % (digit_size * 2);
1042 : }
1043 : }
1044 : }
1045 :
1046 125422 : bindcol->bcp_column_data->datalen = destlen;
1047 125422 : bindcol->bcp_column_data->is_null = null_column;
1048 :
1049 125422 : return TDS_SUCCESS;
1050 : }
|