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