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

Generated on Fri Mar 12 08:38:37 2010 for Veil by  doxygen 1.5.6