1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 1998-2002 Brian Bruns
3 : * Copyright (C) 2004, 2005 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 : #if HAVE_CONFIG_H
22 : #include <config.h>
23 : #endif
24 :
25 : #if HAVE_STDLIB_H
26 : #include <stdlib.h>
27 : #endif /* HAVE_STDLIB_H */
28 :
29 : #if HAVE_STRING_H
30 : #include <string.h>
31 : #endif /* HAVE_STRING_H */
32 :
33 : #if HAVE_ERRNO_H
34 : #include <errno.h>
35 : #endif /* HAVE_ERRNO_H */
36 :
37 : #include <ctype.h>
38 : #include <assert.h>
39 :
40 : #include "tdsodbc.h"
41 :
42 : #ifdef DMALLOC
43 : #include <dmalloc.h>
44 : #endif
45 :
46 : TDS_RCSID(var, "$Id: native.c,v 1.28.2.1 2006/03/21 12:24:48 freddy77 Exp $");
47 :
48 : #define TDS_ISSPACE(c) isspace((unsigned char) (c))
49 : #define TDS_ISALPHA(c) isalpha((unsigned char) (c))
50 :
51 : /*
52 : * Function transformation (from ODBC to Sybase)
53 : * String functions
54 : * ASCII(string) -> ASCII(string)
55 : * BIT_LENGTH(string) -> 8*OCTET_LENGTH(string)
56 : * CHAR_LENGTH(string_exp) -> CHAR_LENGTH(string_exp)
57 : * CHARACTER_LENGTH(string_exp) -> CHAR_LENGTH(string_exp)
58 : * CONCAT(string_exp1, string_exp2) -> string_exp1 + string_exp2
59 : * DIFFERENCE(string_exp1, string_exp2) -> DIFFERENCE(string_exp1, string_exp2)
60 : * INSERT(string_exp1, start, length, string_exp2) -> STUFF(sameparams)
61 : * LCASE(string_exp) -> LOWER(string)
62 : * LEFT(string_exp, count) -> SUBSTRING(string, 1, count)
63 : * LENGTH(string_exp) -> CHAR_LENGTH(RTRIM(string_exp))
64 : * LOCATE(string, string [,start]) -> CHARINDEX(string, string)
65 : * (SQLGetInfo should return third parameter not possible)
66 : * LTRIM(String) -> LTRIM(String)
67 : * OCTET_LENGTH(string_exp) -> OCTET_LENGTH(string_exp)
68 : * POSITION(character_exp IN character_exp) ???
69 : * REPEAT(string_exp, count) -> REPLICATE(same)
70 : * REPLACE(string_exp1, string_exp2, string_exp3) -> ??
71 : * RIGHT(string_exp, count) -> RIGHT(string_exp, count)
72 : * RTRIM(string_exp) -> RTRIM(string_exp)
73 : * SOUNDEX(string_exp) -> SOUNDEX(string_exp)
74 : * SPACE(count) (ODBC 2.0) -> SPACE(count) (ODBC 2.0)
75 : * SUBSTRING(string_exp, start, length) -> SUBSTRING(string_exp, start, length)
76 : * UCASE(string_exp) -> UPPER(string)
77 : *
78 : * Numeric
79 : * Nearly all function use same parameters, except:
80 : * ATAN2 -> ATN2
81 : * TRUNCATE -> ??
82 : */
83 : static SQLRETURN
84 : to_native(struct _hdbc *dbc, struct _hstmt *stmt, char *buf)
85 739 : {
86 : char *d, *s;
87 739 : int nest_syntax = 0;
88 :
89 : /* list of bit, used as stack, is call ? FIXME limites size... */
90 739 : unsigned long is_calls = 0;
91 : int server_scalar;
92 :
93 739 : assert(dbc && buf);
94 :
95 739 : server_scalar = TDS_IS_MSSQL(dbc->tds_socket) && dbc->tds_socket->product_version >= TDS_MS_VER(7, 0, 0);
96 :
97 : /*
98 : * we can do it because result string will be
99 : * not bigger than source string
100 : */
101 739 : d = s = buf;
102 33242 : while (*s) {
103 : /* TODO: test syntax like "select 1 as [pi]]p)p{?=call]]]]o], 2" on mssql7+ */
104 31764 : if (*s == '"' || *s == '\'' || *s == '[') {
105 301 : size_t len_quote = tds_skip_quoted(s) - s;
106 :
107 301 : memmove(d, s, len_quote);
108 301 : s += len_quote;
109 301 : d += len_quote;
110 301 : continue;
111 : }
112 :
113 31463 : if (*s == '{') {
114 : char *pcall;
115 :
116 41 : while (TDS_ISSPACE(*++s));
117 33 : pcall = s;
118 : /* FIXME if nest_syntax > 0 problems */
119 33 : if (server_scalar && strncasecmp(pcall, "fn ", 3) == 0) {
120 0 : *d++ = '{';
121 0 : continue;
122 : }
123 33 : if (*pcall == '?') {
124 : /* skip spaces after ? */
125 24 : while (TDS_ISSPACE(*++pcall));
126 16 : if (*pcall == '=') {
127 20 : while (TDS_ISSPACE(*++pcall));
128 : } else {
129 : /* avoid {?call ... syntax */
130 0 : pcall = s;
131 : }
132 : }
133 33 : if (strncasecmp(pcall, "call ", 5) != 0)
134 0 : pcall = NULL;
135 :
136 33 : if (stmt)
137 33 : stmt->prepared_query_is_rpc = 1;
138 33 : ++nest_syntax;
139 33 : is_calls <<= 1;
140 33 : if (!pcall) {
141 : /* assume syntax in the form {type ...} */
142 0 : while (TDS_ISALPHA(*s))
143 0 : ++s;
144 0 : while (TDS_ISSPACE(*s))
145 0 : ++s;
146 : } else {
147 33 : if (*s == '?' && stmt)
148 16 : stmt->prepared_query_is_func = 1;
149 33 : memcpy(d, "exec ", 5);
150 33 : d += 5;
151 33 : s = pcall + 5;
152 33 : is_calls |= 1;
153 : }
154 31430 : } else if (nest_syntax > 0) {
155 : /* do not copy close syntax */
156 597 : if (*s == '}') {
157 33 : --nest_syntax;
158 33 : is_calls >>= 1;
159 33 : ++s;
160 33 : continue;
161 : /* convert parenthesis in call to spaces */
162 630 : } else if ((is_calls & 1) && (*s == '(' || *s == ')')) {
163 66 : *d++ = ' ';
164 66 : s++;
165 : } else {
166 498 : *d++ = *s++;
167 : }
168 : } else {
169 30833 : *d++ = *s++;
170 : }
171 : }
172 739 : *d = '\0';
173 739 : return SQL_SUCCESS;
174 : }
175 :
176 : const char *
177 : parse_const_param(const char *s, TDS_SERVER_TYPE *type)
178 32 : {
179 : char *end;
180 :
181 : /* binary */
182 32 : if (strncasecmp(s, "0x", 2) == 0) {
183 4 : s += 2;
184 72 : while (isxdigit(*s))
185 64 : ++s;
186 4 : *type = SYBVARBINARY;
187 4 : return s;
188 : }
189 :
190 : /* string */
191 28 : if (*s == '\'') {
192 20 : *type = SYBVARCHAR;
193 20 : return tds_skip_quoted(s);
194 : }
195 :
196 : /* integer/float */
197 8 : if (isdigit(*s) || *s == '+' || *s == '-') {
198 8 : errno = 0;
199 8 : strtod(s, &end);
200 8 : if (end != s && strcspn(s, ".eE") < (end-s)&& errno == 0) {
201 4 : *type = SYBFLT8;
202 4 : return end;
203 : }
204 4 : errno = 0;
205 : /* FIXME success if long is 64bit */
206 4 : strtol(s, &end, 10);
207 4 : if (end != s && errno == 0) {
208 4 : *type = SYBINT4;
209 4 : return end;
210 : }
211 : }
212 :
213 : /* TODO date, time */
214 :
215 0 : return NULL;
216 : }
217 :
218 : SQLRETURN
219 : prepare_call(struct _hstmt * stmt)
220 739 : {
221 : const char *s, *p, *param_start;
222 : char *buf;
223 : SQLRETURN rc;
224 : TDS_SERVER_TYPE type;
225 :
226 739 : if (stmt->prepared_query)
227 97 : buf = stmt->prepared_query;
228 642 : else if (stmt->query)
229 642 : buf = stmt->query;
230 : else
231 0 : return SQL_ERROR;
232 :
233 739 : if ((rc = to_native(stmt->dbc, stmt, buf)) != SQL_SUCCESS)
234 0 : return rc;
235 :
236 : /* now detect RPC */
237 739 : if (stmt->prepared_query_is_rpc == 0)
238 706 : return SQL_SUCCESS;
239 33 : stmt->prepared_query_is_rpc = 0;
240 :
241 33 : s = buf;
242 66 : while (TDS_ISSPACE(*s))
243 0 : ++s;
244 33 : if (strncasecmp(s, "exec", 4) == 0) {
245 33 : if (TDS_ISSPACE(s[4]))
246 33 : s += 5;
247 0 : else if (strncasecmp(s, "execute", 7) == 0 && TDS_ISSPACE(s[7]))
248 0 : s += 8;
249 : else {
250 0 : stmt->prepared_query_is_func = 0;
251 0 : return SQL_SUCCESS;
252 : }
253 : }
254 66 : while (TDS_ISSPACE(*s))
255 0 : ++s;
256 33 : p = s;
257 33 : if (*s == '[') {
258 : /* FIXME handle [dbo].[name] and [master]..[name] syntax */
259 0 : s = (char *) tds_skip_quoted(s);
260 : } else {
261 : /* FIXME: stop at other characters ??? */
262 409 : while (*s && !TDS_ISSPACE(*s))
263 343 : ++s;
264 : }
265 33 : param_start = s;
266 33 : --s; /* trick, now s point to no blank */
267 : for (;;) {
268 118 : while (TDS_ISSPACE(*++s));
269 67 : if (!*s)
270 0 : break;
271 67 : switch (*s) {
272 : case '?':
273 49 : break;
274 : case ',':
275 2 : --s;
276 2 : break;
277 : default:
278 16 : if (!(s = parse_const_param(s, &type))) {
279 0 : stmt->prepared_query_is_func = 0;
280 0 : return SQL_SUCCESS;
281 : }
282 16 : --s;
283 : break;
284 : }
285 100 : while (TDS_ISSPACE(*++s));
286 67 : if (!*s)
287 33 : break;
288 34 : if (*s != ',') {
289 0 : stmt->prepared_query_is_func = 0;
290 0 : return SQL_SUCCESS;
291 : }
292 34 : }
293 33 : stmt->prepared_query_is_rpc = 1;
294 :
295 : /* remove unneeded exec */
296 33 : memmove(buf, p, strlen(p) + 1);
297 33 : stmt->prepared_pos = buf + (param_start - p);
298 :
299 33 : return SQL_SUCCESS;
300 : }
301 :
302 : /* TODO handle output parameter and not terminated string */
303 : SQLRETURN
304 : native_sql(struct _hdbc * dbc, char *s)
305 0 : {
306 0 : return to_native(dbc, NULL, s);
307 : }
308 :
309 : /* function info */
310 : struct func_info;
311 : struct native_info;
312 : typedef void (*special_fn) (struct native_info * ni, struct func_info * fi, char **params);
313 :
314 : struct func_info
315 : {
316 : const char *name;
317 : int num_param;
318 : const char *sql_name;
319 : special_fn special;
320 : };
321 :
322 : struct native_info
323 : {
324 : char *d;
325 : int length;
326 : };
327 :
328 : #if 0 /* developing ... */
329 :
330 : #define MAX_PARAMS 4
331 :
332 : static const struct func_info funcs[] = {
333 : /* string functions */
334 : {"ASCII", 1},
335 : {"BIT_LENGTH", 1, "(8*OCTET_LENGTH", fn_parentheses},
336 : {"CHAR", 1},
337 : {"CHAR_LENGTH", 1},
338 : {"CHARACTER_LENGTH", 1, "CHAR_LENGTH"},
339 : {"CONCAT", 2, NULL, fn_concat}, /* a,b -> a+b */
340 : {"DIFFERENCE", 2},
341 : {"INSERT", 4, "STUFF"},
342 : {"LCASE", 1, "LOWER"},
343 : {"LEFT", 2, "SUBSTRING", fn_left},
344 : {"LENGTH", 1, "CHAR_LENGTH(RTRIM", fn_parentheses},
345 : {"LOCATE", 2, "CHARINDEX"},
346 : /* (SQLGetInfo should return third parameter not possible) */
347 : {"LTRIM", 1},
348 : {"OCTET_LENGTH", 1},
349 : /* POSITION(character_exp IN character_exp) */
350 : {"REPEAT", 2, "REPLICATE"},
351 : /* REPLACE(string_exp1, string_exp2, string_exp3) */
352 : {"RIGHT", 2},
353 : {"RTRIM", 1},
354 : {"SOUNDEX", 1},
355 : {"SPACE", 1},
356 : {"SUBSTRING", 3},
357 : {"UCASE", 1, "UPPER"},
358 :
359 : /* numeric functions */
360 : {"ABS", 1},
361 : {"ACOS", 1},
362 : {"ASIN", 1},
363 : {"ATAN", 1},
364 : {"ATAN2", 2, "ATN2"},
365 : {"CEILING", 1},
366 : {"COS", 1},
367 : {"COT", 1},
368 : {"DEGREES", 1},
369 : {"EXP", 1},
370 : {"FLOOR", 1},
371 : {"LOG", 1},
372 : {"LOG10", 1},
373 : {"MOD", 2, NULL, fn_mod}, /* mod(a,b) -> ((a)%(b)) */
374 : {"PI", 0},
375 : {"POWER", 2},
376 : {"RADIANS", 1},
377 : {"RAND", -1, NULL, fn_rand}, /* accept 0 or 1 parameters */
378 : {"ROUND", 2},
379 : {"SIGN", 1},
380 : {"SIN", 1},
381 : {"SQRT", 1},
382 : {"TAN", 1},
383 : /* TRUNCATE(numeric_exp, integer_exp) */
384 :
385 : /* system functions */
386 : {"DATABASE", 0, "DB_NAME"},
387 : {"IFNULL", 2, "ISNULL"},
388 : {"USER", 0, "USER_NAME"}
389 :
390 : };
391 :
392 : /**
393 : * Parse given sql and return converted sql
394 : */
395 : int
396 : odbc_native_sql(const char *odbc_sql, char **out)
397 : {
398 : char *d;
399 : }
400 :
401 : #endif
|