1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004  Brian Bruns
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 #include <config.h>
21 
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <assert.h>
25 
26 #if HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif /* HAVE_STDLIB_H */
29 
30 #if HAVE_ERRNO_H
31 # include <errno.h>
32 #endif /* HAVE_ERRNO_H */
33 
34 #include <freetds/utils.h>
35 #include <freetds/tds.h>
36 #include <freetds/convert.h>
37 #include <sybdb.h>
38 #include <syberror.h>
39 #include <dblib.h>
40 
41 /*
42  * test include consistency
43  * I don't think all compiler are able to compile this code... if not comment it
44  */
45 #if ENABLE_EXTRA_CHECKS
46 
47 /* TODO test SYBxxx consistency */
48 
49 #define TEST_ATTRIBUTE(t,sa,fa,sb,fb) \
50 	TDS_COMPILE_CHECK(t,sizeof(((sa*)0)->fa) == sizeof(((sb*)0)->fb) && TDS_OFFSET(sa,fa) == TDS_OFFSET(sb,fb))
51 
52 TEST_ATTRIBUTE(t21,TDS_MONEY4,mny4,DBMONEY4,mny4);
53 TEST_ATTRIBUTE(t22,TDS_OLD_MONEY,mnyhigh,DBMONEY,mnyhigh);
54 TEST_ATTRIBUTE(t23,TDS_OLD_MONEY,mnylow,DBMONEY,mnylow);
55 TEST_ATTRIBUTE(t24,TDS_DATETIME,dtdays,DBDATETIME,dtdays);
56 TEST_ATTRIBUTE(t25,TDS_DATETIME,dttime,DBDATETIME,dttime);
57 TEST_ATTRIBUTE(t26,TDS_DATETIME4,days,DBDATETIME4,days);
58 TEST_ATTRIBUTE(t27,TDS_DATETIME4,minutes,DBDATETIME4,minutes);
59 TEST_ATTRIBUTE(t28,TDS_NUMERIC,precision,DBNUMERIC,precision);
60 TEST_ATTRIBUTE(t29,TDS_NUMERIC,scale,DBNUMERIC,scale);
61 TEST_ATTRIBUTE(t30,TDS_NUMERIC,array,DBNUMERIC,array);
62 #endif
63 
64 /*
65  * The next 2 functions receive the info and error messages that come from the TDS layer.
66  * The address of this function is passed to the TDS layer in dbinit().
67  * It takes a pointer to a DBPROCESS, it's just that the TDS layer didn't
68  * know what it really was.
69  */
70 int
_dblib_handle_info_message(const TDSCONTEXT * tds_ctx,TDSSOCKET * tds,TDSMESSAGE * msg)71 _dblib_handle_info_message(const TDSCONTEXT * tds_ctx, TDSSOCKET * tds, TDSMESSAGE * msg)
72 {
73 	DBPROCESS *dbproc = (tds && tds_get_parent(tds))? (DBPROCESS *) tds_get_parent(tds) : NULL;
74 
75 	tdsdump_log(TDS_DBG_FUNC, "_dblib_handle_info_message(%p, %p, %p)\n", tds_ctx, tds, msg);
76 	tdsdump_log(TDS_DBG_FUNC, "msgno %d: \"%s\"\n", msg->msgno, msg->message);
77 
78 	/*
79 	 * Check to see if the user supplied a function, else ignore the message.
80 	 */
81 	if (_dblib_msg_handler) {
82 		_dblib_msg_handler(dbproc,
83 				   msg->msgno,
84 				   msg->state,
85 				   msg->severity, msg->message, msg->server, msg->proc_name, msg->line_number);
86 	}
87 
88 	if (msg->severity > 10 && _dblib_err_handler) {	/* call the application's error handler, if installed. */
89 		/*
90 		 * Sybase docs say SYBESMSG is generated only in specific
91 		 * cases (severity greater than 16, or deadlock occurred, or
92 		 * a syntax error occurred.)  However, actual observed
93 		 * behavior is that SYBESMSG is always generated for
94 		 * server messages with severity greater than 10.
95 		 */
96 		/* Cannot call dbperror() here because server messsage numbers (and text) are not in its lookup table. */
97 		static const char message[] = "General SQL Server error: Check messages from the SQL Server";
98 		(*_dblib_err_handler)(dbproc, msg->severity, SYBESMSG, DBNOERR, (char *) message, NULL);
99 	}
100 	return TDS_SUCCESS;
101 }
102 
103 /** \internal
104  *  \dblib_internal
105  *  \brief handle errors generated by libtds
106  *  \param tds_ctx actually a dbproc: contains all information needed by db-lib to manage communications with the server.
107  *  \param tds contains all information needed by libtds to manage communications with the server.
108  *  \param msg the message to send
109  *  \returns
110  *  \remarks This function is called by libtds via tds_ctx->err_handler.  It exists to convert the db-lib
111  *	error handler's return code into one that libtds will know  how to handle.
112  * 	libtds conveniently issues msgno's with the same values and meanings as db-lib and ODBC use.
113  *	(N.B. ODBC actually uses db-lib msgno numbers for its driver-specific "server errors".
114  */
115 /*
116  * Stack:
117  *		libtds
118  *			tds_ctx->err_handler (pointer to _dblib_handle_err_message)
119  *			_dblib_handle_err_message
120  *				dbperror
121  *					client (or default) error handler
122  *					returns db-lib defined return code INT_something.
123  *				returns db-lib return code with Sybase semantics (adjusting client's code if need be)
124  *			returns libtds return code
125  *		decides what to do based on the universal libtds return code, thank you.
126  */
127 int
_dblib_handle_err_message(const TDSCONTEXT * tds_ctx,TDSSOCKET * tds,TDSMESSAGE * msg)128 _dblib_handle_err_message(const TDSCONTEXT * tds_ctx, TDSSOCKET * tds, TDSMESSAGE * msg)
129 {
130 	DBPROCESS *dbproc = (tds && tds_get_parent(tds))? (DBPROCESS *) tds_get_parent(tds) : NULL;
131 	int rc = INT_CANCEL;
132 
133 	assert(_dblib_err_handler);
134 	assert(msg);
135 
136 	rc = dbperror(dbproc, msg->msgno, msg->oserr, 0, 0, 0, 0);
137 
138 	/*
139 	 * Preprocess the return code to handle INT_TIMEOUT/INT_CONTINUE
140 	 * for non-SYBETIME errors in the strange and different ways as
141 	 * specified by Sybase and Microsoft.
142 	 */
143 	if (msg->msgno != SYBETIME) {
144 		switch (rc) {
145 		case INT_TIMEOUT:
146 			rc = INT_EXIT;
147 			break;
148 		case INT_CONTINUE:
149 			if (!dbproc || !dbproc->msdblib) {
150 				/* Sybase behavior */
151 				assert(0);  /* dbperror() should prevent */
152 				rc = INT_EXIT;
153 			} else {
154 				/* Microsoft behavior */
155 				rc = INT_CANCEL;
156 			}
157 			break;
158 		default:
159 			break;
160 		}
161 	}
162 
163 	/*
164 	 * Convert db-lib return code to libtds return code, and return.
165 	 */
166 	switch (rc) {
167 	case INT_CANCEL:	return TDS_INT_CANCEL;
168 	case INT_TIMEOUT:	return TDS_INT_TIMEOUT;
169 	case INT_CONTINUE:	return TDS_INT_CONTINUE;
170 
171 	case INT_EXIT:
172 		assert(0);  /* dbperror() should prevent */
173 	default:
174 		/* unknown return code from error handler */
175 		exit(EXIT_FAILURE);
176 		break;
177 	}
178 
179 	/* not reached */
180 	assert(0);
181 	return TDS_INT_CANCEL;
182 }
183 
184 /**
185  * \ingroup dblib_internal
186  * \brief check interrupts for libtds.
187  *
188  * \param vdbproc a DBPROCESS pointer, contains all information needed by db-lib to manage communications with the server.
189  * \sa DBDEAD(), dbsetinterrupt().
190  */
191 int
_dblib_check_and_handle_interrupt(void * vdbproc)192 _dblib_check_and_handle_interrupt(void * vdbproc)
193 {
194 	DBPROCESS * dbproc = (DBPROCESS*) vdbproc;
195 	int ret = INT_CONTINUE;
196 
197 	assert( dbproc != NULL );
198 
199 	if (dbproc->chkintr == NULL || dbproc->hndlintr == NULL)
200 		return INT_CONTINUE;
201 
202 	tdsdump_log(TDS_DBG_FUNC, "_dblib_check_and_handle_interrupt %p [%p, %p]\n", dbproc, dbproc->chkintr, dbproc->hndlintr);
203 
204 	if (dbproc->chkintr(dbproc)){
205 		switch (ret = dbproc->hndlintr(dbproc)) {
206 		case INT_EXIT:
207 			tdsdump_log(TDS_DBG_FUNC, "dbproc->hndlintr returned INT_EXIT, goodbye!\n");
208 			exit(1);
209 		case INT_CANCEL:
210 			tdsdump_log(TDS_DBG_FUNC, "dbproc->hndlintr returned INT_CANCEL\n");
211 			break;
212 		case INT_CONTINUE:
213 			tdsdump_log(TDS_DBG_FUNC, "dbproc->hndlintr returned INT_CONTINUE\n");
214 			break;
215 		default:
216 			tdsdump_log(TDS_DBG_FUNC, "dbproc->hndlintr returned an invalid value (%d), returning INT_CONTINUE\n", ret);
217 			ret = INT_CONTINUE;
218 			break;
219 		}
220 	}
221 	return ret;
222 }
223 
224 
225 void
_dblib_setTDS_version(TDSLOGIN * tds_login,DBINT version)226 _dblib_setTDS_version(TDSLOGIN * tds_login, DBINT version)
227 {
228 	switch (version) {
229 	case DBVERSION_42:
230 		tds_set_version(tds_login, 4, 2);
231 		break;
232 	case DBVERSION_46:
233 		tds_set_version(tds_login, 4, 6);
234 		break;
235 	case DBVERSION_100:
236 		tds_set_version(tds_login, 5, 0);
237 		break;
238 	}
239 }
240 
241 void
_dblib_convert_err(DBPROCESS * dbproc,TDS_INT len)242 _dblib_convert_err(DBPROCESS * dbproc, TDS_INT len)
243 {
244 	switch (len) {
245 	case TDS_CONVERT_NOAVAIL:
246 		dbperror(dbproc, SYBERDCN, 0);
247 		break;
248 	case TDS_CONVERT_SYNTAX:
249 		dbperror(dbproc, SYBECSYN, 0);
250 		break;
251 	case TDS_CONVERT_NOMEM:
252 		dbperror(dbproc, SYBEMEM, ENOMEM);
253 		break;
254 	case TDS_CONVERT_OVERFLOW:
255 		dbperror(dbproc, SYBECOFL, 0);
256 		break;
257 	default:
258 	case TDS_CONVERT_FAIL:
259 		dbperror(dbproc, SYBECINTERNAL, 0);
260 		break;
261 	}
262 }
263 
264