src/dbapi/driver/ftds64/freetds/dblib/rpc.c

Go to the documentation of this file.
00001 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
00002  * Copyright (C) 1998, 1999, 2000, 2001  Brian Bruns
00003  * Copyright (C) 2002, 2003, 2004, 2005    James K. Lowden
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Library General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Library General Public
00016  * License along with this library; if not, write to the
00017  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018  * Boston, MA 02111-1307, USA.
00019  */
00020 
00021 #if HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif /* HAVE_CONFIG_H */
00024 
00025 #include <stdio.h>
00026 
00027 #ifdef HAVE_UNISTD_H
00028 #include <unistd.h>
00029 #endif /* HAVE_UNISTD_H */
00030 
00031 #ifdef HAVE_STDLIB_H
00032 #include <stdlib.h>
00033 #endif /* HAVE_STDLIB_H */
00034 
00035 #ifdef HAVE_STRING_H
00036 #include <string.h>
00037 #endif /* HAVE_STRING_H */
00038 
00039 #include <assert.h>
00040 
00041 #include "tds.h"
00042 #include "tdsconvert.h"
00043 #include "sybfront.h"
00044 #include "sybdb.h"
00045 #include "dblib.h"
00046 #include "replacements.h"
00047 
00048 #ifdef DMALLOC
00049 #include <dmalloc.h>
00050 #endif
00051 
00052 TDS_RCSID(var, "$Id: rpc.c 86967 2006-07-31 15:44:10Z ssikorsk $");
00053 
00054 static void rpc_clear(DBREMOTE_PROC * rpc);
00055 static void param_clear(DBREMOTE_PROC_PARAM * pparam);
00056 
00057 static TDSPARAMINFO *param_info_alloc(TDSSOCKET * tds, DBREMOTE_PROC * rpc);
00058 
00059 /**
00060  * \ingroup dblib_rpc
00061  * \brief Initialize a remote procedure call. 
00062  *
00063  * \param dbproc contains all information needed by db-lib to manage communications with the server.
00064  * \param rpcname name of the stored procedure to be run.  
00065  * \param options Only supported option would be DBRPCRECOMPILE, 
00066  * which causes the stored procedure to be recompiled before executing.
00067  * \remark The RPC functions are the only way to get back OUTPUT parameter data with db-lib 
00068  * from modern Microsoft servers.  
00069  * \todo I don't know the value for DBRPCRECOMPILE and have not added it to sybdb.h
00070  * \retval SUCCEED normal.
00071  * \retval FAIL on error
00072  * \sa dbrpcparam(), dbrpcsend()
00073  */
00074 RETCODE
00075 dbrpcinit(DBPROCESS * dbproc, char *rpcname, DBSMALLINT options)
00076 {
00077     DBREMOTE_PROC **rpc;
00078     int dbrpcrecompile = 0;
00079 
00080     /* sanity */
00081     if (dbproc == NULL || rpcname == NULL)
00082         return FAIL;
00083 
00084     if (options & DBRPCRESET) {
00085         rpc_clear(dbproc->rpc);
00086         dbproc->rpc = NULL;
00087         return SUCCEED;
00088     }
00089 
00090     /* any bits we want from the options argument */
00091     dbrpcrecompile = options & DBRPCRECOMPILE;
00092     options &= ~DBRPCRECOMPILE; /* turn that one off, now that we've extracted it */
00093 
00094     /* all other options except DBRPCRECOMPILE are invalid */
00095     if (options) {
00096         /* should show client error message */
00097         return FAIL;
00098     }
00099 
00100     /* to allocate, first find a free node */
00101     for (rpc = &dbproc->rpc; *rpc != NULL; rpc = &(*rpc)->next) {
00102         /* check existing nodes for name match (there shouldn't be one) */
00103         if (!(*rpc)->name)
00104             return FAIL;
00105         if (strcmp((*rpc)->name, rpcname) == 0)
00106             return FAIL /* dbrpcsend should free pointer */ ;
00107     }
00108 
00109     /* rpc now contains the address of the dbproc's first empty (null) DBREMOTE_PROC* */
00110 
00111     /* allocate */
00112     *rpc = (DBREMOTE_PROC *) malloc(sizeof(DBREMOTE_PROC));
00113     if (*rpc == NULL)
00114         return FAIL;
00115     memset(*rpc, 0, sizeof(DBREMOTE_PROC));
00116 
00117     (*rpc)->name = strdup(rpcname);
00118     if ((*rpc)->name == NULL) {
00119         free(*rpc);
00120         *rpc = NULL;
00121         return FAIL;
00122     }
00123 
00124     /* store */
00125     (*rpc)->options = options & DBRPCRECOMPILE;
00126     (*rpc)->param_list = NULL;
00127 
00128     /* completed */
00129     tdsdump_log(TDS_DBG_INFO1, "dbrpcinit() added rpcname \"%s\"\n", rpcname);
00130 
00131     return SUCCEED;
00132 }
00133 
00134 /**
00135  * \ingroup dblib_rpc
00136  * \brief Add a parameter to a remote procedure call.
00137  * Call between dbrpcinit() and dbrpcsend()
00138  * \param dbproc contains all information needed by db-lib to manage communications with the server.
00139  * \param paramname literal name of the parameter, according to the stored procedure (starts with '@').  Optional.  
00140  *        If not used, parameters will be passed in order instead of by name. 
00141  * \param status must be DBRPCRETURN, if this parameter is a return parameter, else 0. 
00142  * \param type datatype of the value parameter e.g., SYBINT4, SYBCHAR.
00143  * \param maxlen Maximum output size of the parameter's value to be returned by the stored procedure, usually the size of your host variable. 
00144  *        Fixed-length datatypes take -1 (NULL or not).  
00145  *        Non-OUTPUT parameters also use -1.  
00146  *     Use 0 to send a NULL value for a variable length datatype.  
00147  * \param datalen For variable-length datatypes, the byte size of the data to be sent, exclusive of any null terminator. 
00148  *        For fixed-length datatypes use -1.  To send a NULL value, use 0.  
00149  * \param value Address of your host variable.  
00150  * \retval SUCCEED normal.
00151  * \retval FAIL on error
00152  * \sa dbrpcinit(), dbrpcsend()
00153  */
00154 RETCODE
00155 dbrpcparam(DBPROCESS * dbproc, char *paramname, BYTE status, int type, DBINT maxlen, DBINT datalen, BYTE * value)
00156 {
00157     char *name = NULL;
00158     DBREMOTE_PROC *rpc;
00159     DBREMOTE_PROC_PARAM **pparam;
00160     DBREMOTE_PROC_PARAM *param;
00161 
00162     /* sanity */
00163     if (dbproc == NULL)
00164         return FAIL;
00165     if (dbproc->rpc == NULL)
00166         return FAIL;
00167 
00168     /* validate datalen parameter */
00169 
00170     if (is_fixed_type(type)) {
00171         if (datalen > 0) 
00172             return FAIL;
00173     } else {
00174         if (datalen < 0) 
00175             return FAIL;
00176     }
00177 
00178     /* validate maxlen parameter */
00179 
00180     if (status & DBRPCRETURN) {
00181         if (is_fixed_type(type)) {
00182             if (maxlen != -1)
00183                 return FAIL;
00184         } else {
00185             if (maxlen == -1)
00186                 maxlen = 255;
00187         }
00188     } else {
00189         /*
00190          * Well, maxlen should be used only for output parameter however it seems
00191          * that ms implementation wrongly require this 0 for NULL variable
00192          * input parameters, so fix it
00193          */
00194         if (maxlen != -1 && maxlen != 0)
00195             return FAIL;
00196         maxlen = -1;
00197     }
00198 
00199     /* TODO add other tests for correctness */
00200 
00201     /* allocate */
00202     param = (DBREMOTE_PROC_PARAM *) malloc(sizeof(DBREMOTE_PROC_PARAM));
00203     if (param == NULL)
00204         return FAIL;
00205 
00206     if (paramname) {
00207         name = strdup(paramname);
00208         if (name == NULL) {
00209             free(param);
00210             return FAIL;
00211         }
00212     }
00213 
00214     /* initialize */
00215     param->next = NULL; /* NULL signifies end of linked list */
00216     param->name = name;
00217     param->status = status;
00218     param->type = type;
00219     param->maxlen = maxlen;
00220     param->datalen = datalen;
00221 
00222     /*
00223      * if datalen = 0, value parameter is ignored       
00224      * this is one way to specify a NULL input parameter 
00225      */
00226 
00227     if (datalen == 0)
00228         param->value = NULL;
00229     else
00230         param->value = value;
00231 
00232     /*
00233      * Add a parameter to the current rpc.  
00234      * 
00235      * Traverse the dbproc's procedure list to find the current rpc, 
00236      * then traverse the parameter linked list until its end,
00237      * then tack on our parameter's address.  
00238      */
00239     for (rpc = dbproc->rpc; rpc->next != NULL; rpc = rpc->next) /* find "current" procedure */
00240         ;
00241     for (pparam = &rpc->param_list; *pparam != NULL; pparam = &(*pparam)->next);
00242 
00243     /* pparam now contains the address of the end of the rpc's parameter list */
00244 
00245     *pparam = param;    /* add to the end of the list */
00246 
00247     tdsdump_log(TDS_DBG_INFO1, "dbrpcparam() added parameter \"%s\"\n", (paramname) ? paramname : "");
00248 
00249     return SUCCEED;
00250 }
00251 
00252 /**
00253  * \ingroup dblib_rpc
00254  * \brief Execute the procedure and free associated memory
00255  *
00256  * \param dbproc contains all information needed by db-lib to manage communications with the server.
00257  * \retval SUCCEED normal.
00258  * \retval FAIL on error
00259  * \sa dbrpcinit(), dbrpcparam()
00260  */
00261 RETCODE
00262 dbrpcsend(DBPROCESS * dbproc)
00263 {
00264     DBREMOTE_PROC *rpc;
00265 
00266     /* sanity */
00267     if (dbproc == NULL || dbproc->rpc == NULL   /* dbrpcinit should allocate pointer */
00268         || dbproc->rpc->name == NULL) { /* can't be ready without a name */
00269         return FAIL;
00270     }
00271 
00272     dbproc->dbresults_state = _DB_RES_INIT;
00273 
00274     /* FIXME do stuff */
00275     tdsdump_log(TDS_DBG_FUNC, "dbrpcsend()\n");
00276 
00277     for (rpc = dbproc->rpc; rpc != NULL; rpc = rpc->next) {
00278         int erc;
00279         TDSPARAMINFO *pparam_info = NULL;
00280 
00281         /*
00282          * liam@inodes.org: allow stored procedures to have no
00283          * paramaters 
00284          */
00285         if (rpc->param_list != NULL) {
00286             pparam_info = param_info_alloc(dbproc->tds_socket, rpc);
00287             if (!pparam_info)
00288                 return FAIL;
00289         }
00290         erc = tds_submit_rpc(dbproc->tds_socket, dbproc->rpc->name, pparam_info);
00291         tds_free_param_results(pparam_info);
00292         if (erc == TDS_FAIL)
00293             return FAIL;
00294     }
00295 
00296     /* free up the memory */
00297     rpc_clear(dbproc->rpc);
00298     dbproc->rpc = NULL;
00299 
00300     return SUCCEED;
00301 }
00302 
00303 /** 
00304  * Tell the TDSPARAMINFO structure where the data go.  This is a kind of "bind" operation.
00305  */
00306 static const unsigned char *
00307 param_row_alloc(TDSPARAMINFO * params, TDSCOLUMN * curcol, int param_num, void *value, int size)
00308 {
00309     const unsigned char *row = tds_alloc_param_row(params, curcol);
00310     tdsdump_log(TDS_DBG_INFO1, "parameter size = %d, offset = %d, row_size = %d\n",
00311                    size, curcol->column_offset, params->row_size);
00312     if (!row)
00313         return NULL;
00314     if (size > 0 && value) {
00315         tdsdump_log(TDS_DBG_FUNC, "copying %d bytes of data to parameter #%d\n", size, param_num);
00316         if (!is_blob_type(curcol->column_type)) {
00317             memcpy(&params->current_row[curcol->column_offset], value, size);
00318         } else {
00319             TDSBLOB *blob = (TDSBLOB *) &params->current_row[curcol->column_offset];
00320             blob->textvalue = malloc(size);
00321             tdsdump_log(TDS_DBG_FUNC, "blob parameter supported, size %d textvalue pointer is %p\n", size, blob->textvalue);
00322             if (!blob->textvalue)
00323                 return NULL;
00324             memcpy(blob->textvalue, value, size);
00325         }
00326     }
00327     else {
00328         tdsdump_log(TDS_DBG_FUNC, "setting parameter #%d to NULL\n", param_num);
00329         curcol->column_cur_size = -1;
00330     }
00331 
00332     return row;
00333 }
00334 
00335 /** 
00336  * Allocate memory and copy the rpc information into a TDSPARAMINFO structure.
00337  */
00338 static TDSPARAMINFO *
00339 param_info_alloc(TDSSOCKET * tds, DBREMOTE_PROC * rpc)
00340 {
00341     int i;
00342     DBREMOTE_PROC_PARAM *p;
00343     TDSCOLUMN *pcol;
00344     TDSPARAMINFO *params = NULL, *new_params;
00345     BYTE *temp_value;
00346     int  temp_datalen;
00347     int  temp_type;
00348     int  param_is_null;
00349 
00350     /* sanity */
00351     if (rpc == NULL)
00352         return NULL;
00353 
00354     /* see v 1.10 2002/11/23 for first broken attempt */
00355 
00356     for (i = 0, p = rpc->param_list; p != NULL; p = p->next, i++) {
00357         const unsigned char *prow;
00358 
00359         if (!(new_params = tds_alloc_param_result(params))) {
00360             tds_free_param_results(params);
00361             tdsdump_log(TDS_DBG_ERROR, "out of rpc memory!");
00362             return NULL;
00363         }
00364         params = new_params;
00365 
00366         /*
00367          * Determine whether an input parameter is NULL
00368          * or not.
00369          */
00370 
00371         param_is_null = 0;
00372         temp_type = p->type;
00373         temp_value = p->value;
00374         temp_datalen = p->datalen;
00375 
00376         if (p->datalen == 0)
00377             param_is_null = 1; 
00378 
00379         tdsdump_log(TDS_DBG_INFO1, "parm_info_alloc(): parameter null-ness = %d\n", param_is_null);
00380 
00381         if (param_is_null || (p->status & DBRPCRETURN)) {
00382             if (param_is_null) {
00383                 temp_datalen = 0;
00384                 temp_value = NULL;
00385             } else if (is_fixed_type(temp_type)) {
00386                 temp_datalen = tds_get_size_by_type(temp_type);
00387             }
00388             temp_type = tds_get_null_type(temp_type);
00389         } else if (is_fixed_type(temp_type)) {
00390             temp_datalen = tds_get_size_by_type(temp_type);
00391         }
00392 
00393         pcol = params->columns[i];
00394 
00395         /* meta data */
00396         if (p->name) {
00397             tds_strlcpy(pcol->column_name, p->name, sizeof(pcol->column_name));
00398             pcol->column_namelen = strlen(pcol->column_name);
00399         }
00400 
00401         tds_set_param_type(tds, pcol, temp_type);
00402 
00403         if (p->maxlen > 0)
00404             pcol->column_size = p->maxlen;
00405         else {
00406             if (is_fixed_type(p->type)) {
00407                 pcol->column_size = tds_get_size_by_type(p->type);
00408             } else {
00409                 pcol->column_size = p->datalen;
00410             }
00411         }
00412         pcol->on_server.column_size = pcol->column_size;
00413 
00414         pcol->column_output = p->status;
00415         pcol->column_cur_size = temp_datalen;
00416 
00417         prow = param_row_alloc(params, pcol, i, temp_value, temp_datalen);
00418 
00419         if (!prow) {
00420             tds_free_param_results(params);
00421             tdsdump_log(TDS_DBG_ERROR, "out of memory for rpc row!");
00422             return NULL;
00423         }
00424 
00425     }
00426 
00427     return params;
00428 
00429 }
00430 
00431 /**
00432  * erase the procedure list
00433  */
00434 static void
00435 rpc_clear(DBREMOTE_PROC * rpc)
00436 {
00437     DBREMOTE_PROC * next;
00438 
00439     while (rpc) {
00440         next = rpc->next;
00441         param_clear(rpc->param_list);
00442         if (rpc->name)
00443             free(rpc->name);
00444         free(rpc);
00445         rpc = next;
00446     }
00447 }
00448 
00449 /**
00450  * erase the parameter list
00451  */
00452 static void
00453 param_clear(DBREMOTE_PROC_PARAM * pparam)
00454 {
00455     DBREMOTE_PROC_PARAM * next;
00456 
00457     while (pparam) {
00458         next = pparam->next;
00459         if (pparam->name)
00460             free(pparam->name);
00461         /* free self */
00462         free(pparam);
00463         pparam = next;
00464     }
00465 }
00466 
00467 

Generated on Sun Dec 6 22:23:11 2009 for NCBI C++ ToolKit by  doxygen 1.4.6
Modified on Mon Dec 07 16:20:59 2009 by modify_doxy.py rev. 173732