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