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