Here's a patch containing the function SPI_iterate_query_roots(...). I'm optimistic so it's complete with documentation :-)

I think that this function is needed so that PL/<lang> authors like myself have a way to investigate the semantics of a prepared query. For me this is essential since I want to prevent that savepoint related statements are executed using normal SQL so that I can enforce the use of the methods stipulated by the connection interface.

I forsee that this might be of interest for other PL/<lang> authors as well. With this patch in place, it will be possible to do things like this (returning false is rejecting in this case since false terminates the iteration):

static bool rejectTransactionCommand(Query* query, void* clientData)
{
   return !(query->commandType == CMD_UTILITY &&
           IsA(query->utilityStmt, TransactionStmt));
}

and then use that like:

result = !SPI_iterate_query_roots(ePlan, rejectTransactionCommand, NULL);

The patch has no side effects since it's a pure addon.

Kind regards,
Thomas Hallgren

Index: doc/src/sgml/spi.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/spi.sgml,v
retrieving revision 1.35
diff -u -r1.35 spi.sgml
--- doc/src/sgml/spi.sgml       13 Sep 2004 20:05:25 -0000      1.35
+++ doc/src/sgml/spi.sgml       1 Dec 2004 19:05:28 -0000
@@ -1305,6 +1305,82 @@
 
 <!-- *********************************************** -->
 
+<refentry id="spi-spi-iterate-query-roots">
+ <refmeta>
+  <refentrytitle>SPI_iterate_query_roots</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+  <refname>SPI_iterate_query_roots</refname>
+  <refpurpose>investigate the semantics of a query</refpurpose>
+ </refnamediv>
+
+ <indexterm><primary>SPI_iterate_query_roots</primary></indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+bool SPI_iterate_query_roots(void * <parameter>plan</parameter>, QueryVisitor 
<parameter>callback</parameter>, void * <parameter>clientData</parameter>)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+    The <function>SPI_iterate_query_roots</function> will invoke the
+    <symbol>queryVisitor</symbol> callback once for each top level
+    <symbol>Query</symbol> found in the supplied execution plan.
+    The iteration is cancelled when a callback returns <symbol>false</symbol>.
+    If no callback returns <symbol>false</symbol>, or if the plan is
+    <symbol>NULL</symbol>, the function returns <symbol>true</symbol>.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Arguments</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>void * <parameter>plan</parameter></literal></term>
+    <listitem>
+     <para>
+      execution plan (returned by <function>SPI_prepare</function>)
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>QueryVisitor 
<parameter>callback</parameter></literal></term>
+    <listitem>
+     <para>
+      the callback to invoke for each query found
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>void * <parameter>clientData</parameter></literal></term>
+    <listitem>
+     <para>
+      user defined data that will be passed on to the callback
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Return Value</title>
+  <para>
+    <symbol>true</symbol> when all callbacks returned <symbol>true</symbol> or
+    <symbol>false</symbol> when a callback returned <symbol>false</symbol> and
+    thus terminated the iteration.
+  </para>
+ </refsect1>
+</refentry>
+
+<!-- *********************************************** -->
+
 <refentry id="spi-spi-cursor-find">
  <refmeta>
   <refentrytitle>SPI_cursor_find</refentrytitle>
Index: src/backend/executor/spi.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/executor/spi.c,v
retrieving revision 1.132
diff -u -r1.132 spi.c
--- src/backend/executor/spi.c  16 Nov 2004 18:10:13 -0000      1.132
+++ src/backend/executor/spi.c  1 Dec 2004 19:05:29 -0000
@@ -1064,6 +1064,42 @@
        return false;
 }
 
+/**
+ * Invokes the queryVisitor callback for each top level Query found in an
+ * execution plan. The iteration is cancelled when a callback returns
+ * false. If no callbacks returns false, or if the plan is NULL, this
+ * function returns true.
+ *
+ * Arguments:
+ *     plan          An ExecutionPlan created by SPI_prepare
+ *     queryVisitor  The callback function
+ *     clientData    User defined data that will be passed on to the callback
+ * Returns: true if the plan is NULL or if all callback invocation returns true
+ */
+bool
+SPI_iterate_query_roots(void *plan, QueryVisitor queryVisitor, void 
*clientData)
+{
+       _SPI_plan *spiplan = (_SPI_plan *) plan;
+
+       if (spiplan != NULL)
+       {
+               ListCell *query_list_list_item;
+               List     *query_list_list = spiplan->qtlist;
+               foreach(query_list_list_item, query_list_list)
+               {
+                       List     *query_list = lfirst(query_list_list_item);
+                       ListCell *query_list_item;
+
+                       foreach(query_list_item, query_list)
+                       {
+                               if(!queryVisitor((Query 
*)lfirst(query_list_item), clientData))
+                                       return false;
+                       }
+               }
+       }
+       return true;
+}
+
 /*
  * SPI_result_code_string --- convert any SPI return code to a string
  *
Index: src/include/executor/spi.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/executor/spi.h,v
retrieving revision 1.50
diff -u -r1.50 spi.h
--- src/include/executor/spi.h  16 Nov 2004 18:10:13 -0000      1.50
+++ src/include/executor/spi.h  1 Dec 2004 19:05:30 -0000
@@ -119,6 +119,9 @@
 extern void SPI_freetuple(HeapTuple pointer);
 extern void SPI_freetuptable(SPITupleTable *tuptable);
 
+typedef bool (*QueryVisitor)(Query* query, void *clientData);
+extern bool SPI_iterate_query_roots(void *plan, QueryVisitor queryVisitor, 
void* clientData);
+
 extern Portal SPI_cursor_open(const char *name, void *plan,
                                Datum *Values, const char *Nulls, bool 
read_only);
 extern Portal SPI_cursor_find(const char *name);
---------------------------(end of broadcast)---------------------------
TIP 7: don't forget to increase your free space map settings

Reply via email to