1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Brian Bruns
3 * Copyright (C) 2006-2015 Frediano Ziglio
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21 #include <config.h>
22
23 #include <stdarg.h>
24
25 #include <freetds/time.h>
26
27 #include <assert.h>
28 #include <stdio.h>
29
30 #if HAVE_STDLIB_H
31 #include <stdlib.h>
32 #endif /* HAVE_STDLIB_H */
33
34 #if HAVE_STRING_H
35 #include <string.h>
36 #endif /* HAVE_STRING_H */
37
38 #if HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif /* HAVE_UNISTD_H */
41
42 #if HAVE_ERRNO_H
43 # include <errno.h>
44 #endif /* HAVE_ERRNO_H */
45
46 /**
47 * \ingroup dblib_core
48 * \remarks Either SYBDBLIB or MSDBLIB (not both) must be defined.
49 * This affects how certain application-addressable
50 * strucures are defined.
51 */
52 #include <freetds/tds.h>
53 #include <freetds/thread.h>
54 #include <freetds/convert.h>
55 #include <freetds/utils/string.h>
56 #include <freetds/data.h>
57 #include <freetds/replacements.h>
58 #include <sybfront.h>
59 #include <sybdb.h>
60 #include <syberror.h>
61 #include <dblib.h>
62
63 static RETCODE _dbresults(DBPROCESS * dbproc);
64 static BYTE *_dbcoldata(TDSCOLUMN *colinfo);
65 static int _get_printable_size(TDSCOLUMN * colinfo);
66 static char *_dbprdate(char *timestr);
67 static int _dbnullable(DBPROCESS * dbproc, int column);
68 static const char *tds_prdatatype(int datatype_token);
69
70 static int default_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr);
71
72 void copy_data_to_host_var(DBPROCESS *, TDS_SERVER_TYPE, const BYTE *, int, BYTE *, DBINT, int, DBINT *);
73 RETCODE dbgetnull(DBPROCESS *dbproc, int bindtype, int varlen, BYTE* varaddr);
74
75 /**
76 * \file dblib.c
77 * Main implementation file for \c db-lib.
78 */
79 /**
80 * \file bcp.c
81 * Implementation of \c db-lib bulk copy functions.
82 */
83 /**
84 * \defgroup dblib_api The db-lib API
85 * Functions callable by \c db-lib client programs
86 *
87 * The \c db_lib interface is implemented by both Sybase and Microsoft. FreeTDS seeks to implement
88 * first the intersection of the functions defined by the vendors.
89 */
90
91 /**
92 * \ingroup dblib_api
93 * \defgroup dblib_core Primary functions
94 * Core functions needed by most db-lib programs.
95 */
96 /**
97 * \ingroup dblib_api
98 * \defgroup dblib_rpc Remote Procedure functions
99 * Functions used with stored procedures.
100 * Especially useful for OUTPUT parameters, because modern Microsoft servers do not
101 * return output parameter data to the client unless the procedure was invoked
102 * with dbrpcsend().
103 */
104 /**
105 * \ingroup dblib_api
106 * \defgroup dblib_bcp Bulk copy functions
107 * Functions to bulk-copy (a/k/a \em bcp) data to/from the database.
108 */
109 /**
110 * \ingroup dblib_bcp
111 * \defgroup dblib_bcp_internal Internal bcp functions
112 * Static functions internal to the bcp library.
113 */
114 /**
115 * \ingroup dblib_api
116 * \defgroup dblib_money Money functions
117 * Functions to manipulate the MONEY datatype.
118 */
119 /**
120 * \ingroup dblib_api
121 * \defgroup dblib_datetime Datetime functions
122 * Functions to manipulate DBDATETIME structures. Defined by Sybase only.
123 * These are not implemented:
124 * - dbdate4cmp()
125 * - dbdate4zero()
126 * - dbdatechar()
127 * - dbdatename()
128 * - dbdateorder()
129 * - dbdatepart()
130 * - dbdatezero()
131 * - dbdayname()
132 */
133 /**
134 * \ingroup dblib_api
135 * \defgroup dblib_internal Internals
136 * Functions called within \c db-lib for self-help.
137 * These functions are of interest only to people hacking on the FreeTDS db-lib implementation.
138 */
139 /**
140 * \ingroup dblib_api
141 * \defgroup dblib_unimplemented Unimplemented
142 * Functions thus far not implemented in the FreeTDS db-lib implementation.
143 * While some of these are simply awaiting someone with time and skill (and inclination)
144 * it might be noted here that the old browse functions (e.g. dbcolbrowse())
145 * are on the never-to-do list.
146 * They were defined by Sybase and were superseded long ago, although they're still
147 * present in Microsoft's implementation.
148 * They were never popular and today better alternatives are available.
149 * For completeness, they are:
150 * - dbcolbrowse()
151 * - dbcolsource()
152 * - dbfreequal()
153 * - dbqual()
154 * - dbtabbrowse()
155 * - dbtabcount()
156 * - dbtabname()
157 * - dbtabsource()
158 * - dbtsnewlen()
159 * - dbtsnewval()
160 * - dbtsput()
161 */
162
163 /* info/err message handler functions (or rather pointers to them) */
164 MHANDLEFUNC _dblib_msg_handler = NULL;
165 EHANDLEFUNC _dblib_err_handler = default_err_handler;
166
167 /** \internal
168 * \dblib_internal
169 * \remarks A db-lib connection has an implicit TDS context.
170 */
171 typedef struct dblib_context
172 {
173 /** reference count, time dbinit called */
174 int ref_count;
175
176 /** libTDS context */
177 TDSCONTEXT *tds_ctx;
178 /** libTDS context reference counter */
179 int tds_ctx_ref_count;
180
181 /* save all connection in a list */
182 TDSSOCKET **connection_list;
183 int connection_list_size;
184 int connection_list_size_represented;
185 char *recftos_filename;
186 int recftos_filenum;
187 int login_timeout; /**< not used unless positive */
188 int query_timeout; /**< not used unless positive */
189 }
190 DBLIBCONTEXT;
191
192 static DBLIBCONTEXT g_dblib_ctx;
193 static tds_mutex dblib_mutex = TDS_MUTEX_INITIALIZER;
194
195 static int g_dblib_version =
196 #if TDS50
197 DBVERSION_100;
198 #elif TDS71
199 DBVERSION_71;
200 #elif TDS72
201 DBVERSION_72;
202 #elif TDS73
203 DBVERSION_73;
204 #elif TDS74
205 DBVERSION_74;
206 #else
207 DBVERSION_UNKNOWN;
208 #endif
209
210
211 static int
dblib_add_connection(DBLIBCONTEXT * ctx,TDSSOCKET * tds)212 dblib_add_connection(DBLIBCONTEXT * ctx, TDSSOCKET * tds)
213 {
214 int i = 0;
215 const int list_size = ctx->connection_list_size_represented;
216
217 tdsdump_log(TDS_DBG_FUNC, "dblib_add_connection(%p, %p)\n", ctx, tds);
218
219 while (i < list_size && ctx->connection_list[i])
220 i++;
221 if (i == list_size) {
222 fprintf(stderr, "Max connections reached, increase value of TDS_MAX_CONN\n");
223 return 1;
224 } else {
225 ctx->connection_list[i] = tds;
226 return 0;
227 }
228 }
229
230 static void
dblib_del_connection(DBLIBCONTEXT * ctx,TDSSOCKET * tds)231 dblib_del_connection(DBLIBCONTEXT * ctx, TDSSOCKET * tds)
232 {
233 int i = 0;
234 const int list_size = ctx->connection_list_size;
235
236 tdsdump_log(TDS_DBG_FUNC, "dblib_del_connection(%p, %p)\n", ctx, tds);
237
238 while (i < list_size && ctx->connection_list[i] != tds)
239 i++;
240 if (i == list_size) {
241 /* connection wasn't on the free list...now what */
242 } else {
243 /* remove it */
244 ctx->connection_list[i] = NULL;
245 }
246 }
247
248 static TDSCONTEXT*
dblib_get_tds_ctx(void)249 dblib_get_tds_ctx(void)
250 {
251 tdsdump_log(TDS_DBG_FUNC, "dblib_get_tds_ctx(void)\n");
252
253 tds_mutex_lock(&dblib_mutex);
254 ++g_dblib_ctx.tds_ctx_ref_count;
255 if (g_dblib_ctx.tds_ctx == NULL) {
256 g_dblib_ctx.tds_ctx = tds_alloc_context(&g_dblib_ctx);
257
258 /*
259 * Set the functions in the TDS layer to point to the correct handler functions
260 */
261 g_dblib_ctx.tds_ctx->msg_handler = _dblib_handle_info_message;
262 g_dblib_ctx.tds_ctx->err_handler = _dblib_handle_err_message;
263 g_dblib_ctx.tds_ctx->int_handler = _dblib_check_and_handle_interrupt;
264
265 if (g_dblib_ctx.tds_ctx->locale && !g_dblib_ctx.tds_ctx->locale->date_fmt) {
266 /* set default in case there's no locale file */
267 const static char date_format[] = "%b %e %Y %l:%M:%S:%z%p";
268 g_dblib_ctx.tds_ctx->locale->date_fmt = strdup(date_format);
269 }
270 }
271 tds_mutex_unlock(&dblib_mutex);
272 return g_dblib_ctx.tds_ctx;
273 }
274
275 static void
dblib_release_tds_ctx(int count)276 dblib_release_tds_ctx(int count)
277 {
278 tdsdump_log(TDS_DBG_FUNC, "dblib_release_tds_ctx(%d)\n", count);
279
280 tds_mutex_lock(&dblib_mutex);
281 g_dblib_ctx.tds_ctx_ref_count -= count;
282 if (g_dblib_ctx.tds_ctx_ref_count <= 0) {
283 tds_free_context(g_dblib_ctx.tds_ctx);
284 g_dblib_ctx.tds_ctx = NULL;
285 }
286 tds_mutex_unlock(&dblib_mutex);
287 }
288
289 #include "buffering.h"
290
291 static void
db_env_chg(TDSSOCKET * tds,int type,char * oldval,char * newval)292 db_env_chg(TDSSOCKET * tds, int type, char *oldval, char *newval)
293 {
294 DBPROCESS *dbproc;
295
296 assert(oldval != NULL && newval != NULL);
297 if (strlen(oldval) == 1 && *oldval == 1)
298 oldval = "(0x1)";
299
300 tdsdump_log(TDS_DBG_FUNC, "db_env_chg(%p, %d, %s, %s)\n", tds, type, oldval, newval);
301
302 if (!tds || !tds_get_parent(tds))
303 return;
304 dbproc = (DBPROCESS *) tds_get_parent(tds);
305
306 dbproc->envchange_rcv |= (1 << (type - 1));
307 switch (type) {
308 case TDS_ENV_DATABASE:
309 strlcpy(dbproc->dbcurdb, newval, sizeof(dbproc->dbcurdb));
310 break;
311 case TDS_ENV_CHARSET:
312 strlcpy(dbproc->servcharset, newval, sizeof(dbproc->servcharset));
313 break;
314 default:
315 break;
316 }
317 return;
318 }
319
320 /** \internal
321 * \ingroup dblib_internal
322 * \brief Sanity checks for column-oriented functions.
323 *
324 * \param dbproc contains all information needed by db-lib to manage communications with the server.
325 * \param pcolinfo address of pointer to a TDSCOLUMN structure.
326 * \remarks Makes sure dbproc and the requested column are valid.
327 * Calls dbperror() if not.
328 * \returns appropriate error or SUCCEED
329 */
330 static TDSCOLUMN*
dbcolptr(DBPROCESS * dbproc,int column)331 dbcolptr(DBPROCESS* dbproc, int column)
332 {
333 if (!dbproc) {
334 dbperror(dbproc, SYBENULL, 0);
335 return NULL;
336 }
337 if (IS_TDSDEAD(dbproc->tds_socket)) {
338 dbperror(dbproc, SYBEDDNE, 0);
339 return NULL;
340 }
341 if (!dbproc->tds_socket->res_info)
342 return NULL;
343 if (column < 1 || column > dbproc->tds_socket->res_info->num_cols) {
344 dbperror(dbproc, SYBECNOR, 0);
345 return NULL;
346 }
347
348 return dbproc->tds_socket->res_info->columns[column - 1];
349 }
350
351 static TDSCOLUMN*
dbacolptr(DBPROCESS * dbproc,int computeid,int column,int is_bind)352 dbacolptr(DBPROCESS* dbproc, int computeid, int column, int is_bind)
353 {
354 unsigned int i;
355 TDSSOCKET *tds;
356 TDSCOMPUTEINFO *info;
357
358 if (!dbproc) {
359 dbperror(dbproc, SYBENULL, 0);
360 return NULL;
361 }
362 tds = dbproc->tds_socket;
363 if (IS_TDSDEAD(tds)) {
364 dbperror(dbproc, SYBEDDNE, 0);
365 return NULL;
366 }
367 for (i = 0;; ++i) {
368 if (i >= tds->num_comp_info) {
369 /* Attempt to bind user variable to a non-existent compute row */
370 if (is_bind)
371 dbperror(dbproc, SYBEBNCR, 0);
372 return NULL;
373 }
374 info = tds->comp_info[i];
375 if (info->computeid == computeid)
376 break;
377 }
378 /* Fail if either the compute id or the column number is invalid. */
379 if (column < 1 || column > info->num_cols) {
380 dbperror(dbproc, is_bind ? SYBEABNC : SYBECNOR, 0);
381 return NULL;
382 }
383
384 return info->columns[column - 1];
385 }
386
387 /*
388 * Default null substitution values
389 * Binding Type Null Substitution Value
390 * TINYBIND 0
391 * SMALLBIND 0
392 * INTBIND 0
393 * CHARBIND Empty string (padded with blanks)
394 * STRINGBIND Empty string (padded with blanks, null-terminated)
395 * NTBSTRINGBIND Empty string (null-terminated)
396 * VARYCHARBIND Empty string
397 * BINARYBIND Empty array (padded with zeros)
398 * VARYBINBIND Empty array
399 * DATETIMEBIND 8 bytes of zeros
400 * SMALLDATETIMEBIND 8 bytes of zeros
401 * MONEYBIND $0.00
402 * SMALLMONEYBIND $0.00
403 * FLT8BIND 0.0
404 * REALBIND 0.0
405 * DECIMALBIND 0.0 (with default scale and precision)
406 * NUMERICBIND 0.0 (with default scale and precision)
407 * BOUNDARYBIND Empty string (null-terminated)
408 * SENSITIVITYBIND Empty string (null-terminated)
409 */
410
411 static const DBBIT null_BIT = 0;
412 static const DBTINYINT null_TINYINT = 0;
413 static const DBSMALLINT null_SMALLINT = 0;
414 static const DBINT null_INT = 0;
415 static const DBBIGINT null_BIGINT = 0;
416 static const DBFLT8 null_FLT8 = 0;
417 static const DBREAL null_REAL = 0;
418
419 static const DBCHAR null_CHAR = '\0';
420 static const DBVARYCHAR null_VARYCHAR = { 0, {0} };
421
422 static const DBDATETIME null_DATETIME = { 0, 0 };
423 static const DBDATETIME4 null_SMALLDATETIME = { 0, 0 };
424 static const DBMONEY null_MONEY = { 0, 0 };
425 static const DBMONEY4 null_SMALLMONEY = {0};
426 static const DBNUMERIC null_NUMERIC = { 0, 0, {0} };
427 static const TDS_DATETIMEALL null_DATETIMEALL = { 0, 0, 0, 0 };
428
429 static NULLREP default_null_representations[MAXBINDTYPES] = {
430 /* CHARBIND 0 */ { NULL, 0 }
431 /* STRINGBIND 1 */ , { NULL, 0 }
432 /* NTBSTRINGBIND 2 */ , { (BYTE*) &null_CHAR, sizeof(null_CHAR) }
433 /* VARYCHARBIND 3 */ , { (BYTE*) &null_VARYCHAR, sizeof(null_VARYCHAR) }
434 /* VARYBINBIND 4 */ , { (BYTE*) &null_VARYCHAR, sizeof(null_VARYCHAR) }
435 /* no such bind 5 */ , { NULL, 0 }
436 /* TINYBIND 6 */ , { &null_TINYINT, sizeof(null_TINYINT) }
437 /* SMALLBIND 7 */ , { (BYTE*) &null_SMALLINT, sizeof(null_SMALLINT) }
438 /* INTBIND 8 */ , { (BYTE*) &null_INT, sizeof(null_INT) }
439 /* FLT8BIND 9 */ , { (BYTE*) &null_FLT8, sizeof(null_FLT8) }
440 /* REALBIND 10 */ , { (BYTE*) &null_REAL, sizeof(null_REAL) }
441 /* DATETIMEBIND 11 */ , { (BYTE*) &null_DATETIME, sizeof(null_DATETIME) }
442 /* SMALLDATETIMEBIND 12 */ , { (BYTE*) &null_SMALLDATETIME, sizeof(null_SMALLDATETIME) }
443 /* MONEYBIND 13 */ , { (BYTE*) &null_MONEY, sizeof(null_MONEY) }
444 /* SMALLMONEYBIND 14 */ , { (BYTE*) &null_SMALLMONEY, sizeof(null_SMALLMONEY) }
445 /* BINARYBIND 15 */ , { NULL, 0 }
446 /* BITBIND 16 */ , { &null_BIT, sizeof(null_BIT) }
447 /* NUMERICBIND 17 */ , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
448 /* DECIMALBIND 18 */ , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
449 /* SRCNUMERICBIND 19 */ , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
450 /* SRCDECIMALBIND 20 */ , { (BYTE*) &null_NUMERIC, sizeof(null_NUMERIC) }
451 /* DATEBIND 21 */ , { (BYTE*) &null_INT, sizeof(null_INT) }
452 /* TIMEBIND 22 */ , { (BYTE*) &null_INT, sizeof(null_INT) }
453 /* BIGDATETIMEBIND 23 */ , { (BYTE*) &null_BIGINT, sizeof(null_BIGINT) }
454 /* BIGTIMEBIND 24 */ , { (BYTE*) &null_BIGINT, sizeof(null_BIGINT) }
455 /* 25 */ , { NULL, 0 }
456 /* 26 */ , { NULL, 0 }
457 /* 27 */ , { NULL, 0 }
458 /* 28 */ , { NULL, 0 }
459 /* 29 */ , { NULL, 0 }
460 /* BIGINTBIND 30 */ , { (BYTE*) &null_BIGINT, sizeof(null_BIGINT) }
461 /* DATETIME2BIND 31 */ , { (BYTE*) &null_DATETIMEALL, sizeof(null_DATETIMEALL) }
462 /* MAXBINDTYPES 32 */
463 };
464
465 static int
dbbindtype(int datatype)466 dbbindtype(int datatype)
467 {
468 switch (datatype) {
469 case SYBIMAGE:
470 case SYBVARBINARY:
471 case SYBBINARY: return BINARYBIND;
472
473 case SYBBIT: return BITBIND;
474
475 case SYBLONGCHAR:
476 case SYBTEXT:
477 case SYBVARCHAR:
478 case SYBCHAR: return NTBSTRINGBIND;
479
480 case SYBDATETIME: return DATETIMEBIND;
481 case SYBDATETIME4: return SMALLDATETIMEBIND;
482
483 case SYBDATE: return DATEBIND;
484 case SYBTIME: return TIMEBIND;
485
486 case SYB5BIGDATETIME: return BIGDATETIMEBIND;
487 case SYB5BIGTIME: return BIGTIMEBIND;
488
489 case SYBDECIMAL: return DECIMALBIND;
490 case SYBNUMERIC: return NUMERICBIND;
491
492 case SYBFLT8: return FLT8BIND;
493 case SYBREAL: return REALBIND;
494
495 case SYBINT1: return TINYBIND;
496 case SYBINT2: return SMALLBIND;
497 case SYBINT4: return INTBIND;
498 case SYBINT8: return BIGINTBIND;
499
500 case SYBMONEY: return MONEYBIND;
501 case SYBMONEY4: return SMALLMONEYBIND;
502
503 case SYBMSDATE:
504 case SYBMSTIME:
505 case SYBMSDATETIME2:
506 case SYBMSDATETIMEOFFSET:
507 return DATETIME2BIND;
508
509 default:
510 assert(0 == "no such datatype");
511 }
512
513 return 0;
514 }
515
516 /** \internal
517 * dbbind() says: "Note that if varlen is 0, no padding takes place"
518 * dbgetnull() will not pad varaddr unless varlen is positive.
519 * Vartype Program Type Padding Terminator
520 * ------------------- -------------- -------------- ----------
521 * CHARBIND DBCHAR blanks none
522 * STRINGBIND DBCHAR blanks \0
523 * NTBSTRINGBIND DBCHAR none \0
524 * VARYCHARBIND DBVARYCHAR none none
525 * BOUNDARYBIND DBCHAR none \0
526 * SENSITIVITYBIND DBCHAR none \0
527 */
528 RETCODE
dbgetnull(DBPROCESS * dbproc,int bindtype,int varlen,BYTE * varaddr)529 dbgetnull(DBPROCESS *dbproc, int bindtype, int varlen, BYTE* varaddr)
530 {
531 NULLREP *pnullrep = default_null_representations + bindtype;
532
533 tdsdump_log(TDS_DBG_FUNC, "dbgetnull(%p, %d, %d, %p)\n", dbproc, bindtype, varlen, varaddr);
534
535 CHECK_PARAMETER(varaddr, SYBENULL, FAIL);
536 CHECK_PARAMETER(0 <= bindtype && bindtype < MAXBINDTYPES, SYBEBTYP, FAIL);
537
538 /* dbproc can be NULL */
539 if (NULL != dbproc) {
540 assert(dbproc->nullreps);
541 pnullrep = dbproc->nullreps + bindtype;
542 }
543
544 /*
545 * Fixed types: ignore varlen
546 * Other types: ignore varlen if <= 0, else varlen must be >= pnullrep->len.
547 */
548 switch (bindtype) {
549 case DATETIMEBIND:
550 case DATETIME2BIND:
551 case DECIMALBIND:
552 case SRCDECIMALBIND:
553 case FLT8BIND:
554 case INTBIND:
555 case MONEYBIND:
556 case NUMERICBIND:
557 case SRCNUMERICBIND:
558 case REALBIND:
559 case SMALLBIND:
560 case SMALLDATETIMEBIND:
561 case SMALLMONEYBIND:
562 case TINYBIND:
563 case BIGINTBIND:
564 case BITBIND:
565 case TIMEBIND:
566 case DATEBIND:
567 case BIGDATETIMEBIND:
568 case BIGTIMEBIND:
569 memcpy(varaddr, pnullrep->bindval, pnullrep->len);
570 return SUCCEED;
571 case CHARBIND:
572 case STRINGBIND:
573 case NTBSTRINGBIND:
574 case BINARYBIND:
575 case VARYCHARBIND:
576 case VARYBINBIND:
577 if (pnullrep->bindval && (varlen <= 0 || (size_t)varlen >= pnullrep->len)) {
578 memcpy(varaddr, pnullrep->bindval, pnullrep->len);
579 }
580 break;
581 default:
582 dbperror(dbproc, SYBEBTYP, 0);
583 return FAIL;
584 }
585
586 /*
587 * For variable-length types, nonpositive varlen indicates
588 * buffer is "big enough" but also not to pad.
589 * Apply terminator (if applicable) and go home.
590 */
591 if (varlen <= 0) {
592 varlen = pnullrep->len;
593 switch (bindtype) {
594 case STRINGBIND:
595 case NTBSTRINGBIND:
596 ++varlen;
597 break;
598 #if 0
599 case BOUNDARYBIND:
600 case SENSITIVITYBIND:
601 #endif
602 }
603 }
604
605 if (varlen < (long)pnullrep->len) {
606 tdsdump_log(TDS_DBG_FUNC, "dbgetnull: error: not setting varaddr(%p) because %d < %lu\n",
607 varaddr, varlen, (unsigned long int) pnullrep->len);
608 return FAIL;
609 }
610
611 tdsdump_log(TDS_DBG_FUNC, "varaddr(%p) varlen %d < %lu?\n",
612 varaddr, varlen, (unsigned long int) pnullrep->len);
613
614 assert(varlen >= 0);
615
616 /*
617 * CHARBIND Empty string (padded with blanks)
618 * STRINGBIND Empty string (padded with blanks, null-terminated)
619 * NTBSTRINGBIND Empty string (unpadded, null-terminated)
620 * BINARYBIND Empty array (padded with zeros)
621 */
622 varaddr += pnullrep->len;
623 varlen -= (int)pnullrep->len;
624 if (varlen > 0) {
625 switch (bindtype) {
626 case CHARBIND:
627 memset(varaddr, ' ', varlen);
628 break;
629 case STRINGBIND:
630 memset(varaddr, ' ', varlen);
631 varaddr[varlen-1] = '\0';
632 break;
633 case NTBSTRINGBIND:
634 varaddr[0] = '\0';
635 break;
636 case BINARYBIND:
637 memset(varaddr, 0, varlen);
638 break;
639 case VARYCHARBIND:
640 case VARYBINBIND:
641 break;
642 default:
643 assert(!"unknown bindtype");
644 }
645 }
646 return SUCCEED;
647 }
648
649 /**
650 * \ingroup dblib_core
651 * \brief Initialize db-lib.
652 *
653 * \remarks Call this function before trying to use db-lib in any way.
654 * Allocates various internal structures and reads \c locales.conf (if any) to determine the default
655 * date format.
656 * \retval SUCCEED normal.
657 * \retval FAIL cannot allocate an array of \c TDS_MAX_CONN \c TDSSOCKET pointers.
658 */
659 RETCODE
dbinit(void)660 dbinit(void)
661 {
662 _dblib_err_handler = default_err_handler;
663
664 tds_mutex_lock(&dblib_mutex);
665
666 tdsdump_log(TDS_DBG_FUNC, "dbinit(void)\n");
667
668 if (++g_dblib_ctx.ref_count != 1) {
669 tds_mutex_unlock(&dblib_mutex);
670 return SUCCEED;
671 }
672 /*
673 * DBLIBCONTEXT stores a list of current connections so they may be closed with dbexit()
674 */
675
676 g_dblib_ctx.connection_list = tds_new0(TDSSOCKET *, TDS_MAX_CONN);
677 if (g_dblib_ctx.connection_list == NULL) {
678 tdsdump_log(TDS_DBG_FUNC, "dbinit: out of memory\n");
679 tds_mutex_unlock(&dblib_mutex);
680 return FAIL;
681 }
682 g_dblib_ctx.connection_list_size = TDS_MAX_CONN;
683 g_dblib_ctx.connection_list_size_represented = TDS_MAX_CONN;
684
685 g_dblib_ctx.login_timeout = -1;
686 g_dblib_ctx.query_timeout = -1;
687
688 tds_mutex_unlock(&dblib_mutex);
689
690 dblib_get_tds_ctx();
691
692 return SUCCEED;
693 }
694
695 /**
696 * \ingroup dblib_core
697 * \brief Allocate a \c LOGINREC structure.
698 *
699 * \remarks A \c LOGINREC structure is passed to \c dbopen() to create a connection to the database.
700 * Does not communicate to the server; interacts strictly with library.
701 * \retval NULL the \c LOGINREC cannot be allocated.
702 * \retval LOGINREC* to valid memory, otherwise.
703 */
704 LOGINREC *
dblogin(void)705 dblogin(void)
706 {
707 LOGINREC *loginrec;
708
709 tdsdump_log(TDS_DBG_FUNC, "dblogin(void)\n");
710
711 if ((loginrec = tds_new0(LOGINREC, 1)) == NULL) {
712 dbperror(NULL, SYBEMEM, errno);
713 return NULL;
714 }
715 if ((loginrec->tds_login = tds_alloc_login(1)) == NULL) {
716 dbperror(NULL, SYBEMEM, errno);
717 free(loginrec);
718 return NULL;
719 }
720
721 /* set default values for loginrec */
722 if (!tds_set_library(loginrec->tds_login, "DB-Library")) {
723 dbperror(NULL, SYBEMEM, errno);
724 free(loginrec);
725 return NULL;
726 }
727
728 return loginrec;
729 }
730
731 /**
732 * \ingroup dblib_core
733 * \brief free the \c LOGINREC
734 *
735 */
736 void
dbloginfree(LOGINREC * login)737 dbloginfree(LOGINREC * login)
738 {
739 tdsdump_log(TDS_DBG_FUNC, "dbloginfree(%p)\n", login);
740
741 if (login) {
742 tds_free_login(login->tds_login);
743 TDS_ZERO_FREE(login);
744 }
745 }
746
747 /** \internal
748 * \ingroup dblib_internal
749 * \brief Set the value of a string in a \c LOGINREC structure.
750 *
751 * Called by various macros to populate \a login.
752 * \param login the \c LOGINREC* to modify.
753 * \param value the value to set it to.
754 * \param which the field to set.
755 * \retval SUCCEED the value was set.
756 * \retval FAIL \c DBSETHID or other invalid \a which was tried.
757 */
758 RETCODE
dbsetlname(LOGINREC * login,const char * value,int which)759 dbsetlname(LOGINREC * login, const char *value, int which)
760 {
761 bool copy_ret;
762 const char *value_nonull = value ? value : "";
763
764 tdsdump_log(TDS_DBG_FUNC, "dbsetlname(%p, %s, %d)\n", login, value, which);
765
766 if (login == NULL) {
767 dbperror(NULL, SYBEASNL, 0);
768 return FAIL;
769 }
770
771 if (TDS_MAX_LOGIN_STR_SZ < strlen(value_nonull)) {
772 dbperror(NULL, SYBENTLL, 0);
773 return FAIL;
774 }
775
776 switch (which) {
777 case DBSETHOST:
778 copy_ret = tds_set_host(login->tds_login, value_nonull);
779 break;
780 case DBSETUSER:
781 copy_ret = tds_set_user(login->tds_login, value_nonull);
782 break;
783 case DBSETPWD:
784 copy_ret = tds_set_passwd(login->tds_login, value_nonull);
785 break;
786 case DBSETAPP:
787 copy_ret = tds_set_app(login->tds_login, value_nonull);
788 break;
789 case DBSETCHARSET:
790 /* TODO NULL == no conversion desired */
791 copy_ret = tds_set_client_charset(login->tds_login, value_nonull);
792 break;
793 case DBSETNATLANG:
794 copy_ret = tds_set_language(login->tds_login, value_nonull);
795 break;
796 case DBSETDBNAME:
797 copy_ret = !!tds_dstr_copy(&login->tds_login->database, value_nonull);
798 break;
799 case DBSETSERVERPRINCIPAL:
800 copy_ret = !!tds_dstr_copy(&login->tds_login->server_spn, value_nonull);
801 break;
802 default:
803 dbperror(NULL, SYBEASUL, 0); /* Attempt to set unknown LOGINREC field */
804 return FAIL;
805 break;
806 }
807
808 if (!copy_ret)
809 return FAIL;
810 return SUCCEED;
811 }
812
813 /** \internal
814 * \ingroup dblib_internal
815 * \brief Set an integer value in a \c LOGINREC structure.
816 *
817 * Called by various macros to populate \a login.
818 * \param login the \c LOGINREC* to modify.
819 * \param value the value to set it to.
820 * \param which the field to set.
821 * \retval SUCCEED the value was set.
822 * \retval FAIL anything other than \c DBSETPACKET was passed for \a which.
823 */
824 RETCODE
dbsetllong(LOGINREC * login,long value,int which)825 dbsetllong(LOGINREC * login, long value, int which)
826 {
827 tdsdump_log(TDS_DBG_FUNC, "dbsetllong(%p, %ld, %d)\n", login, value, which);
828
829 if( login == NULL ) {
830 dbperror(NULL, SYBEASNL, 0);
831 return FAIL;
832 }
833
834 switch (which) {
835 case DBSETPACKET:
836 if (0 <= value && value <= 999999) {
837 tds_set_packet(login->tds_login, value);
838 return SUCCEED;
839 }
840 dbperror(0, SYBEBADPK, 0, (int) value, (int) login->tds_login->block_size);
841 return FAIL;
842 break;
843 default:
844 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetllong() which = %d\n", which);
845 return FAIL;
846 break;
847 }
848 }
849
850 #if defined(DBLIB_UNIMPLEMENTED)
851 /** \internal
852 * \ingroup dblib_internal
853 * \brief Set an integer value in a \c LOGINREC structure.
854 *
855 * Called by various macros to populate \a login.
856 * \param login the \c LOGINREC* to modify.
857 * \param value the value to set it to.
858 * \param which the field to set.
859 * \retval SUCCEED the value was set.
860 * \retval FAIL anything other than \c DBSETHIER was passed for \a which.
861 */
862 RETCODE
dbsetlshort(LOGINREC * login,int value,int which)863 dbsetlshort(LOGINREC * login, int value, int which)
864 {
865 tdsdump_log(TDS_DBG_FUNC, "dbsetlshort(%p, %d, %d)\n", login, value, which);
866
867 if( login == NULL ) {
868 dbperror(NULL, SYBEASNL, 0);
869 return FAIL;
870 }
871
872 switch (which) {
873 case DBSETHIER:
874 default:
875 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetlshort() which = %d\n", which);
876 return FAIL;
877 break;
878 }
879 }
880 #endif
881
882 /** \internal
883 * \ingroup dblib_internal
884 * \brief Set a boolean value in a \c LOGINREC structure.
885 *
886 * Called by various macros to populate \a login.
887 * \param login the \c LOGINREC* to modify.
888 * \param value the value to set it to.
889 * \param which the field to set.
890 * \remark Only DBSETBCP is implemented.
891 * \retval SUCCEED the value was set.
892 * \retval FAIL invalid value passed for \a which.
893 * \todo DBSETNOSHORT, DBSETENCRYPT, DBSETLABELED
894 */
895 RETCODE
dbsetlbool(LOGINREC * login,int value,int which)896 dbsetlbool(LOGINREC * login, int value, int which)
897 {
898 bool b_value;
899
900 tdsdump_log(TDS_DBG_FUNC, "dbsetlbool(%p, %d, %d)\n", login, value, which);
901
902 if (login == NULL) {
903 dbperror(NULL, SYBEASNL, 0);
904 return FAIL;
905 }
906
907 b_value = (value != 0);
908
909 switch (which) {
910 case DBSETBCP:
911 tds_set_bulk(login->tds_login, b_value);
912 return SUCCEED;
913 case DBSETUTF16:
914 login->tds_login->use_utf16 = b_value;
915 return SUCCEED;
916 case DBSETNTLMV2:
917 login->tds_login->use_ntlmv2 = b_value;
918 login->tds_login->use_ntlmv2_specified = 1;
919 return SUCCEED;
920 case DBSETREADONLY:
921 login->tds_login->readonly_intent = b_value;
922 return SUCCEED;
923 case DBSETNETWORKAUTH:
924 login->network_auth = b_value;
925 return SUCCEED;
926 case DBSETMUTUALAUTH:
927 login->tds_login->mutual_authentication = b_value;
928 return SUCCEED;
929 case DBSETDELEGATION:
930 login->tds_login->gssapi_use_delegation = b_value;
931 return SUCCEED;
932 case DBSETENCRYPT:
933 case DBSETLABELED:
934 default:
935 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetlbool() which = %d\n", which);
936 return FAIL;
937 break;
938 }
939 }
940
941 /**
942 * \ingroup dblib_core
943 * \brief Set TDS version for future connections
944 *
945 */
946 RETCODE
dbsetlversion(LOGINREC * login,BYTE version)947 dbsetlversion(LOGINREC * login, BYTE version)
948 {
949 tdsdump_log(TDS_DBG_FUNC, "dbsetlversion(%p, %x)\n", login, version);
950
951 if( login == NULL ) {
952 dbperror(NULL, SYBEASNL, 0);
953 return FAIL;
954 }
955
956 assert(login->tds_login != NULL);
957
958 switch (version) {
959 case DBVERSION_UNKNOWN:
960 tds_set_version(login->tds_login, 0, 0);
961 return SUCCEED;
962 case DBVERSION_42:
963 tds_set_version(login->tds_login, 4, 2);
964 return SUCCEED;
965 case DBVERSION_100:
966 tds_set_version(login->tds_login, 5, 0);
967 return SUCCEED;
968 case DBVERSION_70:
969 tds_set_version(login->tds_login, 7, 0);
970 return SUCCEED;
971 case DBVERSION_71:
972 tds_set_version(login->tds_login, 7, 1);
973 return SUCCEED;
974 case DBVERSION_72:
975 tds_set_version(login->tds_login, 7, 2);
976 return SUCCEED;
977 case DBVERSION_73:
978 tds_set_version(login->tds_login, 7, 3);
979 return SUCCEED;
980 case DBVERSION_74:
981 tds_set_version(login->tds_login, 7, 4);
982 return SUCCEED;
983 }
984
985 return FAIL;
986 }
987
988 static void
dbstring_free(DBSTRING ** dbstrp)989 dbstring_free(DBSTRING ** dbstrp)
990 {
991 DBSTRING *curr, *next;
992 /* tdsdump_log(TDS_DBG_FUNC, "dbstring_free(%p)\n", dbstrp); */
993
994 if (!dbstrp)
995 return;
996
997 curr = *dbstrp;
998 *dbstrp = NULL;
999 for (; curr; ) {
1000 next = curr->strnext;
1001 free(curr->strtext);
1002 free(curr);
1003 curr = next;
1004 }
1005 }
1006
1007 static RETCODE
dbstring_concat(DBSTRING ** dbstrp,const char * p)1008 dbstring_concat(DBSTRING ** dbstrp, const char *p)
1009 {
1010 DBSTRING **strp = dbstrp;
1011
1012 /* tdsdump_log(TDS_DBG_FUNC, "dbstring_concat(%p, %s)\n", *dbstrp, p); */
1013
1014 while (*strp != NULL) {
1015 strp = &((*strp)->strnext);
1016 }
1017 if ((*strp = tds_new(DBSTRING, 1)) == NULL) {
1018 dbperror(NULL, SYBEMEM, errno);
1019 return FAIL;
1020 }
1021 (*strp)->strtotlen = (DBINT)strlen(p);
1022 if (((*strp)->strtext = tds_new(BYTE, (*strp)->strtotlen)) == NULL) {
1023 TDS_ZERO_FREE(*strp);
1024 dbperror(NULL, SYBEMEM, errno);
1025 return FAIL;
1026 }
1027 memcpy((*strp)->strtext, p, (*strp)->strtotlen);
1028 (*strp)->strnext = NULL;
1029 return SUCCEED;
1030 }
1031
1032 static RETCODE
dbstring_assign(DBSTRING ** dbstrp,const char * p)1033 dbstring_assign(DBSTRING ** dbstrp, const char *p)
1034 {
1035 /* tdsdump_log(TDS_DBG_FUNC, "dbstring_assign(%p, %s)\n", *dbstrp, p); */
1036
1037 dbstring_free(dbstrp);
1038 return dbstring_concat(dbstrp, p);
1039 }
1040
1041 static DBINT
dbstring_length(DBSTRING * dbstr)1042 dbstring_length(DBSTRING * dbstr)
1043 {
1044 DBINT len = 0;
1045 DBSTRING *next;
1046
1047 /* tdsdump_log(TDS_DBG_FUNC, "dbstring_length(%p)\n", dbstr); */
1048
1049 for (next = dbstr; next != NULL; next = next->strnext) {
1050 len += next->strtotlen;
1051 }
1052 return len;
1053 }
1054
1055 static int
dbstring_getchar(DBSTRING * dbstr,int i)1056 dbstring_getchar(DBSTRING * dbstr, int i)
1057 {
1058
1059 /* tdsdump_log(TDS_DBG_FUNC, "dbstring_getchar(%p, %d)\n", dbstr, i); */
1060
1061 if (dbstr == NULL) {
1062 return -1;
1063 }
1064 if (i < 0) {
1065 return -1;
1066 }
1067 if (i < dbstr->strtotlen) {
1068 return dbstr->strtext[i];
1069 }
1070 return dbstring_getchar(dbstr->strnext, i - dbstr->strtotlen);
1071 }
1072
1073 static char *
dbstring_get(DBSTRING * dbstr)1074 dbstring_get(DBSTRING * dbstr)
1075 {
1076 DBSTRING *next;
1077 int len;
1078 char *ret;
1079 char *cp;
1080
1081 /* tdsdump_log(TDS_DBG_FUNC, "dbstring_get(%p)\n", dbstr); */
1082
1083 if (dbstr == NULL) {
1084 return NULL;
1085 }
1086 len = dbstring_length(dbstr);
1087 if ((ret = tds_new(char, len + 1)) == NULL) {
1088 dbperror(NULL, SYBEMEM, errno);
1089 return NULL;
1090 }
1091 cp = ret;
1092 for (next = dbstr; next != NULL; next = next->strnext) {
1093 memcpy(cp, next->strtext, next->strtotlen);
1094 cp += next->strtotlen;
1095 }
1096 *cp = '\0';
1097 return ret;
1098 }
1099
1100 static const char *const opttext[DBNUMOPTIONS] = {
1101 "parseonly",
1102 "estimate",
1103 "showplan",
1104 "noexec",
1105 "arithignore",
1106 "nocount",
1107 "arithabort",
1108 "textlimit",
1109 "browse",
1110 "offsets",
1111 "statistics",
1112 "errlvl",
1113 "confirm",
1114 "spid",
1115 "buffer",
1116 "noautofree",
1117 "rowcount",
1118 "textsize",
1119 "language",
1120 "dateformat",
1121 "prpad",
1122 "prcolsep",
1123 "prlinelen",
1124 "prlinesep",
1125 "lfconvert",
1126 "datefirst",
1127 "chained",
1128 "fipsflagger",
1129 "transaction isolation level",
1130 "auth",
1131 "identity_insert",
1132 "no_identity_column",
1133 "cnv_date2char_short",
1134 "client cursors",
1135 "set time",
1136 "quoted_identifier"
1137 };
1138
1139 static DBOPTION *
init_dboptions(void)1140 init_dboptions(void)
1141 {
1142 DBOPTION *dbopts;
1143 int i;
1144
1145 if ((dbopts = tds_new0(DBOPTION, DBNUMOPTIONS)) == NULL) {
1146 dbperror(NULL, SYBEMEM, errno);
1147 return NULL;
1148 }
1149 for (i = 0; i < DBNUMOPTIONS; i++) {
1150 dbopts[i].text = opttext[i];
1151 dbopts[i].param = NULL;
1152 dbopts[i].factive = FALSE;
1153 }
1154 dbstring_assign(&(dbopts[DBPRPAD].param), " ");
1155 dbstring_assign(&(dbopts[DBPRCOLSEP].param), " ");
1156 dbstring_assign(&(dbopts[DBPRLINELEN].param), "80");
1157 dbstring_assign(&(dbopts[DBPRLINESEP].param), "\n");
1158 dbstring_assign(&(dbopts[DBCLIENTCURSORS].param), " ");
1159 dbstring_assign(&(dbopts[DBSETTIME].param), " ");
1160 return dbopts;
1161 }
1162
1163 /** \internal
1164 * \ingroup dblib_internal
1165 * \brief Form a connection with the server.
1166 *
1167 * Called by the \c dbopen() macro, normally. If FreeTDS was configured with \c --enable-msdblib, this
1168 * function is called by (exported) \c dbopen() function. \c tdsdbopen is so-named to avoid
1169 * namespace conflicts with other database libraries that use the same function name.
1170 * \param login \c LOGINREC* carrying the account information.
1171 * \param server name of the dataserver to connect to.
1172 * \return valid pointer on successful login.
1173 * \retval NULL insufficient memory, unable to connect for any reason.
1174 * \sa dbopen()
1175 * \todo use \c asprintf() to avoid buffer overflow.
1176 * \todo separate error messages for \em no-such-server and \em no-such-user.
1177 */
1178 DBPROCESS *
tdsdbopen(LOGINREC * login,const char * server,int msdblib)1179 tdsdbopen(LOGINREC * login, const char *server, int msdblib)
1180 {
1181 DBPROCESS *dbproc = NULL;
1182 TDSLOGIN *connection;
1183
1184 char *tdsdump = getenv("TDSDUMP");
1185 if (tdsdump && *tdsdump) {
1186 tdsdump_open(tdsdump);
1187 tdsdump_log(TDS_DBG_FUNC, "tdsdbopen(%p, %s, [%s])\n", login, server? server : "0x0", msdblib? "microsoft" : "sybase");
1188 }
1189
1190 /*
1191 * Sybase supports the DSQUERY environment variable and falls back to "SYBASE" if server is NULL.
1192 * Microsoft uses a NULL or "" server to indicate a local server.
1193 * FIXME: support local server for win32.
1194 */
1195 if (!server && !msdblib) {
1196 if ((server = getenv("TDSQUERY")) == NULL)
1197 if ((server = getenv("DSQUERY")) == NULL)
1198 server = "SYBASE";
1199 tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: servername set to %s\n", server);
1200 }
1201
1202 if ((dbproc = tds_new0(DBPROCESS, 1)) == NULL) {
1203 dbperror(NULL, SYBEMEM, errno);
1204 return NULL;
1205 }
1206 dbproc->msdblib = msdblib;
1207
1208 dbproc->dbopts = init_dboptions();
1209 if (dbproc->dbopts == NULL) {
1210 free(dbproc);
1211 return NULL;
1212 }
1213 tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: dbproc->dbopts = %p\n", dbproc->dbopts);
1214
1215 dbproc->dboptcmd = NULL;
1216 dbproc->avail_flag = TRUE;
1217 dbproc->command_state = DBCMDNONE;
1218
1219 if (!tds_set_server(login->tds_login, server)) {
1220 dbperror(NULL, SYBEMEM, 0);
1221 free(dbproc);
1222 return NULL;
1223 }
1224 tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: tds_set_server(%p, \"%s\")\n", login->tds_login, server);
1225
1226 if ((dbproc->tds_socket = tds_alloc_socket(dblib_get_tds_ctx(), 512)) == NULL ){
1227 dbperror(NULL, SYBEMEM, 0);
1228 free(dbproc);
1229 return NULL;
1230 }
1231
1232
1233 tds_set_parent(dbproc->tds_socket, dbproc);
1234
1235 dbproc->tds_socket->env_chg_func = db_env_chg;
1236 dbproc->envchange_rcv = 0;
1237
1238 dbproc->dbcurdb[0] = '\0';
1239 dbproc->servcharset[0] = '\0';
1240
1241 tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: About to call tds_read_config_info...\n");
1242
1243 connection = tds_read_config_info(dbproc->tds_socket, login->tds_login, g_dblib_ctx.tds_ctx->locale);
1244 if (!connection) {
1245 dbclose(dbproc);
1246 return NULL;
1247 }
1248 connection->option_flag2 &= ~TDS_ODBC_ON; /* we're not an ODBC driver */
1249 tds_fix_login(connection); /* initialize from Environment variables */
1250
1251 dbproc->chkintr = NULL;
1252 dbproc->hndlintr = NULL;
1253
1254 tds_mutex_lock(&dblib_mutex);
1255
1256 /* override connection timeout if dbsetlogintime() was called */
1257 if (g_dblib_ctx.login_timeout > 0) {
1258 connection->connect_timeout = g_dblib_ctx.login_timeout;
1259 }
1260
1261 /* override query timeout if dbsettime() was called */
1262 if (g_dblib_ctx.query_timeout > 0) {
1263 connection->query_timeout = g_dblib_ctx.query_timeout;
1264 }
1265
1266 tds_mutex_unlock(&dblib_mutex);
1267
1268 tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: Calling tds_connect_and_login(%p, %p)\n",
1269 dbproc->tds_socket, connection);
1270
1271 if (login->network_auth) {
1272 tds_dstr_empty(&connection->user_name);
1273 tds_dstr_empty(&connection->password);
1274 }
1275
1276 if (TDS_FAILED(tds_connect_and_login(dbproc->tds_socket, connection))) {
1277 tdsdump_log(TDS_DBG_ERROR, "tdsdbopen: tds_connect_and_login failed for \"%s\"!\n",
1278 tds_dstr_cstr(&connection->server_name));
1279 tds_free_login(connection);
1280 dbclose(dbproc);
1281 return NULL;
1282 }
1283 tds_free_login(connection);
1284
1285 dbproc->dbbuf = NULL;
1286 dbproc->dbbufsz = 0;
1287
1288 tds_mutex_lock(&dblib_mutex);
1289 dblib_add_connection(&g_dblib_ctx, dbproc->tds_socket);
1290 tds_mutex_unlock(&dblib_mutex);
1291
1292 /* set the DBBUFFER capacity to nil */
1293 buffer_set_capacity(dbproc, 0);
1294
1295 memcpy(dbproc->nullreps, default_null_representations, sizeof(default_null_representations));
1296
1297 tds_mutex_lock(&dblib_mutex);
1298
1299 if (g_dblib_ctx.recftos_filename != NULL) {
1300 char *temp_filename = NULL;
1301 const int len = asprintf(&temp_filename, "%s.%d",
1302 g_dblib_ctx.recftos_filename, g_dblib_ctx.recftos_filenum);
1303 if (len >= 0) {
1304 dbproc->ftos = fopen(temp_filename, "w");
1305 free(temp_filename);
1306 if (dbproc->ftos != NULL) {
1307 char timestr[256];
1308 fprintf(dbproc->ftos, "/* dbopen() at %s */\n", _dbprdate(timestr));
1309 fflush(dbproc->ftos);
1310 g_dblib_ctx.recftos_filenum++;
1311 }
1312 }
1313 }
1314
1315 tds_mutex_unlock(&dblib_mutex);
1316
1317 tdsdump_log(TDS_DBG_FUNC, "tdsdbopen: Returning dbproc = %p\n", dbproc);
1318
1319 return dbproc;
1320 }
1321
1322 /**
1323 * \ingroup dblib_core
1324 * \brief \c printf-like way to form SQL to send to the server.
1325 *
1326 * Forms a command string and writes to the command buffer with dbcmd().
1327 * \param dbproc contains all information needed by db-lib to manage communications with the server.
1328 * \param fmt <tt> man vasprintf</tt> for details.
1329 * \retval SUCCEED success.
1330 * \retval FAIL insufficient memory, or dbcmd() failed.
1331 * \sa dbcmd(), dbfreebuf(), dbgetchar(), dbopen(), dbstrcpy(), dbstrlen().
1332 */
1333 RETCODE
dbfcmd(DBPROCESS * dbproc,const char * fmt,...)1334 dbfcmd(DBPROCESS * dbproc, const char *fmt, ...)
1335 {
1336 va_list ap;
1337 char *s;
1338 int len;
1339 RETCODE ret;
1340
1341 tdsdump_log(TDS_DBG_FUNC, "dbfcmd(%p, %s, ...)\n", dbproc, fmt);
1342 CHECK_CONN(FAIL);
1343 CHECK_NULP(fmt, "dbfcmd", 2, FAIL);
1344
1345 va_start(ap, fmt);
1346 len = vasprintf(&s, fmt, ap);
1347 va_end(ap);
1348
1349 if (len < 0) {
1350 dbperror(dbproc, SYBEMEM, errno);
1351 return FAIL;
1352 }
1353
1354 ret = dbcmd(dbproc, s);
1355 free(s);
1356
1357 return ret;
1358 }
1359
1360 /**
1361 * \ingroup dblib_core
1362 * \brief \c Append SQL to the command buffer.
1363 *
1364 * \param dbproc contains all information needed by db-lib to manage communications with the server.
1365 * \param cmdstring SQL to append to the command buffer.
1366 * \retval SUCCEED success.
1367 * \retval FAIL insufficient memory.
1368 * \remarks set command state to \c DBCMDPEND unless the command state is DBCMDSENT, in which case
1369 * it frees the command buffer. This latter may or may not be the Right Thing to do.
1370 * \sa dbfcmd(), dbfreebuf(), dbgetchar(), dbopen(), dbstrcpy(), dbstrlen().
1371 */
1372 RETCODE
dbcmd(DBPROCESS * dbproc,const char cmdstring[])1373 dbcmd(DBPROCESS * dbproc, const char cmdstring[])
1374 {
1375 size_t cmd_len, buf_len, newsz;
1376
1377 tdsdump_log(TDS_DBG_FUNC, "dbcmd(%p, %s)\n", dbproc, cmdstring);
1378 CHECK_CONN(FAIL);
1379 CHECK_NULP(cmdstring, "dbcmd", 2, FAIL);
1380
1381 dbproc->avail_flag = FALSE;
1382
1383 tdsdump_log(TDS_DBG_FUNC, "dbcmd() bufsz = %d\n", dbproc->dbbufsz);
1384
1385 if (dbproc->command_state == DBCMDSENT) {
1386 if (!dbproc->noautofree) {
1387 dbfreebuf(dbproc);
1388 }
1389 }
1390
1391 buf_len = (dbproc->dbbufsz == 0) ? 0 : dbproc->dbbufsz - 1;
1392 cmd_len = strlen(cmdstring);
1393 newsz = buf_len + cmd_len + 1;
1394 if (newsz > 0x7fffffffu || !TDS_RESIZE(dbproc->dbbuf, newsz)) {
1395 dbperror(dbproc, SYBEMEM, errno);
1396 return FAIL;
1397 }
1398 memcpy(dbproc->dbbuf + buf_len, cmdstring, cmd_len);
1399 dbproc->dbbuf[newsz - 1] = 0;
1400 dbproc->dbbufsz = (int) newsz;
1401
1402 dbproc->command_state = DBCMDPEND;
1403
1404 return SUCCEED;
1405 }
1406
1407 /**
1408 * \ingroup dblib_core
1409 * \brief send the SQL command to the server and wait for an answer.
1410 *
1411 * Please be patient. This function waits for the server to respond. \c dbsqlexec is equivalent
1412 * to dbsqlsend() followed by dbsqlok().
1413 * \param dbproc contains all information needed by db-lib to manage communications with the server.
1414 * \retval SUCCEED query was processed without errors.
1415 * \retval FAIL was returned by dbsqlsend() or dbsqlok().
1416 * \sa dbcmd(), dbfcmd(), dbnextrow(), dbresults(), dbretstatus(), dbsettime(), dbsqlok(), dbsqlsend()
1417 */
1418 RETCODE
dbsqlexec(DBPROCESS * dbproc)1419 dbsqlexec(DBPROCESS * dbproc)
1420 {
1421 RETCODE rc = FAIL;
1422
1423 tdsdump_log(TDS_DBG_FUNC, "dbsqlexec(%p)\n", dbproc);
1424 CHECK_CONN(FAIL);
1425
1426 if (SUCCEED == (rc = dbsqlsend(dbproc))) {
1427 rc = dbsqlok(dbproc);
1428 }
1429 return rc;
1430 }
1431
1432 /**
1433 * \ingroup dblib_core
1434 * \brief Change current database.
1435 *
1436 * Analagous to the unix command \c cd, dbuse() makes \a name the default database. Waits for an answer
1437 * from the server.
1438 * \param dbproc contains all information needed by db-lib to manage communications with the server.
1439 * \param name database to use.
1440 * \retval SUCCEED query was processed without errors.
1441 * \retval FAIL query was not processed
1442 * \sa dbchange(), dbname().
1443 */
1444 RETCODE
dbuse(DBPROCESS * dbproc,const char * name)1445 dbuse(DBPROCESS * dbproc, const char *name)
1446 {
1447 RETCODE rc;
1448 char *query;
1449
1450 tdsdump_log(TDS_DBG_FUNC, "dbuse(%p, %s)\n", dbproc, name);
1451 CHECK_CONN(FAIL);
1452 CHECK_NULP(name, "dbuse", 2, FAIL);
1453
1454 /* quote name */
1455 query = tds_new(char, tds_quote_id(dbproc->tds_socket, NULL, name, -1) + 6);
1456 if (!query) {
1457 dbperror(dbproc, SYBEMEM, errno);
1458 return FAIL;
1459 }
1460 strcpy(query, "use ");
1461 /* TODO PHP suggest to quote by yourself with []... what should I do ?? quote or not ?? */
1462 if (name[0] == '[' && name[strlen(name)-1] == ']')
1463 strcat(query, name);
1464 else
1465 tds_quote_id(dbproc->tds_socket, query + 4, name, -1);
1466
1467 rc = SUCCEED;
1468 if ((dbcmd(dbproc, query) == FAIL)
1469 || (dbsqlexec(dbproc) == FAIL)
1470 || (dbresults(dbproc) == FAIL)
1471 || (dbcanquery(dbproc) == FAIL))
1472 rc = FAIL;
1473 free(query);
1474 return rc;
1475 }
1476
1477 /**
1478 * \ingroup dblib_core
1479 * \brief Close a connection to the server and free associated resources.
1480 *
1481 * \param dbproc contains all information needed by db-lib to manage communications with the server.
1482 * \sa dbexit(), dbopen().
1483 */
1484 void
dbclose(DBPROCESS * dbproc)1485 dbclose(DBPROCESS * dbproc)
1486 {
1487 TDSSOCKET *tds;
1488 int i;
1489 char timestr[256];
1490
1491 tdsdump_log(TDS_DBG_FUNC, "dbclose(%p)\n", dbproc);
1492 CHECK_PARAMETER(dbproc, SYBENULL, );
1493
1494 tds = dbproc->tds_socket;
1495 if (tds) {
1496 /*
1497 * this MUST be done before socket destruction
1498 * it is possible that a TDSSOCKET is allocated on same position
1499 */
1500 tds_mutex_lock(&dblib_mutex);
1501 dblib_del_connection(&g_dblib_ctx, dbproc->tds_socket);
1502 tds_mutex_unlock(&dblib_mutex);
1503
1504 tds_close_socket(tds);
1505 tds_free_socket(tds);
1506 dblib_release_tds_ctx(1);
1507 }
1508 buffer_free(&(dbproc->row_buf));
1509
1510 if (dbproc->ftos != NULL) {
1511 fprintf(dbproc->ftos, "/* dbclose() at %s */\n", _dbprdate(timestr));
1512 fclose(dbproc->ftos);
1513 }
1514
1515 tds_free_bcpinfo(dbproc->bcpinfo);
1516 if (dbproc->hostfileinfo) {
1517 free(dbproc->hostfileinfo->hostfile);
1518 free(dbproc->hostfileinfo->errorfile);
1519 if (dbproc->hostfileinfo->host_columns) {
1520 for (i = 0; i < dbproc->hostfileinfo->host_colcount; i++) {
1521 free(dbproc->hostfileinfo->host_columns[i]->terminator);
1522 free(dbproc->hostfileinfo->host_columns[i]);
1523 }
1524 free(dbproc->hostfileinfo->host_columns);
1525 }
1526 }
1527
1528 for (i = 0; i < DBNUMOPTIONS; i++) {
1529 dbstring_free(&(dbproc->dbopts[i].param));
1530 }
1531 free(dbproc->dbopts);
1532
1533 dbstring_free(&(dbproc->dboptcmd));
1534
1535 for (i=0; i < MAXBINDTYPES; i++) {
1536 if (dbproc->nullreps[i].bindval != default_null_representations[i].bindval)
1537 free((BYTE*)dbproc->nullreps[i].bindval);
1538 }
1539
1540 dbfreebuf(dbproc);
1541 free(dbproc);
1542 }
1543
1544 /**
1545 * \ingroup dblib_core
1546 * \brief Close server connections and free all related structures.
1547 *
1548 * \sa dbclose(), dbinit(), dbopen().
1549 * \todo breaks if ctlib/dblib used in same process.
1550 */
1551 void
dbexit()1552 dbexit()
1553 {
1554 TDSSOCKET *tds;
1555 DBPROCESS *dbproc;
1556 int i, list_size, count = 1;
1557
1558 tdsdump_log(TDS_DBG_FUNC, "dbexit(void)\n");
1559
1560 tds_mutex_lock(&dblib_mutex);
1561
1562 if (--g_dblib_ctx.ref_count != 0) {
1563 tds_mutex_unlock(&dblib_mutex);
1564 return;
1565 }
1566
1567 list_size = g_dblib_ctx.connection_list_size;
1568
1569 for (i = 0; i < list_size; i++) {
1570 tds = g_dblib_ctx.connection_list[i];
1571 g_dblib_ctx.connection_list[i] = NULL;
1572 if (tds) {
1573 ++count;
1574 dbproc = (DBPROCESS *) tds_get_parent(tds);
1575 tds_close_socket(tds);
1576 tds_free_socket(tds);
1577 if (dbproc) {
1578 /* avoid locking in dbclose */
1579 dbproc->tds_socket = NULL;
1580 dbclose(dbproc);
1581 }
1582 }
1583 }
1584 if (g_dblib_ctx.connection_list) {
1585 TDS_ZERO_FREE(g_dblib_ctx.connection_list);
1586 g_dblib_ctx.connection_list_size = 0;
1587 g_dblib_ctx.connection_list_size_represented = 0;
1588 }
1589
1590 tds_mutex_unlock(&dblib_mutex);
1591
1592 dblib_release_tds_ctx(count);
1593 }
1594
1595 static const char *
prdbresults_state(int retcode)1596 prdbresults_state(int retcode)
1597 {
1598 switch(retcode) {
1599 case _DB_RES_INIT: return "_DB_RES_INIT";
1600 case _DB_RES_RESULTSET_EMPTY: return "_DB_RES_RESULTSET_EMPTY";
1601 case _DB_RES_RESULTSET_ROWS: return "_DB_RES_RESULTSET_ROWS";
1602 case _DB_RES_NEXT_RESULT: return "_DB_RES_NEXT_RESULT";
1603 case _DB_RES_NO_MORE_RESULTS: return "_DB_RES_NO_MORE_RESULTS";
1604 case _DB_RES_SUCCEED: return "_DB_RES_SUCCEED";
1605 default: break;
1606 }
1607 return "??";
1608 }
1609
1610 static const char *
prdbretcode(RETCODE retcode)1611 prdbretcode(RETCODE retcode)
1612 {
1613 switch(retcode) {
1614 case REG_ROW: return "REG_ROW/MORE_ROWS";
1615 case NO_MORE_ROWS: return "NO_MORE_ROWS";
1616 case BUF_FULL: return "BUF_FULL";
1617 case NO_MORE_RESULTS: return "NO_MORE_RESULTS";
1618 case SUCCEED: return "SUCCEED";
1619 case FAIL: return "FAIL";
1620 default: break;
1621 }
1622 return "??";
1623 }
1624
1625 static const char *
prretcode(int retcode)1626 prretcode(int retcode)
1627 {
1628 switch(retcode) {
1629 case TDS_SUCCESS: return "TDS_SUCCESS";
1630 case TDS_FAIL: return "TDS_FAIL";
1631 case TDS_NO_MORE_RESULTS: return "TDS_NO_MORE_RESULTS";
1632 case TDS_CANCELLED: return "TDS_CANCELLED";
1633 default: break;
1634 }
1635 return "??";
1636 }
1637
1638 static const char *
prresult_type(int result_type)1639 prresult_type(int result_type)
1640 {
1641 switch(result_type) {
1642 case TDS_ROW_RESULT: return "TDS_ROW_RESULT";
1643 case TDS_PARAM_RESULT: return "TDS_PARAM_RESULT";
1644 case TDS_STATUS_RESULT: return "TDS_STATUS_RESULT";
1645 case TDS_MSG_RESULT: return "TDS_MSG_RESULT";
1646 case TDS_COMPUTE_RESULT: return "TDS_COMPUTE_RESULT";
1647 case TDS_CMD_DONE: return "TDS_CMD_DONE";
1648 case TDS_CMD_SUCCEED: return "TDS_CMD_SUCCEED";
1649 case TDS_CMD_FAIL: return "TDS_CMD_FAIL";
1650 case TDS_ROWFMT_RESULT: return "TDS_ROWFMT_RESULT";
1651 case TDS_COMPUTEFMT_RESULT: return "TDS_COMPUTEFMT_RESULT";
1652 case TDS_DESCRIBE_RESULT: return "TDS_DESCRIBE_RESULT";
1653 case TDS_DONE_RESULT: return "TDS_DONE_RESULT";
1654 case TDS_DONEPROC_RESULT: return "TDS_DONEPROC_RESULT";
1655 case TDS_DONEINPROC_RESULT: return "TDS_DONEINPROC_RESULT";
1656 case TDS_OTHERS_RESULT: return "TDS_OTHERS_RESULT";
1657 default: break;
1658 }
1659 return "??";
1660 }
1661
1662 /**
1663 * \ingroup dblib_core
1664 * \brief Set up query results.
1665 *
1666 * \param dbproc contains all information needed by db-lib to manage communications with the server.
1667 * \retval SUCCEED Some results are available.
1668 * \retval FAIL query was not processed successfully by the server
1669 * \retval NO_MORE_RESULTS query produced no results.
1670 *
1671 * \remarks Call dbresults() after calling dbsqlexec() or dbsqlok(), or dbrpcsend() returns SUCCEED. Unless
1672 * one of them fails, dbresults will return either SUCCEED or NO_MORE_RESULTS.
1673 *
1674 * The meaning of \em results is very specific and not very intuitive. Results are created by either
1675 * - a SELECT statement
1676 * - a stored procedure
1677 *
1678 * When dbresults returns SUCCEED, therefore, it indicates the server processed the query successfully and
1679 * that one or more of these is present:
1680 * - metadata -- dbnumcols() returns 1 or more
1681 * - data -- dbnextrow() returns SUCCEED
1682 * - return status -- dbhasretstat() returns TRUE
1683 * - output parameters -- dbnumrets() returns 1 or more
1684 *
1685 * If none of the above are present, dbresults() returns NO_MORE_RESULTS.
1686 *
1687 * SUCCEED does not imply that DBROWS() will return TRUE or even that dbnumcols() will return nonzero.
1688 * A general algorithm for reading results will call dbresults() until it return NO_MORE_RESULTS (or FAIL).
1689 * An application should check for all the above kinds of results within the dbresults() loop.
1690 *
1691 * \sa dbsqlexec(), dbsqlok(), dbrpcsend(), dbcancel(), DBROWS(), dbnextrow(), dbnumcols(), dbhasretstat(), dbretstatus(), dbnumrets()
1692 */
1693 RETCODE
dbresults(DBPROCESS * dbproc)1694 dbresults(DBPROCESS * dbproc)
1695 {
1696 RETCODE erc = _dbresults(dbproc);
1697
1698 tdsdump_log(TDS_DBG_FUNC, "dbresults returning %d (%s)\n", erc, prdbretcode(erc));
1699 return erc;
1700 }
1701
1702 static RETCODE
_dbresults(DBPROCESS * dbproc)1703 _dbresults(DBPROCESS * dbproc)
1704 {
1705 TDSSOCKET *tds;
1706 int result_type = 0, done_flags;
1707
1708 tdsdump_log(TDS_DBG_FUNC, "dbresults(%p)\n", dbproc);
1709 CHECK_CONN(FAIL);
1710
1711 tds = dbproc->tds_socket;
1712
1713 tdsdump_log(TDS_DBG_FUNC, "dbresults: dbresults_state is %d (%s)\n",
1714 dbproc->dbresults_state, prdbresults_state(dbproc->dbresults_state));
1715 switch ( dbproc->dbresults_state ) {
1716 case _DB_RES_SUCCEED:
1717 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
1718 return SUCCEED;
1719 break;
1720 case _DB_RES_RESULTSET_ROWS:
1721 dbperror(dbproc, SYBERPND, 0); /* dbresults called while rows outstanding.... */
1722 return FAIL;
1723 break;
1724 case _DB_RES_NO_MORE_RESULTS:
1725 return NO_MORE_RESULTS;
1726 break;
1727 default:
1728 break;
1729 }
1730
1731 for (;;) {
1732 TDSRET retcode = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS);
1733
1734 tdsdump_log(TDS_DBG_FUNC, "dbresults() tds_process_tokens returned %d (%s),\n\t\t\tresult_type %d (%s)\n",
1735 retcode, prretcode(retcode), result_type, prresult_type(result_type));
1736
1737 switch (retcode) {
1738
1739 case TDS_SUCCESS:
1740
1741 switch (result_type) {
1742
1743 case TDS_ROWFMT_RESULT:
1744 buffer_free(&dbproc->row_buf);
1745 buffer_alloc(dbproc);
1746 dbproc->dbresults_state = _DB_RES_RESULTSET_EMPTY;
1747 break;
1748
1749 case TDS_COMPUTEFMT_RESULT:
1750 break;
1751
1752 case TDS_ROW_RESULT:
1753 case TDS_COMPUTE_RESULT:
1754
1755 dbproc->dbresults_state = _DB_RES_RESULTSET_ROWS;
1756 return SUCCEED;
1757 break;
1758
1759 case TDS_DONE_RESULT:
1760 case TDS_DONEPROC_RESULT:
1761 tdsdump_log(TDS_DBG_FUNC, "dbresults(): dbresults_state is %d (%s)\n",
1762 dbproc->dbresults_state, prdbresults_state(dbproc->dbresults_state));
1763
1764 /* A done token signifies the end of a logical command.
1765 * There are three possibilities:
1766 * 1. Simple command with no result set, i.e. update, delete, insert
1767 * 2. Command with result set but no rows
1768 * 3. Command with result set and rows
1769 */
1770 switch (dbproc->dbresults_state) {
1771
1772 case _DB_RES_INIT:
1773 case _DB_RES_NEXT_RESULT:
1774 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
1775 if (done_flags & TDS_DONE_ERROR)
1776 return FAIL;
1777 if (result_type == TDS_DONE_RESULT) {
1778 tds_free_all_results(tds);
1779 return SUCCEED;
1780 }
1781 break;
1782
1783 case _DB_RES_RESULTSET_EMPTY:
1784 case _DB_RES_RESULTSET_ROWS:
1785 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
1786 return SUCCEED;
1787 break;
1788 default:
1789 assert(0);
1790 break;
1791 }
1792 break;
1793
1794 case TDS_DONEINPROC_RESULT:
1795 /*
1796 * Return SUCCEED on a command within a stored procedure
1797 * only if the command returned a result set.
1798 */
1799 switch (dbproc->dbresults_state) {
1800 case _DB_RES_INIT:
1801 case _DB_RES_NEXT_RESULT:
1802 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
1803 break;
1804 case _DB_RES_RESULTSET_EMPTY :
1805 case _DB_RES_RESULTSET_ROWS :
1806 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
1807 return SUCCEED;
1808 break;
1809 case _DB_RES_NO_MORE_RESULTS:
1810 case _DB_RES_SUCCEED:
1811 break;
1812 }
1813 break;
1814
1815 case TDS_STATUS_RESULT:
1816 case TDS_MSG_RESULT:
1817 case TDS_DESCRIBE_RESULT:
1818 case TDS_PARAM_RESULT:
1819 default:
1820 break;
1821 }
1822
1823 break;
1824
1825 case TDS_NO_MORE_RESULTS:
1826 dbproc->dbresults_state = _DB_RES_NO_MORE_RESULTS;
1827 return NO_MORE_RESULTS;
1828 break;
1829
1830 default:
1831 assert(TDS_FAILED(retcode));
1832 dbproc->dbresults_state = _DB_RES_INIT;
1833 return FAIL;
1834 break;
1835 }
1836 }
1837 }
1838
1839
1840 /**
1841 * \ingroup dblib_core
1842 * \brief Return number of regular columns in a result set.
1843 *
1844 * \param dbproc contains all information needed by db-lib to manage communications with the server.
1845 * \sa dbcollen(), dbcolname(), dbnumalts().
1846 */
1847 int
dbnumcols(DBPROCESS * dbproc)1848 dbnumcols(DBPROCESS * dbproc)
1849 {
1850 tdsdump_log(TDS_DBG_FUNC, "dbnumcols(%p)\n", dbproc);
1851 CHECK_PARAMETER(dbproc, SYBENULL, 0);
1852
1853 if (dbproc && dbproc->tds_socket && dbproc->tds_socket->res_info)
1854 return dbproc->tds_socket->res_info->num_cols;
1855 return 0;
1856 }
1857
1858 /**
1859 * \ingroup dblib_core
1860 * \brief Return name of a regular result column.
1861 *
1862 * \param dbproc contains all information needed by db-lib to manage communications with the server.
1863 * \param column Nth in the result set, starting with 1.
1864 * \return pointer to ASCII null-terminated string, the name of the column.
1865 * \retval NULL \a column is not in range.
1866 * \sa dbcollen(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols().
1867 * \bug Relies on ASCII column names, post iconv conversion.
1868 * Will not work as described for UTF-8 or UCS-2 clients.
1869 * But maybe it shouldn't.
1870 */
1871 char *
dbcolname(DBPROCESS * dbproc,int column)1872 dbcolname(DBPROCESS * dbproc, int column)
1873 {
1874 TDSCOLUMN *colinfo;
1875
1876 tdsdump_log(TDS_DBG_FUNC, "dbcolname(%p, %d)\n", dbproc, column);
1877
1878 colinfo = dbcolptr(dbproc, column);
1879 if (!colinfo)
1880 return NULL;
1881
1882 return tds_dstr_buf(&colinfo->column_name);
1883 }
1884
1885 /**
1886 * \ingroup dblib_core
1887 * \brief Return name of a computed result column.
1888 *
1889 * \param dbproc contains all information needed by db-lib to manage communications with the server.
1890 * \param computeid identifies which one of potientially many compute rows is meant. The first compute
1891 * clause has \a computeid == 1.
1892 * \param column Nth in the result set, starting with 1.
1893 * \return pointer to ASCII null-terminated string, the name of the column.
1894 * \retval NULL \a column is not in range.
1895 * \sa dbcollen(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols().
1896 */
1897 const char *
dbacolname(DBPROCESS * dbproc,int computeid,int column)1898 dbacolname(DBPROCESS * dbproc, int computeid, int column)
1899 {
1900 TDSCOLUMN *colinfo;
1901
1902 tdsdump_log(TDS_DBG_FUNC, "dbacolname(%p, %d, %d)\n", dbproc, computeid, column);
1903
1904 colinfo = dbacolptr(dbproc, computeid, column, 1);
1905 if (!colinfo)
1906 return NULL;
1907
1908 return tds_dstr_cstr(&colinfo->column_name);
1909 }
1910
1911 /**
1912 * \ingroup dblib_core
1913 * \brief Read a row from the row buffer.
1914 *
1915 * When row buffering is enabled (DBBUFFER option is on), the client can use dbgetrow() to re-read a row previously fetched
1916 * with dbnextrow(). The effect is to move the row pointer -- analogous to fseek() -- back to \a row.
1917 * Calls to dbnextrow() read from \a row + 1 until the buffer is exhausted, at which point it resumes
1918 * its normal behavior, except that as each row is fetched from the server, it is added to the row
1919 * buffer (in addition to being returned to the client). When the buffer is filled, dbnextrow() returns
1920 * \c FAIL until the buffer is at least partially emptied with dbclrbuf().
1921 * \param dbproc contains all information needed by db-lib to manage communications with the server.
1922 * \param row Nth row to read, starting with 1.
1923 * \retval REG_ROW returned row is a regular row.
1924 * \returns computeid when returned row is a compute row.
1925 * \retval NO_MORE_ROWS no such row in the row buffer. Current row is unchanged.
1926 * \retval FAIL unsuccessful; row buffer may be full.
1927 * \sa dbaltbind(), dbbind(), dbclrbuf(), DBCURROW(), DBFIRSTROW(), DBLASTROW(), dbnextrow(), dbsetrow().
1928 */
1929 RETCODE
dbgetrow(DBPROCESS * dbproc,DBINT row)1930 dbgetrow(DBPROCESS * dbproc, DBINT row)
1931 {
1932 RETCODE result = FAIL;
1933 const int idx = buffer_row2idx(&dbproc->row_buf, row);
1934
1935 tdsdump_log(TDS_DBG_FUNC, "dbgetrow(%p, %d)\n", dbproc, row);
1936 CHECK_CONN(FAIL);
1937
1938 if (-1 == idx)
1939 return NO_MORE_ROWS;
1940
1941 dbproc->row_buf.current = idx;
1942 buffer_transfer_bound_data(&dbproc->row_buf, TDS_ROW_RESULT, 0, dbproc, idx);
1943 result = REG_ROW;
1944
1945 return result;
1946 }
1947
1948 /**
1949 * \ingroup dblib_core
1950 * \brief Define substitution values to be used when binding null values.
1951 *
1952 * \param dbproc contains all information needed by db-lib to manage communications with the server.
1953 * \param bindtype type of binding to which the substitute value will apply.
1954 * \param bindlen size of the substitute value you are supplying, in bytes.
1955 * Ignored except for CHARBIND and BINARYBIND.
1956 * \param bindval pointer to a buffer containing the substitute value.
1957 * \retval SUCCEED query was processed without errors.
1958 * \retval FAIL query was not processed
1959 * \sa dbaltbind(), dbbind(), dbconvert(), dbnullbind().
1960 */
1961 RETCODE
dbsetnull(DBPROCESS * dbproc,int bindtype,int bindlen,BYTE * bindval)1962 dbsetnull(DBPROCESS * dbproc, int bindtype, int bindlen, BYTE *bindval)
1963 {
1964 BYTE *pval;
1965
1966 tdsdump_log(TDS_DBG_FUNC, "dbsetnull(%p, %d, %d, %p)\n", dbproc, bindtype, bindlen, bindval);
1967
1968 CHECK_CONN(FAIL);
1969 CHECK_PARAMETER(bindval, SYBENBVP, FAIL);
1970
1971 switch (bindtype) {
1972 case DATETIMEBIND:
1973 case DECIMALBIND:
1974 case SRCDECIMALBIND:
1975 case FLT8BIND:
1976 case INTBIND:
1977 case MONEYBIND:
1978 case NUMERICBIND:
1979 case SRCNUMERICBIND:
1980 case REALBIND:
1981 case SMALLBIND:
1982 case SMALLDATETIMEBIND:
1983 case SMALLMONEYBIND:
1984 case TINYBIND:
1985 case BIGINTBIND:
1986 case DATEBIND:
1987 case TIMEBIND:
1988 case BIGDATETIMEBIND:
1989 case BIGTIMEBIND:
1990 bindlen = (int)default_null_representations[bindtype].len;
1991 break;
1992
1993 case CHARBIND:
1994 case BINARYBIND:
1995 CHECK_PARAMETER(bindlen >= 0, SYBEBBL, FAIL);
1996 break;
1997
1998 case NTBSTRINGBIND: bindlen = (int)strlen((char *) bindval);
1999 break;
2000 case STRINGBIND: bindlen = (int)strlen((char *) bindval);
2001 break;
2002 case VARYBINBIND: bindlen = ((DBVARYBIN*) bindval)->len;
2003 break;
2004 case VARYCHARBIND: bindlen = ((DBVARYCHAR*) bindval)->len;
2005 break;
2006
2007 #if 0
2008 case SENSITIVITYBIND:
2009 case BOUNDARYBIND:
2010 #endif
2011 default:
2012 dbperror(dbproc, SYBEBTYP, 0);
2013 return FAIL;
2014 }
2015
2016 if ((pval = tds_new(BYTE, bindlen)) == NULL) {
2017 dbperror(dbproc, SYBEMEM, errno);
2018 return FAIL;
2019 }
2020
2021 /* free any prior allocation */
2022 if (dbproc->nullreps[bindtype].bindval != default_null_representations[bindtype].bindval)
2023 free((BYTE*)dbproc->nullreps[bindtype].bindval);
2024
2025 memcpy(pval, bindval, bindlen);
2026
2027 dbproc->nullreps[bindtype].bindval = pval;
2028 dbproc->nullreps[bindtype].len = bindlen;
2029
2030 tdsdump_dump_buf(TDS_DBG_NETWORK, "null representation set ", pval, bindlen);
2031 return SUCCEED;
2032 }
2033
2034 /**
2035 * \ingroup dblib_core
2036 * \brief Make a buffered row "current" without fetching it into bound variables.
2037 *
2038 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2039 * \retval MORE_ROWS row found
2040 * \retval NO_MORE_ROWS row not found
2041 * \retval FAIL \a dbproc is dead or not enabled
2042 * \sa dbaltbind(), dbbind(), dbcanquery(), dbclrbuf(), dbgetrow(), dbnextrow(), dbprrow().
2043 */
2044 STATUS
dbsetrow(DBPROCESS * dbproc,DBINT row)2045 dbsetrow(DBPROCESS * dbproc, DBINT row)
2046 {
2047 const int idx = buffer_row2idx(&dbproc->row_buf, row);
2048
2049 tdsdump_log(TDS_DBG_FUNC, "dbsetrow(%p, %d)\n", dbproc, row);
2050 CHECK_CONN(FAIL);
2051
2052 if (-1 == idx)
2053 return NO_MORE_ROWS;
2054
2055 dbproc->row_buf.current = idx;
2056
2057 /* FIXME: should determine REG_ROW or compute_id; */
2058 return REG_ROW;
2059 }
2060
2061 /**
2062 * \ingroup dblib_core
2063 * \brief Read result row into the row buffer and into any bound host variables.
2064 *
2065 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2066 * \retval REG_ROW regular row has been read.
2067 * \returns computeid when a compute row is read.
2068 * \retval BUF_FULL reading next row would cause the buffer to be exceeded (and buffering is turned on).
2069 * No row was read from the server
2070 * \sa dbaltbind(), dbbind(), dbcanquery(), dbclrbuf(), dbgetrow(), dbprrow(), dbsetrow().
2071 */
2072 struct pivot_t;
2073 STATUS
dbnextrow(DBPROCESS * dbproc)2074 dbnextrow(DBPROCESS * dbproc)
2075 {
2076 TDSRESULTINFO *resinfo;
2077 TDSSOCKET *tds;
2078 STATUS result = FAIL;
2079 TDS_INT res_type;
2080 TDS_INT computeid;
2081 int idx; /* row buffer index. Unless DBUFFER is on, idx will always be 0. */
2082 struct pivot_t *pivot;
2083
2084 tdsdump_log(TDS_DBG_FUNC, "dbnextrow(%p)\n", dbproc);
2085 CHECK_CONN(FAIL);
2086
2087 tds = dbproc->tds_socket;
2088 resinfo = tds->res_info;
2089
2090 tdsdump_log(TDS_DBG_FUNC, "dbnextrow() dbresults_state = %d (%s)\n",
2091 dbproc->dbresults_state, prdbresults_state(dbproc->dbresults_state));
2092
2093 if (!resinfo || dbproc->dbresults_state != _DB_RES_RESULTSET_ROWS) {
2094 /* no result set or result set empty (no rows) */
2095 tdsdump_log(TDS_DBG_FUNC, "leaving dbnextrow() returning %d (NO_MORE_ROWS)\n", NO_MORE_ROWS);
2096 return dbproc->row_type = NO_MORE_ROWS;
2097 }
2098
2099 /*
2100 * Try to get the dbproc->row_buf.current item from the buffered rows, if any.
2101 * Else read from the stream, unless the buffer is exhausted.
2102 * If no rows are read, DBROWTYPE() will report NO_MORE_ROWS.
2103 */
2104 dbproc->row_type = NO_MORE_ROWS;
2105 computeid = REG_ROW;
2106 if (-1 != (idx = buffer_current_index(dbproc))) {
2107 /*
2108 * Cool, the item we want is already there
2109 */
2110 result = dbproc->row_type = REG_ROW;
2111 res_type = TDS_ROW_RESULT;
2112
2113 } else if (buffer_is_full(&dbproc->row_buf)) {
2114
2115 result = BUF_FULL;
2116 res_type = TDS_ROWFMT_RESULT;
2117
2118 } else if ((pivot = dbrows_pivoted(dbproc)) != NULL) {
2119
2120 tdsdump_log(TDS_DBG_FUNC, "returning pivoted row\n");
2121 return dbnextrow_pivoted(dbproc, pivot);
2122
2123 } else {
2124 const int mask = TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE;
2125 TDS_INT8 row_count = TDS_NO_COUNT;
2126 bool rows_set = false;
2127 buffer_save_row(dbproc);
2128
2129 /* Get the row from the TDS stream. */
2130 again:
2131 switch (tds_process_tokens(tds, &res_type, NULL, mask)) {
2132 case TDS_SUCCESS:
2133 if (res_type == TDS_ROW_RESULT || res_type == TDS_COMPUTE_RESULT) {
2134 if (res_type == TDS_COMPUTE_RESULT)
2135 computeid = tds->current_results->computeid;
2136 /* Add the row to the row buffer, whose capacity is always at least 1 */
2137 resinfo = tds->current_results;
2138 idx = buffer_add_row(dbproc, resinfo);
2139 assert(idx != -1);
2140 result = dbproc->row_type = (res_type == TDS_ROW_RESULT)? REG_ROW : computeid;
2141 #if 0 /* TODO */
2142 tds_process_tokens(tds, &res_type, NULL, TDS_TOKEN_TRAILING);
2143 #endif
2144 break;
2145 }
2146 /* allows to process trailing tokens */
2147 if (res_type == TDS_DONEINPROC_RESULT) {
2148 if (!rows_set)
2149 row_count = tds->rows_affected;
2150 rows_set = true;
2151 goto again;
2152 }
2153 /* fall through */
2154 case TDS_NO_MORE_RESULTS:
2155 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
2156 result = NO_MORE_ROWS;
2157 break;
2158 default:
2159 tdsdump_log(TDS_DBG_FUNC, "unexpected: leaving dbnextrow() returning FAIL\n");
2160 return FAIL;
2161 break;
2162 }
2163 if (rows_set)
2164 tds->rows_affected = row_count;
2165 }
2166
2167 if (res_type == TDS_ROW_RESULT || res_type == TDS_COMPUTE_RESULT) {
2168 /*
2169 * Transfer the data from the row buffer to the bound variables.
2170 */
2171 buffer_transfer_bound_data(&dbproc->row_buf, res_type, computeid, dbproc, idx);
2172 }
2173
2174 if (res_type == TDS_COMPUTE_RESULT) {
2175 tdsdump_log(TDS_DBG_FUNC, "leaving dbnextrow() returning compute_id %d\n", result);
2176 } else {
2177 tdsdump_log(TDS_DBG_FUNC, "leaving dbnextrow() returning %d (%s)\n", result, prdbretcode(result));
2178 }
2179 return result;
2180 } /* dbnextrow() */
2181
2182 static TDS_SERVER_TYPE
dblib_bound_type(int bindtype)2183 dblib_bound_type(int bindtype)
2184 {
2185 switch (bindtype) {
2186 case CHARBIND:
2187 case STRINGBIND:
2188 case NTBSTRINGBIND:
2189 return SYBCHAR;
2190 break;
2191 case FLT8BIND:
2192 return SYBFLT8;
2193 break;
2194 case REALBIND:
2195 return SYBREAL;
2196 break;
2197 case INTBIND:
2198 return SYBINT4;
2199 break;
2200 case SMALLBIND:
2201 return SYBINT2;
2202 break;
2203 case TINYBIND:
2204 return SYBINT1;
2205 break;
2206 case BIGINTBIND:
2207 return SYBINT8;
2208 break;
2209 case DATETIMEBIND:
2210 return SYBDATETIME;
2211 break;
2212 case SMALLDATETIMEBIND:
2213 return SYBDATETIME4;
2214 break;
2215 case DATEBIND:
2216 return SYBDATE;
2217 break;
2218 case TIMEBIND:
2219 return SYBTIME;
2220 break;
2221 case BIGDATETIMEBIND:
2222 return SYB5BIGDATETIME;
2223 break;
2224 case BIGTIMEBIND:
2225 return SYB5BIGTIME;
2226 break;
2227 case MONEYBIND:
2228 return SYBMONEY;
2229 break;
2230 case SMALLMONEYBIND:
2231 return SYBMONEY4;
2232 break;
2233 case BINARYBIND:
2234 return SYBBINARY;
2235 break;
2236 case VARYBINBIND:
2237 return SYBVARBINARY;
2238 break;
2239 case VARYCHARBIND:
2240 return SYBVARCHAR;
2241 break;
2242 case BITBIND:
2243 return SYBBIT;
2244 break;
2245 case NUMERICBIND:
2246 case SRCNUMERICBIND:
2247 case DECIMALBIND:
2248 case SRCDECIMALBIND:
2249 return SYBNUMERIC;
2250 break;
2251 case DATETIME2BIND:
2252 return SYBMSDATETIMEOFFSET;
2253 break;
2254 default:
2255 return TDS_INVALID_TYPE;
2256 }
2257 }
2258
2259 /**
2260 * \ingroup dblib_core
2261 * \brief Convert one datatype to another.
2262 *
2263 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2264 * \param srctype datatype of the data to convert.
2265 * \param src buffer to convert
2266 * \param srclen length of \a src
2267 * \param desttype target datatype
2268 * \param dest output buffer
2269 * \param destlen size of \a dest
2270 * \returns On success, the count of output bytes in \a dest, else -1. On failure, it will call any user-supplied error handler.
2271 * \remarks Causes of failure:
2272 * - No such conversion unavailable.
2273 * - Character data output was truncated, or numerical data overflowed or lost precision.
2274 * - In converting character data to one of the numeric types, the string could not be interpreted as a number.
2275 *
2276 * Conversion functions are handled in the TDS layer.
2277 *
2278 * The main reason for this is that \c ct-lib and \c ODBC (and presumably \c DBI) need
2279 * to be able to do conversions between datatypes. This is possible because
2280 * the format of complex data (dates, money, numeric, decimal) is defined by
2281 * its representation on the wire; thus what we call \c DBMONEY is exactly its
2282 * format on the wire. CLIs that need a different representation (ODBC?)
2283 * need to convert from this format anyway, so the code would already be in
2284 * place.
2285 *
2286 * Each datatype is also defined by its Server-type so all CLIs should be
2287 * able to map native types to server types as well.
2288 *
2289 * tds_convert() copies from src to dest and returns the output data length,
2290 * period. All padding and termination is the responsibility of the API library
2291 * and is done post-conversion. The peculiar rule in dbconvert() is that
2292 * a \a destlen of -1 and a \a desttype of \c SYBCHAR means the output buffer
2293 * should be null-terminated.
2294 *
2295 * \sa dbaltbind(), dbaltbind_ps(), dbbind(), dbbind_ps(), dbconvert_ps(), dberrhandle(), dbsetnull(), dbsetversion(), dbwillconvert().
2296 * \todo What happens if client does not reset values?
2297 * \todo Microsoft and Sybase define this function differently.
2298 */
2299 DBINT
dbconvert_ps(DBPROCESS * dbproc,int db_srctype,const BYTE * src,DBINT srclen,int db_desttype,BYTE * dest,DBINT destlen,DBTYPEINFO * typeinfo)2300 dbconvert_ps(DBPROCESS * dbproc, int db_srctype, const BYTE * src, DBINT srclen,
2301 int db_desttype, BYTE * dest, DBINT destlen, DBTYPEINFO * typeinfo)
2302 {
2303 CONV_RESULT dres;
2304 DBINT ret;
2305 int i;
2306 int len;
2307 TDS_SERVER_TYPE srctype, desttype;
2308
2309 tdsdump_log(TDS_DBG_FUNC, "dbconvert_ps(%p, %s, %p, %d, %s, %p, %d, %p)\n",
2310 dbproc, tds_prdatatype(db_srctype), src, srclen,
2311 tds_prdatatype(db_desttype), dest, destlen, typeinfo);
2312 /* dbproc and src can be NULLs */
2313 CHECK_PARAMETER(dest, SYBEACNV, -1);
2314
2315 DBPERROR_RETURN(!is_tds_type_valid(db_srctype), SYBEUDTY);
2316 srctype = (TDS_SERVER_TYPE) db_srctype;
2317 DBPERROR_RETURN(!is_tds_type_valid(db_desttype), SYBEUDTY);
2318 desttype = (TDS_SERVER_TYPE) db_desttype;
2319
2320 if (is_numeric_type(desttype)) {
2321 TDS_NUMERIC *d = &dres.n;
2322
2323 if (typeinfo == NULL) {
2324 if (is_numeric_type(srctype)) {
2325 DBNUMERIC *s = (DBNUMERIC *) src;
2326 d->precision = s->precision;
2327 d->scale = s->scale;
2328 } else {
2329 d->precision = 18;
2330 d->scale = 0;
2331 }
2332 } else {
2333 d->precision = typeinfo->precision;
2334 d->scale = typeinfo->scale;
2335 }
2336 }
2337
2338 if (0 == destlen)
2339 return 0;
2340
2341 if (src == NULL || srclen == 0) {
2342 int bind = dbbindtype(desttype);
2343 int size = tds_get_size_by_type(desttype);
2344
2345 if (bind == NTBSTRINGBIND) {
2346 if (destlen > 0) {
2347 size = destlen;
2348 bind = CHARBIND;
2349 } else {
2350 size = 1;
2351 bind = NTBSTRINGBIND;
2352 }
2353 } else if (bind == BINARYBIND) {
2354 if (destlen > 0)
2355 size = destlen;
2356 else
2357 size = 0;
2358 }
2359
2360 dbgetnull(dbproc, bind, size, dest);
2361 return size;
2362 }
2363
2364 /* srclen of -1 means the source data is definitely NULL terminated */
2365 if (srclen == -1)
2366 srclen = (int)strlen((const char *) src);
2367
2368 /* often times we are asked to convert a data type to itself */
2369 if (srctype == desttype && !is_numeric_type(desttype)) {
2370 ret = -2; /* to make sure we always set it */
2371 tdsdump_log(TDS_DBG_INFO1, "dbconvert_ps() srctype == desttype\n");
2372 switch (desttype) {
2373
2374 case SYBBINARY:
2375 case SYBVARBINARY:
2376 case SYBIMAGE:
2377 if (srclen > destlen && destlen >= 0) {
2378 dbperror(dbproc, SYBECOFL, 0);
2379 ret = -1;
2380 } else {
2381 memcpy(dest, src, srclen);
2382 if (srclen < destlen)
2383 memset(dest + srclen, 0, destlen - srclen);
2384 ret = srclen;
2385 }
2386 break;
2387
2388 case SYBCHAR:
2389 case SYBVARCHAR:
2390 case SYBTEXT:
2391 /* srclen of -1 means the source data is definitely NULL terminated */
2392 if (srclen == -1)
2393 srclen = (int)strlen((const char *) src);
2394
2395 switch (destlen) {
2396 case 0: /* nothing to copy */
2397 ret = 0;
2398 break;
2399 case -1: /* rtrim and null terminate */
2400 while (srclen && src[srclen - 1] == ' ') {
2401 --srclen;
2402 }
2403 /* fall thru */
2404 case -2: /* just null terminate */
2405 memcpy(dest, src, srclen);
2406 dest[srclen] = '\0';
2407 ret = srclen;
2408 break;
2409 default:
2410 assert(destlen > 0);
2411 if (destlen < 0 || srclen > destlen) {
2412 dbperror(dbproc, SYBECOFL, 0);
2413 ret = -1;
2414 } else {
2415 memcpy(dest, src, srclen);
2416 if (srclen < destlen)
2417 memset(dest + srclen, ' ', destlen - srclen);
2418 ret = srclen;
2419 }
2420 break;
2421 }
2422 break;
2423 case SYBINT1:
2424 case SYBINT2:
2425 case SYBINT4:
2426 case SYBINT8:
2427 case SYBFLT8:
2428 case SYBREAL:
2429 case SYBBIT:
2430 case SYBBITN:
2431 case SYBMONEY:
2432 case SYBMONEY4:
2433 case SYBDATETIME:
2434 case SYBDATETIME4:
2435 case SYBDATE:
2436 case SYBTIME:
2437 case SYB5BIGDATETIME:
2438 case SYB5BIGTIME:
2439 case SYBUNIQUE:
2440 ret = tds_get_size_by_type(desttype);
2441 memcpy(dest, src, ret);
2442 break;
2443
2444 case SYBMSDATE:
2445 case SYBMSTIME:
2446 case SYBMSDATETIME2:
2447 case SYBMSDATETIMEOFFSET:
2448 ret = sizeof(TDS_DATETIMEALL);
2449 memcpy(dest, src, ret);
2450 break;
2451
2452 default:
2453 ret = -1;
2454 break;
2455 }
2456 assert(ret > -2);
2457 return ret;
2458 }
2459 /* end srctype == desttype */
2460
2461 /*
2462 * Character types need no conversion. Just move the data.
2463 */
2464 if (is_similar_type(srctype, desttype)) {
2465 if (src && dest && srclen > 0 && destlen >= srclen) {
2466 memcpy(dest, src, srclen);
2467 return srclen;
2468 }
2469 }
2470
2471 tdsdump_log(TDS_DBG_INFO1, "dbconvert_ps() calling tds_convert\n");
2472
2473 len = tds_convert(g_dblib_ctx.tds_ctx, srctype, src, srclen, desttype, &dres);
2474 tdsdump_log(TDS_DBG_INFO1, "dbconvert_ps() called tds_convert returned %d\n", len);
2475
2476 if (len < 0) {
2477 _dblib_convert_err(dbproc, len);
2478 return -1;
2479 }
2480
2481 switch (desttype) {
2482 case SYBBINARY:
2483 case SYBVARBINARY:
2484 case SYBIMAGE:
2485 if (len > destlen && destlen >= 0) {
2486 dbperror(dbproc, SYBECOFL, 0);
2487 ret = -1;
2488 } else {
2489 memcpy(dest, dres.ib, len);
2490 if (len < destlen)
2491 memset(dest + len, 0, destlen - len);
2492 ret = len;
2493 }
2494 free(dres.ib);
2495 break;
2496 case SYBINT1:
2497 case SYBINT2:
2498 case SYBINT4:
2499 case SYBINT8:
2500 case SYBFLT8:
2501 case SYBREAL:
2502 case SYBBIT:
2503 case SYBBITN:
2504 case SYBMONEY:
2505 case SYBMONEY4:
2506 case SYBDATETIME:
2507 case SYBDATETIME4:
2508 case SYBTIME:
2509 case SYBDATE:
2510 case SYB5BIGDATETIME:
2511 case SYB5BIGTIME:
2512 case SYBUNIQUE:
2513 case SYBMSDATE:
2514 case SYBMSTIME:
2515 case SYBMSDATETIME2:
2516 case SYBMSDATETIMEOFFSET:
2517 case SYBNUMERIC:
2518 case SYBDECIMAL:
2519 memcpy(dest, &(dres.ti), len);
2520 ret = len;
2521 break;
2522 case SYBCHAR:
2523 case SYBVARCHAR:
2524 case SYBTEXT:
2525 tdsdump_log(TDS_DBG_INFO1, "dbconvert_ps() outputting %d bytes character data destlen = %d \n", len, destlen);
2526
2527 if (destlen < -2)
2528 destlen = 0; /* failure condition */
2529
2530 switch (destlen) {
2531 case 0:
2532 ret = -1;
2533 break;
2534 case -1: /* rtrim and null terminate */
2535 for (i = len - 1; i >= 0 && dres.c[i] == ' '; --i) {
2536 len = i;
2537 }
2538 memcpy(dest, dres.c, len);
2539 dest[len] = '\0';
2540 ret = len;
2541 break;
2542 case -2: /* just null terminate */
2543 memcpy(dest, dres.c, len);
2544 dest[len] = 0;
2545 ret = len;
2546 break;
2547 default:
2548 assert(destlen > 0);
2549 if (destlen < 0 || len > destlen) {
2550 dbperror(dbproc, SYBECOFL, 0);
2551 ret = -1;
2552 tdsdump_log(TDS_DBG_INFO1, "%d bytes type %d -> %d, destlen %d < %d required\n",
2553 srclen, srctype, desttype, destlen, len);
2554 break;
2555 }
2556 /* else pad with blanks */
2557 memcpy(dest, dres.c, len);
2558 if (len < destlen)
2559 memset(dest + len, ' ', destlen - len);
2560 ret = len;
2561
2562 break;
2563 }
2564
2565 free(dres.c);
2566
2567 break;
2568 default:
2569 tdsdump_log(TDS_DBG_INFO1, "error: dbconvert_ps(): unrecognized desttype %d \n", desttype);
2570 ret = -1;
2571 break;
2572
2573 }
2574 return (ret);
2575 }
2576
2577 /**
2578 * \ingroup dblib_core
2579 * \brief cf. dbconvert_ps(), above
2580 *
2581 * \em Sybase: Convert numeric types.
2582 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2583 * \param srctype datatype of the data to convert.
2584 * \param src buffer to convert
2585 * \param srclen length of \a src
2586 * \param desttype target datatype
2587 * \param dest output buffer
2588 * \param destlen size of \a dest
2589 * \param typeinfo address of a \c DBTYPEINFO structure that governs the precision & scale of the output, may be \c NULL.
2590 * \sa dbaltbind(), dbaltbind_ps(), dbbind(), dbbind_ps(), dbconvert_ps(), dberrhandle(), dbsetnull(), dbsetversion(), dbwillconvert().
2591 */
2592 DBINT
dbconvert(DBPROCESS * dbproc,int srctype,const BYTE * src,DBINT srclen,int desttype,BYTE * dest,DBINT destlen)2593 dbconvert(DBPROCESS * dbproc,
2594 int srctype, const BYTE * src, DBINT srclen, int desttype, BYTE * dest, DBINT destlen)
2595 {
2596 DBTYPEINFO ti, *pti = NULL;
2597
2598 tdsdump_log(TDS_DBG_FUNC, "dbconvert(%p)\n", dbproc);
2599 /* dbproc can be NULL*/
2600
2601 DBPERROR_RETURN(!is_tds_type_valid(desttype), SYBEUDTY);
2602
2603 if (is_numeric_type(desttype)) {
2604 DBNUMERIC *num;
2605
2606 /* FIXME what happen if client do not reset values ??? */
2607 if (dbproc->msdblib) {
2608 num = (DBNUMERIC *) dest;
2609 ti.precision = num->precision;
2610 ti.scale = num->scale;
2611 pti = &ti;
2612 } else {
2613 /* for Sybase passing NULL as DBTYPEINFO is fine */
2614 }
2615 }
2616
2617 return dbconvert_ps(dbproc, srctype, src, srclen, desttype, dest, destlen, pti);
2618 }
2619
2620 /**
2621 * \ingroup dblib_core
2622 * \brief Tie a host variable to a resultset column.
2623 *
2624 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2625 * \param column Nth column, starting at 1.
2626 * \param vartype datatype of the host variable that will receive the data
2627 * \param varlen size of host variable pointed to \a varaddr
2628 * \param varaddr address of host variable
2629 * \retval SUCCEED everything worked.
2630 * \retval FAIL no such \a column or no such conversion possible, or target buffer too small.
2631 * \sa
2632 */
2633 RETCODE
dbbind(DBPROCESS * dbproc,int column,int vartype,DBINT varlen,BYTE * varaddr)2634 dbbind(DBPROCESS * dbproc, int column, int vartype, DBINT varlen, BYTE * varaddr)
2635 {
2636 TDSCOLUMN *colinfo = NULL;
2637 TDSRESULTINFO* results;
2638 TDS_SERVER_TYPE srctype, desttype;
2639
2640 tdsdump_log(TDS_DBG_FUNC, "dbbind(%p, %d, %d, %d, %p)\n", dbproc, column, vartype, varlen, varaddr);
2641 CHECK_CONN(FAIL);
2642 CHECK_PARAMETER(varaddr, SYBEABNV, FAIL);
2643
2644 results = dbproc->tds_socket->res_info;
2645
2646 if (results == NULL || results->num_cols < column || column < 1) {
2647 dbperror(dbproc, SYBEABNC, 0);
2648 return FAIL;
2649 }
2650
2651 if (varlen < 0) {
2652 switch (vartype) {
2653 case CHARBIND:
2654 case STRINGBIND:
2655 case NTBSTRINGBIND:
2656 case VARYCHARBIND:
2657 case VARYBINBIND:
2658 /*
2659 * No message for this error. Documentation doesn't define varlen < 0, but
2660 * experimentation with Sybase db-lib shows it's accepted as if zero.
2661 */
2662 tdsdump_log(TDS_DBG_FUNC, "dbbind: setting varlen (%d) to 0\n", varlen);
2663 varlen = 0;
2664 break;
2665 }
2666 }
2667
2668 if (0 == varlen) { /* "Note that if varlen is 0, no padding takes place." */
2669 switch (vartype) {
2670 case CHARBIND:
2671 case STRINGBIND:
2672 case NTBSTRINGBIND:
2673 varlen = -1;
2674 break;
2675 default:
2676 break; /* dbconvert: "The destlen is ignored for all fixed-length, non-NULL data types." */
2677 }
2678 }
2679
2680 dbproc->avail_flag = FALSE;
2681
2682 colinfo = dbproc->tds_socket->res_info->columns[column - 1];
2683 srctype = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
2684 desttype = dblib_bound_type(vartype);
2685 if (desttype == TDS_INVALID_TYPE) {
2686 dbperror(dbproc, SYBEBTYP, 0);
2687 return FAIL;
2688 }
2689
2690 if (!dbwillconvert(srctype, desttype)) {
2691 dbperror(dbproc, SYBEABMT, 0);
2692 return FAIL;
2693 }
2694
2695 colinfo->column_varaddr = (char *) varaddr;
2696 colinfo->column_bindtype = vartype;
2697 colinfo->column_bindlen = varlen;
2698
2699 return SUCCEED;
2700 } /* dbbind() */
2701
2702 /**
2703 * \ingroup dblib_core
2704 * \brief set name and location of the \c interfaces file FreeTDS should use to look up a servername.
2705 *
2706 * Does not affect lookups or location of \c freetds.conf.
2707 * \param filename name of \c interfaces
2708 * \sa dbopen()
2709 */
2710 void
dbsetifile(char * filename)2711 dbsetifile(char *filename)
2712 {
2713 tdsdump_log(TDS_DBG_FUNC, "dbsetifile(%s)\n", filename? filename : "0x00");
2714 if (filename == NULL) {
2715 dbperror(NULL, SYBENULP, 0);
2716 return;
2717 }
2718 tds_set_interfaces_file_loc(filename);
2719 }
2720
2721 /**
2722 * \ingroup dblib_core
2723 * \brief Tie a null-indicator to a regular result column.
2724 *
2725 *
2726 * When a row is fetched, the indicator variable tells the state of the column's data.
2727 *
2728 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2729 * \param column Nth column in the result set, starting with 1.
2730 * \param indicator address of host variable.
2731 * \retval SUCCEED variable accepted.
2732 * \retval FAIL \a indicator is NULL or \a column is out of range.
2733 * \remarks Contents of \a indicator are set with \c dbnextrow(). Possible values are:
2734 * - 0 \a column bound successfully
2735 * - -1 \a column is NULL.
2736 * - >0 true length of data, had \a column not been truncated due to insufficient space in the columns bound host variable .
2737 * \sa dbanullbind(), dbbind(), dbdata(), dbdatlen(), dbnextrow().
2738 */
2739 RETCODE
dbnullbind(DBPROCESS * dbproc,int column,DBINT * indicator)2740 dbnullbind(DBPROCESS * dbproc, int column, DBINT * indicator)
2741 {
2742 TDSCOLUMN *colinfo;
2743
2744 tdsdump_log(TDS_DBG_FUNC, "dbnullbind(%p, %d, %p)\n", dbproc, column, indicator);
2745
2746 colinfo = dbcolptr(dbproc, column);
2747 if (!colinfo)
2748 return FAIL; /* dbcolptr sent SYBECNOR, Column number out of range */
2749
2750 colinfo->column_nullbind = (TDS_SMALLINT *)indicator;
2751 return SUCCEED;
2752 }
2753
2754 /**
2755 * \ingroup dblib_core
2756 * \brief Tie a null-indicator to a compute result column.
2757 *
2758 *
2759 * When a row is fetched, the indicator variable tells the state of the column's data.
2760 *
2761 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2762 * \param computeid identifies which one of potientially many compute rows is meant. The first compute
2763 * clause has \a computeid == 1.
2764 * \param column Nth column in the result set, starting with 1.
2765 * \param indicator address of host variable.
2766 * \retval SUCCEED variable accepted.
2767 * \retval FAIL \a indicator is NULL or \a column is out of range.
2768 * \remarks Contents of \a indicator are set with \c dbnextrow(). Possible values are:
2769 * - 0 \a column bound successfully
2770 * - -1 \a column is NULL.
2771 * - >0 true length of data, had \a column not been truncated due to insufficient space in the columns bound host variable .
2772 * \sa dbadata(), dbadlen(), dbaltbind(), dbnextrow(), dbnullbind().
2773 * \todo Never fails, but only because failure conditions aren't checked.
2774 */
2775 RETCODE
dbanullbind(DBPROCESS * dbproc,int computeid,int column,DBINT * indicator)2776 dbanullbind(DBPROCESS * dbproc, int computeid, int column, DBINT * indicator)
2777 {
2778 TDSCOLUMN *curcol;
2779
2780 tdsdump_log(TDS_DBG_FUNC, "dbanullbind(%p, %d, %d, %p)\n", dbproc, computeid, column, indicator);
2781
2782 curcol = dbacolptr(dbproc, computeid, column, 1);
2783 if (!curcol)
2784 return FAIL;
2785
2786 /*
2787 * XXX Need to check for possibly problems before assuming
2788 * everything is okay
2789 */
2790 curcol->column_nullbind = (TDS_SMALLINT *)indicator;
2791
2792 return SUCCEED;
2793 }
2794
2795 /**
2796 * \ingroup dblib_core
2797 * \brief Indicates whether or not the count returned by dbcount is real (Microsoft-compatibility feature).
2798 *
2799 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2800 * \returns TRUE if the count returned by dbcount is real or FALSE if the count returned by dbcount is not real.
2801 * \sa DBCOUNT(), dbcount().
2802 */
2803 BOOL
dbiscount(DBPROCESS * dbproc)2804 dbiscount(DBPROCESS * dbproc)
2805 {
2806 tdsdump_log(TDS_DBG_FUNC, "dbiscount(%p)\n", dbproc);
2807 CHECK_PARAMETER(dbproc, SYBENULL, -1);
2808
2809 return dbproc->tds_socket && dbproc->tds_socket->rows_affected != TDS_NO_COUNT;
2810 }
2811
2812 /**
2813 * \ingroup dblib_core
2814 * \brief Get count of rows processed
2815 *
2816 *
2817 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2818 * \returns
2819 * - for insert/update/delete, count of rows affected.
2820 * - for select, count of rows returned, after all rows have been fetched.
2821 * \sa DBCOUNT(), dbnextrow(), dbresults().
2822 */
2823 DBINT
dbcount(DBPROCESS * dbproc)2824 dbcount(DBPROCESS * dbproc)
2825 {
2826 tdsdump_log(TDS_DBG_FUNC, "dbcount(%p)\n", dbproc);
2827 CHECK_PARAMETER(dbproc, SYBENULL, -1);
2828
2829 if (!dbproc->tds_socket || dbproc->tds_socket->rows_affected == TDS_NO_COUNT)
2830 return -1;
2831 return (DBINT)dbproc->tds_socket->rows_affected;
2832 }
2833
2834 /**
2835 * \ingroup dblib_core
2836 * \brief Clear \a n rows from the row buffer.
2837 *
2838 *
2839 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2840 * \param n number of rows to remove, >= 0.
2841 * \sa dbgetrow(), dbnextrow(), dbsetopt().
2842 */
2843 void
dbclrbuf(DBPROCESS * dbproc,DBINT n)2844 dbclrbuf(DBPROCESS * dbproc, DBINT n)
2845 {
2846 tdsdump_log(TDS_DBG_FUNC, "dbclrbuf(%p, %d)\n", dbproc, n);
2847 CHECK_PARAMETER(dbproc, SYBENULL, );
2848
2849 if (n <= 0)
2850 return;
2851
2852 if (dbproc->dbopts[DBBUFFER].factive) {
2853 DBPROC_ROWBUF * buf = &(dbproc->row_buf);
2854 int count = buffer_count(buf);
2855 if (n >= count)
2856 n = count - 1;
2857 buffer_delete_rows(&(dbproc->row_buf), n);
2858 }
2859 }
2860
2861 /**
2862 * \ingroup dblib_core
2863 * \brief Test whether or not a datatype can be converted to another datatype
2864 *
2865 * \param srctype type converting from
2866 * \param desttype type converting to
2867 * \remarks dbwillconvert() lies sometimes. Some datatypes \em should be convertible but aren't yet in our implementation.
2868 * Legal unimplemented conversions return \em TRUE.
2869 * \retval TRUE convertible, or should be.
2870 * \retval FAIL not convertible.
2871 * \sa dbaltbind(), dbbind(), dbconvert(), dbconvert_ps(), \c src/dblib/unittests/convert().c().
2872 */
2873 DBBOOL
dbwillconvert(int srctype,int desttype)2874 dbwillconvert(int srctype, int desttype)
2875 {
2876 tdsdump_log(TDS_DBG_FUNC, "dbwillconvert(%s, %s)\n", tds_prdatatype(srctype), tds_prdatatype(desttype));
2877 return tds_willconvert(srctype, desttype) ? TRUE : FALSE;
2878 }
2879
2880 static int
dblib_coltype(TDSCOLUMN * colinfo)2881 dblib_coltype(TDSCOLUMN *colinfo)
2882 {
2883 switch (colinfo->column_type) {
2884 case SYBVARCHAR:
2885 return SYBCHAR;
2886 case SYBVARBINARY:
2887 return SYBBINARY;
2888 case SYBLONGCHAR:
2889 case SYBUNITEXT:
2890 case SYBMSXML:
2891 return SYBTEXT;
2892 /* these types are handled by tds_get_conversion_type */
2893 case SYBBITN:
2894 case SYBDATEN:
2895 case SYBDATETIMN:
2896 case SYBFLTN:
2897 case SYBINTN:
2898 case SYBMONEYN:
2899 case SYBTIMEN:
2900 case SYBUINTN:
2901 break;
2902 /* these types are supported */
2903 case SYBCHAR:
2904 case SYBINT1:
2905 case SYBINT2:
2906 case SYBINT4:
2907 case SYBINT8:
2908 case SYBFLT8:
2909 case SYBDATETIME:
2910 case SYBBIT:
2911 case SYBTEXT:
2912 case SYBNTEXT:
2913 case SYBIMAGE:
2914 case SYBMONEY4:
2915 case SYBMONEY:
2916 case SYBDATETIME4:
2917 case SYBREAL:
2918 case SYBBINARY:
2919 case SYBVOID:
2920 case SYBNUMERIC:
2921 case SYBDECIMAL:
2922 case SYBNVARCHAR:
2923 case SYBDATE:
2924 case SYBTIME:
2925 case SYB5BIGDATETIME:
2926 case SYB5BIGTIME:
2927 case SYBMSDATE:
2928 case SYBMSTIME:
2929 case SYBMSDATETIME2:
2930 case SYBMSDATETIMEOFFSET:
2931 break;
2932 /* exclude cardinal types */
2933 case XSYBVARBINARY:
2934 case XSYBBINARY:
2935 case XSYBNVARCHAR:
2936 case XSYBVARCHAR:
2937 case XSYBNCHAR:
2938 case SYB5INT8:
2939 break;
2940 #if !ENABLE_EXTRA_CHECKS
2941 /* TODO these should be handled in some ways */
2942 case SYBUNIQUE: /* or SYBBLOB */
2943 case SYBVARIANT:
2944 case SYBMSUDT:
2945 case SYBLONGBINARY:
2946 case SYBUINT1:
2947 case SYBUINT2:
2948 case SYBUINT4:
2949 case SYBUINT8:
2950 case SYBINTERVAL:
2951 case SYBSINT1:
2952 case SYBXML:
2953 break;
2954 #endif
2955 }
2956 return tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
2957 }
2958
2959 /**
2960 * \ingroup dblib_core
2961 * \brief Get the datatype of a regular result set column.
2962 *
2963 *
2964 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2965 * \param column Nth in the result set, starting from 1.
2966 * \returns \c SYB* datetype token value, or zero if \a column out of range
2967 * \sa dbcollen(), dbcolname(), dbdata(), dbdatlen(), dbnumcols(), dbprtype(), dbvarylen().
2968 */
2969 int
dbcoltype(DBPROCESS * dbproc,int column)2970 dbcoltype(DBPROCESS * dbproc, int column)
2971 {
2972 TDSCOLUMN *colinfo;
2973
2974 tdsdump_log(TDS_DBG_FUNC, "dbcoltype(%p, %d)\n", dbproc, column);
2975
2976 colinfo = dbcolptr(dbproc, column);
2977 if (!colinfo)
2978 return -1;
2979
2980 return dblib_coltype(colinfo);
2981 }
2982
2983 /**
2984 * \ingroup dblib_core
2985 * \brief Get user-defined datatype of a regular result column.
2986 *
2987 * \param dbproc contains all information needed by db-lib to manage communications with the server.
2988 * \param column Nth in the result set, starting from 1.
2989 * \returns \c SYB* datetype token value, or -1 if \a column out of range
2990 * \sa dbaltutype(), dbcoltype().
2991 */
2992 int
dbcolutype(DBPROCESS * dbproc,int column)2993 dbcolutype(DBPROCESS * dbproc, int column)
2994 {
2995 TDSCOLUMN *colinfo;
2996
2997 tdsdump_log(TDS_DBG_FUNC, "dbcolutype(%p, %d)\n", dbproc, column);
2998
2999 colinfo = dbcolptr(dbproc, column);
3000 if (!colinfo)
3001 return -1;
3002
3003 return colinfo->column_usertype;
3004 }
3005
3006 /**
3007 * \ingroup dblib_core
3008 * \brief Get precision and scale information for a regular result column.
3009 *
3010 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3011 * \param column Nth in the result set, starting from 1.
3012 * \return Pointer to a DBTYPEINFO structure . NULL \a column is out of range.
3013 * \sa dbcollen(), dbcolname(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols(), dbprtype(), dbvarylen().
3014 */
3015 DBTYPEINFO *
dbcoltypeinfo(DBPROCESS * dbproc,int column)3016 dbcoltypeinfo(DBPROCESS * dbproc, int column)
3017 {
3018 /* moved typeinfo from static into dbproc structure to make thread safe. (mlilback 11/7/01) */
3019 TDSCOLUMN *colinfo;
3020
3021 tdsdump_log(TDS_DBG_FUNC, "dbcoltypeinfo(%p, %d)\n", dbproc, column);
3022
3023 colinfo = dbcolptr(dbproc, column);
3024 if (!colinfo)
3025 return NULL;
3026
3027 dbproc->typeinfo.precision = colinfo->column_prec;
3028 dbproc->typeinfo.scale = colinfo->column_scale;
3029 return &dbproc->typeinfo;
3030 }
3031
3032 /**
3033 * \brief Get a bunch of column attributes with a single call (Microsoft-compatibility feature).
3034 *
3035 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3036 * \param type must be CI_REGULAR or CI_ALTERNATE (CI_CURSOR is defined by the vendor, but is not yet implemented).
3037 * \param column Nth in the result set, starting from 1.
3038 * \param computeid (ignored)
3039 * \param pdbcol address of structure to be populated by this function.
3040 * \return SUCCEED or FAIL.
3041 * \sa dbcolbrowse(), dbqual(), dbtabbrowse(), dbtabcount(), dbtabname(), dbtabsource(), dbtsnewlen(), dbtsnewval(), dbtsput().
3042 * \todo Support cursor rows.
3043 */
3044 RETCODE
dbcolinfo(DBPROCESS * dbproc,CI_TYPE type,DBINT column,DBINT computeid,DBCOL * pdbcol)3045 dbcolinfo(DBPROCESS *dbproc, CI_TYPE type, DBINT column, DBINT computeid, DBCOL *pdbcol)
3046 {
3047 DBTYPEINFO *ps;
3048 TDSCOMPUTEINFO *info;
3049 TDSCOLUMN *colinfo;
3050 unsigned int i;
3051
3052 tdsdump_log(TDS_DBG_FUNC, "dbcolinfo(%p, %d, %d, %d, %p)\n", dbproc, type, column, computeid, pdbcol);
3053
3054 colinfo = dbcolptr(dbproc, column);
3055 if (!colinfo)
3056 return FAIL;
3057
3058 CHECK_NULP(pdbcol, "dbcolinfo", 5, FAIL);
3059
3060 if (type == CI_REGULAR) {
3061
3062 strlcpy(pdbcol->Name, dbcolname(dbproc, column), sizeof(pdbcol->Name));
3063 strlcpy(pdbcol->ActualName, dbcolname(dbproc, column), sizeof(pdbcol->ActualName));
3064 strlcpy(pdbcol->TableName, tds_dstr_cstr(&colinfo->table_name), sizeof(pdbcol->TableName));
3065
3066 pdbcol->Type = dbcoltype(dbproc, column);
3067 pdbcol->UserType = dbcolutype(dbproc, column);
3068 pdbcol->MaxLength = dbcollen(dbproc, column);
3069 pdbcol->Null = _dbnullable(dbproc, column);
3070 pdbcol->VarLength = dbvarylen(dbproc, column);
3071
3072 ps = dbcoltypeinfo(dbproc, column);
3073
3074 if( ps ) {
3075 pdbcol->Precision = ps->precision;
3076 pdbcol->Scale = ps->scale;
3077 }
3078
3079 pdbcol->Updatable = colinfo->column_writeable ? TRUE : FALSE;
3080 pdbcol->Identity = colinfo->column_identity ? TRUE : FALSE;
3081
3082 return SUCCEED;
3083 }
3084
3085 if (type == CI_ALTERNATE) {
3086
3087 if (computeid == 0)
3088 return FAIL;
3089
3090 for (i = 0;; ++i) {
3091 if (i >= dbproc->tds_socket->num_comp_info)
3092 return FAIL;
3093 info = dbproc->tds_socket->comp_info[i];
3094 if (info->computeid == computeid)
3095 break;
3096 }
3097
3098 /* if either the compute id or the column number are invalid, return -1 */
3099 if (column < 1 || column > info->num_cols)
3100 return FAIL;
3101
3102 colinfo = info->columns[column - 1];
3103
3104 strlcpy(pdbcol->Name, tds_dstr_cstr(&colinfo->column_name), sizeof(pdbcol->Name));
3105 strlcpy(pdbcol->ActualName, tds_dstr_cstr(&colinfo->column_name), sizeof(pdbcol->ActualName));
3106 strlcpy(pdbcol->TableName, tds_dstr_cstr(&colinfo->table_name), sizeof(pdbcol->TableName));
3107
3108 pdbcol->Type = dbalttype(dbproc, computeid, column);
3109 pdbcol->UserType = dbaltutype(dbproc, computeid, column);
3110 pdbcol->MaxLength = dbaltlen(dbproc, computeid, column);
3111 if (colinfo->column_nullable)
3112 pdbcol->Null = TRUE;
3113 else
3114 pdbcol->Null = FALSE;
3115
3116 pdbcol->VarLength = FALSE;
3117
3118 if (colinfo->column_nullable
3119 || is_nullable_type(colinfo->column_type))
3120 pdbcol->VarLength = TRUE;
3121
3122 pdbcol->Precision = colinfo->column_prec;
3123 pdbcol->Scale = colinfo->column_scale;
3124
3125 pdbcol->Updatable = colinfo->column_writeable ? TRUE : FALSE ;
3126 pdbcol->Identity = colinfo->column_identity ? TRUE : FALSE ;
3127
3128 return SUCCEED;
3129 }
3130
3131 return FAIL;
3132 }
3133
3134 /**
3135 * \ingroup dblib_core
3136 * \brief Get base database column name for a result set column.
3137 *
3138 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3139 * \param column Nth in the result set, starting from 1.
3140 * \return pointer to ASCII null-terminated string, the name of the column. On error, NULL.
3141 * \sa dbcolbrowse(), dbqual(), dbtabbrowse(), dbtabcount(), dbtabname(), dbtabsource(), dbtsnewlen(), dbtsnewval(), dbtsput().
3142 */
3143 char *
dbcolsource(DBPROCESS * dbproc,int column)3144 dbcolsource(DBPROCESS * dbproc, int column)
3145 {
3146 TDSCOLUMN *colinfo;
3147
3148 tdsdump_log(TDS_DBG_FUNC, "dbcolsource(%p, %d)\n", dbproc, column);
3149
3150 colinfo = dbcolptr(dbproc, column);
3151 if (!colinfo)
3152 return NULL;
3153
3154 return tds_dstr_buf(tds_dstr_isempty(&colinfo->table_column_name) ?
3155 &colinfo->column_name :
3156 &colinfo->table_column_name);
3157 }
3158
3159 /**
3160 * \ingroup dblib_core
3161 * \brief Get size of a regular result column.
3162 *
3163 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3164 * \param column Nth in the result set, starting from 1.
3165 * \return size of the column (not of data in any particular row). On error, -1.
3166 * \sa dbcolname(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols().
3167 */
3168 DBINT
dbcollen(DBPROCESS * dbproc,int column)3169 dbcollen(DBPROCESS * dbproc, int column)
3170 {
3171 TDSCOLUMN *colinfo;
3172
3173 tdsdump_log(TDS_DBG_FUNC, "dbcollen(%p, %d)\n", dbproc, column);
3174
3175 colinfo = dbcolptr(dbproc, column);
3176 if (!colinfo)
3177 return -1;
3178
3179 return colinfo->column_size;
3180 }
3181
3182 /**
3183 * \ingroup dblib_core
3184 * \brief Get size of a result column needed to print column.
3185 *
3186 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3187 * \param column Nth in the result set, starting from 1.
3188 * \return size of the column in characters (not of data in any particular row). On error, -1.
3189 * \sa dbcolname(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols().
3190 */
3191 DBINT
dbprcollen(DBPROCESS * dbproc,int column)3192 dbprcollen(DBPROCESS * dbproc, int column)
3193 {
3194 TDSCOLUMN *colinfo;
3195
3196 tdsdump_log(TDS_DBG_FUNC, "dbprcollen(%p, %d)\n", dbproc, column);
3197
3198 colinfo = dbcolptr(dbproc, column);
3199 if (!colinfo)
3200 return 0;
3201
3202 return _get_printable_size(colinfo);
3203 }
3204
3205
3206 /* dbvarylen(), pkleef@openlinksw.com 01/21/02 */
3207 /**
3208 * \ingroup dblib_core
3209 * \brief Determine whether a column can vary in size.
3210 *
3211 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3212 * \param column Nth in the result set, starting from 1.
3213 * \retval TRUE datatype of column can vary in size, or is nullable.
3214 * \retval FALSE datatype of column is fixed and is not nullable.
3215 * \sa dbcollen(), dbcolname(), dbcoltype(), dbdata(), dbdatlen(), dbnumcols(), dbprtype().
3216 */
3217 DBINT
dbvarylen(DBPROCESS * dbproc,int column)3218 dbvarylen(DBPROCESS * dbproc, int column)
3219 {
3220 TDSCOLUMN *colinfo;
3221
3222 tdsdump_log(TDS_DBG_FUNC, "dbvarylen(%p, %d)\n", dbproc, column);
3223
3224 colinfo = dbcolptr(dbproc, column);
3225 if (!colinfo)
3226 return FALSE;
3227
3228 if (colinfo->column_nullable)
3229 return TRUE;
3230
3231 switch (colinfo->column_type) {
3232 /* variable length fields */
3233 case SYBNVARCHAR:
3234 case SYBVARBINARY:
3235 case SYBVARCHAR:
3236 case XSYBVARCHAR:
3237 case XSYBNVARCHAR:
3238 case XSYBVARBINARY:
3239 return TRUE;
3240
3241 /* types that can be null */
3242 case SYBBITN:
3243 case SYBDATETIMN:
3244 case SYBDECIMAL:
3245 case SYBFLTN:
3246 case SYBINTN:
3247 case SYBMONEYN:
3248 case SYBNUMERIC:
3249 case SYBDATEN:
3250 case SYBTIMEN:
3251 case SYBUINTN:
3252 case SYB5BIGDATETIME:
3253 case SYB5BIGTIME:
3254 case SYBMSDATE:
3255 case SYBMSTIME:
3256 case SYBMSDATETIME2:
3257 case SYBMSDATETIMEOFFSET:
3258 case SYBVARIANT:
3259 case SYBUNIQUE:
3260 return TRUE;
3261
3262 /* blob types */
3263 case SYBIMAGE:
3264 case SYBNTEXT:
3265 case SYBTEXT:
3266 case SYBLONGBINARY:
3267 /* case SYBBLOB: */ /* same as SYBUNIQUE */
3268 case SYB5INT8:
3269 case SYBUNITEXT:
3270 case SYBXML:
3271 case SYBMSUDT:
3272 case SYBMSXML:
3273 return TRUE;
3274
3275 case SYBLONGCHAR:
3276 /* case XSYBCHAR: */ /* same as SYBLONGCHAR */
3277 if (colinfo->column_varint_size >= 4)
3278 return TRUE;
3279 break;
3280
3281 /* with defined size*/
3282 case SYBCHAR:
3283 case SYBBINARY:
3284 case XSYBNCHAR:
3285 case XSYBBINARY:
3286 break;
3287
3288 /* fixed */
3289 case SYBINT1:
3290 case SYBINT2:
3291 case SYBINT4:
3292 case SYBFLT8:
3293 case SYBDATETIME:
3294 case SYBBIT:
3295 case SYBMONEY4:
3296 case SYBMONEY:
3297 case SYBDATETIME4:
3298 case SYBREAL:
3299 case SYBINT8:
3300 case SYBUINT1:
3301 case SYBUINT2:
3302 case SYBUINT4:
3303 case SYBUINT8:
3304 case SYBSINT1:
3305 case SYBTIME:
3306 case SYBDATE:
3307 case SYBVOID:
3308 case SYBINTERVAL:
3309 break;
3310 }
3311 return FALSE;
3312 }
3313
3314 /**
3315 * \ingroup dblib_core
3316 * \brief Get size of current row's data in a regular result column.
3317 *
3318 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3319 * \param column Nth in the result set, starting from 1.
3320 * \return size of the data, in bytes.
3321 * \sa dbcollen(), dbcolname(), dbcoltype(), dbdata(), dbnumcols().
3322 */
3323 DBINT
dbdatlen(DBPROCESS * dbproc,int column)3324 dbdatlen(DBPROCESS * dbproc, int column)
3325 {
3326 DBINT len;
3327 TDSCOLUMN *colinfo;
3328
3329 tdsdump_log(TDS_DBG_FUNC, "dbdatlen(%p, %d)\n", dbproc, column);
3330
3331 colinfo = dbcolptr(dbproc, column);
3332 if (!colinfo)
3333 return -1;
3334
3335 len = (colinfo->column_cur_size < 0)? 0 : colinfo->column_cur_size;
3336
3337 tdsdump_log(TDS_DBG_FUNC, "dbdatlen() type = %d, len= %d\n", colinfo->column_type, len);
3338
3339 return len;
3340 }
3341
3342 /**
3343 * \ingroup dblib_core
3344 * \brief Get address of data in a regular result column.
3345 *
3346 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3347 * \param column Nth in the result set, starting from 1.
3348 * \return pointer the data, or NULL if data are NULL, or if \a column is out of range.
3349 * \sa dbbind(), dbcollen(), dbcolname(), dbcoltype(), dbdatlen(), dbnumcols().
3350 */
3351 BYTE *
dbdata(DBPROCESS * dbproc,int column)3352 dbdata(DBPROCESS * dbproc, int column)
3353 {
3354 tdsdump_log(TDS_DBG_FUNC, "dbdata(%p, %d)\n", dbproc, column);
3355
3356 return _dbcoldata(dbcolptr(dbproc, column));
3357 }
3358
3359 /** \internal
3360 * \ingroup dblib_internal
3361 * \brief Return data from a column
3362 *
3363 * \param colinfo contains information on a result column.
3364 * \return pointer to the data, or NULL if data are NULL
3365 * \sa dbdata(), dbretdata()
3366 */
3367 static BYTE *
_dbcoldata(TDSCOLUMN * colinfo)3368 _dbcoldata(TDSCOLUMN *colinfo)
3369 {
3370 BYTE *res;
3371 const static BYTE empty[1] = { 0 };
3372
3373 if (!colinfo || colinfo->column_cur_size < 0)
3374 return NULL;
3375
3376 res = colinfo->column_data;
3377 if (is_blob_col(colinfo))
3378 res = (BYTE *) ((TDSBLOB *) res)->textvalue;
3379 if (!res)
3380 return (BYTE *) empty;
3381 return res;
3382 }
3383
3384 /**
3385 * \ingroup dblib_core
3386 * \brief Cancel the current command batch.
3387 *
3388 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3389 * \retval SUCCEED always.
3390 * \sa dbcanquery(), dbnextrow(), dbresults(), dbsetinterrupt(), dbsqlexec(), dbsqlok(), dbsqlsend().
3391 * \todo Check for failure and return accordingly.
3392 */
3393 RETCODE
dbcancel(DBPROCESS * dbproc)3394 dbcancel(DBPROCESS * dbproc)
3395 {
3396 TDSSOCKET *tds;
3397
3398 tdsdump_log(TDS_DBG_FUNC, "dbcancel(%p)\n", dbproc);
3399 CHECK_CONN(FAIL);
3400
3401 tds = dbproc->tds_socket;
3402
3403 tds_send_cancel(tds);
3404 tds_process_cancel(tds);
3405
3406 return SUCCEED;
3407 }
3408
3409 /**
3410 * \ingroup dblib_core
3411 * \brief Determine size buffer required to hold the results returned by dbsprhead(), dbsprline(), and dbspr1row().
3412 *
3413 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3414 * \return size of buffer requirement, in bytes.
3415 * \remarks An esoteric function.
3416 * \sa dbprhead(), dbprrow(), dbspr1row(), dbsprhead(), dbsprline().
3417 */
3418 DBINT
dbspr1rowlen(DBPROCESS * dbproc)3419 dbspr1rowlen(DBPROCESS * dbproc)
3420 {
3421 TDSSOCKET *tds;
3422 int col, len = 0;
3423
3424 tdsdump_log(TDS_DBG_FUNC, "dbspr1rowlen(%p)\n", dbproc);
3425 CHECK_PARAMETER(dbproc, SYBENULL, 0);
3426 CHECK_PARAMETER(dbproc->tds_socket, SYBEDDNE, 0);
3427
3428 tds = dbproc->tds_socket;
3429
3430 for (col = 0; col < tds->res_info->num_cols; col++) {
3431 TDSCOLUMN *colinfo = tds->res_info->columns[col];
3432 int collen = _get_printable_size(colinfo);
3433 int namlen = tds_dstr_len(&colinfo->column_name);
3434
3435 len += collen > namlen ? collen : namlen;
3436
3437 if (col > 0) /* allow for the space between columns */
3438 len += dbstring_length(dbproc->dbopts[DBPRCOLSEP].param);
3439 }
3440
3441 return ++len; /* allow for the nul */
3442 }
3443
3444 /**
3445 * \ingroup dblib_core
3446 * \brief Print a regular result row to a buffer.
3447 *
3448 * Fills a buffer with one data row, represented as a null-terminated ASCII string. Helpful for debugging.
3449 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3450 * \param buffer \em output: Address of a buffer to hold ASCII null-terminated string.
3451 * \param buf_len size of \a buffer, in bytes.
3452 * \retval SUCCEED on success.
3453 * \retval FAIL trouble encountered.
3454 * \sa dbclropt(), dbisopt(), dbprhead(), dbprrow(), dbspr1rowlen(), dbsprhead(), dbsprline().
3455 */
3456 RETCODE
dbspr1row(DBPROCESS * dbproc,char * buffer,DBINT buf_len)3457 dbspr1row(DBPROCESS * dbproc, char *buffer, DBINT buf_len)
3458 {
3459 TDSSOCKET *tds;
3460 TDSDATEREC when;
3461 int i, c, col;
3462 DBINT len;
3463
3464 tdsdump_log(TDS_DBG_FUNC, "dbspr1row(%p, %s, %d)\n", dbproc, buffer, buf_len);
3465 CHECK_CONN(FAIL);
3466 CHECK_NULP(buffer, "dbspr1row", 2, FAIL);
3467
3468 if (!dbproc->tds_socket)
3469 return FAIL;
3470
3471 tds = dbproc->tds_socket;
3472
3473 for (col = 0; col < tds->res_info->num_cols; col++) {
3474 int padlen, collen, namlen;
3475 TDSCOLUMN *colinfo = tds->res_info->columns[col];
3476 if (colinfo->column_cur_size < 0) {
3477 len = 4;
3478 if (buf_len <= len) {
3479 return FAIL;
3480 }
3481 strcpy(buffer, "NULL");
3482 } else {
3483 TDS_SERVER_TYPE desttype, srctype;
3484
3485 desttype = dblib_bound_type(STRINGBIND);
3486 srctype = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
3487 if (is_datetime_type(srctype)) {
3488 tds_datecrack(srctype, dbdata(dbproc, col + 1), &when);
3489 len = (int)tds_strftime(buffer, buf_len, STD_DATETIME_FMT, &when, 3);
3490 } else {
3491 len = dbconvert(dbproc, srctype, dbdata(dbproc, col + 1), dbdatlen(dbproc, col + 1),
3492 desttype, (BYTE *) buffer, buf_len);
3493 }
3494 if (len == -1) {
3495 return FAIL;
3496 }
3497 }
3498 buffer += len;
3499 buf_len -= len;
3500 collen = _get_printable_size(colinfo);
3501 namlen = tds_dstr_len(&colinfo->column_name);
3502 padlen = (collen > namlen ? collen : namlen) - len;
3503 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) == -1) {
3504 c = ' ';
3505 }
3506 for (; padlen > 0; padlen--) {
3507 if (buf_len < 1) {
3508 return FAIL;
3509 }
3510 *buffer++ = c;
3511 buf_len--;
3512 }
3513 if ((col + 1) < tds->res_info->num_cols) {
3514 i = 0;
3515 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
3516 if (buf_len < 1) {
3517 return FAIL;
3518 }
3519 *buffer++ = c;
3520 buf_len--;
3521 i++;
3522 }
3523 }
3524 }
3525 if (buf_len < 1) {
3526 return FAIL;
3527 }
3528 *buffer = '\0';
3529 return SUCCEED;
3530 }
3531
3532 /**
3533 * \ingroup dblib_core
3534 * \brief Print a result set to stdout.
3535 *
3536 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3537 * \sa dbbind(), dbnextrow(), dbprhead(), dbresults(), dbspr1row(), dbsprhead(), dbsprline().
3538 */
3539 RETCODE
dbprrow(DBPROCESS * dbproc)3540 dbprrow(DBPROCESS * dbproc)
3541 {
3542 TDSCOLUMN *colinfo;
3543 TDSRESULTINFO *resinfo;
3544 TDSSOCKET *tds;
3545 int i, col, collen, namlen, len;
3546 char dest[8192];
3547 int desttype, srctype;
3548 TDSDATEREC when;
3549 STATUS status;
3550 int padlen;
3551 int c;
3552 int selcol;
3553 int linechar;
3554 int op;
3555 const char *opname;
3556
3557 /* these are for compute rows */
3558 DBINT computeid, num_cols, colid;
3559 TDS_SMALLINT *col_printlens = NULL;
3560
3561 tdsdump_log(TDS_DBG_FUNC, "dbprrow(%p)\n", dbproc);
3562 CHECK_CONN(FAIL);
3563
3564 tds = dbproc->tds_socket;
3565
3566 while ((status = dbnextrow(dbproc)) != NO_MORE_ROWS) {
3567
3568 if (status == FAIL) {
3569 free(col_printlens);
3570 return FAIL;
3571 }
3572
3573 if (status == REG_ROW) {
3574
3575 resinfo = tds->res_info;
3576
3577 if (col_printlens == NULL) {
3578 if ((col_printlens = tds_new0(TDS_SMALLINT, resinfo->num_cols)) == NULL) {
3579 dbperror(dbproc, SYBEMEM, errno);
3580 return FAIL;
3581 }
3582 }
3583
3584 for (col = 0; col < resinfo->num_cols; col++) {
3585 colinfo = resinfo->columns[col];
3586 if (colinfo->column_cur_size < 0) {
3587 len = 4;
3588 strcpy(dest, "NULL");
3589 } else {
3590 desttype = dblib_bound_type(STRINGBIND);
3591 srctype = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
3592 if (is_datetime_type(srctype)) {
3593 tds_datecrack(srctype, dbdata(dbproc, col + 1), &when);
3594 len = (int)tds_strftime(dest, sizeof(dest), STD_DATETIME_FMT, &when, 3);
3595 } else {
3596 len = dbconvert(dbproc, srctype, dbdata(dbproc, col + 1), dbdatlen(dbproc, col + 1),
3597 desttype, (BYTE *) dest, sizeof(dest));
3598 }
3599 }
3600
3601 printf("%.*s", len, dest);
3602 collen = _get_printable_size(colinfo);
3603 namlen = tds_dstr_len(&colinfo->column_name);
3604 padlen = (collen > namlen ? collen : namlen) - len;
3605
3606 c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0);
3607 for (; c > -1 && padlen > 0; padlen--) {
3608 putchar(c);
3609 }
3610
3611 if ((col + 1) < resinfo->num_cols) {
3612 i = 0;
3613 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
3614 putchar(c);
3615 }
3616 }
3617 col_printlens[col] = collen;
3618 }
3619 i = 0;
3620 while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
3621 putchar(c);
3622 }
3623
3624 } else {
3625
3626 computeid = status;
3627
3628 for (i = 0;; ++i) {
3629 if (i >= tds->num_comp_info) {
3630 free(col_printlens);
3631 return FAIL;
3632 }
3633 resinfo = tds->comp_info[i];
3634 if (resinfo->computeid == computeid)
3635 break;
3636 }
3637
3638 num_cols = dbnumalts(dbproc, computeid);
3639 tdsdump_log(TDS_DBG_FUNC, "dbprrow num compute cols = %d\n", num_cols);
3640
3641 i = 0;
3642 while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
3643 putchar(c);
3644 }
3645 for (selcol = col = 1; col <= num_cols; col++) {
3646 tdsdump_log(TDS_DBG_FUNC, "dbprrow calling dbaltcolid(%d,%d)\n", computeid, col);
3647 colid = dbaltcolid(dbproc, computeid, col);
3648 /*
3649 * The pad character is pointed to by dbopts[DBPRPAD].param. If that pointer
3650 * is NULL -- meaning padding is turned off -- dbstring_getchar returns -1.
3651 */
3652 while (selcol < colid) {
3653 for (i = 0; i < col_printlens[selcol - 1]; i++) {
3654 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) >= 0)
3655 putchar(c);
3656 }
3657 selcol++;
3658 i = 0;
3659 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
3660 putchar(c);
3661 }
3662 }
3663 op = dbaltop(dbproc, computeid, col);
3664 opname = dbprtype(op);
3665 printf("%s", opname);
3666 for (i = 0; i < ((long) col_printlens[selcol - 1] - (long) strlen(opname)); i++) {
3667 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) >= 0)
3668 putchar(c);
3669 }
3670 selcol++;
3671 if ((colid + 1) < num_cols) {
3672 i = 0;
3673 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
3674 putchar(c);
3675 }
3676 }
3677 }
3678 i = 0;
3679 while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
3680 putchar(c);
3681 }
3682
3683 for (selcol = col = 1; col <= num_cols; col++) {
3684 tdsdump_log(TDS_DBG_FUNC, "dbprrow calling dbaltcolid(%d,%d)\n", computeid, col);
3685 colid = dbaltcolid(dbproc, computeid, col);
3686 while (selcol < colid) {
3687 for (i = 0; i < col_printlens[selcol - 1]; i++) {
3688 putchar(' ');
3689 }
3690 selcol++;
3691 i = 0;
3692 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
3693 putchar(c);
3694 }
3695 }
3696 if (resinfo->by_cols > 0) {
3697 linechar = '-';
3698 } else {
3699 linechar = '=';
3700 }
3701 for (i = 0; i < col_printlens[colid - 1]; i++)
3702 putchar(linechar);
3703 selcol++;
3704 if ((colid + 1) < num_cols) {
3705 i = 0;
3706 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
3707 putchar(c);
3708 }
3709 }
3710 }
3711 i = 0;
3712 while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
3713 putchar(c);
3714 }
3715
3716 for (selcol = col = 1; col <= num_cols; col++) {
3717 colinfo = resinfo->columns[col - 1];
3718
3719 desttype = dblib_bound_type(STRINGBIND);
3720 srctype = dbalttype(dbproc, computeid, col);
3721
3722 if (is_datetime_type(srctype)) {
3723 tds_datecrack(srctype, dbadata(dbproc, computeid, col), &when);
3724 len = (int)tds_strftime(dest, sizeof(dest), STD_DATETIME_FMT, &when, 3);
3725 } else {
3726 len = dbconvert(dbproc, srctype, dbadata(dbproc, computeid, col), -1, desttype,
3727 (BYTE *) dest, sizeof(dest));
3728 }
3729
3730 tdsdump_log(TDS_DBG_FUNC, "dbprrow calling dbaltcolid(%d,%d)\n", computeid, col);
3731 colid = dbaltcolid(dbproc, computeid, col);
3732 tdsdump_log(TDS_DBG_FUNC, "dbprrow select column = %d\n", colid);
3733
3734 while (selcol < colid) {
3735 for (i = 0; i < col_printlens[selcol - 1]; i++) {
3736 putchar(' ');
3737 }
3738 selcol++;
3739 i = 0;
3740 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
3741 putchar(c);
3742 }
3743 }
3744 printf("%.*s", len, dest);
3745 collen = _get_printable_size(colinfo);
3746 namlen = tds_dstr_len(&colinfo->column_name);
3747 padlen = (collen > namlen ? collen : namlen) - len;
3748 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) == -1) {
3749 c = ' ';
3750 }
3751 for (; padlen > 0; padlen--) {
3752 putchar(c);
3753 }
3754 selcol++;
3755 if ((colid + 1) < num_cols) {
3756 i = 0;
3757 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i++)) != -1) {
3758 putchar(c);
3759 }
3760 }
3761 }
3762 i = 0;
3763 while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i++)) != -1) {
3764 putchar(c);
3765 }
3766 }
3767 }
3768
3769 free(col_printlens);
3770
3771 return SUCCEED;
3772 }
3773
3774 static int
_get_printable_size(TDSCOLUMN * colinfo)3775 _get_printable_size(TDSCOLUMN * colinfo)
3776 {
3777 switch (tds_get_conversion_type(colinfo->column_type, colinfo->column_size)) {
3778 case SYBINT1:
3779 return 3;
3780 case SYBINT2:
3781 return 6;
3782 case SYBINT4:
3783 return 11;
3784 case SYBINT8:
3785 return 21;
3786 case SYBVARCHAR:
3787 case SYBCHAR:
3788 case SYBTEXT:
3789 case SYBNTEXT:
3790 case SYBNVARCHAR:
3791 case SYBLONGCHAR:
3792 return colinfo->column_size;
3793 case SYBBINARY:
3794 case SYBIMAGE:
3795 case SYBLONGBINARY:
3796 case SYBVARBINARY:
3797 return colinfo->column_size * 2u;
3798 case SYBFLT8:
3799 case SYBREAL:
3800 return 11; /* FIX ME -- we do not track precision */
3801 case SYBMONEY4:
3802 return 12;
3803 case SYBMONEY:
3804 return 22;
3805 case SYB5BIGDATETIME:
3806 case SYBDATETIME:
3807 case SYBDATETIME4:
3808 return 26;
3809 case SYBTIME:
3810 case SYB5BIGTIME:
3811 return 15;
3812 case SYBDATE:
3813 return 10;
3814 case SYBUNIQUE:
3815 return 36;
3816 case SYBBIT:
3817 return 1;
3818 /* FIX ME -- not all types present */
3819 default:
3820 return 0;
3821 }
3822 }
3823
3824 /**
3825 * \ingroup dblib_core
3826 * \brief Get formatted string for underlining dbsprhead() column names.
3827 *
3828 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3829 * \param buffer output buffer
3830 * \param buf_len size of \a buffer
3831 * \param line_char character to use to represent underlining.
3832 * \retval SUCCEED \a buffer filled.
3833 * \retval FAIL insufficient space in \a buffer, usually.
3834 * \sa dbprhead(), dbprrow(), dbspr1row(), dbspr1rowlen(), dbsprhead().
3835 */
3836 RETCODE
dbsprline(DBPROCESS * dbproc,char * buffer,DBINT buf_len,DBCHAR line_char)3837 dbsprline(DBPROCESS * dbproc, char *buffer, DBINT buf_len, DBCHAR line_char)
3838 {
3839 TDSCOLUMN *colinfo;
3840 TDSRESULTINFO *resinfo;
3841 TDSSOCKET *tds;
3842 int i, col, len, collen, namlen;
3843 int c;
3844
3845 tdsdump_log(TDS_DBG_FUNC, "dbsprline(%p, %s, %d, '%c')\n", dbproc, buffer, buf_len, line_char);
3846 CHECK_CONN(FAIL);
3847 CHECK_NULP(buffer, "dbsprline", 2, FAIL);
3848
3849 tds = dbproc->tds_socket;
3850 resinfo = tds->res_info;
3851
3852 for (col = 0; col < resinfo->num_cols; col++) {
3853 colinfo = resinfo->columns[col];
3854 collen = _get_printable_size(colinfo);
3855 namlen = tds_dstr_len(&colinfo->column_name);
3856 len = collen > namlen ? collen : namlen;
3857 for (i = 0; i < len; i++) {
3858 if (buf_len < 1) {
3859 return FAIL;
3860 }
3861 *buffer++ = line_char;
3862 buf_len--;
3863 }
3864 if ((col + 1) < resinfo->num_cols) {
3865 i = 0;
3866 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
3867 if (buf_len < 1) {
3868 return FAIL;
3869 }
3870 *buffer++ = c;
3871 buf_len--;
3872 i++;
3873 }
3874 }
3875 }
3876 if (buf_len < 1) {
3877 return FAIL;
3878 }
3879 *buffer = '\0';
3880 return SUCCEED;
3881 }
3882
3883 /**
3884 * \ingroup dblib_core
3885 * \brief Print result set headings to a buffer.
3886 *
3887 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3888 * \param buffer output buffer
3889 * \param buf_len size of \a buffer
3890 * \retval SUCCEED \a buffer filled.
3891 * \retval FAIL insufficient space in \a buffer, usually.
3892 * \sa dbprhead(), dbprrow(), dbsetopt(), dbspr1row(), dbspr1rowlen(), dbsprline().
3893 */
3894 RETCODE
dbsprhead(DBPROCESS * dbproc,char * buffer,DBINT buf_len)3895 dbsprhead(DBPROCESS * dbproc, char *buffer, DBINT buf_len)
3896 {
3897 TDSCOLUMN *colinfo;
3898 TDSRESULTINFO *resinfo;
3899 TDSSOCKET *tds;
3900 int i, col, collen, namlen;
3901 int padlen;
3902 int c;
3903
3904 tdsdump_log(TDS_DBG_FUNC, "dbsprhead(%p, %p, %d)\n", dbproc, buffer, buf_len);
3905 CHECK_CONN(FAIL);
3906 CHECK_NULP(buffer, "dbsprhead", 2, FAIL);
3907
3908 tds = dbproc->tds_socket;
3909 resinfo = tds->res_info;
3910
3911 for (col = 0; col < resinfo->num_cols; col++) {
3912 colinfo = resinfo->columns[col];
3913 collen = _get_printable_size(colinfo);
3914 namlen = tds_dstr_len(&colinfo->column_name);
3915 padlen = (collen > namlen ? collen : namlen) - namlen;
3916 if (buf_len < namlen) {
3917 return FAIL;
3918 }
3919 memcpy(buffer, tds_dstr_cstr(&colinfo->column_name), namlen);
3920 buffer += namlen;
3921 buf_len -= namlen;
3922 if ((c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0)) == -1) {
3923 c = ' ';
3924 }
3925 for (; padlen > 0; padlen--) {
3926 if (buf_len < 1) {
3927 return FAIL;
3928 }
3929 *buffer++ = c;
3930 buf_len--;
3931 }
3932 if ((col + 1) < resinfo->num_cols) {
3933 i = 0;
3934 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
3935 if (buf_len < 1) {
3936 return FAIL;
3937 }
3938 *buffer++ = c;
3939 buf_len--;
3940 i++;
3941 }
3942 }
3943 }
3944 if (buf_len < 1) {
3945 return FAIL;
3946 }
3947 *buffer = '\0';
3948 return SUCCEED;
3949 }
3950
3951 /**
3952 * \ingroup dblib_core
3953 * \brief Print result set headings to stdout.
3954 *
3955 * \param dbproc contains all information needed by db-lib to manage communications with the server.
3956 * \sa
3957 */
3958 void
dbprhead(DBPROCESS * dbproc)3959 dbprhead(DBPROCESS * dbproc)
3960 {
3961 TDSCOLUMN *colinfo;
3962 TDSRESULTINFO *resinfo;
3963 TDSSOCKET *tds;
3964 int i, col, len, collen, namlen;
3965 int padlen;
3966 int c;
3967
3968 tdsdump_log(TDS_DBG_FUNC, "dbprhead(%p)\n", dbproc);
3969 CHECK_PARAMETER(dbproc, SYBENULL, );
3970
3971 tds = dbproc->tds_socket;
3972 resinfo = tds->res_info;
3973 if (resinfo == NULL) {
3974 return;
3975 }
3976 for (col = 0; col < resinfo->num_cols; col++) {
3977 colinfo = resinfo->columns[col];
3978 collen = _get_printable_size(colinfo);
3979 namlen = tds_dstr_len(&colinfo->column_name);
3980 padlen = (collen > namlen ? collen : namlen) - namlen;
3981 printf("%s", tds_dstr_cstr(&colinfo->column_name));
3982
3983 c = dbstring_getchar(dbproc->dbopts[DBPRPAD].param, 0);
3984 if (c == -1) {
3985 c = ' ';
3986 }
3987 for (; padlen > 0; padlen--) {
3988 putchar(c);
3989 }
3990
3991 if ((col + 1) < resinfo->num_cols) {
3992 i = 0;
3993 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
3994 putchar(c);
3995 i++;
3996 }
3997 }
3998 }
3999 i = 0;
4000 while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i)) != -1) {
4001 putchar(c);
4002 i++;
4003 }
4004 for (col = 0; col < resinfo->num_cols; col++) {
4005 colinfo = resinfo->columns[col];
4006 collen = _get_printable_size(colinfo);
4007 namlen = tds_dstr_len(&colinfo->column_name);
4008 len = collen > namlen ? collen : namlen;
4009 for (i = 0; i < len; i++)
4010 putchar('-');
4011 if ((col + 1) < resinfo->num_cols) {
4012 i = 0;
4013 while ((c = dbstring_getchar(dbproc->dbopts[DBPRCOLSEP].param, i)) != -1) {
4014 putchar(c);
4015 i++;
4016 }
4017 }
4018 }
4019 i = 0;
4020 while ((c = dbstring_getchar(dbproc->dbopts[DBPRLINESEP].param, i)) != -1) {
4021 putchar(c);
4022 i++;
4023 }
4024 }
4025
4026 /** \internal
4027 * \ingroup dblib_internal
4028 * \brief Indicate whether a query returned rows.
4029 *
4030 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4031 * \sa DBROWS(), DBCMDROW(), dbnextrow(), dbresults(), DBROWTYPE().
4032 */
4033 RETCODE
dbrows(DBPROCESS * dbproc)4034 dbrows(DBPROCESS * dbproc)
4035 {
4036 TDSSOCKET *tds;
4037
4038 tdsdump_log(TDS_DBG_FUNC, "dbrows(%p)\n", dbproc);
4039 CHECK_CONN(FAIL);
4040
4041 if (!(tds=dbproc->tds_socket))
4042 return FAIL;
4043
4044 return (tds->res_info && tds->res_info->rows_exist)? SUCCEED : FAIL;
4045 }
4046
4047 #if defined(DBLIB_UNIMPLEMENTED)
4048 /**
4049 * \ingroup dblib_core
4050 * \brief Set the default character set for an application.
4051 *
4052 * \param language ASCII null-terminated string.
4053 * \sa dbsetdeflang(), dbsetdefcharset(), dblogin(), dbopen().
4054 * \retval SUCCEED Always.
4055 * \todo Unimplemented.
4056 */
4057 RETCODE
dbsetdeflang(char * language)4058 dbsetdeflang(char *language)
4059 {
4060 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetdeflang(%s)\n", language);
4061 CHECK_PARAMETER_NOPROC(language, SYBENULP);
4062 return SUCCEED;
4063 }
4064 #endif
4065
4066 /**
4067 * \ingroup dblib_core
4068 * \brief Get TDS packet size for the connection.
4069 *
4070 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4071 * \return TDS packet size, in bytes.
4072 * \sa DBSETLPACKET()
4073 */
4074 int
dbgetpacket(DBPROCESS * dbproc)4075 dbgetpacket(DBPROCESS * dbproc)
4076 {
4077 TDSSOCKET *tds;
4078
4079 tdsdump_log(TDS_DBG_FUNC, "dbgetpacket(%p)\n", dbproc);
4080 CHECK_PARAMETER(dbproc, SYBENULL, TDS_DEF_BLKSZ);
4081
4082 tds = dbproc->tds_socket;
4083 if (!tds) {
4084 return TDS_DEF_BLKSZ;
4085 } else {
4086 return tds->conn->env.block_size;
4087 }
4088 }
4089
4090 /**
4091 * \ingroup dblib_core
4092 * \brief Set maximum simultaneous connections db-lib will open to the server.
4093 *
4094 * \param maxprocs Limit for process.
4095 * \retval SUCCEED Always.
4096 * \sa dbgetmaxprocs(), dbopen()
4097 */
4098 RETCODE
dbsetmaxprocs(int maxprocs)4099 dbsetmaxprocs(int maxprocs)
4100 {
4101 int i, j;
4102 TDSSOCKET **old_list;
4103
4104 tdsdump_log(TDS_DBG_FUNC, "UNTESTED dbsetmaxprocs(%d)\n", maxprocs);
4105
4106 /* not too few elements */
4107 if (maxprocs <= 0)
4108 return FAIL;
4109
4110 tds_mutex_lock(&dblib_mutex);
4111
4112 old_list = g_dblib_ctx.connection_list;
4113
4114 /* "compress" array */
4115 for (i = 0, j = 0; i < g_dblib_ctx.connection_list_size; ++i) {
4116 if (!old_list[i])
4117 continue;
4118 if (i != j) {
4119 old_list[j] = old_list[i];
4120 old_list[i] = NULL;
4121 }
4122 ++j;
4123 }
4124 /* do not restrict too much, j here contains minimun size */
4125 if (maxprocs < j)
4126 maxprocs = j;
4127
4128 /*
4129 * Don't reallocate less memory.
4130 * If maxprocs is less than was initially allocated, just reduce the represented list size.
4131 * If larger, reallocate and copy.
4132 * We probably should check for valid connections beyond the new max.
4133 */
4134 if (maxprocs <= g_dblib_ctx.connection_list_size) {
4135 g_dblib_ctx.connection_list_size_represented = maxprocs;
4136 tds_mutex_unlock(&dblib_mutex);
4137 return SUCCEED;
4138 }
4139
4140 g_dblib_ctx.connection_list = tds_new0(TDSSOCKET *, maxprocs);
4141
4142 if (g_dblib_ctx.connection_list == NULL) {
4143 g_dblib_ctx.connection_list = old_list;
4144 tds_mutex_unlock(&dblib_mutex);
4145 dbperror(NULL, SYBEMEM, errno);
4146 return FAIL;
4147 }
4148
4149 for (i = 0; i < g_dblib_ctx.connection_list_size; i++) {
4150 g_dblib_ctx.connection_list[i] = old_list[i];
4151 }
4152
4153 g_dblib_ctx.connection_list_size = maxprocs;
4154 g_dblib_ctx.connection_list_size_represented = maxprocs;
4155
4156 tds_mutex_unlock(&dblib_mutex);
4157
4158 free(old_list);
4159
4160 return SUCCEED;
4161 }
4162
4163 /**
4164 * \ingroup dblib_core
4165 * \brief get maximum simultaneous connections db-lib will open to the server.
4166 *
4167 * \return Current maximum.
4168 * \sa dbsetmaxprocs(), dbopen()
4169 */
4170 int
dbgetmaxprocs(void)4171 dbgetmaxprocs(void)
4172 {
4173 int r;
4174
4175 tdsdump_log(TDS_DBG_FUNC, "dbgetmaxprocs(void)\n");
4176
4177 tds_mutex_lock(&dblib_mutex);
4178 r = g_dblib_ctx.connection_list_size_represented;
4179 tds_mutex_unlock(&dblib_mutex);
4180 return r;
4181 }
4182
4183 /**
4184 * \ingroup dblib_core
4185 * \brief Set maximum seconds db-lib waits for a server response to query.
4186 *
4187 * \param seconds New limit for application.
4188 * \retval SUCCEED Always.
4189 * \sa dberrhandle(), DBGETTIME(), dbsetlogintime(), dbsqlexec(), dbsqlok(), dbsqlsend().
4190 */
4191 RETCODE
dbsettime(int seconds)4192 dbsettime(int seconds)
4193 {
4194 TDSSOCKET **tds;
4195 int i;
4196 DBPROCESS *dbproc;
4197 tdsdump_log(TDS_DBG_FUNC, "dbsettime(%d)\n", seconds);
4198
4199 tds_mutex_lock(&dblib_mutex);
4200 g_dblib_ctx.query_timeout = seconds;
4201
4202 tds = g_dblib_ctx.connection_list;
4203 for (i = 0; i < TDS_MAX_CONN; i++) {
4204 if (tds[i]) {
4205 dbproc = (DBPROCESS *) tds_get_parent(tds[i]);
4206 if (!dbisopt(dbproc, DBSETTIME, 0))
4207 tds[i]->query_timeout = seconds;
4208 }
4209 }
4210
4211 tds_mutex_unlock(&dblib_mutex);
4212 return SUCCEED;
4213 }
4214
4215 /**
4216 * \ingroup dblib_core
4217 * \brief Get maximum seconds db-lib waits for a server response to query.
4218 *
4219 * \retval query timeout limit, in seconds
4220 * \sa dberrhandle(), DBSETTIME(), dbsetlogintime(), dbsqlexec(), dbsqlok(), dbsqlsend().
4221 */
4222 int
dbgettime(void)4223 dbgettime(void)
4224 {
4225 tdsdump_log(TDS_DBG_FUNC, "dbgettime()\n");
4226
4227 return g_dblib_ctx.query_timeout;
4228 }
4229
4230 /**
4231 * \ingroup dblib_core
4232 * \brief Set maximum seconds db-lib waits for a server response to a login attempt.
4233 *
4234 * \param seconds New limit for application.
4235 * \retval SUCCEED Always.
4236 * \sa dberrhandle(), dbsettime()
4237 */
4238 RETCODE
dbsetlogintime(int seconds)4239 dbsetlogintime(int seconds)
4240 {
4241 tdsdump_log(TDS_DBG_FUNC, "dbsetlogintime(%d)\n", seconds);
4242
4243 tds_mutex_lock(&dblib_mutex);
4244 g_dblib_ctx.login_timeout = seconds;
4245 tds_mutex_unlock(&dblib_mutex);
4246 return SUCCEED;
4247 }
4248
4249 /** \internal
4250 * \ingroup dblib_internal
4251 * \brief See if the current command can return rows.
4252 *
4253 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4254 * \retval SUCCEED Yes, it can.
4255 * \retval FAIL No, it can't.
4256 * \remarks Use DBCMDROW() macro instead.
4257 * \sa DBCMDROW(), dbnextrow(), dbresults(), DBROWS(), DBROWTYPE().
4258 */
4259 RETCODE
dbcmdrow(DBPROCESS * dbproc)4260 dbcmdrow(DBPROCESS * dbproc)
4261 {
4262 TDSSOCKET *tds;
4263
4264 tdsdump_log(TDS_DBG_FUNC, "dbcmdrow(%p)\n", dbproc);
4265 CHECK_CONN(FAIL);
4266
4267 tds = dbproc->tds_socket;
4268 if (tds->res_info)
4269 return SUCCEED;
4270 return FAIL;
4271 }
4272
4273 /**
4274 * \ingroup dblib_core
4275 * \brief Get column ID of a compute column.
4276 *
4277 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4278 * \param computeid of \c COMPUTE clause to which we're referring.
4279 * \param column Nth column in \a computeid, starting from 1.
4280 * \return Nth column in the base result set, on which \a column was computed.
4281 * \sa dbadata(), dbadlen(), dbaltlen(), dbgetrow(), dbnextrow(), dbnumalts(), dbprtype().
4282 */
4283 int
dbaltcolid(DBPROCESS * dbproc,int computeid,int column)4284 dbaltcolid(DBPROCESS * dbproc, int computeid, int column)
4285 {
4286 TDSCOLUMN *curcol;
4287
4288 tdsdump_log(TDS_DBG_FUNC, "dbaltcolid(%p, %d, %d)\n", dbproc, computeid, column);
4289
4290 curcol = dbacolptr(dbproc, computeid, column, 0);
4291 if (!curcol)
4292 return -1;
4293
4294 return curcol->column_operand;
4295 }
4296
4297 /**
4298 * \ingroup dblib_core
4299 * \brief Get size of data in a compute column.
4300 *
4301 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4302 * \param computeid of \c COMPUTE clause to which we're referring.
4303 * \param column Nth column in \a computeid, starting from 1.
4304 * \return size of the data, in bytes.
4305 * \retval -1 no such \a column or \a computeid.
4306 * \retval 0 data are NULL.
4307 * \sa dbadata(), dbaltlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumalts().
4308 */
4309 DBINT
dbadlen(DBPROCESS * dbproc,int computeid,int column)4310 dbadlen(DBPROCESS * dbproc, int computeid, int column)
4311 {
4312 TDSCOLUMN *colinfo;
4313 DBINT len;
4314
4315 tdsdump_log(TDS_DBG_FUNC, "dbadlen(%p, %d, %d)\n", dbproc, computeid, column);
4316
4317 colinfo = dbacolptr(dbproc, computeid, column, 0);
4318 if (!colinfo)
4319 return -1;
4320
4321 len = colinfo->column_cur_size < 0? 0 : colinfo->column_cur_size;
4322
4323 tdsdump_log(TDS_DBG_FUNC, "leaving dbadlen() type = %d, returning %d\n", colinfo->column_type, len);
4324
4325 return len;
4326 }
4327
4328 /**
4329 * \ingroup dblib_core
4330 * \brief Get datatype for a compute column.
4331 *
4332 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4333 * \param computeid of \c COMPUTE clause to which we're referring.
4334 * \param column Nth column in \a computeid, starting from 1.
4335 * \return \c SYB* dataype token.
4336 * \retval -1 no such \a column or \a computeid.
4337 * \sa dbadata(), dbadlen(), dbaltlen(), dbnextrow(), dbnumalts(), dbprtype().
4338 */
4339 int
dbalttype(DBPROCESS * dbproc,int computeid,int column)4340 dbalttype(DBPROCESS * dbproc, int computeid, int column)
4341 {
4342 TDSCOLUMN *colinfo;
4343
4344 tdsdump_log(TDS_DBG_FUNC, "dbalttype(%p, %d, %d)\n", dbproc, computeid, column);
4345
4346 colinfo = dbacolptr(dbproc, computeid, column, 0);
4347 if (!colinfo)
4348 return -1;
4349
4350 return dblib_coltype(colinfo);
4351 }
4352
4353 /**
4354 * \ingroup dblib_core
4355 * \brief Bind a compute column to a program variable.
4356 *
4357 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4358 * \param computeid of \c COMPUTE clause to which we're referring.
4359 * \param column Nth column in \a computeid, starting from 1.
4360 * \param vartype datatype of the host variable that will receive the data
4361 * \param varlen size of host variable pointed to \a varaddr
4362 * \param varaddr address of host variable
4363 * \retval SUCCEED everything worked.
4364 * \retval FAIL no such \a computeid or \a column, or no such conversion possible, or target buffer too small.
4365 * \sa dbadata(), dbaltbind_ps(), dbanullbind(), dbbind(), dbbind_ps(), dbconvert(),
4366 * dbconvert_ps(), dbnullbind(), dbsetnull(), dbsetversion(), dbwillconvert().
4367 */
4368 RETCODE
dbaltbind(DBPROCESS * dbproc,int computeid,int column,int vartype,DBINT varlen,BYTE * varaddr)4369 dbaltbind(DBPROCESS * dbproc, int computeid, int column, int vartype, DBINT varlen, BYTE * varaddr)
4370 {
4371 TDS_SERVER_TYPE srctype, desttype;
4372 TDSCOLUMN *colinfo = NULL;
4373
4374 tdsdump_log(TDS_DBG_FUNC, "dbaltbind(%p, %d, %d, %d, %d, %p)\n", dbproc, computeid, column, vartype, varlen, varaddr);
4375
4376 colinfo = dbacolptr(dbproc, computeid, column, 1);
4377 if (!colinfo)
4378 return FAIL;
4379 CHECK_PARAMETER(varaddr, SYBEABNV, FAIL);
4380
4381 dbproc->avail_flag = FALSE;
4382
4383 srctype = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
4384 desttype = dblib_bound_type(vartype);
4385 if (desttype == TDS_INVALID_TYPE) {
4386 dbperror(dbproc, SYBEBTYP, 0);
4387 return FAIL;
4388 }
4389
4390 if (!dbwillconvert(srctype, desttype)) {
4391 dbperror(dbproc, SYBEAAMT, 0);
4392 return FAIL;
4393 }
4394
4395 colinfo->column_varaddr = (char *) varaddr;
4396 colinfo->column_bindtype = vartype;
4397 colinfo->column_bindlen = varlen;
4398
4399 return SUCCEED;
4400 }
4401
4402
4403 /**
4404 * \ingroup dblib_core
4405 * \brief Get address of compute column data.
4406 *
4407 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4408 * \param computeid of \c COMPUTE clause to which we're referring.
4409 * \param column Nth column in \a computeid, starting from 1.
4410 * \return pointer to columns's data buffer.
4411 * \retval NULL no such \a computeid or \a column.
4412 * \sa dbadlen(), dbaltbind(), dbaltlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumalts().
4413 */
4414 BYTE *
dbadata(DBPROCESS * dbproc,int computeid,int column)4415 dbadata(DBPROCESS * dbproc, int computeid, int column)
4416 {
4417 TDSCOLUMN *colinfo;
4418
4419 tdsdump_log(TDS_DBG_FUNC, "dbadata(%p, %d, %d)\n", dbproc, computeid, column);
4420
4421 colinfo = dbacolptr(dbproc, computeid, column, 0);
4422 if (!colinfo)
4423 return NULL;
4424
4425 if (is_blob_col(colinfo)) {
4426 return (BYTE *) ((TDSBLOB *) colinfo->column_data)->textvalue;
4427 }
4428
4429 return (BYTE *) colinfo->column_data;
4430 }
4431
4432 /**
4433 * \ingroup dblib_core
4434 * \brief Get aggregation operator for a compute column.
4435 *
4436 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4437 * \param computeid of \c COMPUTE clause to which we're referring.
4438 * \param column Nth column in \a computeid, starting from 1.
4439 * \return token value for the type of the compute column's aggregation operator.
4440 * \retval -1 no such \a computeid or \a column.
4441 * \sa dbadata(), dbadlen(), dbaltlen(), dbnextrow(), dbnumalts(), dbprtype().
4442 */
4443 int
dbaltop(DBPROCESS * dbproc,int computeid,int column)4444 dbaltop(DBPROCESS * dbproc, int computeid, int column)
4445 {
4446 TDSCOLUMN *curcol;
4447
4448 tdsdump_log(TDS_DBG_FUNC, "dbaltop(%p, %d, %d)\n", dbproc, computeid, column);
4449
4450 if ((curcol=dbacolptr(dbproc, computeid, column, 0)) == NULL)
4451 return -1;
4452
4453 return curcol->column_operator;
4454 }
4455
4456 /**
4457 * \ingroup dblib_core
4458 * \brief Set db-lib or server option.
4459 *
4460 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4461 * \param option option to set.
4462 * \param char_param value to set \a option to, if it wants a null-teminated ASCII string.
4463 * \param int_param value to set \a option to, if it wants an integer value.
4464 * \retval SUCCEED everything worked.
4465 * \retval FAIL no such \a option, or insufficient memory, or unimplemented.
4466 * \remarks Many are unimplemented.
4467 * \sa dbclropt(), dbisopt().
4468 * \todo Implement more options.
4469 */
4470 RETCODE
dbsetopt(DBPROCESS * dbproc,int option,const char * char_param,int int_param)4471 dbsetopt(DBPROCESS * dbproc, int option, const char *char_param, int int_param)
4472 {
4473 char *cmd;
4474 RETCODE rc;
4475 int i;
4476
4477 tdsdump_log(TDS_DBG_FUNC, "dbsetopt(%p, %d, %s, %d)\n", dbproc, option, char_param, int_param);
4478 CHECK_CONN(FAIL);
4479 CHECK_NULP(char_param, "dbsetopt", 3, FAIL);
4480
4481 if ((option < 0) || (option >= DBNUMOPTIONS)) {
4482 dbperror(dbproc, SYBEUNOP, 0);
4483 return FAIL;
4484 }
4485
4486 rc = FAIL;
4487 switch (option) {
4488 case DBARITHABORT:
4489 case DBARITHIGNORE:
4490 case DBCHAINXACTS:
4491 case DBFIPSFLAG:
4492 case DBISOLATION:
4493 case DBNOCOUNT:
4494 case DBNOEXEC:
4495 case DBPARSEONLY:
4496 case DBSHOWPLAN:
4497 case DBSTORPROCID:
4498 case DBQUOTEDIDENT:
4499 /* server options (on/off) */
4500 if (asprintf(&cmd, "set %s on\n", dbproc->dbopts[option].text) < 0) {
4501 return FAIL;
4502 }
4503 rc = dbstring_concat(&(dbproc->dboptcmd), cmd);
4504 free(cmd);
4505 break;
4506 case DBNATLANG:
4507 case DBDATEFIRST:
4508 case DBDATEFORMAT:
4509 /* server options (char_param) */
4510 if (asprintf(&cmd, "set %s %s\n", dbproc->dbopts[option].text, char_param) < 0) {
4511 return FAIL;
4512 }
4513 rc = dbstring_concat(&(dbproc->dboptcmd), cmd);
4514 free(cmd);
4515 break;
4516 case DBOFFSET:
4517 /* server option */
4518 /* requires param
4519 * "select", "from", "table", "order", "compute",
4520 * "statement", "procedure", "execute", or "param"
4521 */
4522 rc = SUCCEED;
4523 break;
4524 case DBROWCOUNT:
4525 /* server option */
4526 /* requires param "0" to "2147483647" */
4527 rc = SUCCEED;
4528 break;
4529 case DBSTAT:
4530 /* server option */
4531 /* requires param "io" or "time" */
4532 rc = SUCCEED;
4533 break;
4534 case DBTEXTLIMIT:
4535 /* dblib option */
4536 /* requires param "0" to "2147483647" */
4537 /* dblib do not return more than this length from text/image */
4538 /* TODO required for PHP */
4539 rc = SUCCEED;
4540 break;
4541 case DBTEXTSIZE:
4542 /* server option */
4543 /* requires param "0" to "2147483647" */
4544 /* limit text/image from network */
4545 if (!char_param)
4546 char_param = "0";
4547 i = atoi(char_param);
4548 if (i < 0 || i > 2147483647)
4549 return FAIL;
4550 if (asprintf(&cmd, "set textsize %d\n", i) < 0)
4551 return FAIL;
4552 rc = dbstring_concat(&(dbproc->dboptcmd), cmd);
4553 free(cmd);
4554 break;
4555 case DBAUTH:
4556 /* ??? */
4557 rc = SUCCEED;
4558 break;
4559 case DBNOAUTOFREE:
4560 /* dblib option */
4561 rc = SUCCEED;
4562 break;
4563 case DBBUFFER:
4564 /*
4565 * Requires param "2" to "2147483647"
4566 * (0 or 1 is an error, < 0 yields the default 100)
4567 */
4568 {
4569 int nrows;
4570
4571 /* 100 is the default, according to Microsoft */
4572 if( !char_param )
4573 char_param = "100";
4574
4575 nrows = atoi(char_param);
4576
4577 nrows = (nrows < 0 )? 100 : nrows;
4578
4579 if( 1 < nrows && nrows <= 2147483647 ) {
4580 buffer_set_capacity(dbproc, nrows);
4581 rc = SUCCEED;
4582 }
4583 }
4584 break;
4585 case DBPRCOLSEP:
4586 case DBPRLINELEN:
4587 case DBPRLINESEP:
4588 rc = dbstring_assign(&(dbproc->dbopts[option].param), char_param);
4589 break;
4590 case DBPRPAD:
4591 /*
4592 * "If the character is not specified, the ASCII space character is used."
4593 * A NULL pointer to the pad character signifies that padding is turned off.
4594 */
4595 if (int_param) {
4596 rc = dbstring_assign(&(dbproc->dbopts[option].param), char_param? char_param : " ");
4597 } else {
4598 rc = dbstring_assign(&(dbproc->dbopts[option].param), NULL);
4599 }
4600 break;
4601 case DBSETTIME:
4602 if (char_param) {
4603 i = atoi(char_param);
4604 if (0 < i) {
4605 rc = dbstring_assign(&(dbproc->dbopts[option].param), char_param);
4606 if (rc == SUCCEED) {
4607 dbproc->tds_socket->query_timeout = i;
4608 }
4609 }
4610 }
4611 break;
4612 default:
4613 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetopt(option = %d)\n", option);
4614 return FAIL;
4615 }
4616 if (rc == SUCCEED)
4617 dbproc->dbopts[option].factive = 1;
4618 return rc;
4619 }
4620
4621 /**
4622 * \ingroup dblib_core
4623 * \brief Set interrupt handler for db-lib to use while blocked against a read from the server.
4624 *
4625 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4626 * \param chkintr
4627 * \param hndlintr
4628 * \sa dbcancel(), dbgetuserdata(), dbsetuserdata(), dbsetbusy(), dbsetidle().
4629 */
4630 void
dbsetinterrupt(DBPROCESS * dbproc,DB_DBCHKINTR_FUNC chkintr,DB_DBHNDLINTR_FUNC hndlintr)4631 dbsetinterrupt(DBPROCESS * dbproc, DB_DBCHKINTR_FUNC chkintr, DB_DBHNDLINTR_FUNC hndlintr)
4632 {
4633 tdsdump_log(TDS_DBG_FUNC, "dbsetinterrupt(%p, %p, %p)\n", dbproc, chkintr, hndlintr);
4634 CHECK_PARAMETER(dbproc, SYBENULL, );
4635
4636 dbproc->chkintr = chkintr;
4637 dbproc->hndlintr = hndlintr;
4638 }
4639
4640 /**
4641 * \ingroup dblib_rpc
4642 * \brief Determine if query generated a return status number.
4643 *
4644 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4645 * \retval TRUE fetch return status with dbretstatus().
4646 * \retval FALSE no return status.
4647 * \sa dbnextrow(), dbresults(), dbretdata(), dbretstatus(), dbrpcinit(), dbrpcparam(), dbrpcsend().
4648 */
4649 DBBOOL
dbhasretstat(DBPROCESS * dbproc)4650 dbhasretstat(DBPROCESS * dbproc)
4651 {
4652 TDSSOCKET *tds;
4653
4654 tdsdump_log(TDS_DBG_FUNC, "dbhasretstat(%p)\n", dbproc);
4655 CHECK_PARAMETER(dbproc, SYBENULL, FALSE);
4656
4657 tds = dbproc->tds_socket;
4658 if (tds->has_status) {
4659 return TRUE;
4660 } else {
4661 return FALSE;
4662 }
4663 }
4664
4665 /**
4666 * \ingroup dblib_rpc
4667 * \brief Fetch status value returned by query or remote procedure call.
4668 *
4669 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4670 * \return return value
4671 * \sa dbhasretstat(), dbnextrow(), dbresults(), dbretdata(), dbrpcinit(), dbrpcparam(), dbrpcsend().
4672 */
4673 DBINT
dbretstatus(DBPROCESS * dbproc)4674 dbretstatus(DBPROCESS * dbproc)
4675 {
4676 tdsdump_log(TDS_DBG_FUNC, "dbretstatus(%p)\n", dbproc);
4677 CHECK_PARAMETER(dbproc, SYBENULL, 0);
4678
4679 return dbproc->tds_socket->ret_status;
4680 }
4681
4682 /**
4683 * \ingroup dblib_rpc
4684 * \brief Get count of output parameters filled by a stored procedure.
4685 *
4686 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4687 * \return How many, possibly zero.
4688 * \remarks This name sounds funny.
4689 * \sa
4690 */
4691 int
dbnumrets(DBPROCESS * dbproc)4692 dbnumrets(DBPROCESS * dbproc)
4693 {
4694 TDSSOCKET *tds;
4695 TDS_INT result_type;
4696
4697 tdsdump_log(TDS_DBG_FUNC, "dbnumrets(%p)\n", dbproc);
4698 CHECK_PARAMETER(dbproc, SYBENULL, 0);
4699
4700 tds = dbproc->tds_socket;
4701
4702 tdsdump_log(TDS_DBG_FUNC, "dbnumrets() finds %d columns\n", (tds->param_info? tds->param_info->num_cols : 0));
4703
4704 /* try to fetch output parameters and return status, if we have not already done so */
4705 if (!tds->param_info)
4706 tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_TRAILING);
4707
4708 if (!tds->param_info)
4709 return 0;
4710
4711 return tds->param_info->num_cols;
4712 }
4713
4714 /**
4715 * \ingroup dblib_rpc
4716 * \brief Get name of an output parameter filled by a stored procedure.
4717 *
4718 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4719 * \param retnum Nth parameter between \c 1 and the return value from \c dbnumrets().
4720 * \returns ASCII null-terminated string, \c NULL if no such \a retnum.
4721 * \sa dbnextrow(), dbnumrets(), dbresults(), dbretdata(), dbretlen(), dbrettype(), dbrpcinit(), dbrpcparam().
4722 */
4723 char *
dbretname(DBPROCESS * dbproc,int retnum)4724 dbretname(DBPROCESS * dbproc, int retnum)
4725 {
4726 TDSPARAMINFO *param_info;
4727
4728 tdsdump_log(TDS_DBG_FUNC, "dbretname(%p, %d)\n", dbproc, retnum);
4729 CHECK_PARAMETER(dbproc, SYBENULL, NULL);
4730
4731 if (!dbproc->tds_socket)
4732 return NULL;
4733
4734 dbnumrets(dbproc);
4735
4736 param_info = dbproc->tds_socket->param_info;
4737 if (!param_info || !param_info->columns || retnum < 1 || retnum > param_info->num_cols)
4738 return NULL;
4739 return tds_dstr_buf(¶m_info->columns[retnum - 1]->column_name);
4740 }
4741
4742 /**
4743 * \ingroup dblib_rpc
4744 * \brief Get value of an output parameter filled by a stored procedure.
4745 *
4746 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4747 * \param retnum Nth parameter between \c 1 and the return value from \c dbnumrets().
4748 * \returns Address of a return parameter value, or \c NULL if no such \a retnum.
4749 * \sa dbnextrow(), dbnumrets(), dbresults(), dbretlen(), dbretname(), dbrettype(), dbrpcinit(), dbrpcparam().
4750 * \todo Handle blobs.
4751 */
4752 BYTE *
dbretdata(DBPROCESS * dbproc,int retnum)4753 dbretdata(DBPROCESS * dbproc, int retnum)
4754 {
4755 TDSPARAMINFO *param_info;
4756
4757 tdsdump_log(TDS_DBG_FUNC, "dbretdata(%p, %d)\n", dbproc, retnum);
4758 CHECK_PARAMETER(dbproc, SYBENULL, NULL);
4759
4760 dbnumrets(dbproc);
4761
4762 param_info = dbproc->tds_socket->param_info;
4763 if (!param_info || !param_info->columns || retnum < 1 || retnum > param_info->num_cols)
4764 return NULL;
4765
4766 return _dbcoldata(param_info->columns[retnum - 1]);
4767 }
4768
4769 /**
4770 * \ingroup dblib_rpc
4771 * \brief Get size of an output parameter filled by a stored procedure.
4772 *
4773 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4774 * \param retnum Nth parameter between \c 1 and the return value from \c dbnumrets().
4775 * \returns Size of a return parameter value, or \c NULL if no such \a retnum.
4776 * \sa dbnextrow(), dbnumrets(), dbresults(), dbretdata(), dbretname(), dbrettype(), dbrpcinit(), dbrpcparam().
4777 */
4778 int
dbretlen(DBPROCESS * dbproc,int retnum)4779 dbretlen(DBPROCESS * dbproc, int retnum)
4780 {
4781 TDSCOLUMN *column;
4782 TDSPARAMINFO *param_info;
4783 TDSSOCKET *tds;
4784
4785 tdsdump_log(TDS_DBG_FUNC, "dbretlen(%p, %d)\n", dbproc, retnum);
4786 CHECK_PARAMETER(dbproc, SYBENULL, -1);
4787
4788 dbnumrets(dbproc);
4789
4790 tds = dbproc->tds_socket;
4791 param_info = tds->param_info;
4792 if (!param_info || !param_info->columns || retnum < 1 || retnum > param_info->num_cols)
4793 return -1;
4794
4795 column = param_info->columns[retnum - 1];
4796 if (column->column_cur_size < 0)
4797 return 0;
4798
4799 return column->column_cur_size;
4800 }
4801
4802 /**
4803 * \ingroup dblib_core
4804 * \brief Wait for results of a query from the server.
4805 *
4806 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4807 * \retval SUCCEED everything worked, fetch results with \c dbnextresults().
4808 * \retval FAIL SQL syntax error, typically.
4809 * \sa dbcmd(), dbfcmd(), DBIORDESC(), DBIOWDESC(), dbmoretext(), dbnextrow(),
4810 dbpoll(), DBRBUF(), dbresults(), dbretstatus(), dbrpcsend(), dbsettime(), dbsqlexec(),
4811 dbsqlsend(), dbwritetext().
4812 */
4813 RETCODE
dbsqlok(DBPROCESS * dbproc)4814 dbsqlok(DBPROCESS * dbproc)
4815 {
4816 TDSSOCKET *tds;
4817 TDS_INT result_type;
4818 RETCODE return_code = SUCCEED;
4819
4820 tdsdump_log(TDS_DBG_FUNC, "dbsqlok(%p)\n", dbproc);
4821 CHECK_CONN(FAIL);
4822
4823 tds = dbproc->tds_socket;
4824
4825 /*
4826 * dbsqlok has been called after dbmoretext()
4827 * This is the trigger to send the text data.
4828 */
4829
4830 if (dbproc->text_sent) {
4831 tds_flush_packet(tds);
4832 dbproc->text_sent = 0;
4833 }
4834
4835 /*
4836 * See what the next packet from the server is.
4837 * We want to skip any messages which are not processable.
4838 * We're looking for a result token or a done token.
4839 */
4840 for (;;) {
4841 TDSRET tds_code;
4842 int done_flags = 0;
4843
4844 /*
4845 * If we hit an end token -- e.g. if the command
4846 * submitted returned no data (like an insert) -- then
4847 * we process the end token to extract the status code.
4848 */
4849 tdsdump_log(TDS_DBG_FUNC, "dbsqlok() not done, calling tds_process_tokens()\n");
4850
4851 tds_code = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS);
4852
4853 /*
4854 * The error flag may be set for any intervening DONEINPROC packet, in particular
4855 * by a RAISERROR statement. Microsoft db-lib returns FAIL in that case.
4856 */
4857 if (done_flags & TDS_DONE_ERROR) {
4858 return_code = FAIL;
4859 }
4860
4861 switch (tds_code) {
4862 case TDS_NO_MORE_RESULTS:
4863 return SUCCEED;
4864 break;
4865
4866 case TDS_SUCCESS:
4867 switch (result_type) {
4868 case TDS_ROWFMT_RESULT:
4869 buffer_free(&dbproc->row_buf);
4870 buffer_alloc(dbproc);
4871 case TDS_COMPUTEFMT_RESULT:
4872 dbproc->dbresults_state = _DB_RES_RESULTSET_EMPTY;
4873 case TDS_COMPUTE_RESULT:
4874 case TDS_ROW_RESULT:
4875 tdsdump_log(TDS_DBG_FUNC, "dbsqlok() found result token\n");
4876 return SUCCEED;
4877 break;
4878 case TDS_DONEINPROC_RESULT:
4879 break;
4880 case TDS_DONE_RESULT:
4881 case TDS_DONEPROC_RESULT:
4882 tdsdump_log(TDS_DBG_FUNC, "dbsqlok() end status is %d (%s)\n", return_code, prdbretcode(return_code));
4883 #if 1
4884 if (done_flags & TDS_DONE_ERROR) {
4885
4886 if (done_flags & TDS_DONE_MORE_RESULTS) {
4887 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
4888 } else {
4889 dbproc->dbresults_state = _DB_RES_NO_MORE_RESULTS;
4890 }
4891
4892 } else {
4893 tdsdump_log(TDS_DBG_FUNC, "dbsqlok() end status was success\n");
4894
4895 dbproc->dbresults_state = _DB_RES_SUCCEED;
4896 }
4897
4898 return return_code;
4899 break;
4900 #else
4901 int retcode = (done_flags & TDS_DONE_ERROR)? FAIL : SUCCEED;
4902 dbproc->dbresults_state = (done_flags & TDS_DONE_MORE_RESULTS)?
4903 _DB_RES_NEXT_RESULT : _DB_RES_NO_MORE_RESULTS;
4904
4905 tdsdump_log(TDS_DBG_FUNC, "dbsqlok: returning %s with %s (%#x)\n",
4906 prdbretcode(retcode), prdbresults_state(dbproc->dbresults_state), done_flags);
4907
4908 if (retcode == SUCCEED && (done_flags & TDS_DONE_MORE_RESULTS))
4909 continue;
4910
4911 return retcode;
4912 #endif
4913 default:
4914 tdsdump_log(TDS_DBG_FUNC, "%s %d: logic error: tds_process_tokens result_type %d\n",
4915 __FILE__, __LINE__, result_type);
4916 break;
4917 }
4918 break;
4919
4920 default:
4921 assert(TDS_FAILED(tds_code));
4922 return FAIL;
4923 break;
4924 }
4925 }
4926
4927 return SUCCEED;
4928 }
4929
4930 /**
4931 * \ingroup dblib_core
4932 * \brief Get count of columns in a compute row.
4933 *
4934 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4935 * \param computeid of \c COMPUTE clause to which we're referring.
4936 * \return number of columns, else -1 if no such \a computeid.
4937 * \sa dbadata(), dbadlen(), dbaltlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumcols().
4938 */
4939 int
dbnumalts(DBPROCESS * dbproc,int computeid)4940 dbnumalts(DBPROCESS * dbproc, int computeid)
4941 {
4942 TDSSOCKET *tds;
4943 TDSCOMPUTEINFO *info;
4944 TDS_SMALLINT compute_id;
4945 int i;
4946
4947 tdsdump_log(TDS_DBG_FUNC, "dbnumalts(%p, %d)\n", dbproc, computeid);
4948 CHECK_PARAMETER(dbproc, SYBENULL, -1);
4949
4950 tds = dbproc->tds_socket;
4951 compute_id = computeid;
4952
4953 for (i = 0;; ++i) {
4954 if (i >= tds->num_comp_info)
4955 return -1;
4956 info = tds->comp_info[i];
4957 if (info->computeid == compute_id)
4958 break;
4959 }
4960
4961 return info->num_cols;
4962 }
4963
4964 /**
4965 * \ingroup dblib_core
4966 * \brief Get count of \c COMPUTE clauses for a result set.
4967 *
4968 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4969 * \return number of compute clauses for the current query, possibly zero.
4970 * \sa dbnumalts(), dbresults().
4971 */
4972 int
dbnumcompute(DBPROCESS * dbproc)4973 dbnumcompute(DBPROCESS * dbproc)
4974 {
4975 TDSSOCKET *tds;
4976
4977 tdsdump_log(TDS_DBG_FUNC, "dbnumcompute(%p)\n", dbproc);
4978 CHECK_PARAMETER(dbproc, SYBENULL, -1);
4979
4980 tds = dbproc->tds_socket;
4981
4982 return tds->num_comp_info;
4983 }
4984
4985
4986 /**
4987 * \ingroup dblib_core
4988 * \brief Get \c bylist for a compute row.
4989 *
4990 * \param dbproc contains all information needed by db-lib to manage communications with the server.
4991 * \param computeid of \c COMPUTE clause to which we're referring.
4992 * \param size \em output: size of \c bylist buffer whose address is returned, possibly zero.
4993 * \return address of \c bylist for \a computeid.
4994 * \retval NULL no such \a computeid.
4995 * \remarks Do not free returned pointer.
4996 * \sa dbadata(), dbadlen(), dbaltlen(), dbalttype(), dbcolname(), dbgetrow(), dbnextrow().
4997 */
4998 BYTE *
dbbylist(DBPROCESS * dbproc,int computeid,int * size)4999 dbbylist(DBPROCESS * dbproc, int computeid, int *size)
5000 {
5001 TDSSOCKET *tds;
5002 TDSCOMPUTEINFO *info;
5003 int i;
5004 const TDS_SMALLINT byte_flag = -0x8000;
5005
5006 tdsdump_log(TDS_DBG_FUNC, "dbbylist(%p, %d, %p)\n", dbproc, computeid, size);
5007 CHECK_PARAMETER(dbproc, SYBENULL, NULL);
5008
5009 tds = dbproc->tds_socket;
5010
5011 for (i = 0;; ++i) {
5012 if (i >= tds->num_comp_info) {
5013 if (size)
5014 *size = 0;
5015 return NULL;
5016 }
5017 info = tds->comp_info[i];
5018 if (info->computeid == computeid)
5019 break;
5020 }
5021
5022 if (size)
5023 *size = info->by_cols;
5024
5025 /*
5026 * libtds stores this information using TDS_SMALLINT so we
5027 * have to convert it. We can do this because libtds just
5028 * stores these data.
5029 */
5030 if (info->by_cols > 0 && info->bycolumns[0] != byte_flag) {
5031 int n;
5032 TDS_TINYINT *p = (TDS_TINYINT*) malloc(sizeof(info->bycolumns[0]) + info->by_cols);
5033 if (!p) {
5034 dbperror(dbproc, SYBEMEM, errno);
5035 return NULL;
5036 }
5037 for (n = 0; n < info->by_cols; ++n)
5038 p[sizeof(info->bycolumns[0]) + n] = info->bycolumns[n] > 255 ? 255 : info->bycolumns[n];
5039 *((TDS_SMALLINT *)p) = byte_flag;
5040 free(info->bycolumns);
5041 info->bycolumns = (TDS_SMALLINT *) p;
5042 }
5043 return (BYTE *) (&info->bycolumns[1]);
5044 }
5045
5046 /** \internal
5047 * \ingroup dblib_internal
5048 * \brief Check if \a dbproc is an ex-parrot.
5049 *
5050 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5051 * \retval TRUE process has been marked \em dead.
5052 * \retval FALSE process is OK.
5053 * \remarks dbdead() does not communicate with the server.
5054 * Unless a previously db-lib marked \a dbproc \em dead, dbdead() returns \c FALSE.
5055 * \sa dberrhandle().
5056 */
5057 DBBOOL
dbdead(DBPROCESS * dbproc)5058 dbdead(DBPROCESS * dbproc)
5059 {
5060 tdsdump_log(TDS_DBG_FUNC, "dbdead(%p) [%s]\n", dbproc, dbproc? IS_TDSDEAD(dbproc->tds_socket)? "dead":"alive" : "quite dead");
5061
5062 if( NULL == dbproc )
5063 return TRUE;
5064
5065 if (IS_TDSDEAD(dbproc->tds_socket))
5066 return TRUE;
5067
5068 return FALSE;
5069 }
5070
5071 /** \internal
5072 * \ingroup dblib_internal
5073 * \brief default error handler for db-lib (handles library-generated errors)
5074 *
5075 * The default error handler doesn't print anything. If you want to see your messages printed,
5076 * install an error handler. If you think that should be an optional compile- or run-time default,
5077 * submit a patch. It could be done.
5078 *
5079 * \sa DBDEAD(), dberrhandle().
5080 */
5081 /* Thus saith Sybase:
5082 * "If the user does not supply an error handler (or passes a NULL pointer to
5083 * dberrhandle), DB-Library will exhibit its default error-handling
5084 * behavior: It will abort the program if the error has made the affected
5085 * DBPROCESS unusable (the user can call DBDEAD to determine whether
5086 * or not a DBPROCESS has become unusable). If the error has not made the
5087 * DBPROCESS unusable, DB-Library will simply return an error code to its caller."
5088 *
5089 * It is not the error handler, however, that aborts anything. It is db-lib, cf. dbperror().
5090 */
5091 static int
default_err_handler(DBPROCESS * dbproc,int severity,int dberr,int oserr,char * dberrstr,char * oserrstr)5092 default_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
5093 {
5094 tdsdump_log(TDS_DBG_FUNC, "default_err_handler %p, %d, %d, %d, %p, %p", dbproc, severity, dberr, oserr, dberrstr, oserrstr);
5095
5096 if (DBDEAD(dbproc) && (!dbproc || !dbproc->msdblib)) {
5097 return INT_EXIT;
5098 }
5099
5100 if (!dbproc || !dbproc->msdblib) { /* i.e. Sybase behavior */
5101 switch(dberr) {
5102 case SYBETIME:
5103 return INT_EXIT;
5104 default:
5105 break;
5106 }
5107 }
5108 return INT_CANCEL;
5109 }
5110
5111 /**
5112 * \ingroup dblib_core
5113 * \brief Set an error handler, for messages from db-lib.
5114 *
5115 * \param handler pointer to callback function that will handle errors.
5116 * Pass NULL to restore the default handler.
5117 * \return address of prior handler, or NULL if none was previously installed.
5118 * \sa DBDEAD(), dbmsghandle().
5119 */
5120 EHANDLEFUNC
dberrhandle(EHANDLEFUNC handler)5121 dberrhandle(EHANDLEFUNC handler)
5122 {
5123 EHANDLEFUNC old_handler = _dblib_err_handler;
5124
5125 tdsdump_log(TDS_DBG_FUNC, "dberrhandle(%p)\n", handler);
5126
5127 _dblib_err_handler = handler? handler : default_err_handler;
5128
5129 return (old_handler == default_err_handler)? NULL : old_handler;
5130 }
5131
5132 /**
5133 * \ingroup dblib_core
5134 * \brief Set a message handler, for messages from the server.
5135 *
5136 * \param handler address of the function that will process the messages.
5137 * \sa DBDEAD(), dberrhandle().
5138 */
5139 MHANDLEFUNC
dbmsghandle(MHANDLEFUNC handler)5140 dbmsghandle(MHANDLEFUNC handler)
5141 {
5142 MHANDLEFUNC retFun = _dblib_msg_handler;
5143
5144 tdsdump_log(TDS_DBG_FUNC, "dbmsghandle(%p)\n", handler);
5145
5146 _dblib_msg_handler = handler;
5147 return retFun;
5148 }
5149
5150 #if defined(DBLIB_UNIMPLEMENTED)
5151 /**
5152 * \ingroup dblib_money
5153 * \brief Add two DBMONEY values.
5154 *
5155 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5156 * \param m1 first operand.
5157 * \param m2 other operand.
5158 * \param sum \em output: result of computation.
5159 * \retval SUCCEED Always.
5160 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5161 * \todo Unimplemented.
5162 */
5163 RETCODE
dbmnyadd(DBPROCESS * dbproc,DBMONEY * m1,DBMONEY * m2,DBMONEY * sum)5164 dbmnyadd(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * sum)
5165 {
5166 tdsdump_log(TDS_DBG_FUNC, "dbmnyadd(%p, %p, %p, %p)\n", dbproc, m1, m2, sum);
5167 CHECK_CONN(FAIL);
5168 CHECK_NULP(m1, "dbmnyadd", 2, FAIL);
5169 CHECK_NULP(m2, "dbmnyadd", 3, FAIL);
5170 CHECK_NULP(sum, "dbmnyadd", 4, FAIL);
5171
5172 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyadd()\n");
5173 return SUCCEED;
5174 }
5175
5176
5177 /**
5178 * \ingroup dblib_money
5179 * \brief Subtract two DBMONEY values.
5180 *
5181 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5182 * \param m1 first operand.
5183 * \param m2 other operand, subtracted from \a m1.
5184 * \param difference \em output: result of computation.
5185 * \retval SUCCEED Always.
5186 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5187 * \todo Unimplemented.
5188 */
5189 RETCODE
dbmnysub(DBPROCESS * dbproc,DBMONEY * m1,DBMONEY * m2,DBMONEY * difference)5190 dbmnysub(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * difference)
5191 {
5192 tdsdump_log(TDS_DBG_FUNC, "dbmnysub(%p, %p, %p, %p)\n", dbproc, m1, m2, difference);
5193 CHECK_CONN(FAIL);
5194 CHECK_NULP(m1, "dbmnysub", 2, FAIL);
5195 CHECK_NULP(m2, "dbmnysub", 3, FAIL);
5196 CHECK_NULP(difference, "dbmnysub", 4, FAIL);
5197
5198 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnysyb()\n");
5199 return SUCCEED;
5200 }
5201
5202 /**
5203 * \ingroup dblib_money
5204 * \brief Multiply two DBMONEY values.
5205 *
5206 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5207 * \param m1 first operand.
5208 * \param m2 other operand.
5209 * \param prod \em output: result of computation.
5210 * \retval SUCCEED Always.
5211 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5212 * \todo Unimplemented.
5213 */
5214 RETCODE
dbmnymul(DBPROCESS * dbproc,DBMONEY * m1,DBMONEY * m2,DBMONEY * prod)5215 dbmnymul(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * prod)
5216 {
5217 tdsdump_log(TDS_DBG_FUNC, "dbmnymul(%p, %p, %p, %p)\n", dbproc, m1, m2, prod);
5218 CHECK_CONN(FAIL);
5219 CHECK_NULP(m1, "dbmnymul", 2, FAIL);
5220 CHECK_NULP(m2, "dbmnymul", 3, FAIL);
5221 CHECK_NULP(prod, "dbmnymul", 4, FAIL);
5222
5223 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnymul()\n");
5224 return SUCCEED;
5225 }
5226
5227 /**
5228 * \ingroup dblib_money
5229 * \brief Divide two DBMONEY values.
5230 *
5231 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5232 * \param m1 dividend.
5233 * \param m2 divisor.
5234 * \param quotient \em output: result of computation.
5235 * \retval SUCCEED Always.
5236 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5237 * \todo Unimplemented.
5238 */
5239 RETCODE
dbmnydivide(DBPROCESS * dbproc,DBMONEY * m1,DBMONEY * m2,DBMONEY * quotient)5240 dbmnydivide(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2, DBMONEY * quotient)
5241 {
5242 tdsdump_log(TDS_DBG_FUNC, "dbmnydivide(%p, %p, %p, %p)\n", dbproc, m1, m2, quotient);
5243 CHECK_CONN(FAIL);
5244 CHECK_NULP(m1, "dbmnydivide", 2, FAIL);
5245 CHECK_NULP(m2, "dbmnydivide", 3, FAIL);
5246 CHECK_NULP(quotient, "dbmnydivide", 4, FAIL);
5247
5248 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnydivide()\n");
5249 return SUCCEED;
5250 }
5251 #endif
5252
5253 /**
5254 * \ingroup dblib_money
5255 * \brief Compare two DBMONEY values.
5256 *
5257 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5258 * \param m1 some money.
5259 * \param m2 some other money.
5260 * \retval 0 m1 == m2.
5261 * \retval -1 m1 < m2.
5262 * \retval 1 m1 > m2.
5263 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5264 */
5265 int
dbmnycmp(DBPROCESS * dbproc,DBMONEY * m1,DBMONEY * m2)5266 dbmnycmp(DBPROCESS * dbproc, DBMONEY * m1, DBMONEY * m2)
5267 {
5268 tdsdump_log(TDS_DBG_FUNC, "dbmnycmp(%p, %p, %p)\n", dbproc, m1, m2);
5269 CHECK_PARAMETER(dbproc, SYBENULL, 0);
5270 CHECK_NULP(m1, "dbmnycmp", 2, 0);
5271 CHECK_NULP(m2, "dbmnycmp", 3, 0);
5272
5273 if (m1->mnyhigh < m2->mnyhigh) {
5274 return -1;
5275 }
5276 if (m1->mnyhigh > m2->mnyhigh) {
5277 return 1;
5278 }
5279 if (m1->mnylow < m2->mnylow) {
5280 return -1;
5281 }
5282 if (m1->mnylow > m2->mnylow) {
5283 return 1;
5284 }
5285 return 0;
5286 }
5287
5288 #if defined(DBLIB_UNIMPLEMENTED)
5289 /**
5290 * \ingroup dblib_money
5291 * \brief Multiply a DBMONEY value by a positive integer, and add an amount.
5292 *
5293 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5294 * \param amount starting amount of money, also holds output.
5295 * \param multiplier amount to multiply \a amount by.
5296 * \param addend amount to add to \a amount, after multiplying by \a multiplier.
5297 * \retval SUCCEED Always.
5298 * \remarks This function is goofy.
5299 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5300 * \todo Unimplemented.
5301 */
5302 RETCODE
dbmnyscale(DBPROCESS * dbproc,DBMONEY * amount,int multiplier,int addend)5303 dbmnyscale(DBPROCESS * dbproc, DBMONEY * amount, int multiplier, int addend)
5304 {
5305 tdsdump_log(TDS_DBG_FUNC, "dbmnyscale(%p, %p, %d, %d)\n", dbproc, amount, multiplier, addend);
5306 CHECK_CONN(FAIL);
5307 CHECK_NULP(amount, "dbmnyscale", 2, FAIL);
5308
5309 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyscale()\n");
5310 return SUCCEED;
5311 }
5312 #endif
5313
5314 /**
5315 * \ingroup dblib_money
5316 * \brief Set a DBMONEY value to zero.
5317 *
5318 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5319 * \param dest address of a DBMONEY structure.
5320 * \retval SUCCEED unless \a amount is NULL.
5321 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5322 */
5323 RETCODE
dbmnyzero(DBPROCESS * dbproc,DBMONEY * dest)5324 dbmnyzero(DBPROCESS * dbproc, DBMONEY * dest)
5325 {
5326 tdsdump_log(TDS_DBG_FUNC, "dbmnyzero(%p, %p)\n", dbproc, dest);
5327 CHECK_CONN(FAIL);
5328 CHECK_NULP(dest, "dbmnyzero", 2, FAIL);
5329
5330 dest->mnylow = 0;
5331 dest->mnyhigh = 0;
5332 return SUCCEED;
5333 }
5334
5335 /**
5336 * \ingroup dblib_money
5337 * \brief Get maximum positive DBMONEY value supported.
5338 *
5339 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5340 * \param amount address of a DBMONEY structure.
5341 * \retval SUCCEED Always.
5342 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5343 */
5344 RETCODE
dbmnymaxpos(DBPROCESS * dbproc,DBMONEY * amount)5345 dbmnymaxpos(DBPROCESS * dbproc, DBMONEY * amount)
5346 {
5347 tdsdump_log(TDS_DBG_FUNC, "dbmnymaxpos(%p, %p)\n", dbproc, amount);
5348 CHECK_CONN(FAIL);
5349 CHECK_NULP(amount, "dbmnymaxpos", 2, FAIL);
5350
5351 amount->mnylow = 0xFFFFFFFFlu;
5352 amount->mnyhigh = 0x7FFFFFFFl;
5353 return SUCCEED;
5354 }
5355
5356 /**
5357 * \ingroup dblib_money
5358 * \brief Get maximum negative DBMONEY value supported.
5359 *
5360 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5361 * \param amount address of a DBMONEY structure.
5362 * \retval SUCCEED Always.
5363 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5364 */
5365 RETCODE
dbmnymaxneg(DBPROCESS * dbproc,DBMONEY * amount)5366 dbmnymaxneg(DBPROCESS * dbproc, DBMONEY * amount)
5367 {
5368 tdsdump_log(TDS_DBG_FUNC, "dbmnymaxneg(%p, %p)\n", dbproc, amount);
5369 CHECK_CONN(FAIL);
5370 CHECK_NULP(amount, "dbmnymaxneg", 2, FAIL);
5371
5372 amount->mnylow = 0;
5373 amount->mnyhigh = -0x7FFFFFFFL - 1;
5374 return SUCCEED;
5375 }
5376
5377 #if defined(DBLIB_UNIMPLEMENTED)
5378 /**
5379 * \ingroup dblib_money
5380 * \brief Get the least significant digit of a DBMONEY value, represented as a character.
5381 *
5382 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5383 * \param mnyptr \em input the money amount, \em and \em output: \a mnyptr divided by 10.
5384 * \param digit the character value (between '0' and '9') of the rightmost digit in \a mnyptr.
5385 * \param zero \em output: \c TRUE if \a mnyptr is zero on output, else \c FALSE.
5386 * \retval SUCCEED Always.
5387 * \sa dbconvert(), dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5388 * \remarks Unimplemented and likely to remain so. We'd be amused to learn anyone wants this function.
5389 * \todo Unimplemented.
5390 */
5391 RETCODE
dbmnyndigit(DBPROCESS * dbproc,DBMONEY * mnyptr,DBCHAR * digit,DBBOOL * zero)5392 dbmnyndigit(DBPROCESS * dbproc, DBMONEY * mnyptr, DBCHAR * digit, DBBOOL * zero)
5393 {
5394 tdsdump_log(TDS_DBG_FUNC, "dbmnyndigit(%p, %p, %s, %p)\n", dbproc, mnyptr, digit, zero);
5395 CHECK_CONN(FAIL);
5396 CHECK_NULP(mnyptr, "dbmnyndigit", 2, FAIL);
5397 CHECK_NULP(digit, "dbmnyndigit", 3, FAIL);
5398 CHECK_NULP(zero, "dbmnyndigit", 4, FAIL);
5399
5400 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyndigit()\n");
5401 return SUCCEED;
5402 }
5403
5404 /**
5405 * \ingroup dblib_money
5406 * \brief Prepare a DBMONEY value for use with dbmnyndigit().
5407 *
5408 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5409 * \param amount address of a DBMONEY structure.
5410 * \param trim number of digits to trim from \a amount.
5411 * \param negative \em output: \c TRUE if \a amount < 0.
5412 * \retval SUCCEED Always.
5413 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5414 * \todo Unimplemented.
5415 */
5416 RETCODE
dbmnyinit(DBPROCESS * dbproc,DBMONEY * amount,int trim,DBBOOL * negative)5417 dbmnyinit(DBPROCESS * dbproc, DBMONEY * amount, int trim, DBBOOL * negative)
5418 {
5419 tdsdump_log(TDS_DBG_FUNC, "dbmnyinit(%p, %p, %d, %p)\n", dbproc, amount, trim, negative);
5420 CHECK_CONN(FAIL);
5421 CHECK_NULP(amount, "dbmnyinit", 2, FAIL);
5422 CHECK_NULP(negative, "dbmnyinit", 4, FAIL);
5423
5424 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnyinit()\n");
5425 return SUCCEED;
5426 }
5427
5428
5429 /**
5430 * \ingroup dblib_money
5431 * \brief Divide a DBMONEY value by a positive integer.
5432 *
5433 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5434 * \param amount address of a DBMONEY structure.
5435 * \param divisor of \a amount.
5436 * \param remainder \em output: modulo of integer division.
5437 * \retval SUCCEED Always.
5438 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5439 * \todo Unimplemented.
5440 */
5441 RETCODE
dbmnydown(DBPROCESS * dbproc,DBMONEY * amount,int divisor,int * remainder)5442 dbmnydown(DBPROCESS * dbproc, DBMONEY * amount, int divisor, int *remainder)
5443 {
5444 tdsdump_log(TDS_DBG_FUNC, "dbmnydown(%p, %p, %d, %p)\n", dbproc, amount, divisor, remainder);
5445 CHECK_CONN(FAIL);
5446 CHECK_NULP(amount, "dbmnydown", 2, FAIL);
5447 CHECK_NULP(remainder, "dbmnydown", 4, FAIL);
5448 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmnydown()\n");
5449 return SUCCEED;
5450 }
5451 #endif
5452
5453 /**
5454 * \ingroup dblib_money
5455 * \brief Add $0.0001 to a DBMONEY value.
5456 *
5457 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5458 * \param amount address of a DBMONEY structure.
5459 * \retval SUCCEED or FAIL if overflow or amount NULL.
5460 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5461 */
5462 RETCODE
dbmnyinc(DBPROCESS * dbproc,DBMONEY * amount)5463 dbmnyinc(DBPROCESS * dbproc, DBMONEY * amount)
5464 {
5465 tdsdump_log(TDS_DBG_FUNC, "dbmnyinc(%p, %p)\n", dbproc, amount);
5466 CHECK_CONN(FAIL);
5467 CHECK_NULP(amount, "dbmnyinc", 2, FAIL);
5468
5469 if (amount->mnylow != 0xFFFFFFFFlu) {
5470 ++amount->mnylow;
5471 return SUCCEED;
5472 }
5473 if (amount->mnyhigh == 0x7FFFFFFFl)
5474 return FAIL;
5475 amount->mnylow = 0;
5476 ++amount->mnyhigh;
5477 return SUCCEED;
5478 }
5479
5480
5481 /**
5482 * \ingroup dblib_money
5483 * \brief Subtract $0.0001 from a DBMONEY value.
5484 *
5485 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5486 * \param amount address of a DBMONEY structure.
5487 * \retval SUCCEED or FAIL if overflow or amount NULL.
5488 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5489 */
5490 RETCODE
dbmnydec(DBPROCESS * dbproc,DBMONEY * amount)5491 dbmnydec(DBPROCESS * dbproc, DBMONEY * amount)
5492 {
5493 tdsdump_log(TDS_DBG_FUNC, "dbmnydec(%p, %p)\n", dbproc, amount);
5494 CHECK_CONN(FAIL);
5495 CHECK_NULP(amount, "dbmnydec", 2, FAIL);
5496
5497 if (amount->mnylow != 0) {
5498 --amount->mnylow;
5499 return SUCCEED;
5500 }
5501 if (amount->mnyhigh == -0x7FFFFFFFL - 1)
5502 return FAIL;
5503 amount->mnylow = 0xFFFFFFFFlu;
5504 --amount->mnyhigh;
5505 return SUCCEED;
5506 }
5507
5508 /**
5509 * \ingroup dblib_money
5510 * \brief Negate a DBMONEY value.
5511 *
5512 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5513 * \param src address of a DBMONEY structure.
5514 * \param dest \em output: result of negation.
5515 * \retval SUCCEED or FAIL if overflow or src/dest NULL.
5516 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5517 */
5518 RETCODE
dbmnyminus(DBPROCESS * dbproc,DBMONEY * src,DBMONEY * dest)5519 dbmnyminus(DBPROCESS * dbproc, DBMONEY * src, DBMONEY * dest)
5520 {
5521 tdsdump_log(TDS_DBG_FUNC, "dbmnyminus(%p, %p, %p)\n", dbproc, src, dest);
5522 CHECK_CONN(FAIL);
5523 CHECK_NULP(src, "dbmnyminus", 2, FAIL);
5524 CHECK_NULP(dest, "dbmnyminus", 3, FAIL);
5525
5526 if (src->mnyhigh == -0x7FFFFFFFL - 1 && src->mnylow == 0)
5527 return FAIL;
5528 dest->mnyhigh = -src->mnyhigh;
5529 dest->mnylow = (~src->mnylow) + 1u;
5530 return SUCCEED;
5531 }
5532
5533
5534 /**
5535 * \ingroup dblib_money
5536 * \brief Negate a DBMONEY4 value.
5537 *
5538 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5539 * \param src address of a DBMONEY4 structure.
5540 * \param dest \em output: result of negation.
5541 * \retval SUCCEED usually.
5542 * \retval FAIL on overflow.
5543 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5544 */
5545 RETCODE
dbmny4minus(DBPROCESS * dbproc,DBMONEY4 * src,DBMONEY4 * dest)5546 dbmny4minus(DBPROCESS * dbproc, DBMONEY4 * src, DBMONEY4 * dest)
5547 {
5548 DBMONEY4 zero;
5549
5550 tdsdump_log(TDS_DBG_FUNC, "dbmny4minus(%p, %p, %p)\n", dbproc, src, dest);
5551 CHECK_CONN(FAIL);
5552 CHECK_NULP(src, "dbmny4minus", 2, FAIL);
5553 CHECK_NULP(dest, "dbmny4minus", 3, FAIL);
5554
5555 dbmny4zero(dbproc, &zero);
5556 return (dbmny4sub(dbproc, &zero, src, dest));
5557 }
5558
5559 /**
5560 * \ingroup dblib_money
5561 * \brief Zero a DBMONEY4 value.
5562 *
5563 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5564 * \param dest address of a DBMONEY structure.
5565 * \retval SUCCEED usually.
5566 * \retval FAIL \a dest is NULL.
5567 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5568 */
5569 RETCODE
dbmny4zero(DBPROCESS * dbproc,DBMONEY4 * dest)5570 dbmny4zero(DBPROCESS * dbproc, DBMONEY4 * dest)
5571 {
5572 tdsdump_log(TDS_DBG_FUNC, "dbmny4zero(%p, %p)\n", dbproc, dest);
5573 CHECK_CONN(FAIL);
5574 CHECK_NULP(dest, "dbmny4zero", 2, FAIL);
5575
5576 dest->mny4 = 0;
5577 return SUCCEED;
5578 }
5579
5580 /**
5581 * \ingroup dblib_money
5582 * \brief Add two DBMONEY4 values.
5583 *
5584 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5585 * \param m1 first operand.
5586 * \param m2 other operand.
5587 * \param sum \em output: result of computation.
5588 * \retval SUCCEED usually.
5589 * \retval FAIL on overflow.
5590 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5591 */
5592 RETCODE
dbmny4add(DBPROCESS * dbproc,DBMONEY4 * m1,DBMONEY4 * m2,DBMONEY4 * sum)5593 dbmny4add(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * sum)
5594 {
5595 tdsdump_log(TDS_DBG_FUNC, "dbmny4add(%p, %p, %p, %p)\n", dbproc, m1, m2, sum);
5596 CHECK_CONN(FAIL);
5597 CHECK_NULP(m1, "dbmny4add", 2, FAIL);
5598 CHECK_NULP(m2, "dbmny4add", 3, FAIL);
5599 CHECK_NULP(sum, "dbmny4add", 4, FAIL);
5600
5601 sum->mny4 = m1->mny4 + m2->mny4;
5602 if (((m1->mny4 < 0) && (m2->mny4 < 0) && (sum->mny4 >= 0))
5603 || ((m1->mny4 > 0) && (m2->mny4 > 0) && (sum->mny4 <= 0))) {
5604 /* overflow */
5605 sum->mny4 = 0;
5606 return FAIL;
5607 }
5608 return SUCCEED;
5609 }
5610
5611 /**
5612 * \ingroup dblib_money
5613 * \brief Subtract two DBMONEY4 values.
5614 *
5615 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5616 * \param m1 first operand.
5617 * \param m2 other operand, subtracted from \a m1.
5618 * \param diff \em output: result of computation.
5619 * \retval SUCCEED usually.
5620 * \retval FAIL on overflow.
5621 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5622 */
5623 RETCODE
dbmny4sub(DBPROCESS * dbproc,DBMONEY4 * m1,DBMONEY4 * m2,DBMONEY4 * diff)5624 dbmny4sub(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * diff)
5625 {
5626
5627 tdsdump_log(TDS_DBG_FUNC, "dbmny4sub(%p, %p, %p, %p)\n", dbproc, m1, m2, diff);
5628 CHECK_CONN(FAIL);
5629 CHECK_NULP(m1, "dbmny4sub", 2, FAIL);
5630 CHECK_NULP(m2, "dbmny4sub", 3, FAIL);
5631 CHECK_NULP(diff, "dbmny4sub", 4, FAIL);
5632
5633 diff->mny4 = m1->mny4 - m2->mny4;
5634 if (((m1->mny4 <= 0) && (m2->mny4 > 0) && (diff->mny4 > 0))
5635 || ((m1->mny4 >= 0) && (m2->mny4 < 0) && (diff->mny4 < 0))) {
5636 /* overflow */
5637 diff->mny4 = 0;
5638 return FAIL;
5639 }
5640 return SUCCEED;
5641 }
5642
5643 #if defined(DBLIB_UNIMPLEMENTED)
5644 /**
5645 * \ingroup dblib_money
5646 * \brief Multiply two DBMONEY4 values.
5647 *
5648 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5649 * \param m1 first operand.
5650 * \param m2 other operand.
5651 * \param prod \em output: result of computation.
5652 * \retval SUCCEED usually.
5653 * \retval FAIL a parameter is NULL.
5654 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5655 * \todo Unimplemented.
5656 */
5657 RETCODE
dbmny4mul(DBPROCESS * dbproc,DBMONEY4 * m1,DBMONEY4 * m2,DBMONEY4 * prod)5658 dbmny4mul(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * prod)
5659 {
5660
5661 tdsdump_log(TDS_DBG_FUNC, "dbmny4mul(%p, %p, %p, %p)\n", dbproc, m1, m2, prod);
5662 CHECK_CONN(FAIL);
5663 CHECK_NULP(m1, "dbmny4mul", 2, FAIL);
5664 CHECK_NULP(m2, "dbmny4mul", 3, FAIL);
5665 CHECK_NULP(prod, "dbmny4mul", 4, FAIL);
5666
5667 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmny4mul()\n");
5668 return FAIL;
5669 }
5670
5671 /**
5672 * \ingroup dblib_money
5673 * \brief Divide two DBMONEY4 values.
5674 *
5675 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5676 * \param m1 dividend.
5677 * \param m2 divisor.
5678 * \param quotient \em output: result of computation.
5679 * \retval SUCCEED usually.
5680 * \retval FAIL a parameter is NULL.
5681 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5682 * \todo Unimplemented.
5683 */
5684 RETCODE
dbmny4divide(DBPROCESS * dbproc,DBMONEY4 * m1,DBMONEY4 * m2,DBMONEY4 * quotient)5685 dbmny4divide(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2, DBMONEY4 * quotient)
5686 {
5687
5688 tdsdump_log(TDS_DBG_FUNC, "dbmny4divide(%p, %p, %p, %p)\n", dbproc, m1, m2, quotient);
5689 CHECK_CONN(FAIL);
5690 CHECK_NULP(m1, "dbmny4divide", 2, FAIL);
5691 CHECK_NULP(m2, "dbmny4divide", 3, FAIL);
5692 CHECK_NULP(quotient, "dbmny4divide", 4, FAIL);
5693
5694 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbmny4divide()\n");
5695 return FAIL;
5696 }
5697 #endif
5698
5699 /**
5700 * \ingroup dblib_money
5701 * \brief Compare two DBMONEY4 values.
5702 *
5703 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5704 * \param m1 some money.
5705 * \param m2 some other money.
5706 * \retval 0 m1 == m2.
5707 * \retval -1 m1 < m2.
5708 * \retval 1 m1 > m2.
5709 * \sa dbmnyadd(), dbmnysub(), dbmnymul(), dbmnydivide(), dbmnyminus(), dbmny4add(), dbmny4sub(), dbmny4mul(), dbmny4divide(), dbmny4minus().
5710 */
5711 int
dbmny4cmp(DBPROCESS * dbproc,DBMONEY4 * m1,DBMONEY4 * m2)5712 dbmny4cmp(DBPROCESS * dbproc, DBMONEY4 * m1, DBMONEY4 * m2)
5713 {
5714
5715 tdsdump_log(TDS_DBG_FUNC, "dbmny4cmp(%p, %p, %p)\n", dbproc, m1, m2);
5716 CHECK_PARAMETER(dbproc, SYBENULL, 0);
5717 CHECK_NULP(m1, "dbmny4cmp", 2, 0);
5718 CHECK_NULP(m2, "dbmny4cmp", 3, 0);
5719
5720 if (m1->mny4 < m2->mny4) {
5721 return -1;
5722 }
5723 if (m1->mny4 > m2->mny4) {
5724 return 1;
5725 }
5726 return 0;
5727 }
5728
5729 /**
5730 * \ingroup dblib_money
5731 * \brief Copy a DBMONEY4 value.
5732 *
5733 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5734 * \param src address of a DBMONEY4 structure.
5735 * \param dest \em output: new money.
5736 * \retval SUCCEED or FAIL if src/dest NULL.
5737 * \sa dbmnycopy(), dbmnyminus(), dbmny4minus().
5738 */
5739 RETCODE
dbmny4copy(DBPROCESS * dbproc,DBMONEY4 * src,DBMONEY4 * dest)5740 dbmny4copy(DBPROCESS * dbproc, DBMONEY4 * src, DBMONEY4 * dest)
5741 {
5742
5743 tdsdump_log(TDS_DBG_FUNC, "dbmny4copy(%p, %p, %p)\n", dbproc, src, dest);
5744 CHECK_CONN(FAIL);
5745 CHECK_NULP(src, "dbmny4copy", 2, FAIL);
5746 CHECK_NULP(dest, "dbmny4copy", 3, FAIL);
5747
5748 dest->mny4 = src->mny4;
5749 return SUCCEED;
5750 }
5751
5752 /**
5753 * \ingroup dblib_datetime
5754 * \brief Compare DBDATETIME values, similar to strcmp(3).
5755 *
5756 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5757 * \param d1 a \c DBDATETIME structure address
5758 * \param d2 another \c DBDATETIME structure address
5759 * \retval 0 d1 = d2.
5760 * \retval -1 d1 < d2.
5761 * \retval 1 d1 > d2.
5762 * \sa dbdate4cmp(), dbmnycmp(), dbmny4cmp().
5763 */
5764 int
dbdatecmp(DBPROCESS * dbproc,DBDATETIME * d1,DBDATETIME * d2)5765 dbdatecmp(DBPROCESS * dbproc, DBDATETIME * d1, DBDATETIME * d2)
5766 {
5767 tdsdump_log(TDS_DBG_FUNC, "dbdatecmp(%p, %p, %p)\n", dbproc, d1, d2);
5768 CHECK_CONN(FAIL);
5769 CHECK_NULP(d1, "dbdatecmp", 2, 0);
5770 CHECK_NULP(d2, "dbdatecmp", 3, 0);
5771
5772 if (d1->dtdays == d2->dtdays) {
5773 if (d1->dttime == d2->dttime)
5774 return 0;
5775 return d1->dttime > d2->dttime ? 1 : -1;
5776 }
5777
5778 /* date 1 is before 1900 */
5779 if (d1->dtdays > 2958463) {
5780
5781 if (d2->dtdays > 2958463) /* date 2 is before 1900 */
5782 return d1->dtdays > d2->dtdays ? 1 : -1;
5783 return -1;
5784 }
5785
5786 /* date 1 is after 1900 */
5787 if (d2->dtdays < 2958463) /* date 2 is after 1900 */
5788 return d1->dtdays > d2->dtdays ? 1 : -1;
5789 return 1;
5790 }
5791
5792 static RETCODE
dblib_datecrack(DBPROCESS * dbproc,BOOL nano_precision,DBDATEREC * output,int type,const void * data)5793 dblib_datecrack(DBPROCESS * dbproc, BOOL nano_precision, DBDATEREC * output, int type, const void * data)
5794 {
5795 TDSDATEREC dr;
5796 struct tds_sybase_dbdaterec *di = (struct tds_sybase_dbdaterec*) output;
5797
5798 tdsdump_log(TDS_DBG_FUNC, "dblib_datecrack(%p, %d, %p, %d, %p)\n", dbproc, nano_precision, output, type, data);
5799 CHECK_NULP(output, "dbdatecrack", 2, FAIL);
5800 CHECK_PARAMETER(data, SYBENDTP, FAIL);
5801
5802 if (TDS_FAILED(tds_datecrack(type, data, &dr)))
5803 return FAIL;
5804
5805 di->dateyear = dr.year;
5806 di->quarter = dr.quarter;
5807 di->datemonth = dr.month;
5808 di->datedmonth = dr.day;
5809 di->datedyear = dr.dayofyear;
5810 di->datedweek = dr.weekday;
5811 di->datehour = dr.hour;
5812 di->dateminute = dr.minute;
5813 di->datesecond = dr.second;
5814 di->datetzone = dr.timezone;
5815 if (nano_precision)
5816 /* here we are writing to nanosecond field */
5817 di->datemsecond = dr.decimicrosecond * 100u;
5818 else
5819 di->datemsecond = dr.decimicrosecond / 10000u;
5820 /* Revert to compiled-in default if dbproc can't be used to find the runtime override. */
5821 if (dbproc ? dbproc->msdblib : dblib_msdblib) {
5822 ++di->quarter;
5823 ++di->datemonth;
5824 ++di->datedweek;
5825 }
5826 return SUCCEED;
5827 }
5828
5829 /**
5830 * \ingroup dblib_core
5831 * \brief Break a DBDATETIME value into useful pieces.
5832 *
5833 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5834 * \param di \em output: structure to contain the exploded parts of \a datetime.
5835 * \param datetime \em input: \c DBDATETIME to be converted.
5836 * \retval SUCCEED always.
5837 * \remarks The members of \a di have different names, depending on whether \c --with-msdblib was configured.
5838 *
5839 * If DBPROCESS is NULL, dbdatecrack() uses the compiled in default
5840 * value of MSDBLIB as of when libsybdb was compiled, irrespective of its value when the
5841 * application is compiled. This can lead to incorrect results because Sybase and Microsoft use different
5842 * ranges -- [0,11] vs. [1,12] -- for the month.
5843 *
5844 * \sa dbconvert(), dbdata(), dbdatechar(), dbdatename(), dbdatepart(), tdsdbopen().
5845 */
5846 RETCODE
dbdatecrack(DBPROCESS * dbproc,DBDATEREC * di,DBDATETIME * datetime)5847 dbdatecrack(DBPROCESS * dbproc, DBDATEREC * di, DBDATETIME * datetime)
5848 {
5849 return dblib_datecrack(dbproc, FALSE, di, SYBDATETIME, datetime);
5850 }
5851
5852 /**
5853 * \ingroup dblib_core
5854 * \brief Break any kind of date or time value into useful pieces.
5855 *
5856 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5857 * \param di \em output: structure to contain the exploded parts of \a datetime.
5858 * \param type \em input: \c type of date/time value returned by dbcoltype().
5859 * \param data \em input: \c date/time value to be converted.
5860 * \retval SUCCEED always.
5861 * \remarks The members of \a di have different names, depending on whether \c --with-msdblib was configured.
5862 *
5863 * This is an extension to dbdatecrack(), see it for more information.
5864 *
5865 * \sa dbdatecrack(), dbconvert(), dbdata(), dbdatechar(), dbdatename(), dbdatepart(), tdsdbopen().
5866 */
5867 RETCODE
dbanydatecrack(DBPROCESS * dbproc,DBDATEREC2 * di,int type,const void * data)5868 dbanydatecrack(DBPROCESS * dbproc, DBDATEREC2 * di, int type, const void *data)
5869 {
5870 return dblib_datecrack(dbproc, TRUE, (DBDATEREC *) di, type, data);
5871 }
5872
5873 #if defined(DBLIB_UNIMPLEMENTED)
5874 /**
5875 * \ingroup dblib_core
5876 * \brief Clear remote passwords from the LOGINREC structure.
5877 *
5878 * \param login structure to pass to dbopen().
5879 * \sa dblogin(), dbopen(), dbrpwset(), DBSETLAPP(), DBSETLHOST(), DBSETLPWD(), DBSETLUSER().
5880 * \remarks Useful for remote stored procedure calls, but not in high demand from FreeTDS.
5881 * \todo Unimplemented.
5882 */
5883 void
dbrpwclr(LOGINREC * login)5884 dbrpwclr(LOGINREC * login)
5885 {
5886 tdsdump_log(TDS_DBG_FUNC, "dbrpwclr(%p)\n", login);
5887 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbrpwclr()\n");
5888 }
5889
5890 /**
5891 * \ingroup dblib_core
5892 * \brief Add a remote password to the LOGINREC structure.
5893 *
5894 * \param login structure to pass to dbopen().
5895 * \param srvname server for which \a password should be used.
5896 * \param password you guessed it, let's hope no else does.
5897 * \param pwlen count of \a password, in bytes.
5898 * \remarks Useful for remote stored procedure calls, but not in high demand from FreeTDS.
5899 * \sa dblogin(), dbopen(), dbrpwclr(), DBSETLAPP(), DBSETLHOST(), DBSETLPWD(), DBSETLUSER().
5900 * \todo Unimplemented.
5901 */
5902 RETCODE
dbrpwset(LOGINREC * login,char * srvname,char * password,int pwlen)5903 dbrpwset(LOGINREC * login, char *srvname, char *password, int pwlen)
5904 {
5905 tdsdump_log(TDS_DBG_FUNC, "dbrpwset(%p, %s, %s, %d)\n", login, srvname, password, pwlen);
5906 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbrpwset()\n");
5907 return SUCCEED;
5908 }
5909 #endif
5910
5911 /**
5912 * \ingroup dblib_core
5913 * \brief Get server process ID for a \c DBPROCESS.
5914 *
5915 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5916 * \return \em "spid", the server's process ID.
5917 * \sa dbopen().
5918 */
5919 int
dbspid(DBPROCESS * dbproc)5920 dbspid(DBPROCESS * dbproc)
5921 {
5922 tdsdump_log(TDS_DBG_FUNC, "dbspid(%p)\n", dbproc);
5923 CHECK_CONN(-1);
5924
5925 return dbproc->tds_socket->conn->spid;
5926 }
5927
5928 /**
5929 * \ingroup dblib_core
5930 * \brief Associate client-allocated (and defined) data with a \c DBPROCESS.
5931 *
5932 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5933 * \param ptr address of client-defined data.
5934 * \remarks \a ptr is the location of user data that \c db-lib will associate with \a dbproc.
5935 * The client allocates the buffer addressed by \a ptr. \c db-lib never examines or uses the information;
5936 * it just stashes the pointer for later retrieval by the application with \c dbgetuserdata().
5937 * \sa dbgetuserdata().
5938 */
5939 void
dbsetuserdata(DBPROCESS * dbproc,BYTE * ptr)5940 dbsetuserdata(DBPROCESS * dbproc, BYTE * ptr)
5941 {
5942 tdsdump_log(TDS_DBG_FUNC, "dbsetuserdata(%p, %p)\n", dbproc, ptr);
5943 CHECK_PARAMETER(dbproc, SYBENULL, );
5944
5945 dbproc->user_data = ptr;
5946 }
5947
5948 /**
5949 * \ingroup dblib_core
5950 * \brief Get address of user-allocated data from a \c DBPROCESS.
5951 *
5952 * \param dbproc contains all information needed by db-lib to manage communications with the server.
5953 * \return address of user-defined data that \c db-lib associated with \a dbproc when the client called dbsetuserdata().
5954 * \retval undefined (probably \c NULL) dbsetuserdata() was not previously called.
5955 * \sa dbsetuserdata().
5956 */
5957 BYTE *
dbgetuserdata(DBPROCESS * dbproc)5958 dbgetuserdata(DBPROCESS * dbproc)
5959 {
5960 tdsdump_log(TDS_DBG_FUNC, "dbgetuserdata(%p)\n", dbproc);
5961 CHECK_PARAMETER(dbproc, SYBENULL, NULL);
5962
5963 return dbproc->user_data;
5964 }
5965
5966 /**
5967 * \ingroup dblib_core
5968 * \brief Specify a db-lib version level.
5969 *
5970 * \param version anything, really.
5971 * \retval SUCCEED Always.
5972 * \remarks No effect on behavior of \c db-lib in \c FreeTDS.
5973 * \sa
5974 */
5975 RETCODE
dbsetversion(DBINT version)5976 dbsetversion(DBINT version)
5977 {
5978 tdsdump_log(TDS_DBG_FUNC, "dbsetversion(%d)\n", version);
5979
5980 switch (version) {
5981 case DBVERSION_42:
5982 case DBVERSION_46:
5983 case DBVERSION_100:
5984 case DBVERSION_70:
5985 case DBVERSION_71:
5986 case DBVERSION_72:
5987 case DBVERSION_73:
5988 case DBVERSION_74:
5989 g_dblib_version = version;
5990 return SUCCEED;
5991 default:
5992 break;
5993 }
5994
5995 dbperror(NULL, SYBEIVERS, 0);
5996 return FAIL;
5997 }
5998
5999 /**
6000 * \ingroup dblib_money
6001 * \brief Copy a DBMONEY value.
6002 *
6003 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6004 * \param src address of a DBMONEY structure.
6005 * \param dest \em output: new money.
6006 * \retval SUCCEED always, unless \a src or \a dest is \c NULL.
6007 * \sa
6008 */
6009 RETCODE
dbmnycopy(DBPROCESS * dbproc,DBMONEY * src,DBMONEY * dest)6010 dbmnycopy(DBPROCESS * dbproc, DBMONEY * src, DBMONEY * dest)
6011 {
6012 tdsdump_log(TDS_DBG_FUNC, "dbmnycopy(%p, %p, %p)\n", dbproc, src, dest);
6013 CHECK_CONN(FAIL);
6014 CHECK_NULP(src, "dbmnycopy", 2, FAIL);
6015 CHECK_NULP(dest, "dbmnycopy", 3, FAIL);
6016
6017 dest->mnylow = src->mnylow;
6018 dest->mnyhigh = src->mnyhigh;
6019 return SUCCEED;
6020 }
6021
6022
6023 /**
6024 * \ingroup dblib_core
6025 * \brief Cancel the query currently being retrieved, discarding all pending rows.
6026 *
6027 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6028 * \sa
6029 */
6030 RETCODE
dbcanquery(DBPROCESS * dbproc)6031 dbcanquery(DBPROCESS * dbproc)
6032 {
6033 TDSRET rc;
6034 TDS_INT result_type;
6035
6036 tdsdump_log(TDS_DBG_FUNC, "dbcanquery(%p)\n", dbproc);
6037 CHECK_CONN(FAIL);
6038
6039 /* Just throw away all pending rows from the last query */
6040
6041 rc = tds_process_tokens(dbproc->tds_socket, &result_type, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE);
6042
6043 if (TDS_FAILED(rc))
6044 return FAIL;
6045
6046 dbproc->dbresults_state = _DB_RES_NEXT_RESULT;
6047
6048 return SUCCEED;
6049 }
6050
6051 /**
6052 * \ingroup dblib_core
6053 * \brief Erase the command buffer, in case \c DBNOAUTOFREE was set with dbsetopt().
6054 *
6055 *
6056 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6057 * \sa dbcmd(), dbfcmd(), dbgetchar(), dbsqlexec(), dbsqlsend(), dbsetopt(), dbstrcpy(), dbstrlen().
6058 */
6059 void
dbfreebuf(DBPROCESS * dbproc)6060 dbfreebuf(DBPROCESS * dbproc)
6061 {
6062 tdsdump_log(TDS_DBG_FUNC, "dbfreebuf(%p)\n", dbproc);
6063 CHECK_PARAMETER(dbproc, SYBENULL, );
6064
6065 if (dbproc->dbbuf)
6066 TDS_ZERO_FREE(dbproc->dbbuf);
6067 dbproc->dbbufsz = 0;
6068 }
6069
6070 /**
6071 * \ingroup dblib_core
6072 * \brief Reset an option.
6073 *
6074 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6075 * \param option to be turned off.
6076 * \param param clearing some options requires a parameter, believe it or not.
6077 * \retval SUCCEED \a option and \a parameter seem sane.
6078 * \retval FAIL no such \a option.
6079 * \remarks Only the following options are recognized:
6080 - DBARITHABORT
6081 - DBARITHIGNORE
6082 - DBCHAINXACTS
6083 - DBFIPSFLAG
6084 - DBISOLATION
6085 - DBNOCOUNT
6086 - DBNOEXEC
6087 - DBPARSEONLY
6088 - DBSHOWPLAN
6089 - DBSTORPROCID
6090 - DBQUOTEDIDENT
6091 - DBSETTIME
6092 * \sa dbisopt(), dbsetopt().
6093 */
6094 RETCODE
dbclropt(DBPROCESS * dbproc,int option,const char param[])6095 dbclropt(DBPROCESS * dbproc, int option, const char param[])
6096 {
6097 char *cmd;
6098
6099 tdsdump_log(TDS_DBG_FUNC, "dbclropt(%p, %d, %s)\n", dbproc, option, param);
6100 CHECK_CONN(FAIL);
6101 if (option != DBSETTIME) {
6102 CHECK_NULP(param, "dbclropt", 3, FAIL);
6103 }
6104
6105 if ((option < 0) || (option >= DBNUMOPTIONS)) {
6106 return FAIL;
6107 }
6108 dbproc->dbopts[option].factive = 0;
6109 switch (option) {
6110 case DBARITHABORT:
6111 case DBARITHIGNORE:
6112 case DBCHAINXACTS:
6113 case DBFIPSFLAG:
6114 case DBISOLATION:
6115 case DBNOCOUNT:
6116 case DBNOEXEC:
6117 case DBPARSEONLY:
6118 case DBSHOWPLAN:
6119 case DBSTORPROCID:
6120 case DBQUOTEDIDENT:
6121 /* server options (on/off) */
6122 if (asprintf(&cmd, "set %s off\n", dbproc->dbopts[option].text) < 0) {
6123 return FAIL;
6124 }
6125 dbstring_concat(&(dbproc->dboptcmd), cmd);
6126 free(cmd);
6127 break;
6128 case DBBUFFER:
6129 buffer_set_capacity(dbproc, 1); /* frees row_buf->rows */
6130 return SUCCEED;
6131 break;
6132 case DBSETTIME:
6133 tds_mutex_lock(&dblib_mutex);
6134 /*
6135 * Use global value of query timeout set by dbsettime() if any,
6136 * otherwise set it to zero just like tds_init_socket() does.
6137 */
6138 if (g_dblib_ctx.query_timeout > 0) {
6139 dbproc->tds_socket->query_timeout = g_dblib_ctx.query_timeout;
6140 } else {
6141 dbproc->tds_socket->query_timeout = 0;
6142 }
6143 tds_mutex_unlock(&dblib_mutex);
6144 return SUCCEED;
6145 break;
6146 default:
6147 break;
6148 }
6149 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbclropt(option = %d)\n", option);
6150 return FAIL;
6151 }
6152
6153 /**
6154 * \ingroup dblib_core
6155 * \brief Get value of an option
6156 *
6157 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6158 * \param option the option
6159 * \param param a parameter to \a option.
6160 * \sa dbclropt(), dbsetopt().
6161 */
6162 DBBOOL
dbisopt(DBPROCESS * dbproc,int option,const char param[])6163 dbisopt(DBPROCESS * dbproc, int option, const char param[])
6164 {
6165 tdsdump_log(TDS_DBG_FUNC, "dbisopt(%p, %d, %s)\n", dbproc, option, param);
6166 CHECK_PARAMETER(dbproc, SYBENULL, FALSE);
6167 /* sometimes param can be NULL */
6168
6169 if ((option < 0) || (option >= DBNUMOPTIONS)) {
6170 return FALSE;
6171 }
6172 return dbproc->dbopts[option].factive;
6173 }
6174
6175 /** \internal
6176 * \ingroup dblib_internal
6177 * \brief Get number of the row currently being read.
6178 *
6179 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6180 * \return ostensibly the row number, or 0 if no rows have been read yet.
6181 * \retval 0 Always.
6182 * \sa DBCURROW(), dbclrbuf(), DBFIRSTROW(), dbgetrow(), DBLASTROW(), dbnextrow(), dbsetopt(),.
6183 * \todo Unimplemented.
6184 */
6185 DBINT
dbcurrow(DBPROCESS * dbproc)6186 dbcurrow(DBPROCESS * dbproc)
6187 {
6188 tdsdump_log(TDS_DBG_FUNC, "dbcurrow(%p)\n", dbproc);
6189 CHECK_PARAMETER(dbproc, SYBENULL, 0);
6190 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbcurrow()\n");
6191 return 0;
6192 }
6193
6194
6195 /** \internal
6196 * \ingroup dblib_internal
6197 * \brief Get returned row's type.
6198 *
6199 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6200 * \sa DBROWTYPE().
6201 */
6202 STATUS
dbrowtype(DBPROCESS * dbproc)6203 dbrowtype(DBPROCESS * dbproc)
6204 {
6205 tdsdump_log(TDS_DBG_FUNC, "dbrowtype(%p)\n", dbproc);
6206 CHECK_PARAMETER(dbproc, SYBENULL, NO_MORE_ROWS);
6207 return dbproc->row_type;
6208 }
6209
6210
6211 /** \internal
6212 * \ingroup dblib_internal
6213 * \brief Get number of the row just returned.
6214 *
6215 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6216 * \sa DBCURROW().
6217 * \todo Unimplemented.
6218 */
6219 int
dbcurcmd(DBPROCESS * dbproc)6220 dbcurcmd(DBPROCESS * dbproc)
6221 {
6222 tdsdump_log(TDS_DBG_FUNC, "dbcurcmd(%p)\n", dbproc);
6223 CHECK_PARAMETER(dbproc, SYBENULL, 0);
6224 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbcurcmd()\n");
6225 return 0;
6226 }
6227
6228
6229 /**
6230 * \ingroup dblib_core
6231 * \brief See if more commands are to be processed.
6232 *
6233 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6234 * \sa DBMORECMDS(). DBCMDROW(), dbresults(), DBROWS(), DBROWTYPE().
6235 */
6236 RETCODE
dbmorecmds(DBPROCESS * dbproc)6237 dbmorecmds(DBPROCESS * dbproc)
6238 {
6239 tdsdump_log(TDS_DBG_FUNC, "dbmorecmds(%p)\n", dbproc);
6240 CHECK_CONN(FAIL);
6241
6242 if (dbproc->tds_socket->res_info == NULL) {
6243 return FAIL;
6244 }
6245
6246 if (!dbproc->tds_socket->res_info->more_results) {
6247 tdsdump_log(TDS_DBG_FUNC, "more_results is false; returns FAIL\n");
6248 return FAIL;
6249 }
6250
6251 tdsdump_log(TDS_DBG_FUNC, "more_results is true; returns SUCCEED\n");
6252
6253 return SUCCEED;
6254 }
6255
6256 /**
6257 * \ingroup dblib_rpc
6258 * \brief Get datatype of a stored procedure's return parameter.
6259 *
6260 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6261 * \param retnum Nth return parameter, between 1 and \c dbnumrets().
6262 * \return SYB* datatype token, or -1 if \a retnum is out of range.
6263 * \sa dbnextrow(), dbnumrets(), dbprtype(), dbresults(), dbretdata(), dbretlen(), dbretname(), dbrpcinit(), dbrpcparam().
6264 */
6265 int
dbrettype(DBPROCESS * dbproc,int retnum)6266 dbrettype(DBPROCESS * dbproc, int retnum)
6267 {
6268 TDSCOLUMN *colinfo;
6269
6270 tdsdump_log(TDS_DBG_FUNC, "dbrettype(%p, %d)\n", dbproc, retnum);
6271 CHECK_PARAMETER(dbproc, SYBENULL, -1);
6272 assert(dbproc->tds_socket);
6273 assert(dbproc->tds_socket->param_info);
6274
6275 if (retnum < 1 || retnum > dbproc->tds_socket->param_info->num_cols)
6276 return -1;
6277
6278 colinfo = dbproc->tds_socket->param_info->columns[retnum - 1];
6279
6280 return tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
6281 }
6282
6283 /**
6284 * \ingroup dblib_core
6285 * \brief Get size of the command buffer, in bytes.
6286 *
6287 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6288 * \sa dbcmd(), dbfcmd(), dbfreebuf(), dbgetchar(), dbstrcpy().
6289 */
6290 int
dbstrlen(DBPROCESS * dbproc)6291 dbstrlen(DBPROCESS * dbproc)
6292 {
6293 tdsdump_log(TDS_DBG_FUNC, "dbstrlen(%p)\n", dbproc);
6294 CHECK_PARAMETER(dbproc, SYBENULL, 0);
6295
6296 return dbproc->dbbufsz;
6297 }
6298
6299
6300 /**
6301 * \ingroup dblib_core
6302 * \brief Get address of a position in the command buffer.
6303 *
6304 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6305 * \param pos offset within the command buffer, starting at \em 0.
6306 * \remarks A bit overspecialized, this one.
6307 * \sa dbcmd(), dbfcmd(), dbfreebuf(), dbstrcpy(), dbstrlen(),
6308 */
6309 char *
dbgetchar(DBPROCESS * dbproc,int pos)6310 dbgetchar(DBPROCESS * dbproc, int pos)
6311 {
6312 tdsdump_log(TDS_DBG_FUNC, "dbgetchar(%p, %d)\n", dbproc, pos);
6313 CHECK_PARAMETER(dbproc, SYBENULL, NULL);
6314 tdsdump_log(TDS_DBG_FUNC, "dbgetchar() bufsz = %d, pos = %d\n", dbproc->dbbufsz, pos);
6315
6316 if (dbproc->dbbufsz > 0) {
6317 if (pos >= 0 && pos < (dbproc->dbbufsz - 1))
6318 return (char *) &dbproc->dbbuf[pos];
6319 return NULL;
6320 }
6321 return NULL;
6322 }
6323
6324 /**
6325 * \ingroup dblib_core
6326 * \brief Get a copy of a chunk of the command buffer.
6327 *
6328 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6329 * \param start position in the command buffer to start copying from, starting from \em 0.
6330 * If start is past the end of the command buffer, dbstrcpy() inserts a null terminator at dest[0].
6331 * \param numbytes number of bytes to copy.
6332 - If -1, dbstrcpy() copies the whole command buffer.
6333 - If 0 dbstrcpy() writes a \c NULL to dest[0].
6334 - If the command buffer contains fewer than \a numbytes (taking \a start into account) dbstrcpy()
6335 copies the rest of it.
6336 * \param dest \em output: the buffer to write to. Make sure it's big enough.
6337 * \retval SUCCEED the inputs were valid and \a dest was affected.
6338 * \retval FAIL \a start < 0 or \a numbytes < -1.
6339 * \sa dbcmd(), dbfcmd(), dbfreebuf(), dbgetchar(), dbstrlen().
6340 */
6341 RETCODE
dbstrcpy(DBPROCESS * dbproc,int start,int numbytes,char * dest)6342 dbstrcpy(DBPROCESS * dbproc, int start, int numbytes, char *dest)
6343 {
6344 tdsdump_log(TDS_DBG_FUNC, "dbstrcpy(%p, %d, %d, %s)\n", dbproc, start, numbytes, dest);
6345 CHECK_CONN(FAIL);
6346 CHECK_NULP(dest, "dbstrcpy", 4, FAIL);
6347
6348 if (start < 0) {
6349 dbperror(dbproc, SYBENSIP, 0);
6350 return FAIL;
6351 }
6352 if (numbytes < -1) {
6353 dbperror(dbproc, SYBEBNUM, 0);
6354 return FAIL;
6355 }
6356 dest[0] = 0; /* start with empty string being returned */
6357 if (dbproc->dbbufsz > 0 && start < dbproc->dbbufsz) {
6358 if (numbytes == -1)
6359 numbytes = dbproc->dbbufsz - start;
6360 if (start + numbytes > dbproc->dbbufsz)
6361 numbytes = dbproc->dbbufsz - start;
6362 memcpy(dest, (char *) &dbproc->dbbuf[start], numbytes);
6363 dest[numbytes] = '\0';
6364 }
6365 return SUCCEED;
6366 }
6367
6368 /**
6369 * \ingroup dblib_core
6370 * \brief safely quotes character values in SQL text.
6371 *
6372 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6373 * \param src input string.
6374 * \param srclen length of \a src in bytes, or -1 to indicate it's null-terminated.
6375 * \param dest \em output: client-provided output buffer.
6376 * \param destlen size of \a dest in bytes, or -1 to indicate it's "big enough" and the data should be null-terminated.
6377 * \param quotetype
6378 - \c DBSINGLE Doubles all single quotes (').
6379 - \c DBDOUBLE Doubles all double quotes (").
6380 - \c DBBOTH Doubles all single and double quotes.
6381 * \retval SUCCEED everything worked.
6382 * \retval FAIL no such \a quotetype, or insufficient room in \a dest.
6383 * \sa dbcmd(), dbfcmd().
6384 */
6385 RETCODE
dbsafestr(DBPROCESS * dbproc,const char * src,DBINT srclen,char * dest,DBINT destlen,int quotetype)6386 dbsafestr(DBPROCESS * dbproc, const char *src, DBINT srclen, char *dest, DBINT destlen, int quotetype)
6387 {
6388 int i, j = 0;
6389 int squote = FALSE, dquote = FALSE;
6390
6391 tdsdump_log(TDS_DBG_FUNC, "dbsafestr(%p, %s, %d, %s, %d, %d)\n", dbproc, src, srclen, dest, destlen, quotetype);
6392 CHECK_NULP(src, "dbsafestr", 2, FAIL);
6393 CHECK_NULP(dest, "dbsafestr", 4, FAIL);
6394
6395 /* check parameters */
6396 if (srclen < -1 || destlen < -1)
6397 return FAIL;
6398
6399 if (srclen == -1)
6400 srclen = (int)strlen(src);
6401
6402 if (quotetype == DBSINGLE || quotetype == DBBOTH)
6403 squote = TRUE;
6404 if (quotetype == DBDOUBLE || quotetype == DBBOTH)
6405 dquote = TRUE;
6406
6407 /* return FAIL if invalid quotetype */
6408 if (!dquote && !squote)
6409 return FAIL;
6410
6411
6412 for (i = 0; i < srclen; i++) {
6413
6414 /* dbsafestr returns fail if the deststr is not big enough */
6415 /* need one char + one for terminator */
6416 if (destlen >= 0 && j >= destlen)
6417 return FAIL;
6418
6419 if (squote && src[i] == '\'')
6420 dest[j++] = '\'';
6421 else if (dquote && src[i] == '\"')
6422 dest[j++] = '\"';
6423
6424 if (destlen >= 0 && j >= destlen)
6425 return FAIL;
6426
6427 dest[j++] = src[i];
6428 }
6429
6430 if (destlen >= 0 && j >= destlen)
6431 return FAIL;
6432
6433 dest[j] = '\0';
6434 return SUCCEED;
6435 }
6436
6437 /**
6438 * \ingroup dblib_core
6439 * \brief Print a token value's name to a buffer
6440 *
6441 * \param token server SYB* value, e.g. SYBINT.
6442
6443 * \return ASCII null-terminated string.
6444 * \sa dbaltop(), dbalttype(), dbcoltype(), dbrettype().
6445 */
6446 const char *
dbprtype(int token)6447 dbprtype(int token)
6448 {
6449 tdsdump_log(TDS_DBG_FUNC, "dbprtype(%d)\n", token);
6450 return tds_prtype(token);
6451 }
6452
6453 /**
6454 * \ingroup dblib_core
6455 * \brief describe table column attributes with a single call (Freetds-only API function modelled on dbcolinfo)
6456 *
6457 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6458 * \param column Nth in the result set, starting from 1.
6459 * \param pdbcol address of structure to be populated by this function.
6460 * \return SUCCEED or FAIL.
6461 * \sa dbcolinfo().
6462 */
6463 RETCODE
dbtablecolinfo(DBPROCESS * dbproc,DBINT column,DBCOL * pdbcol)6464 dbtablecolinfo(DBPROCESS *dbproc, DBINT column, DBCOL *pdbcol)
6465 {
6466 TDSCOLUMN *colinfo;
6467
6468 tdsdump_log(TDS_DBG_FUNC, "dbtablecolinfo(%p, %d, %p)\n", dbproc, column, pdbcol);
6469 CHECK_CONN(FAIL);
6470 CHECK_NULP(pdbcol, "dbtablecolinfo", 3, FAIL);
6471 DBPERROR_RETURN(pdbcol->SizeOfStruct != sizeof(DBCOL)
6472 && pdbcol->SizeOfStruct != sizeof(DBCOL2), SYBECOLSIZE);
6473
6474 colinfo = dbcolptr(dbproc, column);
6475 if (!colinfo)
6476 return FAIL;
6477
6478 strlcpy(pdbcol->Name, tds_dstr_cstr(&colinfo->column_name), sizeof(pdbcol->Name));
6479 strlcpy(pdbcol->ActualName, tds_dstr_cstr(&colinfo->column_name), sizeof(pdbcol->ActualName));
6480 strlcpy(pdbcol->TableName, tds_dstr_cstr(&colinfo->table_name), sizeof(pdbcol->TableName));
6481
6482 pdbcol->Type = tds_get_conversion_type(colinfo->column_type, colinfo->column_size);
6483 pdbcol->UserType = colinfo->column_usertype;
6484 pdbcol->MaxLength = colinfo->column_size;
6485 if (colinfo->column_nullable)
6486 pdbcol->Null = TRUE;
6487 else
6488 pdbcol->Null = FALSE;
6489
6490 pdbcol->VarLength = FALSE;
6491
6492 if (colinfo->column_nullable
6493 || is_nullable_type(colinfo->column_type))
6494 pdbcol->VarLength = TRUE;
6495
6496 pdbcol->Precision = colinfo->column_prec;
6497 pdbcol->Scale = colinfo->column_scale;
6498
6499 pdbcol->Updatable = colinfo->column_writeable ? TRUE : FALSE;
6500 pdbcol->Identity = colinfo->column_identity ? TRUE : FALSE;
6501
6502 if (pdbcol->SizeOfStruct >= sizeof(DBCOL2)) {
6503 DBCOL2 *col = (DBCOL2 *) pdbcol;
6504 TDSRET rc;
6505
6506 col->ServerType = colinfo->on_server.column_type;
6507 col->ServerMaxLength = colinfo->on_server.column_size;
6508
6509 rc = tds_get_column_declaration(dbproc->tds_socket, colinfo, col->ServerTypeDeclaration);
6510 if (TDS_FAILED(rc))
6511 return FAIL;
6512 }
6513
6514 return SUCCEED;
6515 }
6516
6517 /**
6518 * \ingroup dblib_core
6519 * \brief Get text timestamp for a column in the current row.
6520 *
6521 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6522 * \param column number of the column in the \c SELECT statement, starting at 1.
6523 * \return timestamp for \a column, may be NULL.
6524 * \sa dbtxptr(), dbwritetext().
6525 */
6526 DBBINARY *
dbtxtimestamp(DBPROCESS * dbproc,int column)6527 dbtxtimestamp(DBPROCESS * dbproc, int column)
6528 {
6529 TDSCOLUMN *colinfo;
6530 TDSBLOB *blob;
6531
6532 tdsdump_log(TDS_DBG_FUNC, "dbtxtimestamp(%p, %d)\n", dbproc, column);
6533
6534 colinfo = dbcolptr(dbproc, column);
6535 if (!colinfo || !is_blob_col(colinfo))
6536 return NULL;
6537
6538 blob = (TDSBLOB *) colinfo->column_data;
6539
6540 /* test if valid */
6541 if (!blob->valid_ptr)
6542 return NULL;
6543
6544 return (DBBINARY *) blob->timestamp;
6545 }
6546
6547 /**
6548 * \ingroup dblib_core
6549 * \brief Get text pointer for a column in the current row.
6550 *
6551 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6552 * \param column number of the column in the \c SELECT statement, starting at 1.
6553 * \return text pointer for \a column, may be NULL.
6554 * \sa dbtxtimestamp(), dbwritetext().
6555 */
6556 DBBINARY *
dbtxptr(DBPROCESS * dbproc,int column)6557 dbtxptr(DBPROCESS * dbproc, int column)
6558 {
6559 TDSCOLUMN *colinfo;
6560 TDSBLOB *blob;
6561
6562 tdsdump_log(TDS_DBG_FUNC, "dbtxptr(%p, %d)\n", dbproc, column);
6563
6564 colinfo = dbcolptr(dbproc, column);
6565 if (!colinfo || !is_blob_col(colinfo))
6566 return NULL;
6567
6568 blob = (TDSBLOB *) colinfo->column_data;
6569
6570 /* test if valid */
6571 if (!blob->valid_ptr)
6572 return NULL;
6573
6574 return (DBBINARY *) blob->textptr;
6575 }
6576
6577 /**
6578 * \ingroup dblib_core
6579 * \brief Send text or image data to the server.
6580 *
6581 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6582 * \param objname table name
6583 * \param textptr text pointer to be modified, obtained from dbtxptr().
6584 * \param textptrlen \em Ignored. Supposed to be \c DBTXPLEN.
6585 * \param timestamp text timestamp to be modified, obtained from dbtxtimestamp() or dbtxtsnewval(), may be \c NULL.
6586 * \param log \c TRUE if the operation is to be recorded in the transaction log.
6587 * \param size overall size of the data (in total, not just for this call), in bytes. A guideline, must not overstate the case.
6588 * \param text the chunk of data to write.
6589 * \retval SUCCEED everything worked.
6590 * \retval FAIL not sent, possibly because \a timestamp is invalid or was changed in the database since it was fetched.
6591 * \sa dbmoretext(), dbtxptr(), dbtxtimestamp(), dbwritetext(), dbtxtsput().
6592 */
6593 RETCODE
dbwritetext(DBPROCESS * dbproc,char * objname,DBBINARY * textptr,DBTINYINT textptrlen,DBBINARY * timestamp,DBBOOL log,DBINT size,BYTE * text)6594 dbwritetext(DBPROCESS * dbproc, char *objname, DBBINARY * textptr, DBTINYINT textptrlen, DBBINARY * timestamp, DBBOOL log,
6595 DBINT size, BYTE * text)
6596 {
6597 char textptr_string[35]; /* 16 * 2 + 2 (0x) + 1 */
6598 char timestamp_string[19]; /* 8 * 2 + 2 (0x) + 1 */
6599 TDS_INT result_type;
6600
6601 tdsdump_log(TDS_DBG_FUNC, "dbwritetext(%p, %s, %p, %d, %p, %d)\n",
6602 dbproc, objname, textptr, textptrlen, timestamp, log);
6603 CHECK_CONN(FAIL);
6604 CHECK_NULP(objname, "dbwritetext", 2, FAIL);
6605 CHECK_NULP(textptr, "dbwritetext", 3, FAIL);
6606 CHECK_NULP(timestamp, "dbwritetext", 5, FAIL);
6607 CHECK_PARAMETER(size, SYBEZTXT, FAIL);
6608
6609 if (IS_TDSDEAD(dbproc->tds_socket))
6610 return FAIL;
6611
6612 if (textptrlen > DBTXPLEN)
6613 return FAIL;
6614
6615 dbconvert(dbproc, SYBBINARY, (BYTE *) textptr, textptrlen, SYBCHAR, (BYTE *) textptr_string, -1);
6616 dbconvert(dbproc, SYBBINARY, (BYTE *) timestamp, 8, SYBCHAR, (BYTE *) timestamp_string, -1);
6617
6618 dbproc->dbresults_state = _DB_RES_INIT;
6619
6620 if (dbproc->tds_socket->state == TDS_PENDING) {
6621 const TDSRET ret = tds_process_tokens(dbproc->tds_socket, &result_type, NULL, TDS_TOKEN_TRAILING);
6622 if (ret != TDS_NO_MORE_RESULTS) {
6623 dbperror(dbproc, SYBERPND, 0);
6624 dbproc->command_state = DBCMDSENT;
6625 return FAIL;
6626 }
6627 }
6628
6629 if (TDS_FAILED(tds_writetext_start(dbproc->tds_socket, objname,
6630 textptr_string, timestamp_string, (log == TRUE), size)))
6631 return FAIL;
6632
6633 if (!text) {
6634 dbproc->text_size = size;
6635 dbproc->text_sent = 0;
6636 return SUCCEED;
6637 }
6638
6639 tds_writetext_continue(dbproc->tds_socket, text, size);
6640 tds_writetext_end(dbproc->tds_socket);
6641 dbproc->text_sent = 0;
6642
6643 if (dbsqlok(dbproc) == SUCCEED && dbresults(dbproc) == SUCCEED)
6644 return SUCCEED;
6645 return FAIL;
6646 }
6647
6648 /**
6649 * \ingroup dblib_core
6650 * \brief Fetch part of a text or image value from the server.
6651 *
6652 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6653 * \param buf \em output: buffer into which text will be placed.
6654 * \param bufsize size of \a buf, in bytes.
6655 * \return
6656 - \c >0 count of bytes placed in \a buf.
6657 - \c 0 end of row.
6658 - \c -1 \em error, no result set ready for \a dbproc.
6659 - \c NO_MORE_ROWS all rows read, no further data.
6660 * \sa dbmoretext(), dbnextrow(), dbwritetext().
6661 */
6662 STATUS
dbreadtext(DBPROCESS * dbproc,void * buf,DBINT bufsize)6663 dbreadtext(DBPROCESS * dbproc, void *buf, DBINT bufsize)
6664 {
6665 TDSSOCKET *tds;
6666 TDSCOLUMN *curcol;
6667 int cpbytes, bytes_avail;
6668 TDS_INT result_type;
6669 TDSRESULTINFO *resinfo;
6670
6671 tdsdump_log(TDS_DBG_FUNC, "dbreadtext(%p, %p, %d)\n", dbproc, buf, bufsize);
6672 CHECK_PARAMETER(dbproc, SYBENULL, -1);
6673 CHECK_NULP(buf, "dbreadtext", 2, -1);
6674
6675 tds = dbproc->tds_socket;
6676
6677 if (!tds || !tds->res_info || !tds->res_info->columns[0])
6678 return -1;
6679
6680 resinfo = tds->res_info;
6681 curcol = resinfo->columns[0];
6682
6683 /*
6684 * if the current position is beyond the end of the text
6685 * set pos to 0 and return 0 to denote the end of the
6686 * text
6687 */
6688 if (curcol->column_textpos && curcol->column_textpos >= curcol->column_cur_size) {
6689 curcol->column_textpos = 0;
6690 return 0;
6691 }
6692
6693 /*
6694 * if pos is 0 (first time through or last call exhausted the text)
6695 * then read another row
6696 */
6697
6698 if (curcol->column_textpos == 0) {
6699 const int mask = TDS_STOPAT_ROWFMT|TDS_STOPAT_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE;
6700 buffer_save_row(dbproc);
6701 switch (tds_process_tokens(dbproc->tds_socket, &result_type, NULL, mask)) {
6702 case TDS_SUCCESS:
6703 if (result_type == TDS_ROW_RESULT || result_type == TDS_COMPUTE_RESULT)
6704 break;
6705 case TDS_NO_MORE_RESULTS:
6706 return NO_MORE_ROWS;
6707 default:
6708 return -1;
6709 }
6710 }
6711
6712 /* find the number of bytes to return */
6713 bytes_avail = curcol->column_cur_size - curcol->column_textpos;
6714 cpbytes = bytes_avail > bufsize ? bufsize : bytes_avail;
6715 memcpy(buf, &((TDSBLOB *) curcol->column_data)->textvalue[curcol->column_textpos], cpbytes);
6716 curcol->column_textpos += cpbytes;
6717 return cpbytes;
6718 }
6719
6720 /**
6721 * \ingroup dblib_core
6722 * \brief Send chunk of a text/image value to the server.
6723 *
6724 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6725 * \param size count of bytes to send.
6726 * \param text textpointer, obtained from dbtxptr.
6727 * \retval SUCCEED always.
6728 * \sa dbtxptr(), dbtxtimestamp(), dbwritetext().
6729 * \todo Check return value of called functions and return \c FAIL if appropriate.
6730 */
6731 RETCODE
dbmoretext(DBPROCESS * dbproc,DBINT size,const BYTE text[])6732 dbmoretext(DBPROCESS * dbproc, DBINT size, const BYTE text[])
6733 {
6734 tdsdump_log(TDS_DBG_FUNC, "dbmoretext(%p, %d, %p)\n", dbproc, size, text);
6735 CHECK_CONN(FAIL);
6736 CHECK_NULP(text, "dbmoretext", 3, FAIL);
6737
6738 assert(dbproc->text_size >= dbproc->text_sent);
6739
6740 /* TODO this test should be inside tds_writetext_continue, currently not */
6741 if (size < 0 || size > dbproc->text_size - dbproc->text_sent)
6742 return FAIL;
6743
6744 if (size) {
6745 if (TDS_FAILED(tds_writetext_continue(dbproc->tds_socket, text, size)))
6746 return FAIL;
6747 dbproc->text_sent += size;
6748
6749 if (dbproc->text_sent == dbproc->text_size) {
6750 tds_writetext_end(dbproc->tds_socket);
6751 dbproc->text_sent = 0;
6752 }
6753 }
6754
6755 return SUCCEED;
6756 }
6757
6758 /**
6759 * \ingroup dblib_core
6760 * \brief Record to a file all SQL commands sent to the server
6761 *
6762 * \param filename name of file to write to.
6763 * \remarks Files are named \em filename.n, where n is an integer, starting with 0, and incremented with each callto dbopen().
6764 * \sa dbopen(), TDSDUMP environment variable().
6765 */
6766 void
dbrecftos(const char filename[])6767 dbrecftos(const char filename[])
6768 {
6769 char *f;
6770
6771 tdsdump_log(TDS_DBG_FUNC, "dbrecftos(%s)\n", filename);
6772 if (filename == NULL) {
6773 dbperror(NULL, SYBENULP, 0);
6774 return;
6775 }
6776
6777 f = strdup(filename);
6778 if (!f) {
6779 dbperror(NULL, SYBEMEM, 0);
6780 return;
6781 }
6782
6783 tds_mutex_lock(&dblib_mutex);
6784 free(g_dblib_ctx.recftos_filename);
6785 g_dblib_ctx.recftos_filename = f;
6786 g_dblib_ctx.recftos_filenum = 0;
6787 tds_mutex_unlock(&dblib_mutex);
6788 }
6789
6790 /** \internal
6791 * \ingroup dblib_internal
6792 * \brief Get the TDS version in use for \a dbproc.
6793 *
6794 *
6795 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6796 * \return a \c DBTDS* token.
6797 * \remarks The integer values of the constants are counterintuitive.
6798 * \sa DBTDS().
6799 */
6800 int
dbtds(DBPROCESS * dbproc)6801 dbtds(DBPROCESS * dbproc)
6802 {
6803 tdsdump_log(TDS_DBG_FUNC, "dbtds(%p)\n", dbproc);
6804 CHECK_PARAMETER(dbproc, SYBENULL, -1);
6805
6806 if (dbproc->tds_socket) {
6807 switch (dbproc->tds_socket->conn->tds_version) {
6808 case 0x402:
6809 return DBTDS_4_2;
6810 case 0x406:
6811 return DBTDS_4_6;
6812 case 0x500:
6813 return DBTDS_5_0;
6814 case 0x700:
6815 return DBTDS_7_0;
6816 case 0x701:
6817 return DBTDS_7_1;
6818 case 0x702:
6819 return DBTDS_7_2;
6820 case 0x703:
6821 return DBTDS_7_3;
6822 case 0x704:
6823 return DBTDS_7_4;
6824 default:
6825 return DBTDS_UNKNOWN;
6826 }
6827 }
6828 return -1;
6829 }
6830
6831 /**
6832 * \ingroup dblib_core
6833 * \brief See which version of db-lib is in use.
6834 *
6835 * \return null-terminated ASCII string representing the version of db-lib.
6836 * \remarks FreeTDS returns the CVS version string of dblib.c.
6837 * \sa
6838 */
6839 const char *
dbversion()6840 dbversion()
6841 {
6842 tdsdump_log(TDS_DBG_FUNC, "dbversion(void)\n");
6843 return TDS_VERSION_NO;
6844 }
6845
6846 #if defined(DBLIB_UNIMPLEMENTED)
6847 /**
6848 * \ingroup dblib_core
6849 * \brief Set the default character set.
6850 *
6851 * \param charset null-terminated ASCII string, matching a row in master..syscharsets.
6852 * \sa dbsetdeflang(), dbsetdefcharset(), dblogin(), dbopen().
6853 * \todo Unimplemented.
6854 */
6855 RETCODE
dbsetdefcharset(char * charset)6856 dbsetdefcharset(char *charset)
6857 {
6858 tdsdump_log(TDS_DBG_FUNC, "dbsetdefcharset(%s)\n", charset);
6859 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbsetdefcharset()\n");
6860 return SUCCEED;
6861 }
6862
6863 /**
6864 * \ingroup dblib_core
6865 * \brief Ready execution of a registered procedure.
6866 *
6867 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6868 * \param procedure_name to call.
6869 * \param namelen size of \a procedure_name, in bytes.
6870 * \sa dbregparam(), dbregexec(), dbregwatch(), dbreglist(), dbregwatchlist
6871 * \todo Unimplemented.
6872 */
6873 RETCODE
dbreginit(DBPROCESS * dbproc,DBCHAR * procedure_name,DBSMALLINT namelen)6874 dbreginit(DBPROCESS * dbproc, DBCHAR * procedure_name, DBSMALLINT namelen)
6875 {
6876 tdsdump_log(TDS_DBG_FUNC, "dbreginit(%p, %s, %d)\n", dbproc, procedure_name, namelen);
6877 CHECK_CONN(FAIL);
6878 CHECK_NULP(procedure_name, "dbreginit", 2, FAIL);
6879 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbreginit()\n");
6880 return SUCCEED;
6881 }
6882
6883
6884 /**
6885 * \ingroup dblib_core
6886 * \brief Get names of Open Server registered procedures.
6887 *
6888 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6889 * \sa dbregparam(), dbregexec(), dbregwatch(), dbreglist(), dbregwatchlist().
6890 * \todo Unimplemented.
6891 */
6892 RETCODE
dbreglist(DBPROCESS * dbproc)6893 dbreglist(DBPROCESS * dbproc)
6894 {
6895 tdsdump_log(TDS_DBG_FUNC, "dbreglist(%p)\n", dbproc);
6896 CHECK_CONN(FAIL);
6897 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbreglist()\n");
6898 return SUCCEED;
6899 }
6900
6901
6902 /**
6903 * \ingroup dblib_core
6904 * \brief Describe parameter of registered procedure .
6905 *
6906 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6907 * \param param_name
6908 * \param type \c SYB* datatype.
6909 * \param datalen size of \a data.
6910 * \param data address of buffer holding value for the parameter.
6911 * \sa dbreginit(), dbregexec(), dbnpdefine(), dbnpcreate(), dbregwatch().
6912 * \todo Unimplemented.
6913 */
6914 RETCODE
dbregparam(DBPROCESS * dbproc,char * param_name,int type,DBINT datalen,BYTE * data)6915 dbregparam(DBPROCESS * dbproc, char *param_name, int type, DBINT datalen, BYTE * data)
6916 {
6917 tdsdump_log(TDS_DBG_FUNC, "dbregparam(%p, %s, %d, %d, %p)\n", dbproc, param_name, type, datalen, data);
6918 CHECK_CONN(FAIL);
6919 CHECK_NULP(param_name, "dbregparam", 2, FAIL);
6920 CHECK_NULP(data, "dbregparam", 5, FAIL);
6921 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbregparam()\n");
6922 return SUCCEED;
6923 }
6924
6925
6926 /**
6927 * \ingroup dblib_core
6928 * \brief Execute a registered procedure.
6929 *
6930 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6931 * \param options
6932 * \sa dbreginit(), dbregparam(), dbregwatch(), dbregnowatch
6933 * \todo Unimplemented.
6934 */
6935 RETCODE
dbregexec(DBPROCESS * dbproc,DBUSMALLINT options)6936 dbregexec(DBPROCESS * dbproc, DBUSMALLINT options)
6937 {
6938 tdsdump_log(TDS_DBG_FUNC, "dbregexec(%p, %d)\n", dbproc, options);
6939 CHECK_CONN(FAIL);
6940 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbregexec()\n");
6941 return SUCCEED;
6942 }
6943 #endif
6944
6945
6946 /**
6947 * \ingroup dblib_datetime
6948 * \brief Get name of a month, in some human language.
6949 *
6950 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6951 * \param language \em ignored.
6952 * \param monthnum number of the month, starting with 1.
6953 * \param shortform set to \c TRUE for a three letter output ("Jan" - "Dec"), else zero.
6954 * \return address of null-terminated ASCII string, or \c NULL on error.
6955 * \sa db12hour(), dbdateorder(), dbdayname(), DBSETLNATLANG(), dbsetopt().
6956 */
6957 const char *
dbmonthname(DBPROCESS * dbproc,char * language,int monthnum,DBBOOL shortform)6958 dbmonthname(DBPROCESS * dbproc, char *language, int monthnum, DBBOOL shortform)
6959 {
6960 static const char shortmon[][4] = {
6961 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
6962 };
6963 static const char longmon[][12] = {
6964 "January", "February", "March", "April", "May", "June",
6965 "July", "August", "September", "October", "November", "December"
6966 };
6967
6968 tdsdump_log(TDS_DBG_FUNC, "dbmonthname(%p, %s, %d, %d)\n", dbproc, language, monthnum, shortform);
6969 CHECK_PARAMETER(dbproc, SYBENULL, NULL);
6970 CHECK_NULP(language, "dbmonthname", 2, NULL);
6971
6972 if (monthnum < 1 || monthnum > 12)
6973 return NULL;
6974 return (shortform) ? shortmon[monthnum - 1] : longmon[monthnum - 1];
6975 }
6976
6977 /**
6978 * \ingroup dblib_core
6979 * \brief See if a command caused the current database to change.
6980 *
6981 * \param dbproc contains all information needed by db-lib to manage communications with the server.
6982 * \return name of new database, if changed, as a null-terminated ASCII string, else \c NULL.
6983
6984 * \sa dbname(), dbresults(), dbsqlexec(), dbsqlsend(), dbuse().
6985 */
6986 char *
dbchange(DBPROCESS * dbproc)6987 dbchange(DBPROCESS * dbproc)
6988 {
6989 tdsdump_log(TDS_DBG_FUNC, "dbchange(%p)\n", dbproc);
6990 CHECK_PARAMETER(dbproc, SYBENULL, NULL);
6991
6992 if (dbproc->envchange_rcv & (1 << (TDS_ENV_DATABASE - 1))) {
6993 return dbproc->dbcurdb;
6994 }
6995 return NULL;
6996 }
6997
6998 /**
6999 * \ingroup dblib_core
7000 * \brief Get name of current database.
7001 *
7002 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7003 * \return current database name, as null-terminated ASCII string.
7004 * \sa dbchange(), dbuse().
7005 */
7006 char *
dbname(DBPROCESS * dbproc)7007 dbname(DBPROCESS * dbproc)
7008 {
7009 tdsdump_log(TDS_DBG_FUNC, "dbname(%p)\n", dbproc);
7010 CHECK_PARAMETER(dbproc, SYBENULL, NULL);
7011 return dbproc->dbcurdb;
7012 }
7013
7014 /**
7015 * \ingroup dblib_core
7016 * \brief Get \c syscharset name of the server character set.
7017 *
7018 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7019 * \return name of server's charset, as null-terminated ASCII string.
7020 * \sa dbcharsetconv(), dbgetcharset(), DBSETLCHARSET().
7021 */
7022 char *
dbservcharset(DBPROCESS * dbproc)7023 dbservcharset(DBPROCESS * dbproc)
7024 {
7025
7026 tdsdump_log(TDS_DBG_FUNC, "dbservcharset(%p)\n", dbproc);
7027 CHECK_PARAMETER(dbproc, SYBENULL, NULL);
7028
7029 return dbproc->servcharset;
7030 }
7031
7032 /**
7033 * \ingroup dblib_core
7034 * \brief Transmit the command buffer to the server. \em Non-blocking, does not wait for a response.
7035 *
7036 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7037 * \retval SUCCEED SQL sent.
7038 * \retval FAIL protocol problem, unless dbsqlsend() when it's not supposed to be (in which case a db-lib error
7039 message will be emitted).
7040 * \sa dbcmd(), dbfcmd(), DBIORDESC(), DBIOWDESC(), dbnextrow(), dbpoll(), dbresults(), dbsettime(), dbsqlexec(), dbsqlok().
7041 */
7042 RETCODE
dbsqlsend(DBPROCESS * dbproc)7043 dbsqlsend(DBPROCESS * dbproc)
7044 {
7045 TDSSOCKET *tds;
7046 char *cmdstr;
7047 TDSRET rc;
7048 TDS_INT result_type;
7049 char timestr[256];
7050
7051 tdsdump_log(TDS_DBG_FUNC, "dbsqlsend(%p)\n", dbproc);
7052 CHECK_CONN(FAIL);
7053
7054 tds = dbproc->tds_socket;
7055
7056 if (tds->state == TDS_PENDING) {
7057
7058 if (tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_TRAILING) != TDS_NO_MORE_RESULTS) {
7059 dbperror(dbproc, SYBERPND, 0);
7060 dbproc->command_state = DBCMDSENT;
7061 return FAIL;
7062 }
7063 }
7064
7065 if (dbproc->dboptcmd) {
7066 if ((cmdstr = dbstring_get(dbproc->dboptcmd)) == NULL) {
7067 dbperror(dbproc, SYBEASEC, 0); /* Attempt to send an empty command buffer to the server */
7068 return FAIL;
7069 }
7070 rc = tds_submit_query(dbproc->tds_socket, cmdstr);
7071 free(cmdstr);
7072 dbstring_free(&(dbproc->dboptcmd));
7073 if (TDS_FAILED(rc)) {
7074 return FAIL;
7075 }
7076 dbproc->avail_flag = FALSE;
7077 dbproc->envchange_rcv = 0;
7078 dbproc->dbresults_state = _DB_RES_INIT;
7079 while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS))
7080 == TDS_SUCCESS);
7081 if (rc != TDS_NO_MORE_RESULTS) {
7082 return FAIL;
7083 }
7084 }
7085 dbproc->more_results = TRUE;
7086
7087 if (dbproc->ftos != NULL) {
7088 fprintf(dbproc->ftos, "%s\n", dbproc->dbbuf);
7089 fprintf(dbproc->ftos, "go /* %s */\n", _dbprdate(timestr));
7090 fflush(dbproc->ftos);
7091 }
7092
7093 if (TDS_FAILED(tds_submit_query(dbproc->tds_socket, (char *) dbproc->dbbuf))) {
7094 return FAIL;
7095 }
7096 dbproc->avail_flag = FALSE;
7097 dbproc->envchange_rcv = 0;
7098 dbproc->dbresults_state = _DB_RES_INIT;
7099 dbproc->command_state = DBCMDSENT;
7100 return SUCCEED;
7101 }
7102
7103 /**
7104 * \ingroup dblib_core
7105 * \brief Get user-defined datatype of a compute column.
7106 *
7107 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7108 * \param computeid of \c COMPUTE clause to which we're referring.
7109 * \param column Nth column in \a computeid, starting from 1.
7110 * \returns user-defined datatype of compute column, else -1.
7111 * \sa dbalttype(), dbcolutype().
7112 */
7113 DBINT
dbaltutype(DBPROCESS * dbproc,int computeid,int column)7114 dbaltutype(DBPROCESS * dbproc, int computeid, int column)
7115 {
7116 TDSCOLUMN *colinfo;
7117
7118 tdsdump_log(TDS_DBG_FUNC, "dbaltutype(%p, %d, %d)\n", dbproc, computeid, column);
7119
7120 colinfo = dbacolptr(dbproc, computeid, column, 0);
7121 if (!colinfo)
7122 return -1;
7123
7124 return colinfo->column_usertype;
7125 }
7126
7127 /**
7128 * \ingroup dblib_core
7129 * \brief Get size of data in compute column.
7130 *
7131 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7132 * \param computeid of \c COMPUTE clause to which we're referring.
7133 * \param column Nth column in \a computeid, starting from 1.
7134 * \sa dbadata(), dbadlen(), dbalttype(), dbgetrow(), dbnextrow(), dbnumalts().
7135 */
7136 DBINT
dbaltlen(DBPROCESS * dbproc,int computeid,int column)7137 dbaltlen(DBPROCESS * dbproc, int computeid, int column)
7138 {
7139 TDSCOLUMN *colinfo;
7140
7141 tdsdump_log(TDS_DBG_FUNC, "dbaltlen(%p, %d, %d)\n", dbproc, computeid, column);
7142
7143 colinfo = dbacolptr(dbproc, computeid, column, 0);
7144 if (!colinfo)
7145 return -1;
7146
7147 return colinfo->column_size;
7148
7149 }
7150
7151 /**
7152 * \ingroup dblib_core
7153 * \brief See if a server response has arrived.
7154 *
7155 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7156 * \param milliseconds how long to wait for the server before returning:
7157 - \c 0 return immediately.
7158 - \c -1 do not return until the server responds or a system interrupt occurs.
7159 * \param ready_dbproc \em output: DBPROCESS for which a response arrived, of \c NULL.
7160 * \param return_reason \em output:
7161 - \c DBRESULT server responded.
7162 - \c DBNOTIFICATION registered procedure notification has arrived. dbpoll() the registered handler, if
7163 any, before it returns.
7164 - \c DBTIMEOUT \a milliseconds elapsed before the server responded.
7165 - \c DBINTERRUPT operating-system interrupt occurred before the server responded.
7166 * \retval SUCCEED everything worked.
7167 * \retval FAIL a server connection died.
7168 * \sa DBIORDESC(), DBRBUF(), dbresults(), dbreghandle(), dbsqlok().
7169 * \todo Unimplemented.
7170 */
7171 #if defined(DBLIB_UNIMPLEMENTED)
7172 RETCODE
dbpoll(DBPROCESS * dbproc,long milliseconds,DBPROCESS ** ready_dbproc,int * return_reason)7173 dbpoll(DBPROCESS * dbproc, long milliseconds, DBPROCESS ** ready_dbproc, int *return_reason)
7174 {
7175 tdsdump_log(TDS_DBG_FUNC, "dbpoll(%p, %ld, %p, %p)\n", dbproc, milliseconds, ready_dbproc, return_reason);
7176 CHECK_CONN(FAIL);
7177 CHECK_NULP(ready_dbproc, "dbpoll", 3, FAIL);
7178 CHECK_NULP(return_reason, "dbpoll", 4, FAIL);
7179 tdsdump_log(TDS_DBG_FUNC, "UNIMPLEMENTED dbpoll()\n");
7180 return SUCCEED;
7181 }
7182 #endif
7183
7184 /** \internal
7185 * \ingroup dblib_internal
7186 * \brief Get number of the first row in the row buffer.
7187 *
7188 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7189 * \sa DBFIRSTROW(), dbclrbuf(), DBCURROW(), dbgetrow(), DBLASTROW(), dbnextrow(), dbsetopt().
7190 */
7191 DBINT
dbfirstrow(DBPROCESS * dbproc)7192 dbfirstrow(DBPROCESS * dbproc)
7193 {
7194 tdsdump_log(TDS_DBG_FUNC, "dbfirstrow(%p)\n", dbproc);
7195 CHECK_CONN(0);
7196 return buffer_idx2row(&dbproc->row_buf, dbproc->row_buf.tail);
7197 }
7198
7199 /** \internal
7200 * \ingroup dblib_internal
7201 * \brief Get number of the last row in the row buffer.
7202 *
7203 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7204 * \sa DBLASTROW(), dbclrbuf(), DBCURROW(), DBFIRSTROW(), dbgetrow(), dbnextrow(), dbsetopt().
7205 */
7206 DBINT
dblastrow(DBPROCESS * dbproc)7207 dblastrow(DBPROCESS * dbproc)
7208 {
7209 int idx;
7210
7211 tdsdump_log(TDS_DBG_FUNC, "dblastrow(%p)\n", dbproc);
7212 CHECK_PARAMETER(dbproc, SYBENULL, 0);
7213 idx = dbproc->row_buf.head;
7214 if (dbproc->row_buf.head != dbproc->row_buf.tail) {
7215 if (--idx < 0)
7216 idx = dbproc->row_buf.capacity - 1;
7217 }
7218 assert(idx >= 0);
7219 return buffer_idx2row(&dbproc->row_buf, idx);
7220 }
7221
7222
7223 /** \internal
7224 * \ingroup dblib_internal
7225 * \brief Get file descriptor of the socket used by a \c DBPROCESS to read data coming from the server. (!)
7226 *
7227 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7228 * \sa dbcmd(), DBIORDESC(), DBIOWDESC(), dbnextrow(), dbpoll(), DBRBUF(), dbresults(), dbsqlok(), dbsqlsend().
7229 */
7230 int
dbiordesc(DBPROCESS * dbproc)7231 dbiordesc(DBPROCESS * dbproc)
7232 {
7233 tdsdump_log(TDS_DBG_FUNC, "dbiordesc(%p)\n", dbproc);
7234 CHECK_PARAMETER(dbproc, SYBENULL, -1);
7235 return (int) tds_get_s(dbproc->tds_socket);
7236 }
7237
7238
7239 /** \internal
7240 * \ingroup dblib_internal
7241 * \brief Get file descriptor of the socket used by a \c DBPROCESS to write data coming to the server. (!)
7242 *
7243 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7244 * \sa dbcmd(), DBIORDESC(), DBIOWDESC(), dbnextrow(), dbpoll(), DBRBUF(), dbresults(), dbsqlok(), dbsqlsend().
7245 */
7246 int
dbiowdesc(DBPROCESS * dbproc)7247 dbiowdesc(DBPROCESS * dbproc)
7248 {
7249 tdsdump_log(TDS_DBG_FUNC, "dbiowdesc(%p)\n", dbproc);
7250 CHECK_PARAMETER(dbproc, SYBENULL, -1);
7251
7252 return (int) tds_get_s(dbproc->tds_socket);
7253 }
7254
7255 DBBOOL
dbisavail(DBPROCESS * dbproc)7256 dbisavail(DBPROCESS * dbproc)
7257 {
7258 tdsdump_log(TDS_DBG_FUNC, "dbisavail(%p)\n", dbproc);
7259 CHECK_PARAMETER(dbproc, SYBENULL, FALSE);
7260 return dbproc->avail_flag;
7261 }
7262
7263
7264 /** \internal
7265 * \ingroup dblib_internal
7266 * \brief Mark a \c DBPROCESS as "available".
7267 *
7268 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7269 * \remarks Basically bogus. \c FreeTDS behaves the way Sybase's implementation does, but so what?
7270 Many \c db-lib functions set the \c DBPROCESS to "not available", but only
7271 dbsetavail() resets it to "available".
7272 * \sa DBISAVAIL(). DBSETAVAIL().
7273 */
7274 void
dbsetavail(DBPROCESS * dbproc)7275 dbsetavail(DBPROCESS * dbproc)
7276 {
7277 tdsdump_log(TDS_DBG_FUNC, "dbsetavail(%p)\n", dbproc);
7278 CHECK_PARAMETER(dbproc, SYBENULL, );
7279 dbproc->avail_flag = TRUE;
7280 }
7281
7282
7283 /**
7284 * \ingroup dblib_core
7285 * \brief Build a printable string from text containing placeholders for variables.
7286 *
7287 * \param dbproc contains all information needed by db-lib to manage communications with the server.
7288 * \param charbuf \em output: buffer that will contain the ASCII null-terminated string built by \c dbstrbuild().
7289 * \param bufsize size of \a charbuf, in bytes.
7290 * \param text null-terminated ASCII string, with \em placeholders for variables. \em A Placeholder is a
7291 * three-byte string, made up of:
7292 - '%' a percent sign
7293 - 0-9 an integer (designates the argument number to use, starting with 1.)
7294 - '!' an exclamation point
7295 * \param formats null-terminated ASCII sprintf-style string. Has one format specifier for each placeholder in \a text.
7296 * \remarks Following \a formats are the arguments, the values to substitute for the placeholders.
7297 * \sa dbconvert(), dbdatename(), dbdatepart().
7298 */
7299 RETCODE
dbstrbuild(DBPROCESS * dbproc,char * charbuf,int bufsize,char * text,char * formats,...)7300 dbstrbuild(DBPROCESS * dbproc, char *charbuf, int bufsize, char *text, char *formats, ...)
7301 {
7302 va_list ap;
7303 TDSRET rc;
7304 int resultlen;
7305
7306 tdsdump_log(TDS_DBG_FUNC, "dbstrbuild(%p, %s, %d, %s, %s, ...)\n", dbproc, charbuf, bufsize, text, formats);
7307 CHECK_NULP(charbuf, "dbstrbuild", 2, FAIL);
7308 CHECK_NULP(text, "dbstrbuild", 4, FAIL);
7309 CHECK_NULP(formats, "dbstrbuild", 5, FAIL);
7310
7311 va_start(ap, formats);
7312 rc = tds_vstrbuild(charbuf, bufsize, &resultlen, text, TDS_NULLTERM, formats, TDS_NULLTERM, ap);
7313 charbuf[resultlen] = '\0';
7314 va_end(ap);
7315 return TDS_SUCCEED(rc) ? SUCCEED : FAIL;
7316 }
7317
7318 static char *
_dbprdate(char * timestr)7319 _dbprdate(char *timestr)
7320 {
7321 time_t currtime = time(NULL);
7322
7323 assert(timestr);
7324
7325 strcpy(timestr, asctime(gmtime(&currtime)));
7326 timestr[strlen(timestr) - 1] = '\0'; /* remove newline */
7327 return timestr;
7328
7329 }
7330
7331 static DBINT
_dbnullable(DBPROCESS * dbproc,int column)7332 _dbnullable(DBPROCESS * dbproc, int column)
7333 {
7334 TDSCOLUMN *colinfo;
7335 TDSRESULTINFO *resinfo;
7336
7337 assert(dbproc && dbproc->tds_socket);
7338 resinfo = dbproc->tds_socket->res_info;
7339 if (!resinfo || column < 1 || column > resinfo->num_cols)
7340 return FALSE;
7341 colinfo = resinfo->columns[column - 1];
7342
7343 if (colinfo->column_nullable)
7344 return TRUE;
7345 return FALSE;
7346 }
7347
7348 /** Returns type in string. Used for debugging purpose */
7349 static const char *
tds_prdatatype(int datatype_token)7350 tds_prdatatype(int datatype_token)
7351 {
7352 switch ((TDS_SERVER_TYPE) datatype_token) {
7353 case SYBCHAR: return "SYBCHAR";
7354 case SYBVARCHAR: return "SYBVARCHAR";
7355 case SYBINTN: return "SYBINTN";
7356 case SYBINT1: return "SYBINT1";
7357 case SYBINT2: return "SYBINT2";
7358 case SYBINT4: return "SYBINT4";
7359 case SYBINT8: return "SYBINT8";
7360 case SYBFLT8: return "SYBFLT8";
7361 case SYBDATETIME: return "SYBDATETIME";
7362 case SYBBIT: return "SYBBIT";
7363 case SYBTEXT: return "SYBTEXT";
7364 case SYBNTEXT: return "SYBNTEXT";
7365 case SYBIMAGE: return "SYBIMAGE";
7366 case SYBMONEY4: return "SYBMONEY4";
7367 case SYBMONEY: return "SYBMONEY";
7368 case SYBDATETIME4: return "SYBDATETIME4";
7369 case SYBREAL: return "SYBREAL";
7370 case SYBBINARY: return "SYBBINARY";
7371 case SYBVOID: return "SYBVOID";
7372 case SYBVARBINARY: return "SYBVARBINARY";
7373 case SYBNVARCHAR: return "SYBNVARCHAR";
7374 case SYBBITN: return "SYBBITN";
7375 case SYBNUMERIC: return "SYBNUMERIC";
7376 case SYBDECIMAL: return "SYBDECIMAL";
7377 case SYBFLTN: return "SYBFLTN";
7378 case SYBMONEYN: return "SYBMONEYN";
7379 case SYBDATETIMN: return "SYBDATETIMN";
7380 case XSYBCHAR: return "XSYBCHAR";
7381 case XSYBVARCHAR: return "XSYBVARCHAR";
7382 case XSYBNVARCHAR: return "XSYBNVARCHAR";
7383 case XSYBNCHAR: return "XSYBNCHAR";
7384 case XSYBVARBINARY: return "XSYBVARBINARY";
7385 case XSYBBINARY: return "XSYBBINARY";
7386 case SYBLONGBINARY: return "SYBLONGBINARY";
7387 case SYBSINT1: return "SYBSINT1";
7388 case SYBUINT2: return "SYBUINT2";
7389 case SYBUINT4: return "SYBUINT4";
7390 case SYBUINT8: return "SYBUINT8";
7391 case SYBUNIQUE: return "SYBUNIQUE";
7392 case SYBVARIANT: return "SYBVARIANT";
7393 case SYBMSXML: return "SYBMSXML";
7394 case SYBMSDATE: return "SYBMSDATE";
7395 case SYBMSTIME: return "SYBMSTIME";
7396 case SYBMSDATETIME2: return "SYBMSDATETIME2";
7397 case SYBMSDATETIMEOFFSET: return "SYBMSDATETIMEOFFSET";
7398 case SYBDATE: return "SYBDATE";
7399 case SYBTIME: return "SYBTIME";
7400 case SYB5BIGDATETIME: return "SYBBIGDATETIME";
7401 case SYB5BIGTIME: return "SYBBIGTIME";
7402 case SYBMSUDT: return "SYBMSUDT";
7403 case SYBUINT1: return "SYBUINT1";
7404 case SYBDATEN: return "SYBDATEN";
7405 case SYB5INT8: return "SYB5INT8";
7406 case SYBINTERVAL: return "SYBINTERVAL";
7407 case SYBTIMEN: return "SYBTIMEN";
7408 case SYBUINTN: return "SYBUINTN";
7409 case SYBUNITEXT: return "SYBUNITEXT";
7410 case SYBXML: return "SYBXML";
7411 }
7412 return "(unknown)";
7413 }
7414 #if 1
7415 void
copy_data_to_host_var(DBPROCESS * dbproc,TDS_SERVER_TYPE srctype,const BYTE * src,DBINT srclen,BYTE * dest,DBINT destlen,int bindtype,DBINT * indicator)7416 copy_data_to_host_var(DBPROCESS * dbproc, TDS_SERVER_TYPE srctype, const BYTE * src, DBINT srclen,
7417 BYTE * dest, DBINT destlen,
7418 int bindtype, DBINT *indicator)
7419 {
7420 CONV_RESULT dres;
7421 DBINT ret;
7422 int len;
7423 DBINT indicator_value = 0;
7424
7425 bool limited_dest_space = false;
7426 TDS_SERVER_TYPE desttype = dblib_bound_type(bindtype);
7427
7428 tdsdump_log(TDS_DBG_FUNC, "copy_data_to_host_var(%d [%s] len %d => %d [%s] len %d)\n",
7429 srctype, tds_prdatatype(srctype), srclen, desttype, tds_prdatatype(desttype), destlen);
7430 CHECK_NULP(src, "copy_data_to_host_var", 3, );
7431 CHECK_NULP(dest, "copy_data_to_host_var", 6, );
7432 if (desttype == TDS_INVALID_TYPE)
7433 return;
7434 /* indicator can be NULL */
7435
7436 assert(srclen >= 0);
7437
7438 if (destlen > 0) {
7439 limited_dest_space = true;
7440 }
7441
7442 /* oft times we are asked to convert a data type to itself */
7443
7444 if (desttype == SYBNUMERIC) {
7445 DBNUMERIC *num = NULL; /* num->scale is unsigned */
7446
7447 /* only MS, use always source */
7448 if (bindtype == SRCNUMERICBIND || bindtype == SRCDECIMALBIND) {
7449 if (is_numeric_type(srctype))
7450 num = (DBNUMERIC*) src;
7451 else
7452 num = (DBNUMERIC*) dest;
7453 } else if (dbproc->msdblib) {
7454 /* MS by default use only destination informations */
7455 num = (DBNUMERIC*) dest;
7456 } else {
7457 /* Sybase, dbbind means source or default */
7458 /* TODO if dbbind_ps is used is more complicated */
7459 if (is_numeric_type(srctype))
7460 num = (DBNUMERIC*) src;
7461 }
7462 if (!num) {
7463 dres.n.precision = 18;
7464 dres.n.scale = 0;
7465 } else {
7466 dres.n.precision = num->precision;
7467 dres.n.scale = num->scale;
7468 }
7469 } else if ((srctype == desttype) ||
7470 (is_similar_type(srctype, desttype))) {
7471
7472 tdsdump_log(TDS_DBG_INFO1, "copy_data_to_host_var() srctype == desttype\n");
7473 switch (desttype) {
7474
7475 case SYBVARBINARY:
7476 case SYBBINARY:
7477 case SYBIMAGE:
7478 if (bindtype == VARYBINBIND) {
7479 DBVARYBIN *bin = (DBVARYBIN*) dest;
7480 if (limited_dest_space) {
7481 if (srclen > sizeof(bin->array)) {
7482 dbperror(dbproc, SYBECOFL, 0);
7483 indicator_value = srclen;
7484 srclen = sizeof(bin->array);
7485 }
7486 }
7487 memcpy(bin->array, src, srclen);
7488 bin->len = srclen;
7489 break;
7490 }
7491
7492 if (srclen > destlen && destlen >= 0) {
7493 dbperror(dbproc, SYBECOFL, 0);
7494 } else {
7495 memcpy(dest, src, srclen);
7496 if (srclen < destlen)
7497 memset(dest + srclen, 0, destlen - srclen);
7498 }
7499 break;
7500
7501 case SYBCHAR:
7502 case SYBVARCHAR:
7503 case SYBTEXT:
7504
7505 switch (bindtype) {
7506 case NTBSTRINGBIND: /* strip trailing blanks, null term */
7507 while (srclen && src[srclen - 1] == ' ') {
7508 --srclen;
7509 }
7510 if (limited_dest_space) {
7511 if (srclen + 1 > destlen) {
7512 dbperror(dbproc, SYBECOFL, 0);
7513 indicator_value = srclen + 1;
7514 srclen = destlen - 1;
7515 }
7516 }
7517 memcpy(dest, src, srclen);
7518 dest[srclen] = '\0';
7519 break;
7520 case STRINGBIND: /* pad with blanks, NUL term */
7521 if (limited_dest_space) {
7522 if (srclen + 1 > destlen) {
7523 dbperror(dbproc, SYBECOFL, 0);
7524 indicator_value = srclen + 1;
7525 srclen = destlen - 1;
7526 }
7527 } else {
7528 destlen = srclen + 1;
7529 }
7530 memcpy(dest, src, srclen);
7531 if (srclen < destlen)
7532 memset(dest + srclen, ' ', destlen - srclen - 1);
7533 dest[destlen - 1] = '\0';
7534 break;
7535 case CHARBIND: /* pad with blanks, NO NUL term */
7536 if (limited_dest_space) {
7537 if (srclen > destlen) {
7538 dbperror(dbproc, SYBECOFL, 0);
7539 indicator_value = srclen;
7540 srclen = destlen;
7541 }
7542 } else {
7543 destlen = srclen;
7544 }
7545 memcpy(dest, src, srclen);
7546 if (srclen < destlen)
7547 memset(dest + srclen, ' ', destlen - srclen);
7548 break;
7549 case VARYCHARBIND: /* strip trailing blanks, NO NUL term */
7550 if (limited_dest_space) {
7551 if (srclen > destlen) {
7552 dbperror(dbproc, SYBECOFL, 0);
7553 indicator_value = srclen;
7554 srclen = destlen;
7555 }
7556 }
7557 memcpy(((DBVARYCHAR *)dest)->str, src, srclen);
7558 ((DBVARYCHAR *)dest)->len = srclen;
7559 break;
7560 }
7561 break;
7562 case SYBINT1:
7563 case SYBINT2:
7564 case SYBINT4:
7565 case SYBINT8:
7566 case SYBFLT8:
7567 case SYBREAL:
7568 case SYBBIT:
7569 case SYBBITN:
7570 case SYBMONEY:
7571 case SYBMONEY4:
7572 case SYBDATETIME:
7573 case SYBDATETIME4:
7574 case SYBDATE:
7575 case SYBTIME:
7576 case SYB5BIGDATETIME:
7577 case SYB5BIGTIME:
7578 case SYBUNIQUE:
7579 ret = tds_get_size_by_type(desttype);
7580 memcpy(dest, src, ret);
7581 break;
7582
7583 case SYBMSDATE:
7584 case SYBMSTIME:
7585 case SYBMSDATETIME2:
7586 case SYBMSDATETIMEOFFSET:
7587 ret = sizeof(TDS_DATETIMEALL);
7588 memcpy(dest, src, ret);
7589 break;
7590
7591 default:
7592 break;
7593 }
7594 if (indicator)
7595 *indicator = indicator_value;
7596
7597 return;
7598
7599 } /* end srctype == desttype */
7600
7601 len = tds_convert(g_dblib_ctx.tds_ctx, srctype, src, srclen, desttype, &dres);
7602
7603 tdsdump_log(TDS_DBG_INFO1, "copy_data_to_host_var(): tds_convert returned %d\n", len);
7604
7605 if (len < 0) {
7606 _dblib_convert_err(dbproc, len);
7607 return;
7608 }
7609
7610 switch (desttype) {
7611 case SYBVARBINARY:
7612 case SYBBINARY:
7613 case SYBIMAGE:
7614 if (bindtype == VARYBINBIND) {
7615 if (limited_dest_space) {
7616 if (len > sizeof(((DBVARYBIN *)dest)->array)) {
7617 dbperror(dbproc, SYBECOFL, 0);
7618 indicator_value = len;
7619 len = sizeof(((DBVARYBIN *)dest)->array);
7620 }
7621 }
7622 memcpy(((DBVARYBIN *)dest)->array, dres.c, len);
7623 ((DBVARYBIN *)dest)->len = len;
7624 } else {
7625 if (len > destlen && destlen >= 0) {
7626 dbperror(dbproc, SYBECOFL, 0);
7627 } else {
7628 memcpy(dest, dres.ib, len);
7629 if (len < destlen)
7630 memset(dest + len, 0, destlen - len);
7631 }
7632 }
7633 TDS_ZERO_FREE(dres.ib);
7634 break;
7635 case SYBINT1:
7636 case SYBINT2:
7637 case SYBINT4:
7638 case SYBINT8:
7639 case SYBFLT8:
7640 case SYBREAL:
7641 case SYBBIT:
7642 case SYBBITN:
7643 case SYBMONEY:
7644 case SYBMONEY4:
7645 case SYBDATETIME:
7646 case SYBDATETIME4:
7647 case SYBDATE:
7648 case SYBTIME:
7649 case SYBNUMERIC:
7650 case SYBDECIMAL:
7651 case SYBUNIQUE:
7652 case SYBMSDATE:
7653 case SYBMSTIME:
7654 case SYB5BIGDATETIME:
7655 case SYB5BIGTIME:
7656 case SYBMSDATETIME2:
7657 case SYBMSDATETIMEOFFSET:
7658 memcpy(dest, &(dres.ti), len);
7659 break;
7660 case SYBCHAR:
7661 case SYBVARCHAR:
7662 case SYBTEXT:
7663 tdsdump_log(TDS_DBG_INFO1, "copy_data_to_host_var() outputs %d bytes char data destlen = %d \n", len, destlen);
7664 switch (bindtype) {
7665 case NTBSTRINGBIND: /* strip trailing blanks, null term */
7666 while (len && dres.c[len - 1] == ' ') {
7667 --len;
7668 }
7669 if (limited_dest_space) {
7670 if (len + 1 > destlen) {
7671 dbperror(dbproc, SYBECOFL, 0);
7672 len = destlen - 1;
7673 }
7674 }
7675 memcpy(dest, dres.c, len);
7676 dest[len] = '\0';
7677 break;
7678 case STRINGBIND: /* pad with blanks, null term */
7679 if (limited_dest_space) {
7680 if (len + 1 > destlen) {
7681 dbperror(dbproc, SYBECOFL, 0);
7682 len = destlen - 1;
7683 }
7684 } else {
7685 destlen = len + 1;
7686 }
7687 memcpy(dest, dres.c, len);
7688 if (len < destlen)
7689 memset(dest + len, ' ', destlen - len - 1);
7690 dest[destlen - 1] = '\0';
7691 break;
7692 case CHARBIND: /* pad with blanks, NO null term */
7693 if (limited_dest_space) {
7694 if (len > destlen) {
7695 dbperror(dbproc, SYBECOFL, 0);
7696 indicator_value = len;
7697 len = destlen;
7698 }
7699 } else {
7700 destlen = len;
7701 }
7702 memcpy(dest, dres.c, len);
7703 if (len < destlen)
7704 memset(dest + len, ' ', destlen - len);
7705 break;
7706 case VARYCHARBIND: /* strip trailing blanks, NO null term */
7707 if (limited_dest_space) {
7708 if (len > sizeof(((DBVARYCHAR *)dest)->str)) {
7709 dbperror(dbproc, SYBECOFL, 0);
7710 indicator_value = len;
7711 len = sizeof(((DBVARYCHAR *)dest)->str);
7712 }
7713 }
7714 memcpy(((DBVARYCHAR *)dest)->str, dres.c, len);
7715 ((DBVARYCHAR *)dest)->len = len;
7716 break;
7717 }
7718
7719 free(dres.c);
7720 break;
7721 default:
7722 tdsdump_log(TDS_DBG_INFO1, "error: copy_data_to_host_var(): unrecognized desttype %d \n", desttype);
7723 break;
7724
7725 }
7726 if (indicator)
7727 *indicator = indicator_value;
7728 }
7729 #endif
7730
7731 /** \internal
7732 * \ingroup dblib_internal
7733 * \remarks member msgno Vendor-defined message number
7734 * \remarks member severity Is passed to the error handler
7735 * \remarks member msgtext Text of message
7736 */
7737 typedef struct _dblib_error_message
7738 {
7739 DBINT msgno;
7740 int severity;
7741 const char *msgtext;
7742 } DBLIB_ERROR_MESSAGE;
7743
7744 /*
7745 * The msgtext member holds up to two strings. The first one is the message text, which may contain placeholders.
7746 * The second one, if it exists, is the format string for dbstrbuild(). Messages containing no placeholders still need
7747 * an extra NULL to indicate a zero-length format string.
7748 */
7749 static const DBLIB_ERROR_MESSAGE dblib_error_messages[] =
7750 { { SYBEVERDOWN, EXINFO, "TDS version downgraded to 7.1!\0" }
7751 , { SYBEICONVIU, EXCONVERSION, "Some character(s) could not be converted into client's character set\0" }
7752 , { SYBEICONVAVAIL, EXCONVERSION, "Character set conversion is not available between client character set '%1!' and "
7753 "server character set '%2!'\0%s %s" }
7754 , { SYBEICONVO, EXCONVERSION, "Error converting characters into server's character set. Some character(s) could "
7755 "not be converted\0" }
7756 , { SYBEICONVI, EXCONVERSION, "Some character(s) could not be converted into client's character set. Unconverted "
7757 "bytes were changed to question marks ('?')\0" }
7758 , { SYBEICONV2BIG, EXCONVERSION, "Buffer overflow converting characters from client into server's character set\0" }
7759
7760
7761 , { SYBEPORT, EXUSER, "Both port and instance specified\0" }
7762 , { SYBETDSVER, EXUSER, "Cannot bcp with TDSVER < 5.0\0" }
7763 , { SYBEAAMT, EXPROGRAM, "User attempted a dbaltbind with mismatched column and variable types\0" }
7764 , { SYBEABMT, EXPROGRAM, "User attempted a dbbind with mismatched column and variable types\0" }
7765 , { SYBEABNC, EXPROGRAM, "Attempt to bind to a non-existent column\0" }
7766 , { SYBEABNP, EXPROGRAM, "Attempt to bind using NULL pointers\0" }
7767 , { SYBEABNV, EXPROGRAM, "Attempt to bind to a NULL program variable\0" }
7768 , { SYBEACNV, EXCONVERSION, "Attempt to do data-conversion with NULL destination variable.\0" }
7769 , { SYBEADST, EXCONSISTENCY, "International Release: Error in attempting to determine the size of a pair of "
7770 "translation tables\0" }
7771 , { SYBEAICF, EXCONSISTENCY, "International Release: Error in attempting to install custom format\0" }
7772 , { SYBEALTT, EXCONSISTENCY, "International Release: Error in attempting to load a pair of translation tables\0" }
7773 , { SYBEAOLF, EXRESOURCE, "International Release: Error in attempting to open a localization file\0" }
7774 , { SYBEAPCT, EXCONSISTENCY, "International Release: Error in attempting to perform a character set translation\0" }
7775 , { SYBEAPUT, EXPROGRAM, "Attempt to print unknown token\0" }
7776 , { SYBEARDI, EXRESOURCE, "International Release: Error in attempting to read datetime information from a "
7777 "localization file\0" }
7778 , { SYBEARDL, EXRESOURCE, "International Release: Error in attempting to read the dblib.loc localization file\0" }
7779 , { SYBEASEC, EXPROGRAM, "Attempt to send an empty command buffer to the server\0" }
7780 , { SYBEASNL, EXPROGRAM, "Attempt to set fields in a null LOGINREC\0" }
7781 , { SYBEASTL, EXPROGRAM, "Synchronous I/O attempted at AST level\0" }
7782 , { SYBEASUL, EXPROGRAM, "Attempt to set unknown LOGINREC field\0" }
7783 , { SYBEAUTN, EXPROGRAM, "Attempt to update the timestamp of a table that has no timestamp column\0" }
7784 , { SYBEBADPK, EXINFO, "Packet size of %1! not supported -- size of %2! used instead!\0%d %d" }
7785 , { SYBEBBCI, EXINFO, "Batch successfully bulk copied to the server\0" }
7786 , { SYBEBBL, EXPROGRAM, "Bad bindlen parameter passed to dbsetnull\0" }
7787 , { SYBEBCBC, EXPROGRAM, "bcp_columns must be called before bcp_colfmt and bcp_colfmt_ps\0" }
7788 , { SYBEBCBNPR, EXPROGRAM, "bcp_bind: if varaddr is NULL, prefixlen must be 0 "
7789 "and no terminator should be specified\0" }
7790 , { SYBEBCBNTYP, EXPROGRAM, "bcp_bind: if varaddr is NULL and varlen greater than 0, the table column type "
7791 "must be SYBTEXT or SYBIMAGE and the program variable type must be SYBTEXT, SYBCHAR, "
7792 "SYBIMAGE or SYBBINARY\0" }
7793 , { SYBEBCBPREF, EXPROGRAM, "Illegal prefix length. Legal values are 0, 1, 2 or 4\0" }
7794 , { SYBEBCFO, EXUSER, "bcp host files must contain at least one column\0" }
7795 , { SYBEBCHLEN, EXPROGRAM, "host_collen should be greater than or equal to -1\0" }
7796 , { SYBEBCIS, EXCONSISTENCY, "Attempt to bulk copy an illegally-sized column value to the server\0" }
7797 , { SYBEBCIT, EXPROGRAM, "It is illegal to use BCP terminators with program variables other than SYBCHAR, "
7798 "SYBBINARY, SYBTEXT, or SYBIMAGE\0" }
7799 , { SYBEBCITBLEN, EXPROGRAM, "bcp_init: tblname parameter is too long\0" }
7800 , { SYBEBCITBNM, EXPROGRAM, "bcp_init: tblname parameter cannot be NULL\0" }
7801 , { SYBEBCMTXT, EXPROGRAM, "bcp_moretext may be used only when there is at least one text or image column in "
7802 "the server table\0" }
7803 , { SYBEBCNL, EXNONFATAL, "Negative length-prefix found in BCP data-file\0" }
7804 , { SYBEBCNN, EXUSER, "Attempt to bulk copy a NULL value into a Server column "
7805 "which does not accept null values\0" }
7806 , { SYBEBCNT, EXUSER, "Attempt to use Bulk Copy with a non-existent Server table\0" }
7807 , { SYBEBCOR, EXCONSISTENCY, "Attempt to bulk copy an oversized row to the server\0" }
7808 , { SYBEBCPB, EXPROGRAM, "bcp_bind, bcp_moretext and bcp_sendrow may not be used after bcp_init has been "
7809 "passed a non-NULL input file name\0" }
7810 , { SYBEBCPCTYP, EXPROGRAM, "bcp_colfmt: If table_colnum is 0, host_type cannot be 0\0" }
7811 , { SYBEBCPI, EXPROGRAM, "bcp_init must be called before any other bcp routines\0" }
7812 , { SYBEBCPN, EXPROGRAM, "bcp_bind, bcp_collen, bcp_colptr, bcp_moretext and bcp_sendrow may be used only "
7813 "after bcp_init has been called with the copy direction set to DB_IN\0" }
7814 , { SYBEBCPREC, EXNONFATAL, "Column %1!: Illegal precision value encountered\0%d" }
7815 , { SYBEBCPREF, EXPROGRAM, "Illegal prefix length. Legal values are -1, 0, 1, 2 or 4\0" }
7816 , { SYBEBCRE, EXNONFATAL, "I/O error while reading bcp datafile\0" }
7817 , { SYBEBCRO, EXINFO, "The BCP hostfile '%1!' contains only %2! rows. It was impossible to read the "
7818 "requested %3! rows\0%s %d %d" }
7819 , { SYBEBCSA, EXUSER, "The BCP hostfile '%1!' contains only %2! rows. "
7820 "Skipping all of these rows is not allowed\0%s %d" }
7821 , { SYBEBCSET, EXCONSISTENCY, "Unknown character-set encountered\0" }
7822 , { SYBEBCSI, EXPROGRAM, "Host-file columns may be skipped only when copying into the Server\0" }
7823 , { SYBEBCSNDROW, EXPROGRAM, "bcp_sendrow may not be called unless all text data for the previous row has been "
7824 "sent using bcp_moretext\0" }
7825 , { SYBEBCSNTYP, EXPROGRAM, "column number %1!: if varaddr is NULL and varlen greater than 0, the table column "
7826 "type must be SYBTEXT or SYBIMAGE and the program variable type must be SYBTEXT, "
7827 "SYBCHAR, SYBIMAGE or SYBBINARY\0%d" }
7828 , { SYBEBCUC, EXRESOURCE, "bcp: Unable to close host datafile\0" }
7829 , { SYBEBCUO, EXRESOURCE, "bcp: Unable to open host datafile\0" }
7830 , { SYBEBCVH, EXPROGRAM, "bcp_exec may be called only after bcp_init has been passed a valid host file\0" }
7831 , { SYBEBCVLEN, EXPROGRAM, "varlen should be greater than or equal to -1\0" }
7832 , { SYBEBCWE, EXNONFATAL, "I/O error while writing bcp datafile\0" }
7833 , { SYBEBDIO, EXPROGRAM, "Bad bulk copy direction. Must be either IN or OUT\0" }
7834 , { SYBEBEOF, EXNONFATAL, "Unexpected EOF encountered in bcp datafile\0" }
7835 , { SYBEBIHC, EXPROGRAM, "Incorrect host-column number found in bcp format file\0" }
7836 , { SYBEBIVI, EXPROGRAM, "bcp_columns, bcp_colfmt and bcp_colfmt_ps may be used only after bcp_init has been "
7837 "passed a valid input file\0" }
7838 , { SYBEBNCR, EXPROGRAM, "Attempt to bind user variable to a non-existent compute row\0" }
7839 , { SYBEBNUM, EXPROGRAM, "Bad numbytes parameter passed to dbstrcpy\0" }
7840 , { SYBEBPKS, EXPROGRAM, "In DBSETLPACKET, the packet size parameter must be between 0 and 999999\0" }
7841 , { SYBEBPREC, EXPROGRAM, "Illegal precision specified\0" }
7842 , { SYBEBPROBADDEF, EXCONSISTENCY, "bcp protocol error: illegal default column id received\0" }
7843 , { SYBEBPROCOL, EXCONSISTENCY, "bcp protocol error: returned column count differs from the actual number of "
7844 "columns received\0" }
7845 , { SYBEBPRODEF, EXCONSISTENCY, "bcp protocol error: expected default information and got none\0" }
7846 , { SYBEBPRODEFID, EXCONSISTENCY, "bcp protocol error: default column id and actual column id are not same\0" }
7847 , { SYBEBPRODEFTYP, EXCONSISTENCY, "bcp protocol error: default value datatype differs from column datatype\0" }
7848 , { SYBEBPROEXTDEF, EXCONSISTENCY, "bcp protocol error: more than one row of default information received\0" }
7849 , { SYBEBPROEXTRES, EXCONSISTENCY, "bcp protocol error: unexpected set of results received\0" }
7850 , { SYBEBPRONODEF, EXCONSISTENCY, "bcp protocol error: default value received for column that does not have default\0" }
7851 , { SYBEBPRONUMDEF, EXCONSISTENCY, "bcp protocol error: expected number of defaults differs from the actual number of "
7852 "defaults received\0" }
7853 , { SYBEBRFF, EXRESOURCE, "I/O error while reading bcp format file\0" }
7854 , { SYBEBSCALE, EXPROGRAM, "Illegal scale specified\0" }
7855 , { SYBEBTMT, EXPROGRAM, "Attempt to send too much text data via the bcp_moretext call\0" }
7856 , { SYBEBTOK, EXCOMM, "Bad token from the server: Datastream processing out of sync\0" }
7857 , { SYBEBTYP, EXPROGRAM, "Unknown bind type passed to DB-Library function\0" }
7858 , { SYBEBTYPSRV, EXPROGRAM, "Datatype is not supported by the server\0" }
7859 , { SYBEBUCE, EXRESOURCE, "bcp: Unable to close error file\0" }
7860 , { SYBEBUCF, EXPROGRAM, "bcp: Unable to close format file\0" }
7861 , { SYBEBUDF, EXPROGRAM, "bcp: Unrecognized datatype found in format file\0" }
7862 , { SYBEBUFF, EXPROGRAM, "bcp: Unable to create format file\0" }
7863 , { SYBEBUFL, EXCONSISTENCY, "DB-Library internal error-send buffer length corrupted\0" }
7864 , { SYBEBUOE, EXRESOURCE, "bcp: Unable to open error file\0" }
7865 , { SYBEBUOF, EXPROGRAM, "bcp: Unable to open format file\0" }
7866 , { SYBEBWEF, EXNONFATAL, "I/O error while writing bcp error file\0" }
7867 , { SYBEBWFF, EXRESOURCE, "I/O error while writing bcp format file\0" }
7868 , { SYBECAP, EXCOMM, "DB-Library capabilities not accepted by the Server\0" }
7869 , { SYBECAPTYP, EXCOMM, "Unexpected capability type in CAPABILITY datastream\0" }
7870 , { SYBECDNS, EXCONSISTENCY, "Datastream indicates that a compute column is derived from a non-existent select "
7871 "list member\0" }
7872 , { SYBECDOMAIN, EXCONVERSION, "Source field value is not within the domain of legal values\0" }
7873 , { SYBECINTERNAL, EXCONVERSION, "Internal Conversion error\0" }
7874 , { SYBECLOS, EXCOMM, "Error in closing network connection\0" }
7875 , { SYBECLPR, EXCONVERSION, "Data conversion resulted in loss of precision\0" }
7876 , { SYBECNOR, EXPROGRAM, "Column number out of range\0" }
7877 , { SYBECNOV, EXCONVERSION, "Attempt to set variable to NULL resulted in overflow\0" }
7878 , { SYBECOFL, EXCONVERSION, "Data conversion resulted in overflow\0" }
7879 , { SYBECONN, EXCOMM, "Unable to connect: Adaptive Server is unavailable or does not exist\0" }
7880 , { SYBECRNC, EXPROGRAM, "The current row is not a result of compute clause %1!, so it is illegal to attempt "
7881 "to extract that data from this row\0%d" }
7882 , { SYBECRSAGR, EXPROGRAM, "Aggregate functions are not allowed in a cursor statement\0" }
7883 , { SYBECRSBROL, EXPROGRAM, "Backward scrolling cannot be used in a forward scrolling cursor\0" }
7884 , { SYBECRSBSKEY, EXPROGRAM, "Keyset cannot be scrolled backward in mixed cursors with a previous fetch type\0" }
7885 , { SYBECRSBUFR, EXPROGRAM, "Row buffering should not be turned on when using cursor APIs\0" }
7886 , { SYBECRSDIS, EXPROGRAM, "Cursor statement contains one of the disallowed phrases compute, union, for "
7887 "browse, or select into\0" }
7888 , { SYBECRSFLAST, EXPROGRAM, "Fetch type LAST requires fully keyset driven cursors\0" }
7889 , { SYBECRSFRAND, EXPROGRAM, "Fetch types RANDOM and RELATIVE can only be used within the keyset of keyset "
7890 "driven cursors\0" }
7891 , { SYBECRSFROWN, EXPROGRAM, "Row number to be fetched is outside valid range\0" }
7892 , { SYBECRSFTYPE, EXRESOURCE, "Unknown fetch type\0" }
7893 , { SYBECRSINV, EXPROGRAM, "Invalid cursor statement\0" }
7894 , { SYBECRSINVALID, EXRESOURCE, "The cursor handle is invalid\0" }
7895 , { SYBECRSMROWS, EXRESOURCE, "Multiple rows are returned, only one is expected while retrieving dbname\0" }
7896 , { SYBECRSNOBIND, EXPROGRAM, "Cursor bind must be called prior to dbcursor invocation\0" }
7897 , { SYBECRSNOCOUNT, EXPROGRAM, "The DBNOCOUNT option should not be turned on "
7898 "when doing updates or deletes with dbcursor\0" }
7899 , { SYBECRSNOFREE, EXPROGRAM, "The DBNOAUTOFREE option should not be turned on when using cursor APIs\0" }
7900 , { SYBECRSNOIND, EXPROGRAM, "One of the tables involved in the cursor statement does not have a unique index\0" }
7901 , { SYBECRSNOKEYS, EXRESOURCE, "The entire keyset must be defined for KEYSET type cursors\0" }
7902 , { SYBECRSNOLEN, EXRESOURCE, "No unique index found\0" }
7903 , { SYBECRSNOPTCC, EXRESOURCE, "No OPTCC was found\0" }
7904 , { SYBECRSNORDER, EXRESOURCE, "The order of clauses must be from, where, and order by\0" }
7905 , { SYBECRSNORES, EXPROGRAM, "Cursor statement generated no results\0" }
7906 , { SYBECRSNROWS, EXRESOURCE, "No rows returned, at least one is expected\0" }
7907 , { SYBECRSNOTABLE, EXRESOURCE, "Table name is NULL\0" }
7908 , { SYBECRSNOUPD, EXPROGRAM, "Update or delete operation did not affect any rows\0" }
7909 , { SYBECRSNOWHERE, EXPROGRAM, "A WHERE clause is not allowed in a cursor update or insert\0" }
7910 , { SYBECRSNUNIQUE, EXRESOURCE, "No unique keys associated with this view\0" }
7911 , { SYBECRSORD, EXPROGRAM, "Only fully keyset driven cursors can have order by, group by, or having phrases\0" }
7912 , { SYBECRSRO, EXPROGRAM, "Data locking or modifications cannot be made in a READONLY cursor\0" }
7913 , { SYBECRSSET, EXPROGRAM, "A SET clause is required for a cursor update or insert\0" }
7914 , { SYBECRSTAB, EXPROGRAM, "Table name must be determined in operations involving data locking or modifications\0" }
7915 , { SYBECRSVAR, EXRESOURCE, "There is no valid address associated with this bind\0" }
7916 , { SYBECRSVIEW, EXPROGRAM, "A view cannot be joined with another table or a view in a cursor statement\0" }
7917 , { SYBECRSVIIND, EXPROGRAM, "The view used in the cursor statement does not include all the unique index "
7918 "columns of the underlying tables\0" }
7919 , { SYBECRSUPDNB, EXPROGRAM, "Update or insert operations cannot use bind variables when binding type is NOBIND\0" }
7920 , { SYBECRSUPDTAB, EXPROGRAM, "Update or insert operations using bind variables require single table cursors\0" }
7921 , { SYBECSYN, EXCONVERSION, "Attempt to convert data stopped by syntax error in source field\0" }
7922 , { SYBECUFL, EXCONVERSION, "Data conversion resulted in underflow\0" }
7923 , { SYBECWLL, EXPROGRAM, "Attempt to set column width less than 1\0" }
7924 , { SYBEDBPS, EXRESOURCE, "Maximum number of DBPROCESSes already allocated\0" }
7925 , { SYBEDDNE, EXCOMM, "DBPROCESS is dead or not enabled\0" }
7926 , { SYBEDIVZ, EXUSER, "Attempt to divide by $0.00 in function %1!\0%s" }
7927 , { SYBEDNTI, EXPROGRAM, "Attempt to use dbtxtsput to put a new text timestamp into a column whose datatype "
7928 "is neither SYBTEXT nor SYBIMAGE\0" }
7929 , { SYBEDPOR, EXPROGRAM, "Out-of-range datepart constant\0" }
7930 , { SYBEDVOR, EXPROGRAM, "Day values must be between 1 and 7\0" }
7931 , { SYBEECAN, EXINFO, "Attempted to cancel unrequested event notification\0" }
7932 , { SYBEEINI, EXINFO, "Must call dbreginit before dbregexec\0" }
7933 , { SYBEETD, EXPROGRAM, "Failure to send the expected amount of TEXT or IMAGE data via dbmoretext\0" }
7934 , { SYBEEUNR, EXCOMM, "Unsolicited event notification received\0" }
7935 , { SYBEEVOP, EXINFO, "Called dbregwatch with a bad options parameter\0" }
7936 , { SYBEEVST, EXINFO, "Must initiate a transaction before calling dbregparam\0" }
7937 , { SYBEFCON, EXCOMM, "Adaptive Server connection failed\0" }
7938 , { SYBEFRES, EXFATAL, "Challenge-Response function failed\0" }
7939 , { SYBEFSHD, EXRESOURCE, "Error in attempting to find the Sybase home directory\0" }
7940 , { SYBEFUNC, EXPROGRAM, "Functionality not supported at the specified version level\0" }
7941 , { SYBEICN, EXPROGRAM, "Invalid computeid or compute column number\0" }
7942 , { SYBEIDCL, EXCONSISTENCY, "Illegal datetime column length returned by Adaptive Server. Legal datetime lengths "
7943 "are 4 and 8 bytes\0" }
7944 , { SYBEIDECCL, EXCONSISTENCY, "Invalid decimal column length returned by the server\0" }
7945 , { SYBEIFCL, EXCONSISTENCY, "Illegal floating-point column length returned by Adaptive Server. Legal "
7946 "floating-point lengths are 4 and 8 bytes\0" }
7947 , { SYBEIFNB, EXPROGRAM, "Illegal field number passed to bcp_control\0" }
7948 , { SYBEIICL, EXCONSISTENCY, "Illegal integer column length returned by Adaptive Server. Legal integer lengths "
7949 "are 1, 2, and 4 bytes\0" }
7950 , { SYBEIMCL, EXCONSISTENCY, "Illegal money column length returned by Adaptive Server. Legal money lengths are 4 "
7951 "and 8 bytes\0" }
7952 , { SYBEINLN, EXUSER, "Interface file: unexpected end-of-line\0" }
7953 , { SYBEINTF, EXUSER, "Server name not found in configuration files\0" }
7954 , { SYBEINUMCL, EXCONSISTENCY, "Invalid numeric column length returned by the server\0" }
7955 , { SYBEIPV, EXINFO, "%1! is an illegal value for the %2! parameter of %3!\0%d %s %s" }
7956 , { SYBEISOI, EXCONSISTENCY, "International Release: Invalid sort-order information found\0" }
7957 , { SYBEISRVPREC, EXCONSISTENCY, "Illegal precision value returned by the server\0" }
7958 , { SYBEISRVSCL, EXCONSISTENCY, "Illegal scale value returned by the server\0" }
7959 , { SYBEITIM, EXPROGRAM, "Illegal timeout value specified\0" }
7960 , { SYBEIVERS, EXPROGRAM, "Illegal version level specified\0" }
7961 , { SYBEKBCI, EXINFO, "1000 rows sent to the server\0" }
7962 , { SYBEKBCO, EXINFO, "1000 rows successfully bulk copied to host file\0" }
7963 , { SYBEMEM, EXRESOURCE, "Unable to allocate sufficient memory\0" }
7964 , { SYBEMOV, EXUSER, "Money arithmetic resulted in overflow in function %1!\0%s" }
7965 , { SYBEMPLL, EXUSER, "Attempt to set maximum number of DBPROCESSes lower than 1\0" }
7966 , { SYBEMVOR, EXPROGRAM, "Month values must be between 1 and 12\0" }
7967 , { SYBENBUF, EXINFO, "Called dbsendpassthru with a NULL buf parameter\0" }
7968 , { SYBENBVP, EXPROGRAM, "Cannot pass dbsetnull a NULL bindval pointer\0" }
7969 , { SYBENDC, EXPROGRAM, "Cannot have negative component in date in numeric form\0" }
7970 , { SYBENDTP, EXPROGRAM, "Called dbdatecrack with NULL datetime parameter\0" }
7971 , { SYBENEG, EXCOMM, "Negotiated login attempt failed\0" }
7972 , { SYBENHAN, EXINFO, "Called dbrecvpassthru with a NULL handle parameter\0" }
7973 , { SYBENMOB, EXPROGRAM, "No such member of order by clause\0" }
7974 , { SYBENOEV, EXINFO, "DBPOLL can not be called when registered procedure notifications have been disabled\0" }
7975 , { SYBENPRM, EXPROGRAM, "NULL parameter not allowed for this dboption\0" }
7976 , { SYBENSIP, EXPROGRAM, "Negative starting index passed to dbstrcpy\0" }
7977 , { SYBENTLL, EXUSER, "Name too long for LOGINREC field\0" }
7978 , { SYBENTTN, EXPROGRAM, "Attempt to use dbtxtsput to put a new text timestamp into a non-existent data row\0" }
7979 , { SYBENULL, EXINFO, "NULL DBPROCESS pointer passed to DB-Library\0" }
7980 , { SYBENULP, EXPROGRAM, "Called %1! with parameter %2! NULL\0%s %d" }
7981 , { SYBENXID, EXNONFATAL, "The Server did not grant us a distributed-transaction ID\0" }
7982 , { SYBEONCE, EXPROGRAM, "Function can be called only once\0" }
7983 , { SYBEOOB, EXCOMM, "Error in sending out-of-band data to the server\0" }
7984 , { SYBEOPIN, EXNONFATAL, "Could not open interface file\0" }
7985 , { SYBEOPNA, EXNONFATAL, "Option is not available with current server\0" }
7986 , { SYBEOREN, EXINFO, "International Release: Warning: an out-of-range error-number was encountered in "
7987 "dblib.loc. The maximum permissible error-number is defined as DBERRCOUNT in sybdb.h\0" }
7988 , { SYBEORPF, EXUSER, "Attempt to set remote password would overflow "
7989 "the login record's remote password field\0" }
7990 , { SYBEPOLL, EXINFO, "There is already an active dbpoll\0" }
7991 , { SYBEPRTF, EXINFO, "dbtracestring may only be called from a printfunc\0" }
7992 , { SYBEPWD, EXUSER, "Login incorrect\0" }
7993 , { SYBERDCN, EXCONVERSION, "Requested data conversion does not exist\0" }
7994 , { SYBERDNR, EXPROGRAM, "Attempt to retrieve data from a non-existent row\0" }
7995 , { SYBEREAD, EXCOMM, "Read from the server failed\0" }
7996 , { SYBERESP, EXPROGRAM, "Response function address passed to dbresponse must be non-NULL\0" }
7997 , { SYBERPCS, EXINFO, "Must call dbrpcinit before dbrpcparam or dbrpcsend\0" }
7998 , { SYBERPIL, EXPROGRAM, "It is illegal to pass -1 to dbrpcparam for the datalen of parameters which are of "
7999 "type SYBCHAR, SYBVARCHAR, SYBBINARY, or SYBVARBINARY\0" }
8000 , { SYBERPNA, EXNONFATAL, "The RPC facility is available only when using a server whose version number is 4.0 "
8001 "or later\0" }
8002 , { SYBERPND, EXPROGRAM, "Attempt to initiate a new Adaptive Server operation with results pending\0" }
8003 , { SYBERPNULL, EXPROGRAM, "value parameter for dbrpcparam can be NULL, only if the datalen parameter is 0\0" }
8004 , { SYBERPTXTIM, EXPROGRAM, "RPC parameters cannot be of type text or image\0" }
8005 , { SYBERPUL, EXPROGRAM, "When passing a SYBINTN, SYBDATETIMN, SYBMONEYN, or SYBFLTN parameter via "
8006 "dbrpcparam, it is necessary to specify the parameter's maximum or actual length so "
8007 "that DB-Library can recognize it as a SYINT1, SYBINT2, SYBINT4, SYBMONEY, SYBMONEY4, "
8008 "and so on\0" }
8009 , { SYBERTCC, EXPROGRAM, "dbreadtext may not be used to receive the results of a query that contains a "
8010 "COMPUTE clause\0" }
8011 , { SYBERTSC, EXPROGRAM, "dbreadtext may be used only to receive the results of a query that contains a "
8012 "single result column\0" }
8013 , { SYBERXID, EXNONFATAL, "The Server did not recognize our distributed-transaction ID\0" }
8014 , { SYBESECURE, EXPROGRAM, "Secure SQL Server function not supported in this version\0" }
8015 , { SYBESEFA, EXPROGRAM, "DBSETNOTIFS cannot be called if connections are present\0" }
8016 , { SYBESEOF, EXCOMM, "Unexpected EOF from the server\0" }
8017 , { SYBESFOV, EXPROGRAM, "International Release: dbsafestr overflowed its destination buffer\0" }
8018 , { SYBESMSG, EXSERVER, "General Adaptive Server error: Check messages from the server\0" }
8019 , { SYBESOCK, EXCOMM, "Unable to open socket\0" }
8020 , { SYBESPID, EXPROGRAM, "Called dbspid with a NULL dbproc\0" }
8021 , { SYBESYNC, EXCOMM, "Read attempted while out of synchronization with Adaptive Server\0" }
8022 , { SYBETEXS, EXINFO, "Called dbmoretext with a bad size parameter\0" }
8023 , { SYBETIME, EXTIME, "Adaptive Server connection timed out\0" }
8024 , { SYBETMCF, EXPROGRAM, "Attempt to install too many custom formats via dbfmtinstall\0" }
8025 , { SYBETMTD, EXPROGRAM, "Attempt to send too much TEXT data via the dbmoretext call\0" }
8026 , { SYBETPAR, EXPROGRAM, "No SYBTEXT or SYBIMAGE parameters were defined\0" }
8027 , { SYBETPTN, EXUSER, "Syntax error: only two periods are permitted in table names\0" }
8028 , { SYBETRAC, EXINFO, "Attempted to turn off a trace flag that was not on\0" }
8029 , { SYBETRAN, EXINFO, "DBPROCESS is being used for another transaction\0" }
8030 , { SYBETRAS, EXINFO, "DB-Library internal error - trace structure not found\0" }
8031 , { SYBETRSN, EXINFO, "Bad numbytes parameter passed to dbtracestring\0" }
8032 , { SYBETSIT, EXINFO, "Attempt to call dbtsput with an invalid timestamp\0" }
8033 , { SYBETTS, EXUSER, "The table which bulk copy is attempting to copy to a host file is shorter than the "
8034 "number of rows which bulk copy was instructed to skip\0" }
8035 , { SYBETYPE, EXINFO, "Invalid argument type given to Hyper/DB-Library\0" }
8036 , { SYBEUCPT, EXUSER, "Unrecognized custom-format parameter-type encountered in dbstrbuild\0" }
8037 , { SYBEUCRR, EXCONSISTENCY, "Internal software error: Unknown connection result reported by dbpasswd\0" }
8038 , { SYBEUDTY, EXCONSISTENCY, "Unknown datatype encountered\0" }
8039 , { SYBEUFDS, EXUSER, "Unrecognized format encountered in dbstrbuild\0" }
8040 , { SYBEUFDT, EXCONSISTENCY, "Unknown fixed-length datatype encountered\0" }
8041 , { SYBEUHST, EXUSER, "Unknown host machine name\0" }
8042 , { SYBEUMSG, EXCOMM, "Unknown message-id in MSG datastream\0" }
8043 , { SYBEUNAM, EXFATAL, "Unable to get current user name from operating system\0" }
8044 , { SYBEUNOP, EXNONFATAL, "Unknown option passed to dbsetopt\0" }
8045 , { SYBEUNT, EXUSER, "Unknown network type found in interface file\0" }
8046 , { SYBEURCI, EXRESOURCE, "International Release: Unable to read copyright information from the DB-Library "
8047 "localization file\0" }
8048 , { SYBEUREI, EXRESOURCE, "International Release: Unable to read error information from the DB-Library "
8049 "localization file\0" }
8050 , { SYBEUREM, EXRESOURCE, "International Release: Unable to read error mnemonic from the DB-Library "
8051 "localization file\0" }
8052 , { SYBEURES, EXRESOURCE, "International Release: Unable to read error string from the DB-Library "
8053 "localization file. 401 Error severities\0" }
8054 , { SYBEURMI, EXRESOURCE, "International Release: Unable to read money-format information from the DB-Library "
8055 "localization file\0" }
8056 , { SYBEUSCT, EXCOMM, "Unable to set communications timer\0" }
8057 , { SYBEUTDS, EXCOMM, "Unrecognized TDS version received from the server\0" }
8058 , { SYBEUVBF, EXPROGRAM, "Attempt to read an unknown version of bcp format file\0" }
8059 , { SYBEUVDT, EXCONSISTENCY, "Unknown variable-length datatype encountered\0" }
8060 , { SYBEVDPT, EXUSER, "For bulk copy, all variable-length data must have either a length-prefix or a "
8061 "terminator specified\0" }
8062 , { SYBEWAID, EXCONSISTENCY, "DB-Library internal error: ALTFMT following ALTNAME has wrong id\0" }
8063 , { SYBEWRIT, EXCOMM, "Write to the server failed\0" }
8064 , { SYBEXOCI, EXNONFATAL, "International Release: A character-set translation overflowed its destination "
8065 "buffer while using bcp to copy data from a host-file to the server\0" }
8066 , { SYBEXTDN, EXPROGRAM, "Warning: the xlt_todisp parameter to dbfree_xlate was NULL. The space associated "
8067 "with the xlt_tosrv parameter has been freed\0" }
8068 , { SYBEXTN, EXPROGRAM, "The xlt_tosrv and xlt_todisp parameters to dbfree_xlate were NULL\0" }
8069 , { SYBEXTSN, EXPROGRAM, "Warning: the xlt_tosrv parameter to dbfree_xlate was NULL. The space associated "
8070 "with the xlt_todisp parameter has been freed\0" }
8071 , { SYBEZTXT, EXINFO, "Attempt to send zero length TEXT or IMAGE to dataserver via dbwritetext\0" }
8072 , { SYBECOLSIZE, EXINFO, "Invalid column information structure size\0" }
8073 };
8074
8075 /** \internal
8076 * \ingroup dblib_internal
8077 * \brief Call client-installed error handler
8078 *
8079 * \param dbproc contains all information needed by db-lib to manage communications with the server.
8080 * \param msgno identifies the error message to be passed to the client's handler.
8081 * \param errnum identifies the OS error (errno), if any. Use 0 if not applicable.
8082 * \returns the handler's return code, subject to correction and adjustment for vendor style:
8083 * - INT_CANCEL The db-lib function that encountered the error will return FAIL.
8084 * - INT_TIMEOUT The db-lib function will cancel the operation and return FAIL. \a dbproc remains useable.
8085 * - INT_CONTINUE The db-lib function will retry the operation.
8086 * \remarks
8087 * The client-installed handler may also return INT_EXIT. If Sybase semantics are used, this function notifies
8088 * the user and calls exit(3). If Microsoft semantics are used, this function returns INT_CANCEL.
8089 *
8090 * If the client-installed handler returns something other than these four INT_* values, or returns timeout-related
8091 * value for anything but SYBETIME, it's treated here as INT_EXIT (see above).
8092 *
8093 * Instead of sprinkling error text all over db-lib, we consolidate it here,
8094 * where it can be translated (one day), and where it can be mapped to the TDS error number.
8095 * The libraries don't use consistent error numbers or messages, so when libtds has to emit
8096 * an error message, it can't include the text. It can pass its error number to a client-library
8097 * function, which will interpret it, add the text, call the application's installed handler
8098 * (if any) and return the handler's return code back to the caller.
8099 *
8100 * The call stack may look something like this:
8101 *
8102 * -# application
8103 * -# db-lib function (encounters error)
8104 * -# dbperror
8105 * -# error handler (installed by application)
8106 *
8107 * The error handling in this case is unambiguous: the caller invokes this function, the client's handler returns its
8108 * instruction, which the caller receives. Quite often the caller will get INT_CANCEL, in which case it should put its
8109 * house in order and return FAIL.
8110 *
8111 * The call stack may otherwise look something like this:
8112 *
8113 * -# application
8114 * -# db-lib function
8115 * -# libtds function (encounters error)
8116 * -# _dblib_handle_err_message
8117 * -# dbperror
8118 * -# error handler (installed by application)
8119 *
8120 * Because different client libraries specify their handler semantics differently,
8121 * and because libtds doesn't know which client library is in charge of any given connection, it cannot interpret the
8122 * raw return code from a db-lib error handler. For these reasons,
8123 * libtds calls _dblib_handle_err_message, which translates between libtds and db-lib semantics.
8124 * \sa dberrhandle(), _dblib_handle_err_message().
8125 */
8126 int
dbperror(DBPROCESS * dbproc,DBINT msgno,long errnum,...)8127 dbperror(DBPROCESS *dbproc, DBINT msgno, long errnum, ...)
8128 {
8129 static const char int_exit_text[] = "FreeTDS: db-lib: exiting because client error handler returned %s for msgno %d\n";
8130 static const char int_invalid_text[] = "%s (%d) received from client-installed error handler for nontimeout for error %d."
8131 " Treating as INT_EXIT\n";
8132 static const DBLIB_ERROR_MESSAGE default_message = { 0, EXCONSISTENCY, "unrecognized msgno" };
8133 DBLIB_ERROR_MESSAGE constructed_message = { 0, EXCONSISTENCY, NULL };
8134 const DBLIB_ERROR_MESSAGE *msg = &default_message;
8135
8136 int i, rc = INT_CANCEL;
8137 const char *os_msgtext = strerror(errnum), *rc_name = "logic error";
8138 char rc_buf[16];
8139
8140 tdsdump_log(TDS_DBG_FUNC, "dbperror(%p, %d, %ld)\n", dbproc, msgno, errnum); /* dbproc can be NULL */
8141
8142 #ifdef _WIN32
8143 /*
8144 * Unfortunately MinGW uses the "old" msvcrt.dll (Visual C++ 2005 uses
8145 * a newer version) which does not set errno when allocation functions
8146 * cannot allocate memory, so we do it for them.
8147 */
8148 if (msgno == SYBEMEM)
8149 errnum = ENOMEM;
8150 #endif
8151
8152 if (os_msgtext == NULL)
8153 os_msgtext = "no OS error";
8154
8155 assert(_dblib_err_handler != NULL); /* always installed by dbinit() or dberrhandle() */
8156
8157 /* look up the error message */
8158 for (i=0; i < TDS_VECTOR_SIZE(dblib_error_messages); i++ ) {
8159 if (dblib_error_messages[i].msgno == msgno) {
8160
8161 /*
8162 * See if the message has placeholders. If so, build a message string on the heap.
8163 * The presence of placeholders is indicated by the existence of a "string after the string",
8164 * i.e., a format string (for dbstrbuild) after a null "terminator" in the message.
8165 * On error -- can't allocate, can't build the string -- give up and call the client handler anyway.
8166 */
8167 const char * ptext = dblib_error_messages[i].msgtext;
8168 const char * pformats = ptext + strlen(ptext) + 1;
8169 msg = &dblib_error_messages[i];
8170 assert(*(pformats - 1) == '\0');
8171 if(*pformats != '\0') {
8172 va_list ap;
8173 int result_len, len = 2 * (int)strlen(ptext);
8174 char * buffer = tds_new0(char, len);
8175
8176 if (buffer == NULL)
8177 break;
8178 va_start(ap, errnum);
8179 rc = tds_vstrbuild(buffer, len, &result_len, ptext, TDS_NULLTERM, pformats, TDS_NULLTERM, ap);
8180 buffer[result_len] = '\0';
8181 va_end(ap);
8182 if (TDS_FAILED(rc)) {
8183 free(buffer);
8184 break;
8185 }
8186 constructed_message.msgtext = buffer;
8187 constructed_message.severity = msg->severity;
8188 msg = &constructed_message;
8189 }
8190 break;
8191 }
8192 }
8193
8194 if (dbproc && dbproc->tds_socket && dbproc->tds_socket->login) {
8195 DSTR server_name_dstr = dbproc->tds_socket->login->server_name;
8196 if (!tds_dstr_isempty(&server_name_dstr)) {
8197 char * buffer = NULL;
8198 if (asprintf(&buffer, "%s (%s)", msg->msgtext,
8199 tds_dstr_cstr(&server_name_dstr)) >= 0) {
8200 free((char*) constructed_message.msgtext);
8201 constructed_message.msgtext = buffer;
8202 constructed_message.severity = msg->severity;
8203 msg = &constructed_message;
8204 }
8205 }
8206 }
8207
8208 tdsdump_log(TDS_DBG_FUNC, "dbperror: Calling dblib_err_handler with msgno = %d; msg->msgtext = \"%s\"\n",
8209 msgno, msg->msgtext);
8210
8211 /* call the error handler */
8212 rc = (*_dblib_err_handler)(dbproc, msg->severity, msgno, errnum, (char*) msg->msgtext, (char*) os_msgtext);
8213 switch (rc) {
8214 case INT_EXIT:
8215 rc_name = "INT_EXIT";
8216 break;
8217 case INT_CONTINUE:
8218 rc_name = "INT_CONTINUE";
8219 break;
8220 case INT_CANCEL:
8221 rc_name = "INT_CANCEL";
8222 break;
8223 case INT_TIMEOUT:
8224 rc_name = "INT_TIMEOUT";
8225 break;
8226 default:
8227 rc_name = "invalid";
8228 break;
8229 }
8230 tdsdump_log(TDS_DBG_FUNC, "dbperror: dblib_err_handler for msgno = %d; msg->msgtext = \"%s\" -- returns %d (%s)\n",
8231 msgno, msg->msgtext, rc, rc_name);
8232
8233 /* we're done with the dynamic string now. */
8234 free((char*) constructed_message.msgtext);
8235
8236 /* Timeout return codes are errors for non-timeout conditions. */
8237 if (msgno != SYBETIME) {
8238 switch (rc) {
8239 case INT_CONTINUE:
8240 tdsdump_log(TDS_DBG_SEVERE, int_invalid_text, "INT_CONTINUE", rc, msgno);
8241 rc = INT_EXIT;
8242 break;
8243 case INT_TIMEOUT:
8244 tdsdump_log(TDS_DBG_SEVERE, int_invalid_text, "INT_TIMEOUT", rc, msgno);
8245 rc = INT_EXIT;
8246 break;
8247 default:
8248 break;
8249 }
8250 }
8251
8252 /*
8253 * Sybase exits on INT_EXIT; Microsoft converts to INT_CANCEL.
8254 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dblibc/dbc_pdc04c_6v39.asp
8255 */
8256 switch (rc) {
8257 case INT_CONTINUE:
8258 /* Microsoft does not define INT_TIMEOUT. Instead, two consecutive INT_CONTINUEs yield INT_CANCEL. */
8259 if (dbproc && dbproc->msdblib && ++dbproc->ntimeouts >=2) {
8260 dbproc->ntimeouts = 0;
8261 rc = INT_CANCEL;
8262 } /* fall through */
8263 case INT_CANCEL:
8264 case INT_TIMEOUT:
8265 return rc; /* normal case */
8266 break;
8267 default:
8268 sprintf(rc_buf, "%d", rc);
8269 rc_name = rc_buf;
8270 tdsdump_log(TDS_DBG_SEVERE, int_invalid_text, "Invalid return code", rc, msgno);
8271 /* fall through */
8272 case INT_EXIT:
8273 if (dbproc && dbproc->msdblib) {
8274 /* Microsoft behavior */
8275 return INT_CANCEL;
8276 }
8277 fprintf(stderr, int_exit_text, rc_name, msgno);
8278 tdsdump_log(TDS_DBG_SEVERE, int_exit_text, rc_name, msgno);
8279 break;
8280 }
8281 exit(EXIT_FAILURE);
8282 return rc; /* not reached */
8283 }
8284
8285