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
</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>
</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>
<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>
<!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">
<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>
<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
</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>
</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;
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</>.
--- /dev/null
+<!--
+$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>
&declare;
&delete;
&discard;
+ &do;
&dropAggregate;
&dropCast;
&dropConversion;
</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>
</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
</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
</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>
<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
/*
* 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.
*/
/*
* 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
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.
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));
+}
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);
char *languageName;
PLTemplate *pltemplate;
Oid handlerOid,
+ inlineOid,
valOid;
Oid funcrettype;
Oid funcargtypes[1];
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.
false, /* isAgg */
false, /* isWindowFunc */
false, /* security_definer */
- false, /* isStrict */
+ true, /* isStrict */
PROVOLATILE_VOLATILE,
buildoidvector(funcargtypes, 1),
PointerGetDatum(NULL),
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
{
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)
{
valOid = InvalidOid;
/* ok, create it */
- create_proc_lang(languageName, GetUserId(), handlerOid, valOid,
- stmt->pltrusted);
+ create_proc_lang(languageName, GetUserId(), handlerOid, inlineOid,
+ valOid, stmt->pltrusted);
}
}
*/
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;
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;
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))
{
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)
return newnode;
}
+static DoStmt *
+_copyDoStmt(DoStmt *from)
+{
+ DoStmt *newnode = makeNode(DoStmt);
+
+ COPY_NODE_FIELD(args);
+
+ return newnode;
+}
+
static RemoveOpClassStmt *
_copyRemoveOpClassStmt(RemoveOpClassStmt *from)
{
COPY_STRING_FIELD(plname);
COPY_NODE_FIELD(plhandler);
+ COPY_NODE_FIELD(plinline);
COPY_NODE_FIELD(plvalidator);
COPY_SCALAR_FIELD(pltrusted);
case T_RemoveFuncStmt:
retval = _copyRemoveFuncStmt(from);
break;
+ case T_DoStmt:
+ retval = _copyDoStmt(from);
+ break;
case T_RemoveOpClassStmt:
retval = _copyRemoveOpClassStmt(from);
break;
return true;
}
+static bool
+_equalDoStmt(DoStmt *a, DoStmt *b)
+{
+ COMPARE_NODE_FIELD(args);
+
+ return true;
+}
+
static bool
_equalRemoveOpClassStmt(RemoveOpClassStmt *a, RemoveOpClassStmt *b)
{
{
COMPARE_STRING_FIELD(plname);
COMPARE_NODE_FIELD(plhandler);
+ COMPARE_NODE_FIELD(plinline);
COMPARE_NODE_FIELD(plvalidator);
COMPARE_SCALAR_FIELD(pltrusted);
case T_RemoveFuncStmt:
retval = _equalRemoveFuncStmt(a, b);
break;
+ case T_DoStmt:
+ retval = _equalDoStmt(a, b);
+ break;
case T_RemoveOpClassStmt:
retval = _equalRemoveOpClassStmt(a, b);
break;
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
%type <list> OptSchemaEltList
%type <boolean> TriggerActionTime TriggerForSpec opt_trusted opt_restart_seqs
-%type <str> opt_lancompiler
%type <ival> TriggerEvents TriggerOneEvent
%type <value> TriggerFuncArg
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
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
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
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
| DefineStmt
| DeleteStmt
| DiscardStmt
+ | DoStmt
| DropAssertStmt
| DropCastStmt
| DropFdwStmt
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;
}
;
| 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; }
| /*EMPTY*/ { $$ = NIL; }
;
-opt_lancompiler:
- LANCOMPILER Sconst { $$ = $2; }
- | /*EMPTY*/ { $$ = NULL; }
- ;
-
DropPLangStmt:
DROP opt_procedural LANGUAGE ColId_or_Sconst opt_drop_behavior
{
{ $$ = 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));
+ }
+ ;
/*****************************************************************************
*
| INDEXES
| INHERIT
| INHERITS
+ | INLINE_P
| INPUT_P
| INSENSITIVE
| INSERT
| INVOKER
| ISOLATION
| KEY
- | LANCOMPILER
| LANGUAGE
| LARGE_P
| LAST_P
}
break;
+ case T_DoStmt:
+ ExecuteDoStmt((DoStmt *) parsetree);
+ break;
+
case T_CreatedbStmt:
PreventTransactionChain(isTopLevel, "CREATE DATABASE");
createdb((CreatedbStmt *) parsetree);
}
break;
+ case T_DoStmt:
+ tag = "DO";
+ break;
+
case T_CreatedbStmt:
tag = "CREATE DATABASE";
break;
lev = LOGSTMT_DDL;
break;
+ case T_DoStmt:
+ lev = LOGSTMT_ALL;
+ break;
+
case T_CreatedbStmt:
lev = LOGSTMT_DDL;
break;
char *pgstat_temp_directory;
+char *default_do_language;
+
int tcp_keepalives_idle;
int tcp_keepalives_interval;
int tcp_keepalives_count;
},
#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
#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'
int i_lanname;
int i_lanpltrusted;
int i_lanplcallfoid;
+ int i_laninline;
int i_lanvalidator;
int i_lanacl;
int i_lanowner;
/* 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, "
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");
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
char *qlanname;
char *lanschema;
FuncInfo *funcInfo;
+ FuncInfo *inlineInfo = NULL;
FuncInfo *validatorInfo = NULL;
if (dataOnly)
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);
* dump it.
*/
useParams = (funcInfo != NULL &&
+ (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
(validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
if (!useParams && !shouldDumpProcLangs())
{
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 ");
DumpableObject dobj;
bool lanpltrusted;
Oid lanplcallfoid;
+ Oid laninline;
Oid lanvalidator;
char *lanacl;
char *lanowner; /* name of owner, or empty string */
const char *progname;
int optindex;
int c;
-
bool listlangs = false;
const char *dbname = NULL;
char *host = NULL;
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;
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);
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);
/*
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
*/
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);
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200908311
+#define CATALOG_VERSION_NO 200909221
#endif
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;
* 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
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 */
* 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
/* ----------------
* ----------------
*/
-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 */
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);
T_CreateFunctionStmt,
T_AlterFunctionStmt,
T_RemoveFuncStmt,
+ T_DoStmt,
T_RenameStmt,
T_RuleStmt,
T_NotifyStmt,
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;
/*
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;
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
* ----------------------
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)
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)
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;
| 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"); }
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);
* 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
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)
{
/*
* 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;
}
+/*
+ * 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
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
*
*/
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);
*/
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);
/* ----------
(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
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$$;