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

Generated by: LCOV version 1.13