Veil
veil_query.c
Go to the documentation of this file.
1 /**
2  * @file veil_query.c
3  * \code
4  * Author: Marc Munro
5  * Copyright (c) 2005 - 2011 Marc Munro
6  * License: BSD
7  *
8  * \endcode
9  * @brief
10  * Functions to simplify SPI-based queries. These are way more
11  * sophisticated than veil really needs but are nice and generic.
12  *
13  */
14 
15 
16 #include <stdio.h>
17 #include "postgres.h"
18 #include "catalog/pg_type.h"
19 #include "executor/spi.h"
20 #include "veil_version.h"
21 #include "access/xact.h"
22 #include "veil_funcs.h"
23 
24 /**
25  * A Fetch_fn is a function that processes records, one at a time,
26  * returned from a query.
27  */
28 typedef bool (Fetch_fn)(HeapTuple, TupleDesc, void *);
29 
30 
31 
32 /**
33  * The number of records to fetch in one go from the query executor.
34  */
35 #define FETCH_SIZE 20
36 
37 
38 /**
39  * If already connected in this session, push the current connection,
40  * and get a new one.
41  * We are already connected, if:
42  * - are within a query
43  * - and the current transaction id matches the saved transaction id
44  */
45 int
46 vl_spi_connect(bool *p_pushed)
47 {
48  int result = SPI_connect();
49  if (result == SPI_ERROR_CONNECT) {
50  SPI_push();
51  *p_pushed = TRUE;
52  return SPI_connect();
53  }
54  *p_pushed = FALSE;
55  return result;
56 }
57 
58 /**
59  * Reciprocal function for vl_spi_connect()
60  */
61 int
62 vl_spi_finish(bool pushed)
63 {
64  int spi_result = SPI_finish();
65  if (pushed) {
66  SPI_pop();
67  }
68  return spi_result;
69 }
70 
71 /**
72  * Prepare a query for query(). This creates and executes a plan. The
73  * caller must have established SPI_connect. It is assumed that no
74  * parameters to the query will be null.
75  * \param qry The text of the SQL query to be performed.
76  * \param nargs The number of input parameters ($1, $2, etc) to the query
77  * \param argtypes Pointer to an array containing the OIDs of the data
78  * \param args Actual parameters
79  * types of the parameters
80  * \param read_only Whether the query should be read-only or not
81  * \param saved_plan Adress of void pointer into which the query plan
82  * will be saved. Passing the same void pointer on a subsequent call
83  * will cause the saved query plan to be re-used.
84  */
85 static void
86 prepare_query(const char *qry,
87  int nargs,
88  Oid *argtypes,
89  Datum *args,
90  bool read_only,
91  void **saved_plan)
92 {
93  void *plan;
94  int exec_result;
95 
96  if (saved_plan && *saved_plan) {
97  /* A previously prepared plan is available, so use it */
98  plan = *saved_plan;
99  }
100  else {
101  if (!(plan = SPI_prepare(qry, nargs, argtypes))) {
102  ereport(ERROR,
103  (errcode(ERRCODE_INTERNAL_ERROR),
104  errmsg("prepare_query fails"),
105  errdetail("SPI_prepare('%s') returns NULL "
106  "(SPI_result = %d)",
107  qry, SPI_result)));
108  }
109 
110  if (saved_plan) {
111  /* We have somewhere to put the saved plan, so save it. */
112  *saved_plan = SPI_saveplan(plan);
113  }
114  }
115 
116  exec_result = SPI_execute_plan(plan, args, NULL, read_only, 0);
117  if (exec_result < 0) {
118  ereport(ERROR,
119  (errcode(ERRCODE_INTERNAL_ERROR),
120  errmsg("prepare_query fails"),
121  errdetail("SPI_execute_plan('%s') returns error %d",
122  qry, exec_result)));
123  }
124 }
125 
126 /**
127  * Prepare and execute a query. Query execution consists of a call to
128  * process_row for each returned record. Process_row can return a
129  * single value to the caller of this function through the fn_param
130  * parameter. It is the caller's responsibility to establish an SPI
131  * connection with SPI_connect. It is assumed that no parameters to
132  * the query, and no results will be null.
133  * \param qry The text of the SQL query to be performed.
134  * \param nargs The number of input parameters ($1, $2, etc) to the query
135  * \param argtypes Pointer to an array containing the OIDs of the data
136  * \param args Actual parameters
137  * types of the parameters
138  * \param read_only Whether the query should be read-only or not
139  * \param saved_plan Adress of void pointer into which the query plan
140  * will be saved. Passing the same void pointer on a subsequent call
141  * will cause the saved query plan to be re-used.
142  * \param process_row The ::Fetch_fn function to be called for each
143  * fetched row to process it. If this is null, we simply count the row,
144  * doing no processing on the tuples returned.
145  * \param fn_param An optional parameter to the process_row function.
146  * This may be used to return a value to the caller.
147  * \return The total number of records fetched and processed by
148  * process_row.
149  */
150 static int
151 query(const char *qry,
152  int nargs,
153  Oid *argtypes,
154  Datum *args,
155  bool read_only,
156  void **saved_plan,
157  Fetch_fn process_row,
158  void *fn_param)
159 {
160  int row;
161  int fetched;
162  int processed = 0;
163  bool cntinue;
164  SPITupleTable *tuptab;
165 
166  prepare_query(qry, nargs, argtypes, args, read_only, saved_plan);
167  fetched = SPI_processed;
168  tuptab = SPI_tuptable;
169  for(row = 0; row < fetched; row++) {
170  processed++;
171  /* Process a row using the processor function */
172  cntinue = process_row(tuptab->vals[row],
173  tuptab->tupdesc,
174  fn_param);
175  if (!cntinue) {
176  break;
177  }
178  }
179  return processed;
180 }
181 
182 
183 /**
184  * ::Fetch_fn function for processing a single row of a single integer for
185  * ::query.
186  * \param tuple The row to be processed
187  * \param tupdesc Descriptor for the types of the fields in the tuple.
188  * \param p_result Pointer to an int4 variable into which the value
189  * returned from the query will be placed.
190  * \return false. This causes ::query to terminate after processing a
191  * single row.
192  */
193 static bool
194 fetch_one_bool(HeapTuple tuple, TupleDesc tupdesc, void *p_result)
195 {
196  static bool ignore_this = false;
197  bool col = DatumGetBool(SPI_getbinval(tuple, tupdesc, 1, &ignore_this));
198  *((bool *) p_result) = col;
199 
200  return false;
201 }
202 
203 /**
204  * ::Fetch_fn function for processing a single row of a single integer for
205  * ::query.
206  * \param tuple The row to be processed
207  * \param tupdesc Descriptor for the types of the fields in the tuple.
208  * \param p_result Pointer to an int4 variable into which the value
209  * returned from the query will be placed.
210  * \return false. This causes ::query to terminate after processing a
211  * single row.
212  */
213 static bool
214 fetch_one_str(HeapTuple tuple, TupleDesc tupdesc, void *p_result)
215 {
216  char *col = SPI_getvalue(tuple, tupdesc, 1);
217  char **p_str = (char **) p_result;
218  *p_str = col;
219 
220  return false;
221 }
222 
223 /**
224  * Executes a query that returns a single bool value.
225  *
226  * @param qry The text of the query to be performed.
227  * @param result Variable into which the result of the query will be placed.
228  *
229  * @return true if the query returned a record, false otherwise.
230  */
231 bool
232 vl_bool_from_query(const char *qry,
233  bool *result)
234 {
235  int rows;
236  Oid argtypes[0];
237  Datum args[0];
238  rows = query(qry, 0, argtypes, args, false, NULL,
239  fetch_one_bool, (void *)result);
240  return (rows > 0);
241 }
242 
243 /**
244  * Executes a query by oid, that returns a single string value.
245  *
246  * @param qry The text of the query to be performed.
247  * @param param The oid of the row to be fetched.
248  * @param result Variable into which the result of the query will be placed.
249  *
250  * @return true if the query returned a record, false otherwise.
251  */
252 static bool
253 str_from_oid_query(const char *qry,
254  const Oid param,
255  char *result)
256 {
257  int rows;
258  Oid argtypes[1] = {OIDOID};
259  Datum args[1];
260 
261  args[0] = ObjectIdGetDatum(param);
262  rows = query(qry, 1, argtypes, args, false, NULL,
263  fetch_one_str, (void *)result);
264  return (rows > 0);
265 }
266 
267 /**
268  * Determine whether the given oid represents an existing database or not.
269  *
270  * @param db_id Oid of the database in which we are interested.
271  *
272  * @result True if the database exists.
273  */
274 extern bool
275 vl_db_exists(Oid db_id)
276 {
277  char dbname[NAMEDATALEN + 1];
278 
279  return str_from_oid_query("select datname from pg_database where oid = $1",
280  db_id, dbname);
281 }
282 
283 
284 /**
285  * ::Fetch_fn function for executing registered veil_init() functions for
286  * ::query.
287  * \param tuple The row to be processed
288  * \param tupdesc Descriptor for the types of the fields in the tuple.
289  * \param p_param Pointer to a boolean value which is the value of the
290  * argument to the init function being called.
291  * \return true. This allows ::query to process further rows.
292  */
293 static bool
294 exec_init_fn(HeapTuple tuple, TupleDesc tupdesc, void *p_param)
295 {
296  char *col = SPI_getvalue(tuple, tupdesc, 1);
297  char *qry = palloc(strlen(col) + 15);
298  bool pushed;
299  bool result;
300  int ok;
301 
302  (void) sprintf(qry, "select %s(%s)", col,
303  *((bool *) p_param)? "true": "false");
304 
305  ok = vl_spi_connect(&pushed);
306  if (ok != SPI_OK_CONNECT) {
307  ereport(ERROR,
308  (errcode(ERRCODE_INTERNAL_ERROR),
309  errmsg("failed to execute exec_init_fn() (1)"),
310  errdetail("SPI_connect() failed, returning %d.", ok)));
311  }
312 
313  (void) vl_bool_from_query(qry, &result);
314 
315 
316  ok = vl_spi_finish(pushed);
317  if (ok != SPI_OK_FINISH) {
318  ereport(ERROR,
319  (errcode(ERRCODE_INTERNAL_ERROR),
320  errmsg("failed to execute exec_init_fn() (2)"),
321  errdetail("SPI_finish() failed, returning %d.", ok)));
322  }
323 
324  return true;
325 }
326 
327 
328 /**
329  * Identify any registered init_functions and execute them.
330  *
331  * @param param The boolean parameter to be passed to each init_function.
332  *
333  * @result The number of init_functions executed.
334  */
335 int
336 vl_call_init_fns(bool param)
337 {
338  Oid argtypes[0];
339  Datum args[0];
340  char *qry = "select fn_name from veil.veil_init_fns order by priority";
341  bool pushed;
342  int rows;
343  int ok;
344 
345  ok = vl_spi_connect(&pushed);
346  if (ok != SPI_OK_CONNECT) {
347  ereport(ERROR,
348  (errcode(ERRCODE_INTERNAL_ERROR),
349  errmsg("failed to execute vl_call_init_fns() (1)"),
350  errdetail("SPI_connect() failed, returning %d.", ok)));
351  }
352 
353  rows = query(qry, 0, argtypes, args, false, NULL,
354  exec_init_fn, (void *) &param);
355 
356  ok = vl_spi_finish(pushed);
357  if (ok != SPI_OK_FINISH) {
358  ereport(ERROR,
359  (errcode(ERRCODE_INTERNAL_ERROR),
360  errmsg("failed to execute vl_call_init_fns() (2)"),
361  errdetail("SPI_finish() failed, returning %d.", ok)));
362  }
363  return rows;
364 }
static bool fetch_one_bool(HeapTuple tuple, TupleDesc tupdesc, void *p_result)
Fetch_fn function for processing a single row of a single integer for query.
Definition: veil_query.c:194
bool() Fetch_fn(HeapTuple, TupleDesc, void *)
A Fetch_fn is a function that processes records, one at a time, returned from a query.
Definition: veil_query.c:28
static bool fetch_one_str(HeapTuple tuple, TupleDesc tupdesc, void *p_result)
Fetch_fn function for processing a single row of a single integer for query.
Definition: veil_query.c:214
int vl_spi_finish(bool pushed)
Reciprocal function for vl_spi_connect()
Definition: veil_query.c:62
static int query(const char *qry, int nargs, Oid *argtypes, Datum *args, bool read_only, void **saved_plan, Fetch_fn process_row, void *fn_param)
Prepare and execute a query.
Definition: veil_query.c:151
static void prepare_query(const char *qry, int nargs, Oid *argtypes, Datum *args, bool read_only, void **saved_plan)
Prepare a query for query().
Definition: veil_query.c:86
Provide definitions for all non-local C-callable Veil functions.
Provides version information for veil.
static bool str_from_oid_query(const char *qry, const Oid param, char *result)
Executes a query by oid, that returns a single string value.
Definition: veil_query.c:253
static bool exec_init_fn(HeapTuple tuple, TupleDesc tupdesc, void *p_param)
Fetch_fn function for executing registered veil_init() functions for query.
Definition: veil_query.c:294
bool vl_db_exists(Oid db_id)
Determine whether the given oid represents an existing database or not.
Definition: veil_query.c:275
int vl_call_init_fns(bool param)
Identify any registered init_functions and execute them.
Definition: veil_query.c:336
bool vl_bool_from_query(const char *qry, bool *result)
Executes a query that returns a single bool value.
Definition: veil_query.c:232
int vl_spi_connect(bool *p_pushed)
If already connected in this session, push the current connection, and get a new one.
Definition: veil_query.c:46