SPI function to investigate query semantics - Mailing list pgsql-patches

From Thomas Hallgren
Subject SPI function to investigate query semantics
Date
Msg-id 41AE188C.40508@mailblocks.com
Whole thread Raw
Responses Re: SPI function to investigate query semantics
List pgsql-patches
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);

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: Developer's FAQ update
Next
From: Thomas Hallgren
Date:
Subject: Re: SPI function to investigate query semantics