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