1 /*
2 Copyright (C) 2008 Ulric Eriksson <ulric@siag.nu>
3
4 Based on tsql.c from freetds:
5 FreeTDS - Library of routines accessing Sybase and Microsoft databases
6 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Brian Bruns
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the Licence, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21 MA 02111-1307, USA.
22 */
23
24 #ifdef HAVE_LIBTDS
25
26 # include <sys/time.h>
27 # include <time.h>
28
29 #include <stdio.h>
30 #include <assert.h>
31 #include <ctype.h>
32
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <locale.h>
38
39 #ifdef HAVE_LANGINFO_H
40 #include <langinfo.h>
41 #endif /* HAVE_LANGINFO_H */
42
43 #include <tds.h>
44 #include <tdsconvert.h>
45 #include "common.h"
46 #include "sdb.h"
47
48 enum
49 {
50 OPT_VERSION = 0x01,
51 OPT_TIMER = 0x02,
52 OPT_NOFOOTER = 0x04,
53 OPT_NOHEADER = 0x08,
54 OPT_QUIET = 0x10
55 };
56
57 struct conn_info {
58 TDSSOCKET *tds;
59 TDSLOGIN *login;
60 TDSCONTEXT *context;
61 TDSCONNECTION *connection;
62 };
63
64 #define QUIET (sdb_debuglevel == 0)
65
sdb_handle_message(const TDSCONTEXT * context,TDSSOCKET * tds,TDSMESSAGE * msg)66 static int sdb_handle_message(const TDSCONTEXT * context, TDSSOCKET * tds, TDSMESSAGE * msg)
67 {
68 if (msg->msgno == 0) {
69 sdb_debug("%s", msg->message);
70 return 0;
71 }
72
73 if (msg->msgno != 5701 && msg->msgno != 5703
74 && msg->msgno != 20018) {
75 sdb_debug("Msg %d, Level %d, State %d, Server %s, Line %d\n%s",
76 msg->msgno, msg->severity, msg->state, msg->server, msg->line_number, msg->message);
77 }
78
79 return 0;
80 }
81
do_query(TDSSOCKET * tds,char * buf,int opt_flags,int (* callback)(int,char **,void *),void * closure)82 static int do_query(TDSSOCKET* tds, char *buf, int opt_flags,
83 int (*callback)(int, char **, void *), void *closure)
84 {
85 int rows = -1;
86 int rc, i;
87 TDSCOLUMN *col;
88 int ctype;
89 CONV_RESULT dres;
90 unsigned char *src;
91 TDS_INT srclen;
92 TDS_INT resulttype;
93 struct timeval start, stop;
94 char message[128];
95 char **p;
96
97 rc = tds_submit_query(tds, buf);
98 if (rc != TDS_SUCCEED) {
99 sdb_debug("tds_submit_query() failed");
100 return -1;
101 }
102
103 while ((rc = tds_process_tokens(tds, &resulttype, NULL, TDS_TOKEN_RESULTS)) == TDS_SUCCEED) {
104 if (opt_flags & OPT_TIMER) {
105 gettimeofday(&start, NULL);
106 }
107 switch (resulttype) {
108 case TDS_ROWFMT_RESULT:
109 if (sdb_debuglevel && tds->current_results) {
110 for (i = 0; i < tds->current_results->num_cols; i++) {
111 sdb_debug("Column %d = '%s'", i, tds->current_results->columns[i]->column_name);
112 }
113 }
114 break;
115 case TDS_COMPUTE_RESULT:
116 case TDS_ROW_RESULT:
117 rows = 0;
118 p = sdb_malloc(tds->current_results->num_cols * sizeof *p);
119
120 while ((rc = tds_process_tokens(tds, &resulttype, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE)) == TDS_SUCCEED) {
121 if (resulttype != TDS_ROW_RESULT && resulttype != TDS_COMPUTE_RESULT)
122 break;
123
124 rows++;
125
126 if (!tds->current_results)
127 continue;
128
129 for (i = 0; i < tds->current_results->num_cols; i++) {
130 col = tds->current_results->columns[i];
131 if (col->column_cur_size < 0) {
132 if (sdb_debuglevel)
133 sdb_debug("[%d,%d] %s", rows, i, "NULL");
134 p[i] = NULL;
135 continue;
136 }
137 ctype = tds_get_conversion_type(col->column_type, col->column_size);
138
139 src = &(tds->current_results->current_row[col->column_offset]);
140 if (is_blob_type(col->column_type))
141 src = (unsigned char *) ((TDSBLOB *) src)->textvalue;
142 srclen = col->column_cur_size;
143
144
145 if (tds_convert(tds->tds_ctx, ctype, (TDS_CHAR *) src, srclen, SYBVARCHAR, &dres) < 0)
146 continue;
147 if (sdb_debuglevel)
148 sdb_debug("[%d,%d] %s", rows, i, dres.c);
149 p[i] = sdb_strdup(dres.c);
150 free(dres.c);
151 }
152 if (sdb_debuglevel)
153 sdb_debug("Call the callback here");
154 (*callback)(tds->current_results->num_cols, p, closure);
155 for (i = 0; i < tds->current_results->num_cols; i++) {
156 sdb_free(p[i]);
157 }
158 }
159 sdb_free(p);
160 break;
161 case TDS_STATUS_RESULT:
162 sdb_debug("(return status = %d)", tds->ret_status);
163 break;
164 default:
165 break;
166 }
167
168 if (opt_flags & OPT_VERSION) {
169 char version[64];
170 int line = 0;
171
172 line = tds_version(tds, version);
173 if (line) {
174 sprintf(message, "using TDS version %s", version);
175 tds_client_msg(tds->tds_ctx, tds, line, line, line, line, message);
176 }
177 }
178 if (opt_flags & OPT_TIMER) {
179 gettimeofday(&stop, NULL);
180 sprintf(message, "Total time for processing %d rows: %ld msecs\n",
181 rows, (long) ((stop.tv_sec - start.tv_sec) * 1000) + ((stop.tv_usec - start.tv_usec) / 1000));
182 tds_client_msg(tds->tds_ctx, tds, 1, 1, 1, 1, message);
183 }
184 }
185 return rows;
186 }
187
populate_login(TDSLOGIN * login,char * url)188 static void populate_login(TDSLOGIN *login, char *url)
189 {
190 char *hostname = sdb_url_value(url, "host");
191 char *servername = sdb_url_value(url, "server");
192 char *username = sdb_url_value(url, "uid");
193 char *password = sdb_url_value(url, "pwd");
194 char *confile = sdb_url_value(url, "cfg");
195 char *portname = sdb_url_value(url, "port");
196 int port = 1433;
197 const char *locale = NULL;
198 char *charset = NULL;
199
200 if (portname) port = atoi(portname);
201
202 setlocale(LC_ALL, "");
203 locale = setlocale(LC_ALL, NULL);
204
205 #if HAVE_LOCALE_CHARSET
206 charset = locale_charset();
207 #endif
208 #if HAVE_NL_LANGINFO && defined(CODESET)
209 if (!charset)
210 charset = nl_langinfo(CODESET);
211 #endif
212
213 if (locale)
214 if (!QUIET) sdb_debug("locale is \"%s\"", locale);
215 if (charset) {
216 if (!QUIET) sdb_debug("locale charset is \"%s\"", charset);
217 } else {
218 charset = "ISO-8859-1";
219 if (!QUIET) sdb_debug("using default charset \"%s\"", charset);
220 }
221
222 /* validate parameters */
223 if (!servername && !hostname) {
224 sdb_error("Missing server or host");
225 }
226 if (hostname && !port) {
227 sdb_error("Missing port");
228 }
229 if (!username) {
230 sdb_error("Missing user");
231 }
232 if (!password) {
233 sdb_error("Missing password");
234 }
235
236 /* all validated, let's do it */
237
238 /* if it's a servername */
239
240 if (servername) {
241 tds_set_user(login, username);
242 tds_set_app(login, "TSQL");
243 tds_set_library(login, "TDS-Library");
244 tds_set_server(login, servername);
245 tds_set_client_charset(login, charset);
246 tds_set_language(login, "us_english");
247 tds_set_passwd(login, password);
248 if (confile) {
249 tds_set_interfaces_file_loc(confile);
250 }
251 /* else we specified hostname/port */
252 } else {
253 tds_set_user(login, username);
254 tds_set_app(login, "TSQL");
255 tds_set_library(login, "TDS-Library");
256 tds_set_server(login, hostname);
257 tds_set_port(login, port);
258 tds_set_client_charset(login, charset);
259 tds_set_language(login, "us_english");
260 tds_set_passwd(login, password);
261 }
262
263 sdb_free(hostname);
264 sdb_free(servername);
265 sdb_free(username);
266 sdb_free(password);
267 sdb_free(confile);
268 sdb_free(portname);
269 }
270
sdb_tds_open(char * url)271 static void *sdb_tds_open(char *url)
272 {
273 struct conn_info *ci = sdb_malloc(sizeof *ci);
274
275 if (sdb_debuglevel) sdb_debug("sdb_tds_open(%s)", url);
276 /* grab a login structure */
277 ci->login = tds_alloc_login();
278
279 ci->context = tds_alloc_context(NULL);
280 if (ci->context->locale && !ci->context->locale->date_fmt) {
281 /* set default in case there's no locale file */
282 ci->context->locale->date_fmt = strdup("%b %e %Y %I:%M%p");
283 }
284
285 ci->context->msg_handler = sdb_handle_message;
286 ci->context->err_handler = sdb_handle_message;
287
288 /* process all the command line args into the login structure */
289 populate_login(ci->login, url);
290
291 /* Try to open a connection */
292 ci->tds = tds_alloc_socket(ci->context, 512);
293 tds_set_parent(ci->tds, NULL);
294 ci->connection = tds_read_config_info(NULL, ci->login, ci->context->locale);
295 if (!ci->connection || tds_connect(ci->tds, ci->connection) == TDS_FAIL) {
296 tds_free_socket(ci->tds);
297 tds_free_connection(ci->connection);
298 sdb_debug("There was a problem connecting to the server");
299 sdb_free(ci);
300 return NULL;
301 }
302 tds_free_connection(ci->connection);
303 return ci;
304 }
305
sdb_tds_close(void * db)306 static int sdb_tds_close(void *db)
307 {
308 struct conn_info *ci = db;
309
310 if (sdb_debuglevel) sdb_debug("sdb_tds_close(%p)", db);
311
312 /* close up shop */
313 tds_free_socket(ci->tds);
314 tds_free_login(ci->login);
315 tds_free_context(ci->context);
316
317 sdb_free(ci);
318 return 0;
319 }
320
321 /* Parameters:
322 pdb = pointer to previously opened database connection, or NULL
323 url = url string
324 query = query string
325 callback = function to send the data to
326 closure = pointer to data passed from application
327 */
tds_driver(void * pdb,char * url,char * query,int (* callback)(int,char **,void *),void * closure)328 static int tds_driver(void *pdb, char *url, char *query,
329 int (*callback)(int, char **, void *), void *closure)
330 {
331 struct conn_info *ci;
332 int opt_flags = 0;
333 int rows;
334
335 if (sdb_debuglevel) sdb_debug("tds_driver(%p, %s, %s, %p, %p)",
336 pdb, url, query, callback, closure);
337 if (pdb == NULL) ci = sdb_tds_open(url);
338 else ci = pdb;
339 if (ci == NULL) {
340 sdb_debug("Can't open database");
341 return -1;
342 }
343
344 rows = do_query(ci->tds, query, opt_flags, callback, closure);
345
346 if (pdb == NULL) sdb_tds_close(ci);
347
348 return rows;
349 }
350
sdb_init_tds(void)351 void sdb_init_tds(void)
352 {
353 sdb_register_driver("tds", tds_driver,
354 sdb_tds_open, sdb_tds_close);
355 }
356
357 #else
358
sdb_init_tds(void)359 void sdb_init_tds(void)
360 {
361 ;
362 }
363
364 #endif /* HAVE_LIBTDS */
365