LCOV - code coverage report
Current view: top level - src/odbc - native.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 122 148 82.4 %
Date: 2024-03-23 08:24:27 Functions: 4 5 80.0 %

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

Generated by: LCOV version 1.13