veil_query.c

Go to the documentation of this file.
00001 /**
00002  * @file   veil_query.c
00003  * \code
00004  *     Author:       Marc Munro
00005  *     Copyright (c) 2005, 2006 Marc Munro
00006  *     License:      BSD
00007  * $Id: veil_query.c,v 1.5 2007/07/31 22:18:27 bloodnok Exp $
00008  * \endcode
00009  * @brief  
00010  * Functions to simplify SPI-based queries.  These are way more
00011  * sophisticated than veil really needs but are nice and generic.
00012  * 
00013  */
00014 
00015 
00016 #include <stdio.h>
00017 #include "postgres.h"
00018 #include "executor/spi.h"
00019 #include "veil_funcs.h"
00020 #include "veil_version.h"
00021 
00022 /**
00023  * A Fetch_fn is a function that processes records, one at a time,
00024  * returned from a query.
00025  */
00026 typedef bool (Fetch_fn)(HeapTuple, TupleDesc, void *);
00027 
00028 
00029 
00030 /**
00031  * The number of records to fetch in one go from the query executor.
00032  */
00033 #define FETCH_SIZE    20
00034 
00035 
00036 /**
00037  * Counter to assess depth of recursive spi calls, so that we can
00038  * sensibly and safely use spi_push and spi_pop when appropriate.
00039  */
00040 static int4 query_depth = 0;
00041 
00042 /**
00043  * State variable used to assess whther query_depth may have been left
00044  * in an invalid state following an error being raised.
00045  */
00046 static TransactionId connection_xid = 0;
00047 
00048 /***
00049  * If already connected in this session, push the current connection,
00050  * and get a new one.
00051  * We are already connected, if:
00052  * - are within a query
00053  * - and the current transaction id matches the saved transaction id
00054  */
00055 extern int
00056 vl_spi_connect()
00057 {
00058     TransactionId xid = GetCurrentTransactionId();
00059 
00060     if (query_depth > 0) {
00061         if (xid == connection_xid) {
00062             //elog(WARNING, "spi_connect, recursing");
00063             SPI_push();
00064         }
00065         else {
00066             // The previous transaction must have aborted without
00067             // resetting query_depth
00068             //elog(WARNING, "resetting query depth");
00069             query_depth = 0;
00070         }
00071     }
00072 
00073     connection_xid = xid;
00074     //elog(WARNING, "spi_connect");
00075     return SPI_connect();
00076 }
00077 
00078 /***
00079  * Reciprocal function for vl_spi_connect()
00080  */
00081 extern int
00082 vl_spi_finish()
00083 {
00084     int spi_result = SPI_finish();
00085     //elog(WARNING, "spi_finish");
00086     if (query_depth > 0) {
00087         SPI_pop();
00088         //elog(WARNING, "spi_finish, recursing");
00089     }
00090 
00091     return spi_result;
00092 }
00093 
00094 /** 
00095  * Prepare a query for query().  This creates and executes a plan.  The
00096  * caller must have established SPI_connect.  It is assumed that no
00097  * parameters to the query will be null.
00098  * \param qry The text of the SQL query to be performed.
00099  * \param nargs The number of input parameters ($1, $2, etc) to the query
00100  * \param argtypes Pointer to an array containing the OIDs of the data
00101  * \param args Actual parameters
00102  * types of the parameters 
00103  * \param read_only Whether the query should be read-only or not
00104  * \param saved_plan Adress of void pointer into which the query plan
00105  * will be saved.  Passing the same void pointer on a subsequent call
00106  * will cause the saved query plan to be re-used.
00107  */
00108 static void
00109 prepare_query(const char *qry,
00110               int nargs,
00111               Oid *argtypes,
00112               Datum *args,
00113               bool read_only,
00114               void **saved_plan)
00115 {
00116     void   *plan;
00117     int     prep_ok;
00118     int     exec_result;
00119     
00120     if (saved_plan && *saved_plan) {
00121         // A previously prepared plan is available, so use it
00122         plan = *saved_plan;
00123     }
00124     else {
00125         if (!(plan = SPI_prepare(qry, nargs, argtypes))) {
00126             ereport(ERROR,
00127                     (errcode(ERRCODE_INTERNAL_ERROR),
00128                      errmsg("prepare_query fails"),
00129                      errdetail("SPI_prepare('%s') returns NULL "
00130                                "(SPI_result = %d)", 
00131                                qry, SPI_result)));
00132         }
00133 
00134         if (saved_plan) {
00135             // We have somewhere to put the saved plan, so save it.
00136             *saved_plan = SPI_saveplan(plan);
00137         }
00138     }
00139     
00140     exec_result = SPI_execute_plan(plan, args, NULL, read_only, 0);
00141     if (exec_result < 0) {
00142         ereport(ERROR,
00143                 (errcode(ERRCODE_INTERNAL_ERROR),
00144                  errmsg("prepare_query fails"),
00145                  errdetail("SPI_execute_plan('%s') returns error %d",
00146                            qry, exec_result)));
00147     }
00148 }
00149 
00150 /** 
00151  * Prepare and execute a query.  Query execution consists of a call to
00152  * process_row for each returned record.  Process_row can return a
00153  * single value to the caller of this function through the fn_param
00154  * parameter.  It is the caller's responsibility to establish an SPI
00155  * connection with SPI_connect.  It is assumed that no parameters to
00156  * the query, and no results will be null.
00157  * \param qry The text of the SQL query to be performed.
00158  * \param nargs The number of input parameters ($1, $2, etc) to the query
00159  * \param argtypes Pointer to an array containing the OIDs of the data
00160  * \param args Actual parameters
00161  * types of the parameters 
00162  * \param read_only Whether the query should be read-only or not
00163  * \param saved_plan Adress of void pointer into which the query plan
00164  * will be saved.  Passing the same void pointer on a subsequent call
00165  * will cause the saved query plan to be re-used.
00166  * \param process_row  The ::Fetch_fn function to be called for each
00167  * fetched row to process it.  If this is null, we simply count the row,
00168  * doing no processing on the tuples returned.
00169  * \param fn_param  An optional parameter to the process_row function.
00170  * This may be used to return a value to the caller.
00171  * \return The total number of records fetched and processed by
00172  * process_row.
00173  */
00174 static int
00175 query(const char *qry,
00176       int nargs,
00177       Oid *argtypes,
00178       Datum *args,
00179       bool  read_only,
00180       void **saved_plan,
00181       Fetch_fn process_row,
00182       void *fn_param)
00183 {
00184     int    row;
00185     int    fetched = 0;
00186     int    exec_result;
00187 
00188     query_depth++;
00189     // elog(WARNING, "starting query");
00190     prepare_query(qry, nargs, argtypes, args, read_only, saved_plan);
00191     
00192     for(row = 0; row < SPI_processed; row++) {
00193         fetched++;
00194         // Process a row using the processor function
00195         if (!process_row(SPI_tuptable->vals[row], 
00196                          SPI_tuptable->tupdesc,
00197                          fn_param)) 
00198         {
00199             break;
00200         }
00201     }
00202     // elog(WARNING, "exitting query");
00203     query_depth--;
00204     return fetched;
00205 }
00206 
00207 /** 
00208  * ::Fetch_fn function for processing a single row of a single integer for 
00209  * ::query.
00210  * \param tuple The row to be processed
00211  * \param tupdesc Descriptor for the types of the fields in the tuple.
00212  * \param p_result Pointer to an int4 variable into which the value
00213  * returned from the query will be placed.
00214  * \return false.  This causes ::query to terminate after processing a
00215  * single row.
00216  */
00217 static bool
00218 fetch_one_int(HeapTuple tuple, TupleDesc tupdesc, void *p_result)
00219 {
00220     int4 col = DatumGetInt32(SPI_getbinval(tuple, tupdesc, 1, false));
00221     *((int4 *) p_result) = col;
00222     
00223     return false;
00224 }
00225 
00226 /** 
00227  * ::Fetch_fn function for processing a single row of a single integer for 
00228  * ::query.
00229  * \param tuple The row to be processed
00230  * \param tupdesc Descriptor for the types of the fields in the tuple.
00231  * \param p_result Pointer to an int4 variable into which the value
00232  * returned from the query will be placed.
00233  * \return false.  This causes ::query to terminate after processing a
00234  * single row.
00235  */
00236 static bool
00237 fetch_one_bool(HeapTuple tuple, TupleDesc tupdesc, void *p_result)
00238 {
00239     bool col = DatumGetBool(SPI_getbinval(tuple, tupdesc, 1, false));
00240     *((bool *) p_result) = col;
00241     
00242     return false;
00243 }
00244 
00245 /** 
00246  * ::Fetch_fn function for processing a single row of a single integer for 
00247  * ::query.
00248  * \param tuple The row to be processed
00249  * \param tupdesc Descriptor for the types of the fields in the tuple.
00250  * \param p_result Pointer to an int4 variable into which the value
00251  * returned from the query will be placed.
00252  * \return false.  This causes ::query to terminate after processing a
00253  * single row.
00254  */
00255 static bool
00256 fetch_one_str(HeapTuple tuple, TupleDesc tupdesc, void *p_result)
00257 {
00258     char *col = SPI_getvalue(tuple, tupdesc, 1);
00259     char **p_str = (char **) p_result;
00260     *p_str = col;
00261     
00262     return false;
00263 }
00264 
00265 /** 
00266  * Executes a query that returns a single int4 value.
00267  * 
00268  * @param qry The text of the query to be performed.
00269  * @param result Variable into which the result of the query will be placed.
00270  * 
00271  * @return true if the query returned a record, false otherwise.
00272  */
00273 static bool
00274 int_from_query(const char *qry,
00275                int4 *result)
00276 {
00277     int     rows;
00278     Oid     argtypes[0];
00279     Datum   args[0];
00280     rows = query(qry, 0, argtypes, args, false, NULL, 
00281                  fetch_one_int, (void *)result);
00282     return (rows > 0);
00283 }
00284 
00285 /** 
00286  * Executes a query that returns a single bool value.
00287  * 
00288  * @param qry The text of the query to be performed.
00289  * @param result Variable into which the result of the query will be placed.
00290  * 
00291  * @return true if the query returned a record, false otherwise.
00292  */
00293 bool
00294 vl_bool_from_query(const char *qry,
00295                 bool *result)
00296 {
00297     int     rows;
00298     Oid     argtypes[0];
00299     Datum   args[0];
00300     rows = query(qry, 0, argtypes, args, false, NULL, 
00301                  fetch_one_bool, (void *)result);
00302     return (rows > 0);
00303 }
00304 
00305 /** 
00306  * Executes a query by oid, that returns a single string value.
00307  * 
00308  * @param qry The text of the query to be performed.
00309  * @param param The oid of the row to be fetched.
00310  * @param result Variable into which the result of the query will be placed.
00311  * 
00312  * @return true if the query returned a record, false otherwise.
00313  */
00314 static bool
00315 str_from_oid_query(const char *qry,
00316                    const Oid param,
00317                    char *result)
00318 {
00319     int     rows;
00320     Oid     argtypes[1] = {OIDOID};
00321     Datum   args[1];
00322     
00323     args[0] = ObjectIdGetDatum(param);
00324     rows = query(qry, 1, argtypes, args, false, NULL, 
00325                  fetch_one_str, (void *)result);
00326     return (rows > 0);
00327 }
00328 
00329 /** 
00330  * Raise an error if type is not as expected.
00331  * 
00332  * @param oid Type oid of value.
00333  * @param expected_oid Type oid of value that we are expecting.
00334  * @param msg Supplementary text for error message if the types do not match.
00335  */
00336 static void
00337 check_type(Oid oid,
00338            Oid expected_oid,
00339            const char *msg)
00340 {
00341     if (oid != expected_oid) {
00342         char *expected;
00343         char *actual;
00344         bool  ok;
00345         if (str_from_oid_query("select typname from pg_type where oid = $1",
00346                                expected_oid, (void *) &expected) &&
00347             str_from_oid_query("select typname from pg_type where oid = $1",
00348                                oid, (void *) &actual))
00349         {
00350             ereport(ERROR,
00351                     (errcode(ERRCODE_INTERNAL_ERROR),
00352                      errmsg("Type mismatch"),
00353                      errdetail("Expected %s, got %s): %s", 
00354                                expected, actual, msg)));
00355         }
00356         else {
00357             // Failed to read db, so put out a simpler message
00358             ereport(ERROR,
00359                     (errcode(ERRCODE_INTERNAL_ERROR),
00360                      errmsg("Type mismatch"),
00361                      errdetail("Incorrect type: %s", msg)));
00362         }
00363     }
00364 }
00365            
00366 /** 
00367  * Determine whether the given oid represents an existing database or not.
00368  * 
00369  * @param dbid Oid of the database in which we are interested.
00370  * 
00371  * @result True if the database exists.
00372  */
00373 
00374 extern bool
00375 vl_db_exists(Oid db_id)
00376 {
00377     char dbname[NAMEDATALEN + 1];
00378 
00379     return str_from_oid_query("select datname from pg_database where oid = $1",
00380                               db_id, dbname);
00381 }
00382 
00383 

Generated on Tue Jul 31 15:35:52 2007 for Veil by  doxygen 1.5.2