Implement the DO statement to support execution of PL code without having
authorTom Lane <[email protected]>
Tue, 22 Sep 2009 23:43:43 +0000 (23:43 +0000)
committerTom Lane <[email protected]>
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 , 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$$;