diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 3f7fdf16b45e377848d1488a172fed673c76d8bb..dd103573a5d68c2c9f6df2ef106fac6e5ee6a2b4 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.206 2009/08/10 22:13:50 alvherre Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.207 2009/09/22 23:43:37 tgl Exp $ --> <!-- Documentation of the system catalogs, directed toward PostgreSQL developers --> @@ -2941,6 +2941,18 @@ </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> @@ -3547,6 +3559,12 @@ <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> diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index fa2a12feab1987fd4a0a3066ee4415d2c9a6c7b0..ee28bbb107902560c26ced80fd8a946f07c7d492 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.228 2009/09/13 19:52:29 petere Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.229 2009/09/22 23:43:37 tgl Exp $ --> <chapter Id="runtime-config"> <title>Server Configuration</title> @@ -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> diff --git a/doc/src/sgml/keywords.sgml b/doc/src/sgml/keywords.sgml index cb167ce10b4e3c48c529b3e025cfd04990ec8250..8bfede77d6fe63311de304f75d6aed00e9c26261 100644 --- a/doc/src/sgml/keywords.sgml +++ b/doc/src/sgml/keywords.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/keywords.sgml,v 2.25 2009/04/06 15:01:36 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/keywords.sgml,v 2.26 2009/09/22 23:43:37 tgl Exp $ --> <appendix id="sql-keywords-appendix"> <title><acronym>SQL</acronym> Key Words</title> @@ -2375,6 +2375,14 @@ <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> @@ -2575,14 +2583,6 @@ <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> diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index 6c20b623c49ad8f866edab0b3256de0b736fd639..845033b6b6696b2bfe11e60f93c99c8dcda35974 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.74 2008/12/19 16:25:16 petere Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.75 2009/09/22 23:43:37 tgl Exp $ PostgreSQL documentation Complete list of usable sgml source files in this directory. --> @@ -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"> diff --git a/doc/src/sgml/ref/create_language.sgml b/doc/src/sgml/ref/create_language.sgml index ae02995e37fea41c6bdda02a85a006d180409a89..4c0463ddec1f251eafb1f9a70fe8798de94c5d00 100644 --- a/doc/src/sgml/ref/create_language.sgml +++ b/doc/src/sgml/ref/create_language.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/create_language.sgml,v 1.45 2008/11/14 10:22:46 petere Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/create_language.sgml,v 1.46 2009/09/22 23:43:37 tgl Exp $ PostgreSQL documentation --> @@ -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 index 0000000000000000000000000000000000000000..2fb538066303d82e5ccc103afdfd40814e3db453 --- /dev/null +++ b/doc/src/sgml/ref/do.sgml @@ -0,0 +1,122 @@ +<!-- +$PostgreSQL: pgsql/doc/src/sgml/ref/do.sgml,v 1.1 2009/09/22 23:43:37 tgl Exp $ +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> diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index d3a862959d900c224ecd0207fcc86f652cfe1974..48f8040541dfda098d658bac66b54f719e4dce2b 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.67 2008/12/19 16:25:16 petere Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.68 2009/09/22 23:43:37 tgl Exp $ --> <part id="reference"> <title>Reference</title> @@ -105,6 +105,7 @@ &declare; &delete; &discard; + &do; &dropAggregate; &dropCast; &dropConversion; diff --git a/doc/src/sgml/xplang.sgml b/doc/src/sgml/xplang.sgml index 9882d835e35f6c345b2078f5824dc85906746d4f..b48b78f95ba6ae27b766afda31174ada22b03378 100644 --- a/doc/src/sgml/xplang.sgml +++ b/doc/src/sgml/xplang.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/xplang.sgml,v 1.34 2007/02/01 00:28:18 momjian Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/xplang.sgml,v 1.35 2009/09/22 23:43:37 tgl Exp $ --> <chapter id="xplang"> <title id="xplang-title">Procedural Languages</title> @@ -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 diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 64920f5d2a89c7504ee375ad6e153f2d31836cba..41c6d1e2fb16314f8b8ed7d04dbce3e754dc0715 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.164 2009/06/11 14:48:55 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.165 2009/09/22 23:43:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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. diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 2151fd94f09d0e4a270c8654028881c2c1515c1d..cf206b3f090fee193cd2fd2d943a02f4d09d386c 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.110 2009/06/11 14:48:55 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.111 2009/09/22 23:43:37 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -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)); +} diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 27af379d7e154ba93b8ddd74ac5c21c00f2d621c..2a5bcd6363de918ad6028e346b6e848d4433bf64 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.86 2009/07/12 17:12:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.87 2009/09/22 23:43:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 48039b86cb9735e0389d4e3d0e1f49ed50e69398..5feff5d1691d75f5b431ced8180573179f3eb761 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.437 2009/07/30 02:45:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.438 2009/09/22 23:43:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 6c75f2e2747f0e94c69a3cd026596a2cb912b1c1..d7ed08cc68b77755781f036ee014150be2a2c29d 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.360 2009/07/30 02:45:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.361 2009/09/22 23:43:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 81d57f65fb5c20954835a6567965b84a70213090..9a203a5a48dd10ada3f107eae8a747ca6ff9c9ec 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.678 2009/09/21 20:10:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.679 2009/09/22 23:43:38 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -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 diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index c6fefccfc6f6110298f22f005b9405efb7462355..0d2079fc0aaba3aa1b045dcbfd2c61ebae055575 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.313 2009/07/29 20:56:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.314 2009/09/22 23:43:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 17bbbd7cb4aa9f4ed3579e4fe210bc8afbb5e6fa..cb743bb55ef4219dc311cf967aeb4aade81bd629 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut <peter_e@gmx.net>. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.518 2009/09/17 21:15:18 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.519 2009/09/22 23:43:38 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -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 diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index b10775cc2df8de67966a7cfb29e83368e9639a90..d6cf7e21c732ede9b2cb42c3f29cc5b4ab929cd6 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -422,6 +422,7 @@ #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' diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 5856dc7049a0fd3a9cb5ab5322d04002f13841d5..34ebc27168dbd853ca8d658d6339eaf63cac56b5 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.547 2009/09/11 19:17:04 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.548 2009/09/22 23:43:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 "); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 6b84d59723dbe8506e8189dd03a425b71065501c..beec160110c590bb5feb3642309ca02f21b61236 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.156 2009/08/02 22:14:52 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.157 2009/09/22 23:43:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 */ diff --git a/src/bin/scripts/droplang.c b/src/bin/scripts/droplang.c index 7038e08b8af57bc79abe324cad52f1762b473b03..16898e264a9cc3e9704c8e85867ddc1a1228087c 100644 --- a/src/bin/scripts/droplang.c +++ b/src/bin/scripts/droplang.c @@ -5,7 +5,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/scripts/droplang.c,v 1.31 2009/02/26 16:02:39 petere Exp $ + * $PostgreSQL: pgsql/src/bin/scripts/droplang.c,v 1.32 2009/09/22 23:43:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index e162aa71076875a76e5c8836e960bcc974bf3674..5e0fc241549e41442871cc96b6779049dc1e204c 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.538 2009/09/01 03:53:08 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.539 2009/09/22 23:43:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200908311 +#define CATALOG_VERSION_NO 200909221 #endif diff --git a/src/include/catalog/pg_language.h b/src/include/catalog/pg_language.h index dd8d35e830df744cda258fefdb156f5d25caf209..dff946b4119da7c24bbf98356bda1fc09cc475fc 100644 --- a/src/include/catalog/pg_language.h +++ b/src/include/catalog/pg_language.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_language.h,v 1.34 2009/01/01 17:23:57 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_language.h,v 1.35 2009/09/22 23:43:41 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -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 diff --git a/src/include/catalog/pg_pltemplate.h b/src/include/catalog/pg_pltemplate.h index 9775133ceb5584a9ad6bfededfc2afd58dd155aa..2065a5332bb218ffbd0e8a6deebd51fc43f2912c 100644 --- a/src/include/catalog/pg_pltemplate.h +++ b/src/include/catalog/pg_pltemplate.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_pltemplate.h,v 1.7 2009/01/01 17:23:57 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_pltemplate.h,v 1.8 2009/09/22 23:43:41 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -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 */ diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 4396a497385be58ddbce3ab84c5a9b3a012b5ae4..89bb227a90f27caa6d7c861a6ae390be0b6e5c52 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.96 2009/07/29 20:56:20 tgl Exp $ + * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.97 2009/09/22 23:43:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6be4bf3959806b475dbfdbd09f40567aceae95d9..d842c9c3199f83d83f5822a502fd510be300794c 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.225 2009/09/17 20:49:29 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.226 2009/09/22 23:43:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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; /* diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index f0b3941abf8f0c6b0faa3b383cc8f56bd2fc635c..1ce28b77541ff6cb835576b56b4ff01baf8847f2 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.401 2009/08/02 22:14:53 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.402 2009/09/22 23:43:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 * ---------------------- diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 23f5d87a7a68deb78c0eadea8c147704b1ea7a8d..d1fd91795b60386ad169b575871d3992bdd0cea3 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.2 2009/04/06 08:42:53 heikki Exp $ + * $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.3 2009/09/22 23:43:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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) diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 6f4acdef70185aaaecf0a64541d6e534e2e5b229..435a722aa0b6b7f93657e5eade41a9ba1a35f00a 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -7,7 +7,7 @@ * Copyright (c) 2000-2009, PostgreSQL Global Development Group * Written by Peter Eisentraut <peter_e@gmx.net>. * - * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.104 2009/09/03 22:08:05 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.105 2009/09/22 23:43:41 tgl Exp $ *-------------------------------------------------------------------- */ #ifndef GUC_H @@ -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; diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer index 6040a4b012d50a271bcceb7aa5aba04f6e64dcbb..5eb43937ca8d68721042417993a1e8f46d942f4d 100644 --- a/src/interfaces/ecpg/preproc/ecpg.trailer +++ b/src/interfaces/ecpg/preproc/ecpg.trailer @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/ecpg.trailer,v 1.11 2009/08/14 13:28:22 meskes Exp $ */ +/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/ecpg.trailer,v 1.12 2009/09/22 23:43:42 tgl Exp $ */ statements: /*EMPTY*/ | statements statement @@ -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"); } diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index c589aa1994dbe39926a359611f6791ce46d07f68..604b7dbf0865479afd8e8301bf5ede35e1064f66 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.138 2009/09/20 01:53:32 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.139 2009/09/22 23:43:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index 2abe67c85262fe8361ad8b22cee8302d82f5429e..4f506eb97ca29e3b718dc60b563695a137c43843 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.45 2009/08/04 21:22:46 alvherre Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.46 2009/09/22 23:43:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 * diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 21c9edb52cad02219aba6a3dc7de668bbfc62de3..fb627e7a898128e0540374938634e57be076023e 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.115 2009/08/04 21:22:46 alvherre Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.116 2009/09/22 23:43:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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); /* ---------- diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 078c14d81583e8d5afd68fbb314925df70fad38e..ca4c9dc23314f8defe5ff606701d198814495cb5 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -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 diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index da12211847858025dbea6a22ff8a6cdd45bfd757..96f89144b7735fa866ea31ff2fbbb78ea240d3f5 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -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$$;