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