Implement the DO statement to support execution of PL code without having
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 22 Sep 2009 23:43:43 +0000 (23:43 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 22 Sep 2009 23:43:43 +0000 (23:43 +0000)
to create a function for it.

Procedural languages now have an additional entry point, namely a function
to execute an inline code block.  This seemed a better design than trying
to hide the transient-ness of the code from the PL.  As of this patch, only
plpgsql has an inline handler, but probably people will soon write handlers
for the other standard PLs.

In passing, remove the long-dead LANCOMPILER option of CREATE LANGUAGE.

Petr Jelinek

34 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/config.sgml
doc/src/sgml/keywords.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/create_language.sgml
doc/src/sgml/ref/do.sgml [new file with mode: 0644]
doc/src/sgml/reference.sgml
doc/src/sgml/xplang.sgml
src/backend/catalog/pg_proc.c
src/backend/commands/functioncmds.c
src/backend/commands/proclang.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/scripts/droplang.c
src/include/catalog/catversion.h
src/include/catalog/pg_language.h
src/include/catalog/pg_pltemplate.h
src/include/commands/defrem.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/kwlist.h
src/include/utils/guc.h
src/interfaces/ecpg/preproc/ecpg.trailer
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/pl_handler.c
src/pl/plpgsql/src/plpgsql.h
src/test/regress/expected/plpgsql.out
src/test/regress/sql/plpgsql.sql

index a69cef47aa3bddb5116e2cb76a289e165d2d0256..cc40c140ff455aba97ea174620ec893ab326bfd6 100644 (file)
       </entry>
      </row>
 
+     <row>
+      <entry><structfield>laninline</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>
+       This references a function that is responsible for executing
+       <quote>inline</> anonymous code blocks
+       (<xref linkend="sql-do" endterm="sql-do-title"> blocks).
+       Zero if inline blocks are not supported
+      </entry>
+     </row>
+
      <row>
       <entry><structfield>lanvalidator</structfield></entry>
       <entry><type>oid</type></entry>
       <entry>Name of call handler function</entry>
      </row>
 
+     <row>
+      <entry><structfield>tmplinline</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry>Name of anonymous-block handler function, or NULL if none</entry>
+     </row>
+
      <row>
       <entry><structfield>tmplvalidator</structfield></entry>
       <entry><type>text</type></entry>
index d400592c9112b248f1b88c30820c29096b4b56a8..b834ae411cb77f53ff6484aa23ef751bb387421b 100644 (file)
@@ -3964,6 +3964,21 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-default-do-language" xreflabel="default_do_language">
+      <term><varname>default_do_language</varname> (<type>string</type>)</term>
+      <indexterm>
+       <primary><varname>default_do_language</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        This parameter specifies the language to use when the
+        <literal>LANGUAGE</> option is omitted in a
+        <xref linkend="sql-do" endterm="sql-do-title"> statement.
+        The default is <literal>plpgsql</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-default-transaction-isolation" xreflabel="default_transaction_isolation">
       <indexterm>
        <primary>transaction isolation level</primary>
index 415c01c1425a95ad728624319e2e7a10f025f7c5..a12ec7c16620f218aa586f3cf45528e218e846c6 100644 (file)
     <entry>reserved</entry>
     <entry>reserved</entry>
    </row>
+   <row>
+    <entry><token>INLINE</token></entry>
+    <entry>non-reserved</entry>
+    <entry></entry>
+    <entry></entry>
+    <entry></entry>
+    <entry></entry>
+   </row>
    <row>
     <entry><token>INNER</token></entry>
     <entry>reserved (can be function or type)</entry>
     <entry></entry>
     <entry></entry>
    </row>
-   <row>
-    <entry><token>LANCOMPILER</token></entry>
-    <entry>non-reserved</entry>
-    <entry></entry>
-    <entry></entry>
-    <entry></entry>
-    <entry></entry>
-   </row>
    <row>
     <entry><token>LANGUAGE</token></entry>
     <entry>non-reserved</entry>
index 2ba23192442205a862ef9f9b34856e8f94d25573..dbf3a3a953cea7cbff5a44aa30e1e404a70a3011 100644 (file)
@@ -77,6 +77,7 @@ Complete list of usable sgml source files in this directory.
 <!entity declare            system "declare.sgml">
 <!entity delete             system "delete.sgml">
 <!entity discard            system "discard.sgml">
+<!entity do                 system "do.sgml">
 <!entity dropAggregate      system "drop_aggregate.sgml">
 <!entity dropCast           system "drop_cast.sgml">
 <!entity dropConversion     system "drop_conversion.sgml">
index 8aa81c9b0d227b1e750ce9229b8ed834e50da8ac..1ea06e6b3a57d4aa086265f0a32bd9c62d91a9b3 100644 (file)
@@ -23,7 +23,7 @@ PostgreSQL documentation
 <synopsis>
 CREATE [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</replaceable>
 CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</replaceable>
-    HANDLER <replaceable class="parameter">call_handler</replaceable> [ VALIDATOR <replaceable>valfunction</replaceable> ]
+    HANDLER <replaceable class="parameter">call_handler</replaceable> [ INLINE <replaceable class="parameter">inline_handler</replaceable> ] [ VALIDATOR <replaceable>valfunction</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -133,7 +133,7 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</
       <para>
        <replaceable class="parameter">call_handler</replaceable> is
        the name of a previously registered function that will be
-       called to execute the procedural language functions.  The call
+       called to execute the procedural language's functions.  The call
        handler for a procedural language must be written in a compiled
        language such as C with version 1 call convention and
        registered with <productname>PostgreSQL</productname> as a
@@ -144,6 +144,27 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</
      </listitem>
     </varlistentry>
 
+    <varlistentry>
+     <term><literal>INLINE</literal> <replaceable class="parameter">inline_handler</replaceable></term>
+
+     <listitem>
+      <para>
+       <replaceable class="parameter">inline_handler</replaceable> is the
+       name of a previously registered function that will be called
+       to execute an anonymous code block
+       (<xref linkend="sql-do" endterm="sql-do-title"> command)
+       in this language.
+       If no <replaceable class="parameter">inline_handler</replaceable>
+       function is specified, the language does not support anonymous code
+       blocks.
+       The handler function must take one argument of
+       type <type>internal</type>, which will be the <command>DO</> command's
+       internal representation, and it will typically return
+       <type>void</>.  The return value of the handler is ignored.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry>
      <term><literal>VALIDATOR</literal> <replaceable class="parameter">valfunction</replaceable></term>
 
@@ -216,7 +237,8 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</
   </para>
 
   <para>
-   The call handler function and the validator function (if any)
+   The call handler function, the inline handler function (if any),
+   and the validator function (if any)
    must already exist if the server does not have an entry for the language
    in <structname>pg_pltemplate</>.  But when there is an entry,
    the functions need not already exist;
@@ -230,7 +252,7 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">name</
    In <productname>PostgreSQL</productname> versions before 7.3, it was
    necessary to declare handler functions as returning the placeholder
    type <type>opaque</>, rather than <type>language_handler</>.
-   To support loading 
+   To support loading
    of old dump files, <command>CREATE LANGUAGE</> will accept a function
    declared as returning <type>opaque</>, but it will issue a notice and
    change the function's declared return type to <type>language_handler</>.
diff --git a/doc/src/sgml/ref/do.sgml b/doc/src/sgml/ref/do.sgml
new file mode 100644 (file)
index 0000000..f1e25e9
--- /dev/null
@@ -0,0 +1,122 @@
+<!--
+$PostgreSQL$
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-DO">
+ <refmeta>
+  <refentrytitle id="sql-do-title">DO</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DO</refname>
+  <refpurpose>execute an anonymous code block</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-do">
+  <primary>DO</primary>
+ </indexterm>
+
+ <indexterm zone="sql-do">
+  <primary>anonymous code blocks</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+DO <replaceable class="PARAMETER">code</replaceable> [ LANGUAGE <replaceable class="PARAMETER">lang_name</replaceable> ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DO</command> executes an anonymous code block, or in other
+   words a transient anonymous function in a procedural language.
+  </para>
+
+  <para>
+   The code block is treated as though it were the body of a function
+   with no parameters, returning <type>void</>.  It is parsed and
+   executed a single time.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="PARAMETER">code</replaceable></term>
+    <listitem>
+     <para>
+      The procedural language code to be executed.  This must be specified
+      as a string literal, just as in <command>CREATE FUNCTION</>.
+      Use of a dollar-quoted literal is recommended.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">lang_name</replaceable></term>
+    <listitem>
+     <para>
+      The name of the procedural language the code is written in.
+      If omitted, the default is determined by the runtime parameter
+      <xref linkend="guc-default-do-language">.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   The procedural language to be used must already have been installed
+   into the current database by means of <command>CREATE LANGUAGE</>.
+  </para>
+
+  <para>
+   The user must have <literal>USAGE</> privilege for the procedural
+   language, or must be a superuser if the language is untrusted.
+   This is the same privilege requirement as for creating a function
+   in the language.
+  </para>
+ </refsect1>
+
+ <refsect1 id="sql-do-examples">
+  <title id="sql-do-examples-title">Examples</title>
+  <para>
+   Execute a simple PL/pgsql loop without needing to create a function:
+<programlisting>
+DO $$
+DECLARE r record;
+BEGIN
+    FOR r IN SELECT rtrim(roomno) AS roomno, comment FROM Room ORDER BY roomno
+    LOOP
+        RAISE NOTICE '%, %', r.roomno, r.comment;
+    END LOOP;
+END$$;
+</programlisting>
+  </para>
+ </refsect1>
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   There is no <command>DO</command> statement in the SQL standard.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createlanguage" endterm="sql-createlanguage-title"></member>
+  </simplelist>
+ </refsect1>
+</refentry>
index 8ce451c3683e087eade33de9ee7c3e2c432577c7..723bb9c519b7c0b4102decc2e92a360ad80f1137 100644 (file)
    &declare;
    &delete;
    &discard;
+   &do;
    &dropAggregate;
    &dropCast;
    &dropConversion;
index aea1287042d3d8f9f371aec89390767cd9af136c..29d9c8347819f97345db4081095ca8022b1215cc 100644 (file)
@@ -75,9 +75,9 @@ createlang plpgsql template1
     </title>
 
     <para>
-     A procedural language is installed in a database in four steps,
+     A procedural language is installed in a database in five steps,
      which must be carried out by a database superuser.  (For languages
-     known to <command>CREATE LANGUAGE</>, the second and third steps
+     known to <command>CREATE LANGUAGE</>, the second through fourth steps
      can be omitted, because they will be carried out automatically
      if needed.)
     </para>
@@ -110,12 +110,28 @@ CREATE FUNCTION <replaceable>handler_function_name</replaceable>()
     </step>
 
     <step performance="optional" id="xplang-install-cr3">
+     <para>
+      Optionally, the language handler can provide an <quote>inline</>
+      handler function that executes anonymous code blocks
+      (<xref linkend="sql-do" endterm="sql-do-title"> commands)
+      written in this language.  If an inline handler function
+      is provided by the language, declare it with a command like
+<synopsis>
+CREATE FUNCTION <replaceable>inline_function_name</replaceable>(internal)
+    RETURNS void
+    AS '<replaceable>path-to-shared-object</replaceable>'
+    LANGUAGE C;
+</synopsis>
+     </para>
+    </step>
+
+    <step performance="optional" id="xplang-install-cr4">
      <para>
       Optionally, the language handler can provide a <quote>validator</>
       function that checks a function definition for correctness without
       actually executing it.  The validator function is called by
       <command>CREATE FUNCTION</> if it exists.  If a validator function
-      is provided by the handler, declare it with a command like
+      is provided by the language, declare it with a command like
 <synopsis>
 CREATE FUNCTION <replaceable>validator_function_name</replaceable>(oid)
     RETURNS void
@@ -125,12 +141,13 @@ CREATE FUNCTION <replaceable>validator_function_name</replaceable>(oid)
      </para>
     </step>
 
-    <step performance="required" id="xplang-install-cr4">
+    <step performance="required" id="xplang-install-cr5">
      <para>
       The PL must be declared with the command
 <synopsis>
 CREATE <optional>TRUSTED</optional> <optional>PROCEDURAL</optional> LANGUAGE <replaceable>language-name</replaceable>
     HANDLER <replaceable>handler_function_name</replaceable>
+    <optional>INLINE <replaceable>inline_function_name</replaceable></optional>
     <optional>VALIDATOR <replaceable>validator_function_name</replaceable></optional> ;
 </synopsis>
       The optional key word <literal>TRUSTED</literal> specifies that
@@ -173,10 +190,13 @@ CREATE FUNCTION plpgsql_call_handler() RETURNS language_handler AS
      </para>
 
      <para>
-      <application>PL/pgSQL</application> has a validator function,
-      so we declare that too:
+      <application>PL/pgSQL</application> has an inline handler function
+      and a validator function, so we declare those too:
 
 <programlisting>
+CREATE FUNCTION plpgsql_inline_handler(internal) RETURNS void AS
+    '$libdir/plpgsql' LANGUAGE C;
+
 CREATE FUNCTION plpgsql_validator(oid) RETURNS void AS
     '$libdir/plpgsql' LANGUAGE C;
 </programlisting>
@@ -187,6 +207,7 @@ CREATE FUNCTION plpgsql_validator(oid) RETURNS void AS
 <programlisting>
 CREATE TRUSTED PROCEDURAL LANGUAGE plpgsql
     HANDLER plpgsql_call_handler
+    INLINE plpgsql_inline_handler
     VALIDATOR plpgsql_validator;
 </programlisting>
       then defines that the previously declared functions
index 1a065308561c8b003fe985450249d64d9f66c7c3..342381e3416b026b801bf1c282e272c5cf69e4fb 100644 (file)
@@ -782,11 +782,12 @@ sql_function_parse_error_callback(void *arg)
 
 /*
  * Adjust a syntax error occurring inside the function body of a CREATE
- * FUNCTION command.  This can be used by any function validator, not only
- * for SQL-language functions. It is assumed that the syntax error position
- * is initially relative to the function body string (as passed in).  If
- * possible, we adjust the position to reference the original CREATE command;
- * if we can't manage that, we set up an "internal query" syntax error instead.
+ * FUNCTION or DO command.  This can be used by any function validator or
+ * anonymous-block handler, not only for SQL-language functions.
+ * It is assumed that the syntax error position is initially relative to the
+ * function body string (as passed in).  If possible, we adjust the position
+ * to reference the original command text; if we can't manage that, we set
+ * up an "internal query" syntax error instead.
  *
  * Returns true if a syntax error was processed, false if not.
  */
@@ -843,8 +844,8 @@ function_parse_error_transpose(const char *prosrc)
 
 /*
  * Try to locate the string literal containing the function body in the
- * given text of the CREATE FUNCTION command.  If successful, return the
- * character (not byte) index within the command corresponding to the
+ * given text of the CREATE FUNCTION or DO command.  If successful, return
+ * the character (not byte) index within the command corresponding to the
  * given character index within the literal.  If not successful, return 0.
  */
 static int
@@ -852,7 +853,7 @@ match_prosrc_to_query(const char *prosrc, const char *queryText,
                                          int cursorpos)
 {
        /*
-        * Rather than fully parsing the CREATE FUNCTION command, we just scan the
+        * Rather than fully parsing the original command, we just scan the
         * command looking for $prosrc$ or 'prosrc'.  This could be fooled (though
         * not in any very probable scenarios), so fail if we find more than one
         * match.
index f0989bf1d9475ae944454ad6f5031abb45d19846..e3618feb407ec74576aaabee19d2c44c42ea87f2 100644 (file)
@@ -1915,3 +1915,110 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
 
        heap_close(procRel, RowExclusiveLock);
 }
+
+
+/*
+ * ExecuteDoStmt
+ *             Execute inline procedural-language code
+ */
+void
+ExecuteDoStmt(DoStmt *stmt)
+{
+       InlineCodeBlock *codeblock = makeNode(InlineCodeBlock);
+       ListCell   *arg;
+       DefElem    *as_item = NULL;
+       DefElem    *language_item = NULL;
+       char       *language;
+       char       *languageName;
+       Oid                     laninline;
+       HeapTuple       languageTuple;
+       Form_pg_language languageStruct;
+
+       /* Process options we got from gram.y */
+       foreach(arg, stmt->args)
+       {
+               DefElem    *defel = (DefElem *) lfirst(arg);
+
+               if (strcmp(defel->defname, "as") == 0)
+               {
+                       if (as_item)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                                errmsg("conflicting or redundant options")));
+                       as_item = defel;
+               }
+               else if (strcmp(defel->defname, "language") == 0)
+               {
+                       if (language_item)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                                errmsg("conflicting or redundant options")));
+                       language_item = defel;
+               }
+               else
+                       elog(ERROR, "option \"%s\" not recognized",
+                                defel->defname);
+       }
+
+       if (as_item)
+               codeblock->source_text = strVal(as_item->arg);
+       else
+               ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("no inline code specified")));
+
+       /* if LANGUAGE option wasn't specified, use the default language */
+       if (language_item)
+               language = strVal(language_item->arg);
+       else
+               language = default_do_language;
+
+       /* Convert language name to canonical case */
+       languageName = case_translate_language_name(language);
+
+       /* Look up the language and validate permissions */
+       languageTuple = SearchSysCache(LANGNAME,
+                                                                  PointerGetDatum(languageName),
+                                                                  0, 0, 0);
+       if (!HeapTupleIsValid(languageTuple))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("language \"%s\" does not exist", languageName),
+                                (PLTemplateExists(languageName) ?
+                                 errhint("Use CREATE LANGUAGE to load the language into the database.") : 0)));
+
+       codeblock->langOid = HeapTupleGetOid(languageTuple);
+       languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
+
+       if (languageStruct->lanpltrusted)
+       {
+               /* if trusted language, need USAGE privilege */
+               AclResult       aclresult;
+
+               aclresult = pg_language_aclcheck(codeblock->langOid, GetUserId(),
+                                                                                ACL_USAGE);
+               if (aclresult != ACLCHECK_OK)
+                       aclcheck_error(aclresult, ACL_KIND_LANGUAGE,
+                                                  NameStr(languageStruct->lanname));
+       }
+       else
+       {
+               /* if untrusted language, must be superuser */
+               if (!superuser())
+                       aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_LANGUAGE,
+                                                  NameStr(languageStruct->lanname));
+       }
+
+       /* get the handler function's OID */
+       laninline = languageStruct->laninline;
+       if (!OidIsValid(laninline))
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("language \"%s\" does not support inline code execution",
+                                               NameStr(languageStruct->lanname))));
+
+       ReleaseSysCache(languageTuple);
+
+       /* execute the inline handler */
+       OidFunctionCall1(laninline, PointerGetDatum(codeblock));
+}
index 9dec622a18c59218390eb859c911a41080ee9ffa..1ad3fc8039a1cc6c4d3a6b33c7bb24751d2fdf02 100644 (file)
@@ -44,12 +44,14 @@ typedef struct
        bool            tmpltrusted;    /* trusted? */
        bool            tmpldbacreate;  /* db owner allowed to create? */
        char       *tmplhandler;        /* name of handler function */
+       char       *tmplinline;         /* name of anonymous-block handler, or NULL */
        char       *tmplvalidator;      /* name of validator function, or NULL */
        char       *tmpllibrary;        /* path of shared library */
 } PLTemplate;
 
 static void create_proc_lang(const char *languageName,
-                                Oid languageOwner, Oid handlerOid, Oid valOid, bool trusted);
+                                Oid languageOwner, Oid handlerOid, Oid inlineOid,
+                                Oid valOid, bool trusted);
 static PLTemplate *find_language_template(const char *languageName);
 static void AlterLanguageOwner_internal(HeapTuple tup, Relation rel,
                                                        Oid newOwnerId);
@@ -65,6 +67,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
        char       *languageName;
        PLTemplate *pltemplate;
        Oid                     handlerOid,
+                               inlineOid,
                                valOid;
        Oid                     funcrettype;
        Oid                     funcargtypes[1];
@@ -154,6 +157,44 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
                                                                                 0);
                }
 
+               /*
+                * Likewise for the anonymous block handler, if required;
+                * but we don't care about its return type.
+                */
+               if (pltemplate->tmplinline)
+               {
+                       funcname = SystemFuncName(pltemplate->tmplinline);
+                       funcargtypes[0] = INTERNALOID;
+                       inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
+                       if (!OidIsValid(inlineOid))
+                       {
+                               inlineOid = ProcedureCreate(pltemplate->tmplinline,
+                                                                                       PG_CATALOG_NAMESPACE,
+                                                                                       false, /* replace */
+                                                                                       false, /* returnsSet */
+                                                                                       VOIDOID,
+                                                                                       ClanguageId,
+                                                                                       F_FMGR_C_VALIDATOR,
+                                                                                       pltemplate->tmplinline,
+                                                                                       pltemplate->tmpllibrary,
+                                                                                       false, /* isAgg */
+                                                                                       false, /* isWindowFunc */
+                                                                                       false, /* security_definer */
+                                                                                       true, /* isStrict */
+                                                                                       PROVOLATILE_VOLATILE,
+                                                                                       buildoidvector(funcargtypes, 1),
+                                                                                       PointerGetDatum(NULL),
+                                                                                       PointerGetDatum(NULL),
+                                                                                       PointerGetDatum(NULL),
+                                                                                       NIL,
+                                                                                       PointerGetDatum(NULL),
+                                                                                       1,
+                                                                                       0);
+                       }
+               }
+               else
+                       inlineOid = InvalidOid;
+
                /*
                 * Likewise for the validator, if required; but we don't care about
                 * its return type.
@@ -177,7 +218,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
                                                                                 false, /* isAgg */
                                                                                 false, /* isWindowFunc */
                                                                                 false, /* security_definer */
-                                                                                false, /* isStrict */
+                                                                                true, /* isStrict */
                                                                                 PROVOLATILE_VOLATILE,
                                                                                 buildoidvector(funcargtypes, 1),
                                                                                 PointerGetDatum(NULL),
@@ -193,8 +234,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
                        valOid = InvalidOid;
 
                /* ok, create it */
-               create_proc_lang(languageName, GetUserId(), handlerOid, valOid,
-                                                pltemplate->tmpltrusted);
+               create_proc_lang(languageName, GetUserId(), handlerOid, inlineOid,
+                                                valOid, pltemplate->tmpltrusted);
        }
        else
        {
@@ -246,6 +287,16 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
                                                 NameListToString(stmt->plhandler))));
                }
 
+               /* validate the inline function */
+               if (stmt->plinline)
+               {
+                       funcargtypes[0] = INTERNALOID;
+                       inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
+                       /* return value is ignored, so we don't check the type */
+               }
+               else
+                       inlineOid = InvalidOid;
+
                /* validate the validator function */
                if (stmt->plvalidator)
                {
@@ -257,8 +308,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
                        valOid = InvalidOid;
 
                /* ok, create it */
-               create_proc_lang(languageName, GetUserId(), handlerOid, valOid,
-                                                stmt->pltrusted);
+               create_proc_lang(languageName, GetUserId(), handlerOid, inlineOid,
+                                                valOid, stmt->pltrusted);
        }
 }
 
@@ -267,7 +318,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
  */
 static void
 create_proc_lang(const char *languageName,
-                                Oid languageOwner, Oid handlerOid, Oid valOid, bool trusted)
+                                Oid languageOwner, Oid handlerOid, Oid inlineOid,
+                                Oid valOid, bool trusted)
 {
        Relation        rel;
        TupleDesc       tupDesc;
@@ -293,6 +345,7 @@ create_proc_lang(const char *languageName,
        values[Anum_pg_language_lanispl - 1] = BoolGetDatum(true);
        values[Anum_pg_language_lanpltrusted - 1] = BoolGetDatum(trusted);
        values[Anum_pg_language_lanplcallfoid - 1] = ObjectIdGetDatum(handlerOid);
+       values[Anum_pg_language_laninline - 1] = ObjectIdGetDatum(inlineOid);
        values[Anum_pg_language_lanvalidator - 1] = ObjectIdGetDatum(valOid);
        nulls[Anum_pg_language_lanacl - 1] = true;
 
@@ -321,6 +374,15 @@ create_proc_lang(const char *languageName,
        referenced.objectSubId = 0;
        recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
+       /* dependency on the inline handler function, if any */
+       if (OidIsValid(inlineOid))
+       {
+               referenced.classId = ProcedureRelationId;
+               referenced.objectId = inlineOid;
+               referenced.objectSubId = 0;
+               recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+       }
+
        /* dependency on the validator function, if any */
        if (OidIsValid(valOid))
        {
@@ -371,6 +433,11 @@ find_language_template(const char *languageName)
                if (!isnull)
                        result->tmplhandler = TextDatumGetCString(datum);
 
+               datum = heap_getattr(tup, Anum_pg_pltemplate_tmplinline,
+                                                        RelationGetDescr(rel), &isnull);
+               if (!isnull)
+                       result->tmplinline = TextDatumGetCString(datum);
+
                datum = heap_getattr(tup, Anum_pg_pltemplate_tmplvalidator,
                                                         RelationGetDescr(rel), &isnull);
                if (!isnull)
index 25961d13181290e34cea73d5aa97ec3c163c7c4e..6420e4adb8323efdd67c2262fe65419539e749e3 100644 (file)
@@ -2561,6 +2561,16 @@ _copyRemoveFuncStmt(RemoveFuncStmt *from)
        return newnode;
 }
 
+static DoStmt *
+_copyDoStmt(DoStmt *from)
+{
+       DoStmt     *newnode = makeNode(DoStmt);
+
+       COPY_NODE_FIELD(args);
+
+       return newnode;
+}
+
 static RemoveOpClassStmt *
 _copyRemoveOpClassStmt(RemoveOpClassStmt *from)
 {
@@ -3104,6 +3114,7 @@ _copyCreatePLangStmt(CreatePLangStmt *from)
 
        COPY_STRING_FIELD(plname);
        COPY_NODE_FIELD(plhandler);
+       COPY_NODE_FIELD(plinline);
        COPY_NODE_FIELD(plvalidator);
        COPY_SCALAR_FIELD(pltrusted);
 
@@ -3797,6 +3808,9 @@ copyObject(void *from)
                case T_RemoveFuncStmt:
                        retval = _copyRemoveFuncStmt(from);
                        break;
+               case T_DoStmt:
+                       retval = _copyDoStmt(from);
+                       break;
                case T_RemoveOpClassStmt:
                        retval = _copyRemoveOpClassStmt(from);
                        break;
index 142aa8ffc23943322ec4755f22df6f828699b312..f125719a3c73af22f8d08e0bdf6fc5e3a8eac888 100644 (file)
@@ -1212,6 +1212,14 @@ _equalRemoveFuncStmt(RemoveFuncStmt *a, RemoveFuncStmt *b)
        return true;
 }
 
+static bool
+_equalDoStmt(DoStmt *a, DoStmt *b)
+{
+       COMPARE_NODE_FIELD(args);
+
+       return true;
+}
+
 static bool
 _equalRemoveOpClassStmt(RemoveOpClassStmt *a, RemoveOpClassStmt *b)
 {
@@ -1667,6 +1675,7 @@ _equalCreatePLangStmt(CreatePLangStmt *a, CreatePLangStmt *b)
 {
        COMPARE_STRING_FIELD(plname);
        COMPARE_NODE_FIELD(plhandler);
+       COMPARE_NODE_FIELD(plinline);
        COMPARE_NODE_FIELD(plvalidator);
        COMPARE_SCALAR_FIELD(pltrusted);
 
@@ -2576,6 +2585,9 @@ equal(void *a, void *b)
                case T_RemoveFuncStmt:
                        retval = _equalRemoveFuncStmt(a, b);
                        break;
+               case T_DoStmt:
+                       retval = _equalDoStmt(a, b);
+                       break;
                case T_RemoveOpClassStmt:
                        retval = _equalRemoveOpClassStmt(a, b);
                        break;
index 69883cfd125c239098e865d77ae830138531ff71..732b1d7adc9f554c011355ac4b0d80270f1462fd 100644 (file)
@@ -196,7 +196,7 @@ static TypeName *TableFuncTypeName(List *columns);
                CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
                CreateFdwStmt CreateForeignServerStmt CreateAssertStmt CreateTrigStmt
                CreateUserStmt CreateUserMappingStmt CreateRoleStmt
-               CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt
+               CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
                DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
                DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
                DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
@@ -246,7 +246,6 @@ static TypeName *TableFuncTypeName(List *columns);
 %type <list>   OptSchemaEltList
 
 %type <boolean> TriggerActionTime TriggerForSpec opt_trusted opt_restart_seqs
-%type <str>            opt_lancompiler
 
 %type <ival>   TriggerEvents TriggerOneEvent
 %type <value>  TriggerFuncArg
@@ -256,7 +255,7 @@ static TypeName *TableFuncTypeName(List *columns);
                                index_name name file_name cluster_index_specification
 
 %type <list>   func_name handler_name qual_Op qual_all_Op subquery_Op
-                               opt_class opt_validator validator_clause
+                               opt_class opt_inline_handler opt_validator validator_clause
 
 %type <range>  qualified_name OptConstrFromTable
 
@@ -295,12 +294,12 @@ static TypeName *TableFuncTypeName(List *columns);
                                execute_param_clause using_clause returning_clause
                                enum_val_list table_func_column_list
                                create_generic_options alter_generic_options
-                               relation_expr_list
+                               relation_expr_list dostmt_opt_list
 
 %type <range>  OptTempTableName
 %type <into>   into_clause create_as_target
 
-%type <defelt> createfunc_opt_item common_func_opt_item
+%type <defelt> createfunc_opt_item common_func_opt_item dostmt_opt_item
 %type <fun_param> func_arg func_arg_with_default table_func_column
 %type <fun_param_mode> arg_class
 %type <typnam> func_return func_type
@@ -481,7 +480,7 @@ static TypeName *TableFuncTypeName(List *columns);
        HANDLER HAVING HEADER_P HOLD HOUR_P
 
        IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P
-       INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY
+       INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P
        INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
        INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
 
@@ -489,7 +488,7 @@ static TypeName *TableFuncTypeName(List *columns);
 
        KEY
 
-       LANCOMPILER LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
+       LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
        LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
        LOCATION LOCK_P LOGIN_P
 
@@ -676,6 +675,7 @@ stmt :
                        | DefineStmt
                        | DeleteStmt
                        | DiscardStmt
+                       | DoStmt
                        | DropAssertStmt
                        | DropCastStmt
                        | DropFdwStmt
@@ -2771,19 +2771,20 @@ CreatePLangStmt:
                                n->plname = $5;
                                /* parameters are all to be supplied by system */
                                n->plhandler = NIL;
+                               n->plinline = NIL;
                                n->plvalidator = NIL;
                                n->pltrusted = false;
                                $$ = (Node *)n;
                        }
                        | CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst
-                         HANDLER handler_name opt_validator opt_lancompiler
+                         HANDLER handler_name opt_inline_handler opt_validator
                        {
                                CreatePLangStmt *n = makeNode(CreatePLangStmt);
                                n->plname = $5;
                                n->plhandler = $7;
-                               n->plvalidator = $8;
+                               n->plinline = $8;
+                               n->plvalidator = $9;
                                n->pltrusted = $2;
-                               /* LANCOMPILER is now ignored entirely */
                                $$ = (Node *)n;
                        }
                ;
@@ -2802,6 +2803,11 @@ handler_name:
                        | name attrs                            { $$ = lcons(makeString($1), $2); }
                ;
 
+opt_inline_handler:
+                       INLINE_P handler_name                                   { $$ = $2; }
+                       | /*EMPTY*/                                                             { $$ = NIL; }
+               ;
+
 validator_clause:
                        VALIDATOR handler_name                                  { $$ = $2; }
                        | NO VALIDATOR                                                  { $$ = NIL; }
@@ -2812,11 +2818,6 @@ opt_validator:
                        | /*EMPTY*/                                                             { $$ = NIL; }
                ;
 
-opt_lancompiler:
-                       LANCOMPILER Sconst                                              { $$ = $2; }
-                       | /*EMPTY*/                                                             { $$ = NULL; }
-               ;
-
 DropPLangStmt:
                        DROP opt_procedural LANGUAGE ColId_or_Sconst opt_drop_behavior
                                {
@@ -5139,6 +5140,38 @@ any_operator:
                                        { $$ = lcons(makeString($1), $3); }
                ;
 
+/*****************************************************************************
+ *
+ *             DO <anonymous code block> [ LANGUAGE language ]
+ *
+ * We use a DefElem list for future extensibility, and to allow flexibility
+ * in the clause order.
+ *
+ *****************************************************************************/
+
+DoStmt: DO dostmt_opt_list
+                               {
+                                       DoStmt *n = makeNode(DoStmt);
+                                       n->args = $2;
+                                       $$ = (Node *)n;
+                               }
+               ;
+
+dostmt_opt_list:
+                       dostmt_opt_item                                         { $$ = list_make1($1); }
+                       | dostmt_opt_list dostmt_opt_item       { $$ = lappend($1, $2); }
+               ;
+
+dostmt_opt_item:
+                       Sconst
+                               {
+                                       $$ = makeDefElem("as", (Node *)makeString($1));
+                               }
+                       | LANGUAGE ColId_or_Sconst
+                               {
+                                       $$ = makeDefElem("language", (Node *)makeString($2));
+                               }
+               ;
 
 /*****************************************************************************
  *
@@ -10362,6 +10395,7 @@ unreserved_keyword:
                        | INDEXES
                        | INHERIT
                        | INHERITS
+                       | INLINE_P
                        | INPUT_P
                        | INSENSITIVE
                        | INSERT
@@ -10369,7 +10403,6 @@ unreserved_keyword:
                        | INVOKER
                        | ISOLATION
                        | KEY
-                       | LANCOMPILER
                        | LANGUAGE
                        | LARGE_P
                        | LAST_P
index ee4ed8be79e86b4863a116c05b6d59028df17684..ddf87b4e5d8c1db3144e7681253aeaf41ea70e4d 100644 (file)
@@ -840,6 +840,10 @@ ProcessUtility(Node *parsetree,
                        }
                        break;
 
+               case T_DoStmt:
+                       ExecuteDoStmt((DoStmt *) parsetree);
+                       break;
+
                case T_CreatedbStmt:
                        PreventTransactionChain(isTopLevel, "CREATE DATABASE");
                        createdb((CreatedbStmt *) parsetree);
@@ -1761,6 +1765,10 @@ CreateCommandTag(Node *parsetree)
                        }
                        break;
 
+               case T_DoStmt:
+                       tag = "DO";
+                       break;
+
                case T_CreatedbStmt:
                        tag = "CREATE DATABASE";
                        break;
@@ -2276,6 +2284,10 @@ GetCommandLogLevel(Node *parsetree)
                        lev = LOGSTMT_DDL;
                        break;
 
+               case T_DoStmt:
+                       lev = LOGSTMT_ALL;
+                       break;
+
                case T_CreatedbStmt:
                        lev = LOGSTMT_DDL;
                        break;
index 33b1f8b3892b172a41d76d76713afb477394e478..fc55e9017ac1915df5727255b4b81e1898c5c48e 100644 (file)
@@ -385,6 +385,8 @@ char           *external_pid_file;
 
 char      *pgstat_temp_directory;
 
+char      *default_do_language;
+
 int                    tcp_keepalives_idle;
 int                    tcp_keepalives_interval;
 int                    tcp_keepalives_count;
@@ -2539,6 +2541,15 @@ static struct config_string ConfigureNamesString[] =
        },
 #endif   /* USE_SSL */
 
+       {
+               {"default_do_language", PGC_USERSET, CLIENT_CONN_STATEMENT,
+                       gettext_noop("Sets the language used in DO statement if LANGUAGE is not specified."),
+                       NULL
+               },
+               &default_do_language,
+               "plpgsql", NULL, NULL
+       },
+
        /* End-of-list marker */
        {
                {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL
index b10775cc2df8de67966a7cfb29e83368e9639a90..d6cf7e21c732ede9b2cb42c3f29cc5b4ab929cd6 100644 (file)
 #temp_tablespaces = ''                 # a list of tablespace names, '' uses
                                        # only default tablespace
 #check_function_bodies = on
+#default_do_language = 'plpgsql'
 #default_transaction_isolation = 'read committed'
 #default_transaction_read_only = off
 #session_replication_role = 'origin'
index 64e328c486d7317e67647631d5568efa12d0bf9c..b5c2f9355e3fa6f8cc2d0f9228e487aadb66cc64 100644 (file)
@@ -4445,6 +4445,7 @@ getProcLangs(int *numProcLangs)
        int                     i_lanname;
        int                     i_lanpltrusted;
        int                     i_lanplcallfoid;
+       int                     i_laninline;
        int                     i_lanvalidator;
        int                     i_lanacl;
        int                     i_lanowner;
@@ -4452,7 +4453,19 @@ getProcLangs(int *numProcLangs)
        /* Make sure we are in proper schema */
        selectSourceSchema("pg_catalog");
 
-       if (g_fout->remoteVersion >= 80300)
+       if (g_fout->remoteVersion >= 80500)
+       {
+               /* pg_language has a laninline column */
+               appendPQExpBuffer(query, "SELECT tableoid, oid, "
+                                                 "lanname, lanpltrusted, lanplcallfoid, "
+                                                 "laninline, lanvalidator,  lanacl, "
+                                                 "(%s lanowner) AS lanowner "
+                                                 "FROM pg_language "
+                                                 "WHERE lanispl "
+                                                 "ORDER BY oid",
+                                                 username_subquery);
+       }
+       else if (g_fout->remoteVersion >= 80300)
        {
                /* pg_language has a lanowner column */
                appendPQExpBuffer(query, "SELECT tableoid, oid, "
@@ -4515,6 +4528,7 @@ getProcLangs(int *numProcLangs)
        i_lanpltrusted = PQfnumber(res, "lanpltrusted");
        i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
        /* these may fail and return -1: */
+       i_laninline = PQfnumber(res, "laninline");
        i_lanvalidator = PQfnumber(res, "lanvalidator");
        i_lanacl = PQfnumber(res, "lanacl");
        i_lanowner = PQfnumber(res, "lanowner");
@@ -4529,6 +4543,10 @@ getProcLangs(int *numProcLangs)
                planginfo[i].dobj.name = strdup(PQgetvalue(res, i, i_lanname));
                planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
                planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
+               if (i_laninline >= 0)
+                       planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
+               else
+                       planginfo[i].laninline = InvalidOid;
                if (i_lanvalidator >= 0)
                        planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
                else
@@ -6994,6 +7012,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
        char       *qlanname;
        char       *lanschema;
        FuncInfo   *funcInfo;
+       FuncInfo   *inlineInfo = NULL;
        FuncInfo   *validatorInfo = NULL;
 
        if (dataOnly)
@@ -7011,6 +7030,13 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
        if (funcInfo != NULL && !funcInfo->dobj.dump)
                funcInfo = NULL;                /* treat not-dumped same as not-found */
 
+       if (OidIsValid(plang->laninline))
+       {
+               inlineInfo = findFuncByOid(plang->laninline);
+               if (inlineInfo != NULL && !inlineInfo->dobj.dump)
+                       inlineInfo = NULL;
+       }
+
        if (OidIsValid(plang->lanvalidator))
        {
                validatorInfo = findFuncByOid(plang->lanvalidator);
@@ -7024,6 +7050,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
         * dump it.
         */
        useParams = (funcInfo != NULL &&
+                                (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
                                 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
 
        if (!useParams && !shouldDumpProcLangs())
@@ -7054,6 +7081,16 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
        {
                appendPQExpBuffer(defqry, " HANDLER %s",
                                                  fmtId(funcInfo->dobj.name));
+               if (OidIsValid(plang->laninline))
+               {
+                       appendPQExpBuffer(defqry, " INLINE ");
+                       /* Cope with possibility that inline is in different schema */
+                       if (inlineInfo->dobj.namespace != funcInfo->dobj.namespace)
+                               appendPQExpBuffer(defqry, "%s.",
+                                                       fmtId(inlineInfo->dobj.namespace->dobj.name));
+                       appendPQExpBuffer(defqry, "%s",
+                                                         fmtId(inlineInfo->dobj.name));
+               }
                if (OidIsValid(plang->lanvalidator))
                {
                        appendPQExpBuffer(defqry, " VALIDATOR ");
index 1356f0575eb7207b374bb1d6ff247442e375904f..ea489e84f28d23923b5c55b5d6cf3cda9c2f9c6c 100644 (file)
@@ -357,6 +357,7 @@ typedef struct _procLangInfo
        DumpableObject dobj;
        bool            lanpltrusted;
        Oid                     lanplcallfoid;
+       Oid                     laninline;
        Oid                     lanvalidator;
        char       *lanacl;
        char       *lanowner;           /* name of owner, or empty string */
index fcd47fc90363e8ec2ea5b81de841a4c73ab613fd..31a81d3baa8b76a2745324c55c5c4c53ecc155a9 100644 (file)
@@ -38,7 +38,6 @@ main(int argc, char *argv[])
        const char *progname;
        int                     optindex;
        int                     c;
-
        bool            listlangs = false;
        const char *dbname = NULL;
        char       *host = NULL;
@@ -47,19 +46,20 @@ main(int argc, char *argv[])
        enum trivalue prompt_password = TRI_DEFAULT;
        bool            echo = false;
        char       *langname = NULL;
-
        char       *p;
        Oid                     lanplcallfoid;
+       Oid                     laninline;
        Oid                     lanvalidator;
        char       *handler;
+       char       *inline_handler;
        char       *validator;
        char       *handler_ns;
+       char       *inline_ns;
        char       *validator_ns;
        bool            keephandler;
+       bool            keepinline;
        bool            keepvalidator;
-
        PQExpBufferData sql;
-
        PGconn     *conn;
        PGresult   *result;
 
@@ -190,10 +190,10 @@ main(int argc, char *argv[])
        executeCommand(conn, "SET search_path = pg_catalog;", progname, echo);
 
        /*
-        * Make sure the language is installed and find the OIDs of the handler
-        * and validator functions
+        * Make sure the language is installed and find the OIDs of the
+        * language support functions
         */
-       printfPQExpBuffer(&sql, "SELECT lanplcallfoid, lanvalidator "
+       printfPQExpBuffer(&sql, "SELECT lanplcallfoid, laninline, lanvalidator "
                                          "FROM pg_language WHERE lanname = '%s' AND lanispl;",
                                          langname);
        result = executeQuery(conn, sql.data, progname, echo);
@@ -206,7 +206,8 @@ main(int argc, char *argv[])
                exit(1);
        }
        lanplcallfoid = atooid(PQgetvalue(result, 0, 0));
-       lanvalidator = atooid(PQgetvalue(result, 0, 1));
+       laninline = atooid(PQgetvalue(result, 0, 1));
+       lanvalidator = atooid(PQgetvalue(result, 0, 2));
        PQclear(result);
 
        /*
@@ -260,6 +261,44 @@ main(int argc, char *argv[])
                handler_ns = NULL;
        }
 
+       /*
+        * Check that the inline function isn't used by some other language
+        */
+       if (OidIsValid(laninline))
+       {
+               printfPQExpBuffer(&sql, "SELECT count(*) FROM pg_language "
+                                                 "WHERE laninline = %u AND lanname <> '%s';",
+                                                 laninline, langname);
+               result = executeQuery(conn, sql.data, progname, echo);
+               if (strcmp(PQgetvalue(result, 0, 0), "0") == 0)
+                       keepinline = false;
+               else
+                       keepinline = true;
+               PQclear(result);
+       }
+       else
+               keepinline = true;      /* don't try to delete it */
+
+       /*
+        * Find the inline handler name
+        */
+       if (!keepinline)
+       {
+               printfPQExpBuffer(&sql, "SELECT proname, (SELECT nspname "
+                                                 "FROM pg_namespace ns WHERE ns.oid = pronamespace) "
+                                                 "AS prons FROM pg_proc WHERE oid = %u;",
+                                                 laninline);
+               result = executeQuery(conn, sql.data, progname, echo);
+               inline_handler = strdup(PQgetvalue(result, 0, 0));
+               inline_ns = strdup(PQgetvalue(result, 0, 1));
+               PQclear(result);
+       }
+       else
+       {
+               inline_handler = NULL;
+               inline_ns = NULL;
+       }
+
        /*
         * Check that the validator function isn't used by some other language
         */
@@ -305,6 +344,9 @@ main(int argc, char *argv[])
        if (!keephandler)
                appendPQExpBuffer(&sql, "DROP FUNCTION \"%s\".\"%s\" ();\n",
                                                  handler_ns, handler);
+       if (!keepinline)
+               appendPQExpBuffer(&sql, "DROP FUNCTION \"%s\".\"%s\" (internal);\n",
+                                                 inline_ns, inline_handler);
        if (!keepvalidator)
                appendPQExpBuffer(&sql, "DROP FUNCTION \"%s\".\"%s\" (oid);\n",
                                                  validator_ns, validator);
index 7d63071b479a9081ae18ac8b3a694c5a0065fcbd..03010376321316f9c3ea974df0f78e8ac993db11 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200908311
+#define CATALOG_VERSION_NO     200909221
 
 #endif
index ea887df63d76d4950558113efafcab3fffa52cd0..dc782f88942933de2fba26ea8636524ff4bac1eb 100644 (file)
@@ -35,6 +35,7 @@ CATALOG(pg_language,2612)
        bool            lanispl;                /* Is a procedural language */
        bool            lanpltrusted;   /* PL is trusted */
        Oid                     lanplcallfoid;  /* Call handler for PL */
+       Oid                     laninline;              /* Optional anonymous-block handler function */
        Oid                     lanvalidator;   /* Optional validation function */
        aclitem         lanacl[1];              /* Access privileges */
 } FormData_pg_language;
@@ -50,27 +51,28 @@ typedef FormData_pg_language *Form_pg_language;
  *             compiler constants for pg_language
  * ----------------
  */
-#define Natts_pg_language                              7
+#define Natts_pg_language                              8
 #define Anum_pg_language_lanname               1
 #define Anum_pg_language_lanowner              2
 #define Anum_pg_language_lanispl               3
 #define Anum_pg_language_lanpltrusted  4
 #define Anum_pg_language_lanplcallfoid 5
-#define Anum_pg_language_lanvalidator  6
-#define Anum_pg_language_lanacl                        7
+#define Anum_pg_language_laninline             6
+#define Anum_pg_language_lanvalidator  7
+#define Anum_pg_language_lanacl                        8
 
 /* ----------------
  *             initial contents of pg_language
  * ----------------
  */
 
-DATA(insert OID = 12 ( "internal" PGUID f f 0 2246 _null_ ));
+DATA(insert OID = 12 ( "internal"      PGUID f f 0 0 2246 _null_ ));
 DESCR("built-in functions");
 #define INTERNALlanguageId 12
-DATA(insert OID = 13 ( "c" PGUID f f 0 2247 _null_ ));
+DATA(insert OID = 13 ( "c"                     PGUID f f 0 0 2247 _null_ ));
 DESCR("dynamically-loaded C functions");
 #define ClanguageId 13
-DATA(insert OID = 14 ( "sql" PGUID f t 0 2248 _null_ ));
+DATA(insert OID = 14 ( "sql"           PGUID f t 0 0 2248 _null_ ));
 DESCR("SQL-language functions");
 #define SQLlanguageId 14
 
index b710e18fb121d640d6d33cf6a371ee04adb9a6f0..5ef97df278cd91fd1debc7ebb7983f3c757a1994 100644 (file)
@@ -34,6 +34,7 @@ CATALOG(pg_pltemplate,1136) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
        bool            tmpltrusted;    /* PL is trusted? */
        bool            tmpldbacreate;  /* PL is installable by db owner? */
        text            tmplhandler;    /* name of call handler function */
+       text            tmplinline;             /* name of anonymous-block handler, or NULL */
        text            tmplvalidator;  /* name of validator function, or NULL */
        text            tmpllibrary;    /* path of shared library */
        aclitem         tmplacl[1];             /* access privileges for template */
@@ -50,14 +51,15 @@ typedef FormData_pg_pltemplate *Form_pg_pltemplate;
  *             compiler constants for pg_pltemplate
  * ----------------
  */
-#define Natts_pg_pltemplate                                    7
+#define Natts_pg_pltemplate                                    8
 #define Anum_pg_pltemplate_tmplname                    1
 #define Anum_pg_pltemplate_tmpltrusted         2
 #define Anum_pg_pltemplate_tmpldbacreate       3
 #define Anum_pg_pltemplate_tmplhandler         4
-#define Anum_pg_pltemplate_tmplvalidator       5
-#define Anum_pg_pltemplate_tmpllibrary         6
-#define Anum_pg_pltemplate_tmplacl                     7
+#define Anum_pg_pltemplate_tmplinline          5
+#define Anum_pg_pltemplate_tmplvalidator       6
+#define Anum_pg_pltemplate_tmpllibrary         7
+#define Anum_pg_pltemplate_tmplacl                     8
 
 
 /* ----------------
@@ -65,11 +67,11 @@ typedef FormData_pg_pltemplate *Form_pg_pltemplate;
  * ----------------
  */
 
-DATA(insert ( "plpgsql"                t t "plpgsql_call_handler" "plpgsql_validator" "$libdir/plpgsql" _null_ ));
-DATA(insert ( "pltcl"          t t "pltcl_call_handler" _null_ "$libdir/pltcl" _null_ ));
-DATA(insert ( "pltclu"         f f "pltclu_call_handler" _null_ "$libdir/pltcl" _null_ ));
-DATA(insert ( "plperl"         t t "plperl_call_handler" "plperl_validator" "$libdir/plperl" _null_ ));
-DATA(insert ( "plperlu"                f f "plperl_call_handler" "plperl_validator" "$libdir/plperl" _null_ ));
-DATA(insert ( "plpythonu"      f f "plpython_call_handler" _null_ "$libdir/plpython" _null_ ));
+DATA(insert ( "plpgsql"                t t "plpgsql_call_handler" "plpgsql_inline_handler" "plpgsql_validator" "$libdir/plpgsql" _null_ ));
+DATA(insert ( "pltcl"          t t "pltcl_call_handler" _null_ _null_ "$libdir/pltcl" _null_ ));
+DATA(insert ( "pltclu"         f f "pltclu_call_handler" _null_ _null_ "$libdir/pltcl" _null_ ));
+DATA(insert ( "plperl"         t t "plperl_call_handler" _null_ "plperl_validator" "$libdir/plperl" _null_ ));
+DATA(insert ( "plperlu"                f f "plperl_call_handler" _null_ "plperl_validator" "$libdir/plperl" _null_ ));
+DATA(insert ( "plpythonu"      f f "plpython_call_handler" _null_ _null_ "$libdir/plpython" _null_ ));
 
 #endif   /* PG_PLTEMPLATE_H */
index 8c9c3cfce8fe8148257f92c906bd8b4549d20914..a230e4c34c0e30b3b1558c62704a1ac52fa9b1c1 100644 (file)
@@ -61,6 +61,7 @@ extern void DropCast(DropCastStmt *stmt);
 extern void DropCastById(Oid castOid);
 extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
                                           const char *newschema);
+extern void ExecuteDoStmt(DoStmt *stmt);
 
 /* commands/operatorcmds.c */
 extern void DefineOperator(List *names, List *parameters);
index 19e28e71f3a6cde3e61219bc807d980fa945b70e..3977081e7d046404e58379df492329f8d95ddf54 100644 (file)
@@ -274,6 +274,7 @@ typedef enum NodeTag
        T_CreateFunctionStmt,
        T_AlterFunctionStmt,
        T_RemoveFuncStmt,
+       T_DoStmt,
        T_RenameStmt,
        T_RuleStmt,
        T_NotifyStmt,
@@ -388,7 +389,8 @@ typedef enum NodeTag
        T_TriggerData = 950,            /* in commands/trigger.h */
        T_ReturnSetInfo,                        /* in nodes/execnodes.h */
        T_WindowObjectData,                     /* private in nodeWindowAgg.c */
-       T_TIDBitmap                                     /* in nodes/tidbitmap.h */
+       T_TIDBitmap,                            /* in nodes/tidbitmap.h */
+       T_InlineCodeBlock                       /* in nodes/parsenodes.h */
 } NodeTag;
 
 /*
index 8d5c0583eaffff2bb2409152af0932ac30b16945..076ed8b03af133b11277da200dc0bae68c35387a 100644 (file)
@@ -1570,6 +1570,7 @@ typedef struct CreatePLangStmt
        NodeTag         type;
        char       *plname;                     /* PL name */
        List       *plhandler;          /* PL call handler function (qual. name) */
+       List       *plinline;           /* optional inline function (qual. name) */
        List       *plvalidator;        /* optional validator function (qual. name) */
        bool            pltrusted;              /* PL is trusted */
 } CreatePLangStmt;
@@ -1922,6 +1923,25 @@ typedef struct RemoveFuncStmt
        bool            missing_ok;             /* skip error if missing? */
 } RemoveFuncStmt;
 
+/* ----------------------
+ *             DO Statement
+ *
+ * DoStmt is the raw parser output, InlineCodeBlock is the execution-time API
+ * ----------------------
+ */
+typedef struct DoStmt
+{
+       NodeTag         type;
+       List       *args;                       /* List of DefElem nodes */
+} DoStmt;
+
+typedef struct InlineCodeBlock
+{
+       NodeTag         type;
+       char       *source_text;        /* source text of anonymous code block */
+       Oid                     langOid;                /* OID of selected language */
+} InlineCodeBlock;
+
 /* ----------------------
  *             Drop Operator Class Statement
  * ----------------------
index 67e9cb4d4b26a97879efa127d25470fcbdbe2d2b..c8f776816dbf467c78158b204fd34bd6d160bcaf 100644 (file)
@@ -187,6 +187,7 @@ PG_KEYWORD("indexes", INDEXES, UNRESERVED_KEYWORD)
 PG_KEYWORD("inherit", INHERIT, UNRESERVED_KEYWORD)
 PG_KEYWORD("inherits", INHERITS, UNRESERVED_KEYWORD)
 PG_KEYWORD("initially", INITIALLY, RESERVED_KEYWORD)
+PG_KEYWORD("inline", INLINE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("inner", INNER_P, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("inout", INOUT, COL_NAME_KEYWORD)
 PG_KEYWORD("input", INPUT_P, UNRESERVED_KEYWORD)
@@ -204,7 +205,6 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD)
 PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD)
-PG_KEYWORD("lancompiler", LANCOMPILER, UNRESERVED_KEYWORD)
 PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD)
 PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD)
index 16055f542668708b272f0abedc60c10d0a61f722..7abe41ac73eb198296f11794ca9bf628510f4b01 100644 (file)
@@ -178,6 +178,8 @@ extern char *HbaFileName;
 extern char *IdentFileName;
 extern char *external_pid_file;
 
+extern char *default_do_language;
+
 extern int     tcp_keepalives_idle;
 extern int     tcp_keepalives_interval;
 extern int     tcp_keepalives_count;
index 767e78c448c510fef317404224a59a43c795e84e..34bd5c66865b19c5e14fe9ccbf3f76ba1b7e408c 100644 (file)
@@ -1614,12 +1614,12 @@ ECPGunreserved_con:       ABORT_P                       { $$ = make_str("abort"); }
                | INDEXES                       { $$ = make_str("indexes"); }
                | INHERIT                       { $$ = make_str("inherit"); }
                | INHERITS                      { $$ = make_str("inherits"); }
+               | INLINE_P                      { $$ = make_str("inline"); }
                | INSENSITIVE           { $$ = make_str("insensitive"); }
                | INSERT                        { $$ = make_str("insert"); }
                | INSTEAD                       { $$ = make_str("instead"); }
                | ISOLATION                     { $$ = make_str("isolation"); }
                | KEY                           { $$ = make_str("key"); }
-               | LANCOMPILER           { $$ = make_str("lancompiler"); }
                | LANGUAGE                      { $$ = make_str("language"); }
                | LARGE_P                       { $$ = make_str("large"); }
                | LAST_P                        { $$ = make_str("last"); }
index 1bec949b8f3ddf487c23258adf4563fc7127a835..e1cfbd2897cde7d1ad30a6dba85e0950525f7585 100644 (file)
@@ -95,6 +95,7 @@ static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
                   PLpgSQL_function *function,
                   PLpgSQL_func_hashkey *hashkey,
                   bool forValidator);
+static void add_dummy_return(PLpgSQL_function *function);
 static PLpgSQL_row *build_row_from_class(Oid classOid);
 static PLpgSQL_row *build_row_from_vars(PLpgSQL_variable **vars, int numvars);
 static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
@@ -670,36 +671,11 @@ do_compile(FunctionCallInfo fcinfo,
         * If it has OUT parameters or returns VOID or returns a set, we allow
         * control to fall off the end without an explicit RETURN statement. The
         * easiest way to implement this is to add a RETURN statement to the end
-        * of the statement list during parsing.  However, if the outer block has
-        * an EXCEPTION clause, we need to make a new outer block, since the added
-        * RETURN shouldn't act like it is inside the EXCEPTION clause.
+        * of the statement list during parsing.
         */
        if (num_out_args > 0 || function->fn_rettype == VOIDOID ||
                function->fn_retset)
-       {
-               if (function->action->exceptions != NULL)
-               {
-                       PLpgSQL_stmt_block *new;
-
-                       new = palloc0(sizeof(PLpgSQL_stmt_block));
-                       new->cmd_type = PLPGSQL_STMT_BLOCK;
-                       new->body = list_make1(function->action);
-
-                       function->action = new;
-               }
-               if (function->action->body == NIL ||
-                       ((PLpgSQL_stmt *) llast(function->action->body))->cmd_type != PLPGSQL_STMT_RETURN)
-               {
-                       PLpgSQL_stmt_return *new;
-
-                       new = palloc0(sizeof(PLpgSQL_stmt_return));
-                       new->cmd_type = PLPGSQL_STMT_RETURN;
-                       new->expr = NULL;
-                       new->retvarno = function->out_param_varno;
-
-                       function->action->body = lappend(function->action->body, new);
-               }
-       }
+               add_dummy_return(function);
 
        /*
         * Complete the function's info
@@ -735,12 +711,150 @@ do_compile(FunctionCallInfo fcinfo,
        return function;
 }
 
+/* ----------
+ * plpgsql_compile_inline      Make an execution tree for an anonymous code block.
+ *
+ * Note: this is generally parallel to do_compile(); is it worth trying to
+ * merge the two?
+ *
+ * Note: we assume the block will be thrown away so there is no need to build
+ * persistent data structures.
+ * ----------
+ */
+PLpgSQL_function *
+plpgsql_compile_inline(char *proc_source)
+{
+       char       *func_name = "inline_code_block";
+       PLpgSQL_function *function;
+       ErrorContextCallback plerrcontext;
+       Oid                     typinput;
+       PLpgSQL_variable *var;
+       int                     parse_rc;
+       MemoryContext func_cxt;
+       int                     i;
+
+       /*
+        * Setup the scanner input and error info.      We assume that this function
+        * cannot be invoked recursively, so there's no need to save and restore
+        * the static variables used here.
+        */
+       plpgsql_scanner_init(proc_source);
+
+       plpgsql_error_funcname = func_name;
+       plpgsql_error_lineno = 0;
+
+       /*
+        * Setup error traceback support for ereport()
+        */
+       plerrcontext.callback = plpgsql_compile_error_callback;
+       plerrcontext.arg = proc_source;
+       plerrcontext.previous = error_context_stack;
+       error_context_stack = &plerrcontext;
+
+       plpgsql_ns_init();
+       plpgsql_ns_push(func_name);
+       plpgsql_DumpExecTree = false;
+
+       datums_alloc = 128;
+       plpgsql_nDatums = 0;
+       plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc);
+       datums_last = 0;
+
+       /* Do extra syntax checking if check_function_bodies is on */
+       plpgsql_check_syntax = check_function_bodies;
+
+       /* Function struct does not live past current statement */
+       function = (PLpgSQL_function *) palloc0(sizeof(PLpgSQL_function));
+
+       plpgsql_curr_compile = function;
+
+       /*
+        * All the rest of the compile-time storage (e.g. parse tree) is kept in
+        * its own memory context, so it can be reclaimed easily.
+        */
+       func_cxt = AllocSetContextCreate(CurrentMemoryContext,
+                                                                        "PL/PgSQL function context",
+                                                                        ALLOCSET_DEFAULT_MINSIZE,
+                                                                        ALLOCSET_DEFAULT_INITSIZE,
+                                                                        ALLOCSET_DEFAULT_MAXSIZE);
+       compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
+
+       function->fn_name = pstrdup(func_name);
+       function->fn_is_trigger = false;
+       function->fn_cxt = func_cxt;
+       function->out_param_varno = -1;         /* set up for no OUT param */
+
+       /* Set up as though in a function returning VOID */
+       function->fn_rettype = VOIDOID;
+       function->fn_retset = false;
+       function->fn_retistuple = false;
+       /* a bit of hardwired knowledge about type VOID here */
+       function->fn_retbyval = true;
+       function->fn_rettyplen = sizeof(int32);
+       getTypeInputInfo(VOIDOID, &typinput, &function->fn_rettypioparam);
+       fmgr_info(typinput, &(function->fn_retinput));
+
+       /*
+        * Remember if function is STABLE/IMMUTABLE.  XXX would it be better
+        * to set this TRUE inside a read-only transaction?  Not clear.
+        */
+       function->fn_readonly = false;
+
+       /*
+        * Create the magic FOUND variable.
+        */
+       var = plpgsql_build_variable("found", 0,
+                                                                plpgsql_build_datatype(BOOLOID, -1),
+                                                                true);
+       function->found_varno = var->dno;
+
+       /*
+        * Now parse the function's text
+        */
+       parse_rc = plpgsql_yyparse();
+       if (parse_rc != 0)
+               elog(ERROR, "plpgsql parser returned %d", parse_rc);
+       function->action = plpgsql_yylval.program;
+
+       plpgsql_scanner_finish();
+
+       /*
+        * If it returns VOID (always true at the moment), we allow control to
+        * fall off the end without an explicit RETURN statement.
+        */
+       if (function->fn_rettype == VOIDOID)
+               add_dummy_return(function);
+
+       /*
+        * Complete the function's info
+        */
+       function->fn_nargs = 0;
+       function->ndatums = plpgsql_nDatums;
+       function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
+       for (i = 0; i < plpgsql_nDatums; i++)
+               function->datums[i] = plpgsql_Datums[i];
+
+       /*
+        * Pop the error context stack
+        */
+       error_context_stack = plerrcontext.previous;
+       plpgsql_error_funcname = NULL;
+       plpgsql_error_lineno = 0;
+
+       plpgsql_check_syntax = false;
+
+       MemoryContextSwitchTo(compile_tmp_cxt);
+       compile_tmp_cxt = NULL;
+       return function;
+}
+
 
 /*
- * error context callback to let us supply a call-stack traceback. If
- * we are validating, the function source is passed as an
- * argument. This function is public only for the sake of an assertion
- * in gram.y
+ * error context callback to let us supply a call-stack traceback.
+ * If we are validating or executing an anonymous code block, the function
+ * source text is passed as an argument.
+ *
+ * This function is public only for the sake of an assertion in gram.y
  */
 void
 plpgsql_compile_error_callback(void *arg)
@@ -749,7 +863,7 @@ plpgsql_compile_error_callback(void *arg)
        {
                /*
                 * Try to convert syntax error position to reference text of original
-                * CREATE FUNCTION command.
+                * CREATE FUNCTION or DO command.
                 */
                if (function_parse_error_transpose((const char *) arg))
                        return;
@@ -766,6 +880,42 @@ plpgsql_compile_error_callback(void *arg)
 }
 
 
+/*
+ * Add a dummy RETURN statement to the given function's body
+ */
+static void
+add_dummy_return(PLpgSQL_function *function)
+{
+       /*
+        * If the outer block has an EXCEPTION clause, we need to make a new outer
+        * block, since the added RETURN shouldn't act like it is inside the
+        * EXCEPTION clause.
+        */
+       if (function->action->exceptions != NULL)
+       {
+               PLpgSQL_stmt_block *new;
+
+               new = palloc0(sizeof(PLpgSQL_stmt_block));
+               new->cmd_type = PLPGSQL_STMT_BLOCK;
+               new->body = list_make1(function->action);
+
+               function->action = new;
+       }
+       if (function->action->body == NIL ||
+               ((PLpgSQL_stmt *) llast(function->action->body))->cmd_type != PLPGSQL_STMT_RETURN)
+       {
+               PLpgSQL_stmt_return *new;
+
+               new = palloc0(sizeof(PLpgSQL_stmt_return));
+               new->cmd_type = PLPGSQL_STMT_RETURN;
+               new->expr = NULL;
+               new->retvarno = function->out_param_varno;
+
+               function->action->body = lappend(function->action->body, new);
+       }
+}
+
+
 /* ----------
  * plpgsql_parse_word          The scanner calls this to postparse
  *                             any single word not found by a
index 7c2818c21ca659bbe9ec707f07e4eaadb2ffb35e..fc61d20e9cdbca2988bc7ac9a653abe27c8d85db 100644 (file)
@@ -114,6 +114,57 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
        return retval;
 }
 
+/* ----------
+ * plpgsql_inline_handler
+ *
+ * Called by PostgreSQL to execute an anonymous code block
+ * ----------
+ */
+PG_FUNCTION_INFO_V1(plpgsql_inline_handler);
+
+Datum
+plpgsql_inline_handler(PG_FUNCTION_ARGS)
+{
+       InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
+       PLpgSQL_function *func;
+       FunctionCallInfoData fake_fcinfo;
+       FmgrInfo        flinfo;
+       Datum           retval;
+       int                     rc;
+
+       Assert(IsA(codeblock, InlineCodeBlock));
+
+       /*
+        * Connect to SPI manager
+        */
+       if ((rc = SPI_connect()) != SPI_OK_CONNECT)
+               elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
+
+       /* Compile the anonymous code block */
+       func = plpgsql_compile_inline(codeblock->source_text);
+
+       /*
+        * Set up a fake fcinfo with just enough info to satisfy
+        * plpgsql_exec_function().  In particular note that this sets things up
+        * with no arguments passed.
+        */
+       MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+       MemSet(&flinfo, 0, sizeof(flinfo));
+       fake_fcinfo.flinfo = &flinfo;
+       flinfo.fn_oid = InvalidOid;
+       flinfo.fn_mcxt = CurrentMemoryContext;
+
+       retval = plpgsql_exec_function(func, &fake_fcinfo);
+
+       /*
+        * Disconnect from SPI manager
+        */
+       if ((rc = SPI_finish()) != SPI_OK_FINISH)
+               elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
+
+       return retval;
+}
+
 /* ----------
  * plpgsql_validator
  *
index d7b279770c1f5248eee5964dbcfbe672972dc26b..98cabd19f0e1c88a8722413fb3b819177d74ff86 100644 (file)
@@ -799,6 +799,7 @@ extern PLpgSQL_plugin **plugin_ptr;
  */
 extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
                                bool forValidator);
+extern PLpgSQL_function *plpgsql_compile_inline(char *proc_source);
 extern int     plpgsql_parse_word(const char *word);
 extern int     plpgsql_parse_dblword(const char *word);
 extern int     plpgsql_parse_tripword(const char *word);
@@ -828,6 +829,7 @@ extern void plpgsql_compile_error_callback(void *arg);
  */
 extern void _PG_init(void);
 extern Datum plpgsql_call_handler(PG_FUNCTION_ARGS);
+extern Datum plpgsql_inline_handler(PG_FUNCTION_ARGS);
 extern Datum plpgsql_validator(PG_FUNCTION_ARGS);
 
 /* ----------
index 078c14d81583e8d5afd68fbb314925df70fad38e..ca4c9dc23314f8defe5ff606701d198814495cb5 100644 (file)
@@ -3861,3 +3861,40 @@ NOTICE:  foo\bar!baz
 (1 row)
 
 drop function strtest();
+-- Test anonymous code blocks.
+DO $$
+DECLARE r record;
+BEGIN 
+    FOR r IN SELECT rtrim(roomno) AS roomno, comment FROM Room ORDER BY roomno
+    LOOP
+        RAISE NOTICE '%, %', r.roomno, r.comment;
+    END LOOP;
+END$$ LANGUAGE plpgsql;
+NOTICE:  001, Entrance
+NOTICE:  002, Office
+NOTICE:  003, Office
+NOTICE:  004, Technical
+NOTICE:  101, Office
+NOTICE:  102, Conference
+NOTICE:  103, Restroom
+NOTICE:  104, Technical
+NOTICE:  105, Office
+NOTICE:  106, Office
+-- these are to check syntax error reporting
+DO LANGUAGE plpgsql $$begin return 1; end$$;
+ERROR:  RETURN cannot have a parameter in function returning void at or near "1"
+LINE 1: DO LANGUAGE plpgsql $$begin return 1; end$$;
+                                           ^
+DO LANGUAGE plpgsql $$
+DECLARE r record;
+BEGIN 
+    FOR r IN SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
+    LOOP
+        RAISE NOTICE '%, %', r.roomno, r.comment;
+    END LOOP;
+END$$;
+ERROR:  column "foo" does not exist
+LINE 1:  SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY room...
+                                         ^
+QUERY:   SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
+CONTEXT:  PL/pgSQL function "inline_code_block" line 3 at FOR over SELECT rows
index da12211847858025dbea6a22ff8a6cdd45bfd757..96f89144b7735fa866ea31ff2fbbb78ea240d3f5 100644 (file)
@@ -3079,3 +3079,26 @@ $$ language plpgsql;
 select strtest();
 
 drop function strtest();
+
+-- Test anonymous code blocks.
+
+DO $$
+DECLARE r record;
+BEGIN 
+    FOR r IN SELECT rtrim(roomno) AS roomno, comment FROM Room ORDER BY roomno
+    LOOP
+        RAISE NOTICE '%, %', r.roomno, r.comment;
+    END LOOP;
+END$$ LANGUAGE plpgsql;
+
+-- these are to check syntax error reporting
+DO LANGUAGE plpgsql $$begin return 1; end$$;
+
+DO LANGUAGE plpgsql $$
+DECLARE r record;
+BEGIN 
+    FOR r IN SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
+    LOOP
+        RAISE NOTICE '%, %', r.roomno, r.comment;
+    END LOOP;
+END$$;