Add optional "validator" function to languages that can validate the
authorPeter Eisentraut <peter_e@gmx.net>
Wed, 22 May 2002 17:21:02 +0000 (17:21 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Wed, 22 May 2002 17:21:02 +0000 (17:21 +0000)
function body (and other properties) as a function in the language
is created.  This generalizes ad hoc code that already existed for
the built-in languages.

The validation now happens after the pg_proc tuple of the new function
is created, so it is possible to define recursive SQL functions.

Add some regression test cases that cover bogus function definition
attempts.

18 files changed:
doc/src/sgml/ref/create_language.sgml
doc/src/sgml/release.sgml
src/backend/catalog/pg_aggregate.c
src/backend/catalog/pg_proc.c
src/backend/commands/functioncmds.c
src/backend/commands/proclang.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/utils/adt/sets.c
src/bin/pg_dump/pg_dump.c
src/include/catalog/catversion.h
src/include/catalog/pg_language.h
src/include/catalog/pg_proc.h
src/include/nodes/parsenodes.h
src/test/regress/input/create_function_1.source
src/test/regress/output/create_function_1.source

index c956eda4dd00095b12206dd205635f0b708ca4a1..0f599d3f14f886763976e162a0e889febba66569 100644 (file)
@@ -17,7 +17,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">langname</replaceable>
-    HANDLER <replaceable class="parameter">call_handler</replaceable>
+    HANDLER <replaceable class="parameter">call_handler</replaceable> [ VALIDATOR <replaceable>valfunction</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -113,6 +113,32 @@ CREATE [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">langna
       </para>
      </listitem>
     </varlistentry>
+
+    <varlistentry>
+     <term><literal>VALIDATOR</literal> <replaceable class="parameter">valfunction</replaceable></term>
+
+     <listitem>
+      <para>
+       <replaceable class="parameter">valfunction</replaceable> is the
+       name of a previously registered function that will be called
+       when a new function in the language is created, to validate the
+       new function.  The validator function must take one argument of
+       type <type>oid</type>, which will be the OID of the
+       to-be-created function, and can have any return type.  If no
+       validator function is specified, then a new function will not
+       be checked when it is created.
+      </para>
+
+      <para>
+       A validator function would typically inspect the function body
+       for syntactical correctness, but it can also look at other
+       properties of the function, for example if the language cannot
+       handle certain argument types.  To signal an error, the
+       validator function should use the <function>elog()</function>
+       function.  The return value of the function is ignored.
+      </para>
+     </listitem>
+    </varlistentry>
    </variablelist>
  </refsect1>
 
index 94657108b499ae597048c188efe09b18845597cc..c3f5d6ecae33d593cb5913753574db3bc31c042c 100644 (file)
@@ -24,6 +24,8 @@ CDATA means the content is "SGML-free", so you can write without
 worries about funny characters.
 -->
 <literallayout><![CDATA[
+Recursive SQL functions can be defined
+User-defined procedural languages can register a validator function to check new functions as they are created
 Functions can be executed with the privileges of the owner
 Syntax of CREATE FUNCTION has been extended to resemble SQL99
 Effects of SET within a transaction block now roll back if transaction aborts
@@ -72,7 +74,7 @@ Database and user-specific session defaults for run-time configuration variables
     <title>Changes</title>
 
     <para>
-     <programlisting>
+<literallayout>
 Ensure that sequence counters do not go backwards after a crash (Tom)
 Fix pgaccess kanji-coversion key binding (Tatsuo)
 Optimizer improvements (Tom)
@@ -90,7 +92,7 @@ contrib/tsearch dictionary improvements, see README.tsearch for
   an additional installation step (Thomas T. Thai, Teodor Sigaev)
 Fix for array subscripts handling (Tom)
 Allow EXECUTE of "CREATE TABLE AS ... SELECT" in PL/PgSQL (Tom)
-     </programlisting>
+</literallayout>
     </para>
    </sect2>
   </sect1>
index 60a8508e1f6ea91292bbea59609d8a85ffe0710a..b1fef480558f304bd7b91a53d2a8020642a99a7b 100644 (file)
@@ -136,6 +136,7 @@ AggregateCreate(const char *aggName,
                                                          false,                /* doesn't return a set */
                                                          finaltype,    /* returnType */
                                                          INTERNALlanguageId,   /* languageObjectId */
+                                                         0,
                                                          "aggregate_dummy",    /* placeholder proc */
                                                          "-",                  /* probin */
                                                          true,                 /* isAgg */
index 6a686e8a4f04d5d39ec08b6cb02ab38ab8fb8cdd..60cd0e779edd0916ec1fe8a4e48bfc71fe6ecca1 100644 (file)
@@ -20,6 +20,7 @@
 #include "catalog/pg_language.h"
 #include "catalog/pg_proc.h"
 #include "executor/executor.h"
+#include "fmgr.h"
 #include "miscadmin.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
@@ -32,6 +33,9 @@
 
 
 static void checkretval(Oid rettype, List *queryTreeList);
+Datum fmgr_internal_validator(PG_FUNCTION_ARGS);
+Datum fmgr_c_validator(PG_FUNCTION_ARGS);
+Datum fmgr_sql_validator(PG_FUNCTION_ARGS);
 
 
 /* ----------------------------------------------------------------
@@ -45,6 +49,7 @@ ProcedureCreate(const char *procedureName,
                                bool returnsSet,
                                Oid returnType,
                                Oid languageObjectId,
+                               Oid languageValidator,
                                const char *prosrc,
                                const char *probin,
                                bool isAgg,
@@ -66,7 +71,6 @@ ProcedureCreate(const char *procedureName,
        char            nulls[Natts_pg_proc];
        Datum           values[Natts_pg_proc];
        char            replaces[Natts_pg_proc];
-       List       *querytree_list;
        Oid                     typev[FUNC_MAX_ARGS];
        Oid                     relid;
        NameData        procname;
@@ -126,12 +130,6 @@ ProcedureCreate(const char *procedureName,
                }
        }
 
-       if (!OidIsValid(returnType))
-       {
-               if (languageObjectId == SQLlanguageId)
-                       elog(ERROR, "SQL functions cannot return type \"opaque\"");
-       }
-
        /*
         * don't allow functions of complex types that have the same name as
         * existing attributes of the type
@@ -142,65 +140,6 @@ ProcedureCreate(const char *procedureName,
                elog(ERROR, "method %s already an attribute of type %s",
                         procedureName, format_type_be(typev[0]));
 
-       /*
-        * If this is a postquel procedure, we parse it here in order to be
-        * sure that it contains no syntax errors.      We should store the plan
-        * in an Inversion file for use later, but for now, we just store the
-        * procedure's text in the prosrc attribute.
-        */
-
-       if (languageObjectId == SQLlanguageId)
-       {
-               querytree_list = pg_parse_and_rewrite((char *) prosrc,
-                                                                                         typev,
-                                                                                         parameterCount);
-               /* typecheck return value */
-               checkretval(returnType, querytree_list);
-       }
-
-       /*
-        * If this is an internal procedure, check that the given internal
-        * function name (the 'prosrc' value) is a known builtin function.
-        *
-        * NOTE: in Postgres versions before 6.5, the SQL name of the created
-        * function could not be different from the internal name, and
-        * 'prosrc' wasn't used.  So there is code out there that does CREATE
-        * FUNCTION xyz AS '' LANGUAGE 'internal'.      To preserve some modicum
-        * of backwards compatibility, accept an empty 'prosrc' value as
-        * meaning the supplied SQL function name.
-        */
-       if (languageObjectId == INTERNALlanguageId)
-       {
-               if (strlen(prosrc) == 0)
-                       prosrc = procedureName;
-               if (fmgr_internal_function((char *) prosrc) == InvalidOid)
-                       elog(ERROR,
-                                "there is no built-in function named \"%s\"",
-                                prosrc);
-       }
-
-       /*
-        * If this is a dynamically loadable procedure, make sure that the
-        * library file exists, is loadable, and contains the specified link
-        * symbol.      Also check for a valid function information record.
-        *
-        * We used to perform these checks only when the function was first
-        * called, but it seems friendlier to verify the library's validity at
-        * CREATE FUNCTION time.
-        */
-       if (languageObjectId == ClanguageId)
-       {
-               void       *libraryhandle;
-
-               /* If link symbol is specified as "-", substitute procedure name */
-               if (strcmp(prosrc, "-") == 0)
-                       prosrc = procedureName;
-               (void) load_external_function((char *) probin,
-                                                                         (char *) prosrc,
-                                                                         true,
-                                                                         &libraryhandle);
-               (void) fetch_finfo_record(libraryhandle, (char *) prosrc);
-       }
 
        /*
         * All seems OK; prepare the data to be inserted into pg_proc.
@@ -316,6 +255,14 @@ ProcedureCreate(const char *procedureName,
 
        heap_close(rel, RowExclusiveLock);
 
+       /* Verify function body */
+       if (OidIsValid(languageValidator))
+       {
+               /* Advance command counter so recursive functions can be defined */
+               CommandCounterIncrement();
+               OidFunctionCall1(languageValidator, retval);
+       }
+
        return retval;
 }
 
@@ -454,3 +401,122 @@ checkretval(Oid rettype, List *queryTreeList)
 
        heap_close(reln, AccessShareLock);
 }
+
+
+
+/*
+ * Validator for internal functions
+ *
+ * Check that the given internal function name (the "prosrc" value) is
+ * a known builtin function.
+ */
+Datum
+fmgr_internal_validator(PG_FUNCTION_ARGS)
+{
+       Oid                     funcoid = PG_GETARG_OID(0);
+       HeapTuple       tuple;
+       Form_pg_proc proc;
+       bool            isnull;
+       Datum           tmp;
+       char       *prosrc;
+
+       tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "cache lookup of function %u failed", funcoid);
+       proc = (Form_pg_proc) GETSTRUCT(tuple);
+
+       tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
+       if (isnull)
+               elog(ERROR, "null prosrc");
+       prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
+
+       if (fmgr_internal_function(prosrc) == InvalidOid)
+               elog(ERROR, "there is no built-in function named \"%s\"", prosrc);
+
+       ReleaseSysCache(tuple);
+       PG_RETURN_BOOL(true);
+}
+
+
+
+/*
+ * Validator for C language functions
+ *
+ * Make sure that the library file exists, is loadable, and contains
+ * the specified link symbol. Also check for a valid function
+ * information record.
+ */
+Datum
+fmgr_c_validator(PG_FUNCTION_ARGS)
+{
+       Oid                     funcoid = PG_GETARG_OID(0);
+       void       *libraryhandle;
+       HeapTuple       tuple;
+       Form_pg_proc proc;
+       bool            isnull;
+       Datum           tmp;
+       char       *prosrc;
+       char       *probin;
+
+       tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "cache lookup of function %u failed", funcoid);
+       proc = (Form_pg_proc) GETSTRUCT(tuple);
+
+       tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
+       if (isnull)
+               elog(ERROR, "null prosrc");
+       prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
+
+       tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_probin, &isnull);
+       if (isnull)
+               elog(ERROR, "null probin");
+       probin = DatumGetCString(DirectFunctionCall1(textout, tmp));
+       
+       (void) load_external_function(probin, prosrc, true, &libraryhandle);
+       (void) fetch_finfo_record(libraryhandle, prosrc);
+
+       ReleaseSysCache(tuple);
+       PG_RETURN_BOOL(true);
+}
+
+
+
+/*
+ * Validator for SQL language functions
+ *
+ * Parse it here in order to be sure that it contains no syntax
+ * errors.
+ */
+Datum
+fmgr_sql_validator(PG_FUNCTION_ARGS)
+{
+       Oid                     funcoid = PG_GETARG_OID(0);
+       HeapTuple       tuple;
+       Form_pg_proc proc;
+       List       *querytree_list;
+       bool            isnull;
+       Datum           tmp;
+       char       *prosrc;
+
+       tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "cache lookup of function %u failed", funcoid);
+
+       proc = (Form_pg_proc) GETSTRUCT(tuple);
+
+       if (!OidIsValid(proc->prorettype))
+                       elog(ERROR, "SQL functions cannot return type \"opaque\"");
+
+       tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
+       if (isnull)
+               elog(ERROR, "null prosrc");
+
+       prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
+
+       querytree_list = pg_parse_and_rewrite(prosrc, proc->proargtypes, proc->pronargs);
+       checkretval(proc->prorettype, querytree_list);
+
+       ReleaseSysCache(tuple);
+       PG_RETURN_BOOL(true);
+}
index 9d45ec0f8923a81f22d6ff97e847493f779e1a9f..c46f41af2282d535de6f2f99984eb7550742c515 100644 (file)
@@ -388,6 +388,7 @@ CreateFunction(CreateFunctionStmt *stmt)
        char       *language;
        char            languageName[NAMEDATALEN];
        Oid                     languageOid;
+       Oid                     languageValidator;
        char       *funcname;
        Oid                     namespaceId;
        AclResult       aclresult;
@@ -457,6 +458,8 @@ CreateFunction(CreateFunctionStmt *stmt)
                        aclcheck_error(ACLCHECK_NO_PRIV, NameStr(languageStruct->lanname));
        }
 
+       languageValidator = languageStruct->lanvalidator;
+
        ReleaseSysCache(languageTuple);
 
        /*
@@ -477,6 +480,28 @@ CreateFunction(CreateFunctionStmt *stmt)
        interpret_AS_clause(languageOid, languageName, as_clause,
                                                &prosrc_str, &probin_str);
 
+       if (languageOid == INTERNALlanguageId)
+       {
+               /*
+                * In PostgreSQL versions before 6.5, the SQL name of the
+                * created function could not be different from the internal
+                * name, and "prosrc" wasn't used.  So there is code out there
+                * that does CREATE FUNCTION xyz AS '' LANGUAGE 'internal'.
+                * To preserve some modicum of backwards compatibility, accept
+                * an empty "prosrc" value as meaning the supplied SQL
+                * function name.
+                */
+               if (strlen(prosrc_str) == 0)
+                       prosrc_str = funcname;
+       }
+
+       if (languageOid == ClanguageId)
+       {
+               /* If link symbol is specified as "-", substitute procedure name */
+               if (strcmp(prosrc_str, "-") == 0)
+                       prosrc_str = funcname;
+       }
+
        /*
         * And now that we have all the parameters, and know we're permitted
         * to do so, go ahead and create the function.
@@ -487,6 +512,7 @@ CreateFunction(CreateFunctionStmt *stmt)
                                        returnsSet,
                                        prorettype,
                                        languageOid,
+                                       languageValidator,
                                        prosrc_str, /* converted to text later */
                                        probin_str, /* converted to text later */
                                        false,          /* not an aggregate */
index 75f4546dba74f2ccfb0a56171178b85cf7db96ea..5cc0119777e89c6586ce3aaccfbf8767e024ef9d 100644 (file)
@@ -21,6 +21,7 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
 #include "commands/proclang.h"
 #include "commands/defrem.h"
 #include "fmgr.h"
@@ -39,7 +40,7 @@ void
 CreateProceduralLanguage(CreatePLangStmt *stmt)
 {
        char            languageName[NAMEDATALEN];
-       Oid                     procOid;
+       Oid                     procOid, valProcOid;
        Oid                     typev[FUNC_MAX_ARGS];
        char            nulls[Natts_pg_language];
        Datum           values[Natts_pg_language];
@@ -76,9 +77,21 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
                elog(ERROR, "PL handler function %s() doesn't exist",
                         NameListToString(stmt->plhandler));
        if (get_func_rettype(procOid) != InvalidOid)
-               elog(ERROR, "PL handler function %s() isn't of return type Opaque",
+               elog(ERROR, "PL handler function %s() does not return type \"opaque\"",
                         NameListToString(stmt->plhandler));
 
+       /* validate the validator function */
+       if (stmt->plvalidator)
+       {
+               typev[0] = OIDOID;
+               valProcOid = LookupFuncName(stmt->plvalidator, 1, typev);
+               if (!OidIsValid(valProcOid))
+                       elog(ERROR, "PL validator function %s(oid) doesn't exist",
+                                NameListToString(stmt->plvalidator));
+       }
+       else
+               valProcOid = 0;
+
        /*
         * Insert the new language into pg_language
         */
@@ -93,6 +106,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
        values[i++] = BoolGetDatum(true);       /* lanispl */
        values[i++] = BoolGetDatum(stmt->pltrusted);
        values[i++] = ObjectIdGetDatum(procOid);
+       values[i++] = ObjectIdGetDatum(valProcOid);
        values[i++] = DirectFunctionCall1(textin,
                                                                          CStringGetDatum(stmt->plcompiler));
        nulls[i] = 'n';                         /* lanacl */
index 134fc617699320c033c47acbb03a5605a34ef25d..455c89c6efe2d21e8c1fef0b9dd8be55302de791 100644 (file)
@@ -2418,6 +2418,7 @@ _copyCreatePLangStmt(CreatePLangStmt *from)
        if (from->plname)
                newnode->plname = pstrdup(from->plname);
        Node_Copy(from, newnode, plhandler);
+       Node_Copy(from, newnode, plvalidator);
        if (from->plcompiler)
                newnode->plcompiler = pstrdup(from->plcompiler);
        newnode->pltrusted = from->pltrusted;
index 640ef92493d96b33be3c7ecc67be73da9c2d0fb0..0f1fbfa90f8ff5c577411aec7e8578f0624469ca 100644 (file)
@@ -1252,6 +1252,8 @@ _equalCreatePLangStmt(CreatePLangStmt *a, CreatePLangStmt *b)
                return false;
        if (!equal(a->plhandler, b->plhandler))
                return false;
+       if (!equal(a->plvalidator, b->plvalidator))
+               return false;
        if (!equalstr(a->plcompiler, b->plcompiler))
                return false;
        if (a->pltrusted != b->pltrusted)
index 7374281a664fd9d3546ef0a3ce095f66120bcd98..60112bbd93a4451eda0539b5f4536fe641e24065 100644 (file)
@@ -182,7 +182,7 @@ static void doNegateFloat(Value *v);
                index_name, name, function_name, file_name
 
 %type <list>   func_name, handler_name, qual_Op, qual_all_Op, OptUseOp,
-               opt_class
+               opt_class, opt_validator
 
 %type <range>  qualified_name, OptConstrFromTable
 
@@ -375,7 +375,7 @@ static void doNegateFloat(Value *v);
        UNENCRYPTED, UNION, UNIQUE, UNKNOWN, UNLISTEN, UNTIL, UPDATE, USAGE,
        USER, USING,
 
-       VACUUM, VALID, VALUES, VARCHAR, VARYING, VERBOSE, VERSION, VIEW, VOLATILE,
+       VACUUM, VALID, VALIDATOR, VALUES, VARCHAR, VARYING, VERBOSE, VERSION, VIEW, VOLATILE,
        WHEN, WHERE, WITH, WITHOUT, WORK,
        YEAR_P,
        ZONE
@@ -1835,12 +1835,13 @@ IntegerOnly:  Iconst
  *****************************************************************************/
 
 CreatePLangStmt:  CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst
-                       HANDLER handler_name opt_lancompiler
+                       HANDLER handler_name opt_validator opt_lancompiler
                        {
                                CreatePLangStmt *n = makeNode(CreatePLangStmt);
                                n->plname = $5;
                                n->plhandler = $7;
-                               n->plcompiler = $8;
+                               n->plvalidator = $8;
+                               n->plcompiler = $9;
                                n->pltrusted = $2;
                                $$ = (Node *)n;
                        }
@@ -1864,6 +1865,10 @@ opt_lancompiler: LANCOMPILER Sconst { $$ = $2; }
                        | /*EMPTY*/                     { $$ = ""; }
                ;
 
+opt_validator: VALIDATOR handler_name { $$ = $2; }
+                       | /*EMPTY*/ { $$ = NULL; }
+               ;
+
 DropPLangStmt:  DROP opt_procedural LANGUAGE ColId_or_Sconst
                        {
                                DropPLangStmt *n = makeNode(DropPLangStmt);
@@ -6357,6 +6362,7 @@ unreserved_keyword:
                | USAGE
                | VACUUM
                | VALID
+               | VALIDATOR
                | VALUES
                | VARYING
                | VERSION
index 743a01a84fd51c102d2bf17befb632d23fcc4058..71761873c1d6f9a89e13f1e5da6a5c6fb4e0f255 100644 (file)
@@ -292,6 +292,7 @@ static const ScanKeyword ScanKeywords[] = {
        {"using", USING},
        {"vacuum", VACUUM},
        {"valid", VALID},
+       {"validator", VALIDATOR},
        {"values", VALUES},
        {"varchar", VARCHAR},
        {"varying", VARYING},
index bbc5642a31f2672bed2847a9904dddc60d20ea9b..8a17fb23ee9120330a20e925e1a4658d837138ac 100644 (file)
@@ -58,6 +58,7 @@ SetDefine(char *querystr, Oid elemType)
                                                         true,          /* returnsSet */
                                                         elemType,      /* returnType */
                                                         SQLlanguageId, /* language */
+                                                        SQLvalidatorId,
                                                         querystr,      /* prosrc */
                                                         fileName,      /* probin */
                                                         false,         /* not aggregate */
index 8efc2d2014dbd6e0cd7a3a7cd4248d468442d5d4..08824ac8d3663667d4bbd6d7a773ecc758c4db07 100644 (file)
@@ -3108,6 +3108,7 @@ dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs)
        int                     i_lanname;
        int                     i_lanpltrusted;
        int                     i_lanplcallfoid;
+       int                     i_lanvalidator = -1;
        int                     i_lancompiler;
        int                     i_lanacl = -1;
        char       *lanoid;
@@ -3115,10 +3116,12 @@ dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs)
        char       *lancompiler;
        char       *lanacl;
        const char *lanplcallfoid;
+       const char *lanvalidator;
        const char *((*deps)[]);
        int                     depIdx;
        int                     i,
-                               fidx;
+                               fidx,
+                               vidx = -1;
 
        /* Make sure we are in proper schema */
        selectSourceSchema("pg_catalog");
@@ -3142,7 +3145,10 @@ dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs)
        i_lancompiler = PQfnumber(res, "lancompiler");
        i_oid = PQfnumber(res, "oid");
        if (fout->remoteVersion >= 70300)
+       {
+               i_lanvalidator = PQfnumber(res, "lanvalidator");
                i_lanacl = PQfnumber(res, "lanacl");
+       }
 
        for (i = 0; i < ntups; i++)
        {
@@ -3151,9 +3157,15 @@ dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs)
                lanname = PQgetvalue(res, i, i_lanname);
                lancompiler = PQgetvalue(res, i, i_lancompiler);
                if (fout->remoteVersion >= 70300)
+               {
+                       lanvalidator = PQgetvalue(res, i, i_lanvalidator);
                        lanacl = PQgetvalue(res, i, i_lanacl);
+               }
                else
-                       lanacl = "{=U}";                                
+               {
+                       lanvalidator = "0";
+                       lanacl = "{=U}";
+               }
 
                fidx = findFuncByOid(finfo, numFuncs, lanplcallfoid);
                if (fidx < 0)
@@ -3163,6 +3175,17 @@ dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs)
                        exit_nicely();
                }
 
+               if (strcmp(lanvalidator, "0") != 0)
+               {
+                       vidx = findFuncByOid(finfo, numFuncs, lanvalidator);
+                       if (vidx < 0)
+                       {
+                               write_msg(NULL, "validator procedure for procedural language %s not found\n",
+                                                 lanname);
+                               exit_nicely();
+                       }
+               }
+
                /*
                 * Current theory is to dump PLs iff their underlying functions
                 * will be dumped (are in a dumpable namespace, or have a non-system
@@ -3178,7 +3201,7 @@ dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs)
                resetPQExpBuffer(delqry);
 
                /* Make a dependency to ensure function is dumped first */
-               deps = malloc(sizeof(char *) * 2);
+               deps = malloc(sizeof(char *) * (2 + (strcmp(lanvalidator, "0")!=0) ? 1 : 0));
                depIdx = 0;
 
                (*deps)[depIdx++] = strdup(lanplcallfoid);
@@ -3189,8 +3212,15 @@ dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs)
                                                  (PQgetvalue(res, i, i_lanpltrusted)[0] == 't') ?
                                                  "TRUSTED " : "",
                                                  fmtId(lanname, force_quotes));
-               appendPQExpBuffer(defqry, " HANDLER %s;\n",
+               appendPQExpBuffer(defqry, " HANDLER %s",
                                                  fmtId(finfo[fidx].proname, force_quotes));
+               if (strcmp(lanvalidator, "0")!=0)
+               {
+                       appendPQExpBuffer(defqry, " VALIDATOR %s",
+                                                         fmtId(finfo[vidx].proname, force_quotes));
+                       (*deps)[depIdx++] = strdup(lanvalidator);
+               }
+               appendPQExpBuffer(defqry, ";\n");
 
                (*deps)[depIdx++] = NULL;               /* End of List */
 
index 83b69212f73659375ded58866b4815bdb1b94116..b70d950e75d7e39f8a9307f977088283b0177a85 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200205181
+#define CATALOG_VERSION_NO     200205221
 
 #endif
index 15ccfdcb4f7592f56612fcb001e2ae4ed38d2f30..9643d1604632f6e95e09c37709a5526c4b646b1c 100644 (file)
@@ -37,6 +37,7 @@ CATALOG(pg_language)
        bool            lanispl;                /* Is a procedural language */
        bool            lanpltrusted;   /* PL is trusted */
        Oid                     lanplcallfoid;  /* Call handler for PL */
+       Oid                     lanvalidator;   /* optional validation function */
        text            lancompiler;    /* VARIABLE LENGTH FIELD */
     aclitem            lanacl[1];              /* Access privileges */
 } FormData_pg_language;
@@ -52,26 +53,27 @@ typedef FormData_pg_language *Form_pg_language;
  *             compiler constants for pg_language
  * ----------------
  */
-#define Natts_pg_language                              6
+#define Natts_pg_language                              7
 #define Anum_pg_language_lanname               1
 #define Anum_pg_language_lanispl               2
 #define Anum_pg_language_lanpltrusted          3
 #define Anum_pg_language_lanplcallfoid         4
-#define Anum_pg_language_lancompiler           5
-#define Anum_pg_language_lanacl                        6
+#define Anum_pg_language_lanvalidator          5
+#define Anum_pg_language_lancompiler           6
+#define Anum_pg_language_lanacl                        7
 
 /* ----------------
  *             initial contents of pg_language
  * ----------------
  */
 
-DATA(insert OID = 12 ( "internal" f f 0 "n/a" _null_ ));
+DATA(insert OID = 12 ( "internal" f f 0 2246 "n/a" _null_ ));
 DESCR("Built-in functions");
 #define INTERNALlanguageId 12
-DATA(insert OID = 13 ( "c" f f 0 "/bin/cc" _null_ ));
+DATA(insert OID = 13 ( "c" f f 0 2247 "/bin/cc" _null_ ));
 DESCR("Dynamically-loaded C functions");
 #define ClanguageId 13
-DATA(insert OID = 14 ( "sql" f t 0 "postgres" _null_ ));
+DATA(insert OID = 14 ( "sql" f t 0 2248 "postgres" _null_ ));
 DESCR("SQL-language functions");
 #define SQLlanguageId 14
 
index 7b92897542f24bf1ac56865c470567fb29a50f7f..ea304c1bb96d8e89f01c4af3c7b91add95f54187 100644 (file)
@@ -2962,6 +2962,13 @@ DESCR("(internal)");
 DATA(insert OID = 2221 (  regtypeout           PGNSP PGUID 12 f f f t f s 1   23 "0" 100 0 0 100       regtypeout - _null_ ));
 DESCR("(internal)");
 
+DATA(insert OID = 2246 ( fmgr_internal_validator PGNSP PGUID 12 f f f t f s 1 26 "23" 100 0 0 100 fmgr_internal_validator - _null_ ));
+DESCR("(internal)");
+DATA(insert OID = 2247 ( fmgr_c_validator      PGNSP PGUID 12 f f f t f s 1   26 "23" 100 0 0 100      fmgr_c_validator - _null_ ));
+DESCR("(internal)");
+DATA(insert OID = 2248 ( fmgr_sql_validator    PGNSP PGUID 12 f f f t f s 1   26 "23" 100 0 0 100      fmgr_sql_validator - _null_ ));
+DESCR("(internal)");
+#define SQLvalidatorId 2248
 
 /*
  * Symbolic values for provolatile column: these indicate whether the result
@@ -2985,6 +2992,7 @@ extern Oid ProcedureCreate(const char *procedureName,
                                bool returnsSet,
                                Oid returnType,
                                Oid languageObjectId,
+                               Oid languageValidator,
                                const char *prosrc,
                                const char *probin,
                                bool isAgg,
index 9b155fc11a4b848b674ca02e9c82abf53546d22f..9e4df52d65f8c6a676d9cae64496f79de7c72ffe 100644 (file)
@@ -990,6 +990,7 @@ typedef struct CreatePLangStmt
        NodeTag         type;
        char       *plname;                     /* PL name */
        List       *plhandler;          /* PL call handler function (qual. name) */
+       List       *plvalidator;        /* optional validator function (qual. name) */
        char       *plcompiler;         /* lancompiler text */
        bool            pltrusted;              /* PL is trusted */
 } CreatePLangStmt;
index 6d91674cd56d1c0e9ddf03c5cb1bb10d26432793..14ae6ff2bbb3cc7dd0c6caa2ab45d2974b31ca67 100644 (file)
@@ -42,3 +42,28 @@ CREATE FUNCTION set_ttdummy (int4)
         AS '@abs_builddir@/regress@DLSUFFIX@'
         LANGUAGE 'C';
 
+-- Things that shouldn't work:
+
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
+    AS 'SELECT ''not an integer'';';
+
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
+    AS 'not even SQL';
+
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
+    AS 'SELECT 1, 2, 3;';
+
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
+    AS 'SELECT $2;';
+
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
+    AS 'a', 'b';
+
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE c
+    AS 'nosuchfile';
+
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE c
+    AS '@abs_builddir@/regress@DLSUFFIX@', 'nosuchsymbol';
+
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE internal
+    AS 'nosuch';
index ded580ee7c47885e49844bcb1616ea5d45b0a969..2f59a693319419f1f744c54aac5053d7e891a7d9 100644 (file)
@@ -34,3 +34,28 @@ CREATE FUNCTION set_ttdummy (int4)
         RETURNS int4
         AS '@abs_builddir@/regress@DLSUFFIX@'
         LANGUAGE 'C';
+-- Things that shouldn't work:
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
+    AS 'SELECT ''not an integer'';';
+ERROR:  return type mismatch in function: declared to return integer, returns "unknown"
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
+    AS 'not even SQL';
+ERROR:  parser: parse error at or near "not"
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
+    AS 'SELECT 1, 2, 3;';
+ERROR:  function declared to return integer returns multiple columns in final SELECT
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
+    AS 'SELECT $2;';
+ERROR:  Parameter '$2' is out of range
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE sql
+    AS 'a', 'b';
+ERROR:  CREATE FUNCTION: only one AS item needed for sql language
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE c
+    AS 'nosuchfile';
+ERROR:  stat failed on file 'nosuchfile': No such file or directory
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE c
+    AS '@abs_builddir@/regress@DLSUFFIX@', 'nosuchsymbol';
+ERROR:  Can't find function nosuchsymbol in file @abs_builddir@/regress@DLSUFFIX@
+CREATE FUNCTION test1 (int) RETURNS int LANGUAGE internal
+    AS 'nosuch';
+ERROR:  there is no built-in function named "nosuch"