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