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

Generated on Mon Sep 12 15:26:45 2011 for Veil by  doxygen 1.5.6