LCOV - code coverage report
Current view: top level - src/dblib - rpc.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 138 169 81.7 %
Date: 2025-04-13 14:39:30 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 1998, 1999, 2000, 2001  Brian Bruns
       3             :  * Copyright (C) 2002, 2003, 2004, 2005    James K. Lowden
       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             : #include <stdarg.h>
      24             : #include <stdio.h>
      25             : 
      26             : #ifdef HAVE_UNISTD_H
      27             : #include <unistd.h>
      28             : #endif /* HAVE_UNISTD_H */
      29             : 
      30             : #ifdef HAVE_STDLIB_H
      31             : #include <stdlib.h>
      32             : #endif /* HAVE_STDLIB_H */
      33             : 
      34             : #ifdef HAVE_STRING_H
      35             : #include <string.h>
      36             : #endif /* HAVE_STRING_H */
      37             : 
      38             : #if HAVE_ERRNO_H
      39             : # include <errno.h>
      40             : #endif /* HAVE_ERRNO_H */
      41             : 
      42             : #include <freetds/tds.h>
      43             : #include <freetds/convert.h>
      44             : #include <freetds/utils/string.h>
      45             : #include <freetds/replacements.h>
      46             : #include <sybfront.h>
      47             : #include <sybdb.h>
      48             : #include <dblib.h>
      49             : 
      50             : static void rpc_clear(DBREMOTE_PROC * rpc);
      51             : static void param_clear(DBREMOTE_PROC_PARAM * pparam);
      52             : 
      53             : static TDSPARAMINFO *param_info_alloc(TDSSOCKET * tds, DBREMOTE_PROC * rpc);
      54             : 
      55             : /**
      56             :  * \ingroup dblib_rpc
      57             :  * \brief Initialize a remote procedure call. 
      58             :  *
      59             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
      60             :  * \param rpcname name of the stored procedure to be run.  
      61             :  * \param options Only supported option would be DBRPCRECOMPILE, 
      62             :  * which causes the stored procedure to be recompiled before executing.
      63             :  * \remark The RPC functions are the only way to get back OUTPUT parameter data with db-lib 
      64             :  * from modern Microsoft servers.  
      65             :  * \retval SUCCEED normal.
      66             :  * \retval FAIL on error
      67             :  * \sa dbrpcparam(), dbrpcsend()
      68             :  */
      69             : RETCODE
      70         258 : dbrpcinit(DBPROCESS * dbproc, const char rpcname[], DBSMALLINT options)
      71             : {
      72             :         DBREMOTE_PROC **rpc;
      73             : 
      74         258 :         tdsdump_log(TDS_DBG_FUNC, "dbrpcinit(%p, %s, %d)\n", dbproc, rpcname, options);
      75         258 :         CHECK_CONN(FAIL);
      76         258 :         CHECK_NULP(rpcname, "dbrpcinit", 2, FAIL);
      77             : 
      78             :         /*
      79             :          * TODO: adhere to docs.  Only Microsoft supports DBRPCRESET.  They say:
      80             :          * "Cancels a single stored procedure or a batch of stored procedures. 
      81             :          *  If rpcname is specified, that new stored procedure is initialized after the cancellation is complete."
      82             :          */
      83         258 :         if (options & DBRPCRESET) {
      84           0 :                 rpc_clear(dbproc->rpc);
      85           0 :                 dbproc->rpc = NULL;
      86           0 :                 return SUCCEED;
      87             :         }
      88             : 
      89             :         /* any bits we want from the options argument */
      90             :         /* dbrpcrecompile = options & DBRPCRECOMPILE; */
      91         258 :         options &= ~DBRPCRECOMPILE; /* turn that one off, now that we've extracted it */
      92             : 
      93             :         /* all other options except DBRPCRECOMPILE are invalid */
      94         258 :         DBPERROR_RETURN3(options, SYBEIPV, (int) options, "options", "dbrpcinit");
      95             : 
      96             :         /* find a free node */
      97         258 :         for (rpc = &dbproc->rpc; *rpc != NULL; rpc = &(*rpc)->next) {
      98             :                 /* check existing nodes for name match (there shouldn't be one) */
      99           0 :                 if ((*rpc)->name == NULL  || strcmp((*rpc)->name, rpcname) == 0) {
     100           0 :                         tdsdump_log(TDS_DBG_INFO1, "error: dbrpcinit called twice for procedure \"%s\"\n", rpcname);
     101             :                         return FAIL; 
     102             :                 }
     103             :         }
     104             : 
     105             :         /* rpc now contains the address of the dbproc's first empty (null) DBREMOTE_PROC* */
     106             : 
     107             :         /* allocate */
     108         258 :         if ((*rpc = tds_new0(DBREMOTE_PROC, 1)) == NULL) {
     109           0 :                 dbperror(dbproc, SYBEMEM, errno);
     110           0 :                 return FAIL;
     111             :         }
     112             : 
     113         258 :         if (((*rpc)->name = strdup(rpcname)) == NULL) {
     114           0 :                 free(*rpc);
     115           0 :                 *rpc = NULL;
     116           0 :                 dbperror(dbproc, SYBEMEM, errno);
     117           0 :                 return FAIL;
     118             :         }
     119             : 
     120             :         /* store */
     121         258 :         (*rpc)->options = options & DBRPCRECOMPILE;
     122         258 :         (*rpc)->param_list = NULL;
     123             : 
     124             :         /* completed */
     125         258 :         tdsdump_log(TDS_DBG_INFO1, "dbrpcinit() added rpcname \"%s\"\n", rpcname);
     126             : 
     127             :         return SUCCEED;
     128             : }
     129             : 
     130             : /**
     131             :  * \ingroup dblib_rpc
     132             :  * \brief Add a parameter to a remote procedure call.
     133             :  *
     134             :  * Call between dbrpcinit() and dbrpcsend()
     135             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
     136             :  * \param paramname literal name of the parameter, according to the stored procedure (starts with '@').  Optional.  
     137             :  *        If not used, parameters will be passed in order instead of by name. 
     138             :  * \param status must be DBRPCRETURN, if this parameter is a return parameter, else 0. 
     139             :  * \param type datatype of the value parameter e.g., SYBINT4, SYBCHAR.
     140             :  * \param maxlen Maximum output size of the parameter's value to be returned by the stored procedure, 
     141             :  *        usually the size of your host variable. 
     142             :  *        Fixed-length datatypes take -1 (NULL or not).  
     143             :  *        Non-OUTPUT parameters also use -1.  
     144             :  *         Use 0 to send a NULL value for a variable length datatype.  
     145             :  * \param datalen For variable-length datatypes, the byte size of the data to be sent, exclusive of any null terminator. 
     146             :  *        For fixed-length datatypes use -1.  To send a NULL value, use 0.  
     147             :  * \param value Address of your host variable.  
     148             :  * \retval SUCCEED normal.
     149             :  * \retval FAIL on error
     150             :  * \sa dbrpcinit(), dbrpcsend()
     151             :  */
     152             : RETCODE
     153         340 : dbrpcparam(DBPROCESS * dbproc, const char paramname[], BYTE status, int db_type, DBINT maxlen, DBINT datalen, BYTE * value)
     154             : {
     155         340 :         char *name = NULL;
     156             :         DBREMOTE_PROC *rpc;
     157             :         DBREMOTE_PROC_PARAM **pparam;
     158             :         DBREMOTE_PROC_PARAM *param;
     159             :         TDS_SERVER_TYPE type;
     160             : 
     161         340 :         tdsdump_log(TDS_DBG_FUNC, "dbrpcparam(%p, %s, 0x%x, %d, %d, %d, %p)\n", 
     162             :                                    dbproc, paramname, status, db_type, maxlen, datalen, value);
     163         340 :         CHECK_CONN(FAIL);
     164         340 :         CHECK_PARAMETER(dbproc->rpc, SYBERPCS, FAIL);
     165             : 
     166         340 :         DBPERROR_RETURN(!is_tds_type_valid(db_type), SYBEUDTY);
     167         340 :         type = (TDS_SERVER_TYPE) db_type;
     168             : 
     169             :         /* validate datalen parameter */
     170             : 
     171         340 :         if (is_fixed_type(type)) {
     172          24 :                 if (datalen != 0)
     173          10 :                         datalen = -1; 
     174             :         } else {        /* Sybooks: "Passing datalen as -1 for any of these [non-fixed] datatypes results 
     175             :                          * in the DBPROCESS referenced by dbproc being marked as "dead," or unusable."
     176             :                          */
     177         316 :                 DBPERROR_RETURN(datalen < 0, SYBERPIL);
     178             :         }
     179             : 
     180             :         /* "value parameter for dbprcparam() can be NULL, only if the datalen parameter is 0." */
     181         340 :         DBPERROR_RETURN(value == NULL && datalen != 0, SYBERPNULL);
     182             :         
     183             :         /* nullable types must provide a data length */
     184         340 :         DBPERROR_RETURN(is_nullable_type(type) && datalen < 0, SYBERPUL);
     185             : 
     186             :         /* validate maxlen parameter */
     187             : 
     188         340 :         if (status & DBRPCRETURN) {
     189          64 :                 if (is_fixed_type(type)) {
     190             :                         maxlen = -1;
     191             :                 } else {
     192          40 :                         if (maxlen == -1)
     193          10 :                                 maxlen = 255;
     194             :                 }
     195             :         } else {
     196             :                 /*
     197             :                  * Well, maxlen should be used only for output parameter however it seems
     198             :                  * that ms implementation wrongly require this 0 for NULL variable
     199             :                  * input parameters, so fix it
     200             :                  */
     201         276 :                 DBPERROR_RETURN3(maxlen != -1 && maxlen != 0, SYBEIPV, (int) maxlen, "maxlen", "dbrpcparam");
     202             :                 maxlen = -1;
     203             :         }
     204             :         
     205             :         /* end validation */
     206             : 
     207             :         /* This trick is to allow for client using utf8 to insert any character into a NVARCHAR parameter
     208             :          * The 4000 check is to allow varchar with more then 4000 characters (varchar is limited to 8000
     209             :          * characters) which can't be converted to nvarchar (which is limited to 4000 character)
     210             :          */
     211         340 :         if (type == SYBVARCHAR && IS_TDS7_PLUS(dbproc->tds_socket->conn)
     212          40 :             && maxlen <= 4000 && datalen <= 4000)
     213          32 :                 type = XSYBNVARCHAR;
     214             : 
     215             :         /* allocate */
     216         340 :         param = tds_new(DBREMOTE_PROC_PARAM, 1);
     217         340 :         if (param == NULL) {
     218           0 :                 dbperror(dbproc, SYBEMEM, 0);
     219           0 :                 return FAIL;
     220             :         }
     221             : 
     222         340 :         if (paramname) {
     223         316 :                 name = strdup(paramname);
     224         316 :                 if (name == NULL) {
     225           0 :                         free(param);
     226           0 :                         dbperror(dbproc, SYBEMEM, 0);
     227           0 :                         return FAIL;
     228             :                 }
     229             :         }
     230             : 
     231             :         /* initialize */
     232         340 :         param->next = NULL;  /* NULL signifies end of linked list */
     233         340 :         param->name = name;
     234         340 :         param->status = status;
     235         340 :         param->type = type;
     236         340 :         param->maxlen = maxlen;
     237         340 :         param->datalen = datalen;
     238             : 
     239             :         /*
     240             :          * If datalen = 0, value parameter is ignored.
     241             :          * This is one way to specify a NULL input parameter. 
     242             :          */
     243         340 :         if (datalen == 0)
     244          64 :                 param->value = NULL;
     245             :         else
     246         276 :                 param->value = value;
     247             : 
     248             :         /*
     249             :          * Add a parameter to the current rpc.  
     250             :          * 
     251             :          * Traverse the dbproc's procedure list to find the current rpc, 
     252             :          * then traverse the parameter linked list until its end,
     253             :          * then tack on our parameter's address.  
     254             :          */
     255         340 :         for (rpc = dbproc->rpc; rpc->next != NULL; rpc = rpc->next)    /* find "current" procedure */
     256           0 :                 continue;
     257         618 :         for (pparam = &rpc->param_list; *pparam != NULL; pparam = &(*pparam)->next)
     258         278 :                 continue;
     259             : 
     260             :         /* pparam now contains the address of the end of the rpc's parameter list */
     261             : 
     262         340 :         *pparam = param;        /* add to the end of the list */
     263             : 
     264         340 :         tdsdump_log(TDS_DBG_INFO1, "dbrpcparam() added parameter \"%s\"\n", (paramname) ? paramname : "");
     265             : 
     266             :         return SUCCEED;
     267             : }
     268             : 
     269             : /**
     270             :  * \ingroup dblib_rpc
     271             :  * \brief Execute the procedure and free associated memory
     272             :  *
     273             :  * \param dbproc contains all information needed by db-lib to manage communications with the server.
     274             :  * \retval SUCCEED normal.
     275             :  * \retval FAIL on error
     276             :  * \sa dbrpcinit(), dbrpcparam()
     277             :  */
     278             : RETCODE
     279         258 : dbrpcsend(DBPROCESS * dbproc)
     280             : {
     281             :         DBREMOTE_PROC *rpc;
     282             : 
     283         258 :         tdsdump_log(TDS_DBG_FUNC, "dbrpcsend(%p)\n", dbproc);
     284         258 :         CHECK_CONN(FAIL);
     285         258 :         CHECK_PARAMETER(dbproc->rpc, SYBERPCS, FAIL);        /* dbrpcinit should allocate pointer */
     286             : 
     287             :         /* sanity */
     288         258 :         if (dbproc->rpc->name == NULL) {  /* can't be ready without a name */
     289           0 :                 tdsdump_log(TDS_DBG_INFO1, "returning FAIL: name is NULL\n");
     290             :                 return FAIL;
     291             :         }
     292             : 
     293         258 :         dbproc->dbresults_state = _DB_RES_INIT;
     294             : 
     295         516 :         for (rpc = dbproc->rpc; rpc != NULL; rpc = rpc->next) {
     296             :                 TDSRET erc;
     297         258 :                 TDSPARAMINFO *pparam_info = NULL;
     298             : 
     299             :                 /*
     300             :                  * liam@inodes.org: allow stored procedures to have no paramaters 
     301             :                  */
     302         258 :                 if (rpc->param_list != NULL) {
     303         258 :                         pparam_info = param_info_alloc(dbproc->tds_socket, rpc);
     304         258 :                         if (!pparam_info)
     305             :                                 return FAIL;
     306             :                 }
     307         258 :                 erc = tds_submit_rpc(dbproc->tds_socket, dbproc->rpc->name, pparam_info, NULL);
     308         258 :                 tds_free_param_results(pparam_info);
     309         258 :                 if (TDS_FAILED(erc)) {
     310           0 :                         tdsdump_log(TDS_DBG_INFO1, "returning FAIL: tds_submit_rpc() failed\n");
     311             :                         return FAIL;
     312             :                 }
     313             :         }
     314             : 
     315             :         /* free up the memory */
     316         258 :         rpc_clear(dbproc->rpc);
     317         258 :         dbproc->rpc = NULL;
     318             : 
     319         258 :         tdsdump_log(TDS_DBG_FUNC, "dbrpcsend() returning SUCCEED\n");
     320             : 
     321             :         return SUCCEED;
     322             : }
     323             : 
     324             : /** 
     325             :  * Tell the TDSPARAMINFO structure where the data go.  This is a kind of "bind" operation.
     326             :  */
     327             : static const unsigned char *
     328         340 : param_row_alloc(TDSPARAMINFO * params, TDSCOLUMN * curcol, int param_num, void *value, int size)
     329             : {
     330         340 :         const void *row = tds_alloc_param_data(curcol);
     331         340 :         tdsdump_log(TDS_DBG_INFO1, "parameter size = %d, data = %p, row_size = %d\n",
     332             :                                    size, curcol->column_data, params->row_size);
     333         340 :         if (!row)
     334             :                 return NULL;
     335         340 :         if (size > 0 && value) {
     336         276 :                 tdsdump_log(TDS_DBG_FUNC, "copying %d bytes of data to parameter #%d\n", size, param_num);
     337         276 :                 if (!is_blob_col(curcol)) {
     338         276 :                         if (is_numeric_type(curcol->column_type))
     339         240 :                                 memset(curcol->column_data, 0, sizeof(TDS_NUMERIC));
     340         276 :                         memcpy(curcol->column_data, value, size);
     341             :                 } else {
     342           0 :                         TDSBLOB *blob = (TDSBLOB *) curcol->column_data;
     343           0 :                         blob->textvalue = tds_new(TDS_CHAR, size);
     344           0 :                         tdsdump_log(TDS_DBG_FUNC, "blob parameter supported, size %d textvalue pointer is %p\n", 
     345             :                                                   size, blob->textvalue);
     346           0 :                         if (!blob->textvalue)
     347             :                                 return NULL;
     348           0 :                         memcpy(blob->textvalue, value, size);
     349             :                 }
     350             :         } else {
     351          64 :                 tdsdump_log(TDS_DBG_FUNC, "setting parameter #%d to NULL\n", param_num);
     352          64 :                 curcol->column_cur_size = -1;
     353             :         }
     354             : 
     355             :         return (const unsigned char*) row;
     356             : }
     357             : 
     358             : /** 
     359             :  * Allocate memory and copy the rpc information into a TDSPARAMINFO structure.
     360             :  */
     361             : static TDSPARAMINFO *
     362         258 : param_info_alloc(TDSSOCKET * tds, DBREMOTE_PROC * rpc)
     363             : {
     364             :         int i;
     365             :         DBREMOTE_PROC_PARAM *p;
     366             :         TDSCOLUMN *pcol;
     367         258 :         TDSPARAMINFO *params = NULL, *new_params;
     368             :         BYTE *temp_value;
     369             :         int  temp_datalen;
     370             :         TDS_SERVER_TYPE temp_type;
     371             :         int  param_is_null;
     372             : 
     373             :         /* sanity */
     374         258 :         if (rpc == NULL)
     375             :                 return NULL;
     376             : 
     377             :         /* see v 1.10 2002/11/23 for first broken attempt */
     378             : 
     379         598 :         for (i = 0, p = rpc->param_list; p != NULL; p = p->next, i++) {
     380             :                 const unsigned char *prow;
     381             : 
     382         340 :                 if (!(new_params = tds_alloc_param_result(params))) {
     383           0 :                         tds_free_param_results(params);
     384           0 :                         tdsdump_log(TDS_DBG_ERROR, "out of rpc memory!");
     385             :                         return NULL;
     386             :                 }
     387         340 :                 params = new_params;
     388             : 
     389             :                 /*
     390             :                  * Determine whether an input parameter is NULL or not.
     391             :                  */
     392         340 :                 param_is_null = 0;
     393         340 :                 temp_type = p->type;
     394         340 :                 temp_value = p->value;
     395         340 :                 temp_datalen = p->datalen;
     396             : 
     397         340 :                 if (p->datalen == 0)
     398          64 :                         param_is_null = 1; 
     399             : 
     400         340 :                 tdsdump_log(TDS_DBG_INFO1, "parm_info_alloc(): parameter null-ness = %d\n", param_is_null);
     401             : 
     402         340 :                 pcol = params->columns[i];
     403             : 
     404         340 :                 if (temp_value && is_numeric_type(temp_type)) {
     405         240 :                         DBDECIMAL *dec = (DBDECIMAL*) temp_value;
     406         240 :                         pcol->column_prec = dec->precision;
     407         240 :                         pcol->column_scale = dec->scale;
     408         240 :                         if (dec->precision > 0 && dec->precision <= MAXPRECISION)
     409         240 :                                 temp_datalen = tds_numeric_bytes_per_prec[dec->precision] + 2;
     410             :                 }
     411         340 :                 if (param_is_null || (p->status & DBRPCRETURN)) {
     412          74 :                         if (param_is_null) {
     413             :                                 temp_datalen = 0;
     414             :                                 temp_value = NULL;
     415          10 :                         } else if (is_fixed_type(temp_type)) {
     416          10 :                                 temp_datalen = tds_get_size_by_type(temp_type);
     417             :                         }
     418          74 :                         temp_type = tds_get_null_type(temp_type);
     419         266 :                 } else if (is_fixed_type(temp_type)) {
     420           0 :                         temp_datalen = tds_get_size_by_type(temp_type);
     421             :                 }
     422             : 
     423             :                 /* meta data */
     424         340 :                 if (p->name)
     425         316 :                         if (!tds_dstr_copy(&pcol->column_name, p->name)) {
     426           0 :                                 tds_free_param_results(params);
     427           0 :                                 tdsdump_log(TDS_DBG_ERROR, "out of rpc memory!");
     428             :                                 return NULL;
     429             :                         }
     430             : 
     431         340 :                 tds_set_param_type(tds->conn, pcol, temp_type);
     432             : 
     433         340 :                 if (p->maxlen > 0)
     434          40 :                         pcol->column_size = p->maxlen;
     435             :                 else {
     436         300 :                         if (is_fixed_type(p->type)) {
     437          24 :                                 pcol->column_size = tds_get_size_by_type(p->type);
     438             :                         } else {
     439         276 :                                 pcol->column_size = p->datalen;
     440             :                         }
     441             :                 }
     442         340 :                 if (p->type == XSYBNVARCHAR)
     443          40 :                         pcol->column_size *= 2;
     444         340 :                 pcol->on_server.column_size = pcol->column_size;
     445             : 
     446         340 :                 pcol->column_output = p->status;
     447         340 :                 pcol->column_cur_size = temp_datalen;
     448             : 
     449         340 :                 prow = param_row_alloc(params, pcol, i, temp_value, temp_datalen);
     450             : 
     451         340 :                 if (!prow) {
     452           0 :                         tds_free_param_results(params);
     453           0 :                         tdsdump_log(TDS_DBG_ERROR, "out of memory for rpc row!");
     454             :                         return NULL;
     455             :                 }
     456             : 
     457             :         }
     458             : 
     459             :         return params;
     460             : 
     461             : }
     462             : 
     463             : /**
     464             :  * erase the procedure list
     465             :  */
     466             : static void
     467         258 : rpc_clear(DBREMOTE_PROC * rpc)
     468             : {
     469             :         DBREMOTE_PROC * next;
     470             : 
     471         774 :         while (rpc) {
     472         258 :                 next = rpc->next;
     473         258 :                 param_clear(rpc->param_list);
     474         258 :                 free(rpc->name);
     475         258 :                 free(rpc);
     476         258 :                 rpc = next;
     477             :         }
     478         258 : }
     479             : 
     480             : /**
     481             :  * erase the parameter list
     482             :  */
     483             : static void
     484         258 : param_clear(DBREMOTE_PROC_PARAM * pparam)
     485             : {
     486             :         DBREMOTE_PROC_PARAM * next;
     487             : 
     488         856 :         while (pparam) {
     489         340 :                 next = pparam->next;
     490         340 :                 free(pparam->name);
     491             :                 /* free self */
     492         340 :                 free(pparam);
     493         340 :                 pparam = next;
     494             :         }
     495         258 : }

Generated by: LCOV version 1.13