*** src/backend/commands/proclang.c.orig Thu Apr 14 16:03:24 2005 --- src/backend/commands/proclang.c Fri Sep 2 19:51:06 2005 *************** *** 13,37 **** */ #include "postgres.h" - #include - #include "access/heapam.h" #include "catalog/dependency.h" #include "catalog/indexing.h" #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" #include "miscadmin.h" #include "parser/parse_func.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/syscache.h" /* --------------------------------------------------------------------- * CREATE PROCEDURAL LANGUAGE * --------------------------------------------------------------------- --- 13,52 ---- */ #include "postgres.h" #include "access/heapam.h" #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_language.h" + #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "commands/proclang.h" #include "commands/defrem.h" #include "fmgr.h" #include "miscadmin.h" + #include "parser/gramparse.h" #include "parser/parse_func.h" #include "utils/builtins.h" + #include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/syscache.h" + typedef struct + { + char *lanname; /* PL name */ + bool lantrusted; /* trusted? */ + char *lanhandler; /* name of handler function */ + char *lanvalidator; /* name of validator function, or NULL */ + char *lanlibrary; /* path of shared library */ + } PLTemplate; + + static void create_proc_lang(const char *languageName, + Oid handlerOid, Oid valOid, bool trusted); + static PLTemplate *find_language_template(const char *languageName); + + /* --------------------------------------------------------------------- * CREATE PROCEDURAL LANGUAGE * --------------------------------------------------------------------- *************** *** 40,58 **** CreateProceduralLanguage(CreatePLangStmt *stmt) { char *languageName; ! Oid procOid, ! valProcOid; Oid funcrettype; Oid funcargtypes[1]; - NameData langname; - char nulls[Natts_pg_language]; - Datum values[Natts_pg_language]; - Relation rel; - HeapTuple tup; - TupleDesc tupDesc; - int i; - ObjectAddress myself, - referenced; /* * Check permission --- 55,65 ---- CreateProceduralLanguage(CreatePLangStmt *stmt) { char *languageName; ! PLTemplate *pltemplate; ! Oid handlerOid, ! valOid; Oid funcrettype; Oid funcargtypes[1]; /* * Check permission *************** *** 76,139 **** errmsg("language \"%s\" already exists", languageName))); /* ! * Lookup the PL handler function and check that it is of the expected ! * return type */ ! procOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false); ! funcrettype = get_func_rettype(procOid); ! if (funcrettype != LANGUAGE_HANDLEROID) { /* ! * We allow OPAQUE just so we can load old dump files. When we ! * see a handler function declared OPAQUE, change it to ! * LANGUAGE_HANDLER. */ ! if (funcrettype == OPAQUEOID) { ! ereport(WARNING, ! (errcode(ERRCODE_WRONG_OBJECT_TYPE), ! errmsg("changing return type of function %s from \"opaque\" to \"language_handler\"", ! NameListToString(stmt->plhandler)))); ! SetFunctionReturnType(procOid, LANGUAGE_HANDLEROID); } else ! ereport(ERROR, ! (errcode(ERRCODE_WRONG_OBJECT_TYPE), ! errmsg("function %s must return type \"language_handler\"", ! NameListToString(stmt->plhandler)))); ! } ! /* validate the validator function */ ! if (stmt->plvalidator) ! { ! funcargtypes[0] = OIDOID; ! valProcOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false); ! /* return value is ignored, so we don't check the type */ } else ! valProcOid = InvalidOid; /* * Insert the new language into pg_language */ ! for (i = 0; i < Natts_pg_language; i++) ! { ! nulls[i] = ' '; ! values[i] = (Datum) NULL; ! } ! i = 0; ! namestrcpy(&langname, languageName); ! values[i++] = NameGetDatum(&langname); /* lanname */ ! values[i++] = BoolGetDatum(true); /* lanispl */ ! values[i++] = BoolGetDatum(stmt->pltrusted); /* lanpltrusted */ ! values[i++] = ObjectIdGetDatum(procOid); /* lanplcallfoid */ ! values[i++] = ObjectIdGetDatum(valProcOid); /* lanvalidator */ ! nulls[i] = 'n'; /* lanacl */ ! rel = heap_open(LanguageRelationId, RowExclusiveLock); - tupDesc = rel->rd_att; tup = heap_formtuple(tupDesc, values, nulls); simple_heap_insert(rel, tup); --- 83,256 ---- errmsg("language \"%s\" already exists", languageName))); /* ! * If we have template information for the language, ignore the supplied ! * parameters (if any) and use the template information. */ ! if ((pltemplate = find_language_template(languageName)) != NULL) { + List *funcname; + /* ! * Find or create the handler function, which we force to be in ! * the pg_catalog schema. If already present, it must have the ! * correct return type. */ ! funcname = SystemFuncName(pltemplate->lanhandler); ! handlerOid = LookupFuncName(funcname, 0, funcargtypes, true); ! if (OidIsValid(handlerOid)) { ! funcrettype = get_func_rettype(handlerOid); ! if (funcrettype != LANGUAGE_HANDLEROID) ! ereport(ERROR, ! (errcode(ERRCODE_WRONG_OBJECT_TYPE), ! errmsg("function %s must return type \"language_handler\"", ! NameListToString(funcname)))); } else ! { ! handlerOid = ProcedureCreate(pltemplate->lanhandler, ! PG_CATALOG_NAMESPACE, ! false, /* replace */ ! false, /* returnsSet */ ! LANGUAGE_HANDLEROID, ! ClanguageId, ! F_FMGR_C_VALIDATOR, ! pltemplate->lanhandler, ! pltemplate->lanlibrary, ! false, /* isAgg */ ! false, /* security_definer */ ! false, /* isStrict */ ! PROVOLATILE_VOLATILE, ! buildoidvector(funcargtypes, 0), ! PointerGetDatum(NULL), ! PointerGetDatum(NULL), ! PointerGetDatum(NULL)); ! } ! /* ! * Likewise for the validator, if required; but we don't care about ! * its return type. ! */ ! if (pltemplate->lanvalidator) ! { ! funcname = SystemFuncName(pltemplate->lanvalidator); ! funcargtypes[0] = OIDOID; ! valOid = LookupFuncName(funcname, 1, funcargtypes, true); ! if (!OidIsValid(valOid)) ! { ! valOid = ProcedureCreate(pltemplate->lanvalidator, ! PG_CATALOG_NAMESPACE, ! false, /* replace */ ! false, /* returnsSet */ ! VOIDOID, ! ClanguageId, ! F_FMGR_C_VALIDATOR, ! pltemplate->lanvalidator, ! pltemplate->lanlibrary, ! false, /* isAgg */ ! false, /* security_definer */ ! false, /* isStrict */ ! PROVOLATILE_VOLATILE, ! buildoidvector(funcargtypes, 1), ! PointerGetDatum(NULL), ! PointerGetDatum(NULL), ! PointerGetDatum(NULL)); ! } ! } ! else ! valOid = InvalidOid; ! ! /* ok, create it */ ! create_proc_lang(languageName, handlerOid, valOid, ! pltemplate->lantrusted); } else ! { ! /* ! * No template, so use the provided information. There MUST be ! * a handler clause. ! */ ! if (!stmt->plhandler) ! ereport(ERROR, ! (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), ! errmsg("no handler specified for procedural language"))); ! ! /* ! * Lookup the PL handler function and check that it is of the expected ! * return type ! */ ! handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false); ! funcrettype = get_func_rettype(handlerOid); ! if (funcrettype != LANGUAGE_HANDLEROID) ! { ! /* ! * We allow OPAQUE just so we can load old dump files. When we ! * see a handler function declared OPAQUE, change it to ! * LANGUAGE_HANDLER. (This is probably obsolete and removable?) ! */ ! if (funcrettype == OPAQUEOID) ! { ! ereport(WARNING, ! (errcode(ERRCODE_WRONG_OBJECT_TYPE), ! errmsg("changing return type of function %s from \"opaque\" to \"language_handler\"", ! NameListToString(stmt->plhandler)))); ! SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID); ! } ! else ! ereport(ERROR, ! (errcode(ERRCODE_WRONG_OBJECT_TYPE), ! errmsg("function %s must return type \"language_handler\"", ! NameListToString(stmt->plhandler)))); ! } ! ! /* validate the validator function */ ! if (stmt->plvalidator) ! { ! funcargtypes[0] = OIDOID; ! valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false); ! /* return value is ignored, so we don't check the type */ ! } ! else ! valOid = InvalidOid; ! ! /* ok, create it */ ! create_proc_lang(languageName, handlerOid, valOid, stmt->pltrusted); ! } ! } ! ! /* ! * Guts of language creation. ! */ ! static void ! create_proc_lang(const char *languageName, ! Oid handlerOid, Oid valOid, bool trusted) ! { ! Relation rel; ! TupleDesc tupDesc; ! Datum values[Natts_pg_language]; ! char nulls[Natts_pg_language]; ! NameData langname; ! HeapTuple tup; ! ObjectAddress myself, ! referenced; /* * Insert the new language into pg_language */ ! rel = heap_open(LanguageRelationId, RowExclusiveLock); ! tupDesc = rel->rd_att; ! memset(values, 0, sizeof(values)); ! memset(nulls, ' ', sizeof(nulls)); ! namestrcpy(&langname, languageName); ! values[Anum_pg_language_lanname - 1] = NameGetDatum(&langname); ! 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_lanvalidator - 1] = ObjectIdGetDatum(valOid); ! nulls[Anum_pg_language_lanacl - 1] = 'n'; tup = heap_formtuple(tupDesc, values, nulls); simple_heap_insert(rel, tup); *************** *** 149,163 **** /* dependency on the PL handler function */ referenced.classId = ProcedureRelationId; ! referenced.objectId = procOid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on the validator function, if any */ ! if (OidIsValid(valProcOid)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = valProcOid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } --- 266,280 ---- /* dependency on the PL handler function */ referenced.classId = ProcedureRelationId; ! referenced.objectId = handlerOid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on the validator function, if any */ ! if (OidIsValid(valOid)) { referenced.classId = ProcedureRelationId; ! referenced.objectId = valOid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } *************** *** 165,170 **** --- 282,322 ---- heap_close(rel, RowExclusiveLock); } + /* + * Look to see if we have template information for the given language name. + * + * XXX for PG 8.1, the template info is hard-wired. This is to be replaced + * by a shared system catalog in 8.2. + */ + static PLTemplate * + find_language_template(const char *languageName) + { + static PLTemplate templates[] = { + { "plpgsql", true, "plpgsql_call_handler", "plpgsql_validator", + "$libdir/plpgsql" }, + { "pltcl", true, "pltcl_call_handler", NULL, + "$libdir/pltcl" }, + { "pltclu", false, "pltclu_call_handler", NULL, + "$libdir/pltcl" }, + { "plperl", true, "plperl_call_handler", "plperl_validator", + "$libdir/plperl" }, + { "plperlu", false, "plperl_call_handler", "plperl_validator", + "$libdir/plperl" }, + { "plpythonu", false, "plpython_call_handler", NULL, + "$libdir/plpython" }, + { NULL, false, NULL, NULL, NULL } + }; + + PLTemplate *ptr; + + for (ptr = templates; ptr->lanname != NULL; ptr++) + { + if (strcmp(languageName, ptr->lanname) == 0) + return ptr; + } + return NULL; + } + /* --------------------------------------------------------------------- * DROP PROCEDURAL LANGUAGE *************** *** 186,193 **** errmsg("must be superuser to drop procedural language"))); /* ! * Translate the language name, check that this language exist and is ! * a PL */ languageName = case_translate_language_name(stmt->plname); --- 338,344 ---- errmsg("must be superuser to drop procedural language"))); /* ! * Translate the language name, check that the language exists */ languageName = case_translate_language_name(stmt->plname); *************** *** 244,249 **** --- 395,404 ---- HeapTuple tup; Relation rel; + /* Translate both names for consistency with CREATE */ + oldname = case_translate_language_name(oldname); + newname = case_translate_language_name(newname); + rel = heap_open(LanguageRelationId, RowExclusiveLock); tup = SearchSysCacheCopy(LANGNAME, *************** *** 262,268 **** (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("language \"%s\" already exists", newname))); ! /* must be superuser */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), --- 417,423 ---- (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("language \"%s\" already exists", newname))); ! /* must be superuser, since we do not have owners for PLs */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), *** src/backend/parser/gram.y.orig Wed Aug 24 15:34:33 2005 --- src/backend/parser/gram.y Fri Sep 2 18:48:27 2005 *************** *** 194,200 **** index_name name function_name file_name %type func_name handler_name qual_Op qual_all_Op subquery_Op ! opt_class opt_validator %type qualified_name OptConstrFromTable --- 194,200 ---- index_name name function_name file_name %type func_name handler_name qual_Op qual_all_Op subquery_Op ! opt_class opt_handler opt_validator %type qualified_name OptConstrFromTable *************** *** 2303,2315 **** CreatePLangStmt: CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst ! HANDLER handler_name opt_validator opt_lancompiler { CreatePLangStmt *n = makeNode(CreatePLangStmt); n->plname = $5; ! n->plhandler = $7; ! n->plvalidator = $8; n->pltrusted = $2; $$ = (Node *)n; } ; --- 2303,2316 ---- CreatePLangStmt: CREATE opt_trusted opt_procedural LANGUAGE ColId_or_Sconst ! opt_handler opt_validator opt_lancompiler { CreatePLangStmt *n = makeNode(CreatePLangStmt); n->plname = $5; ! n->plhandler = $6; ! n->plvalidator = $7; n->pltrusted = $2; + /* LANCOMPILER is now ignored entirely */ $$ = (Node *)n; } ; *************** *** 2319,2324 **** --- 2320,2335 ---- | /*EMPTY*/ { $$ = FALSE; } ; + opt_handler: + HANDLER handler_name { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + + opt_validator: + VALIDATOR handler_name { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + /* This ought to be just func_name, but that causes reduce/reduce conflicts * (CREATE LANGUAGE is the only place where func_name isn't followed by '('). * Work around by using simple names, instead. *************** *** 2330,2341 **** opt_lancompiler: LANCOMPILER Sconst { $$ = $2; } ! | /*EMPTY*/ { $$ = ""; } ! ; ! ! opt_validator: ! VALIDATOR handler_name { $$ = $2; } ! | /*EMPTY*/ { $$ = NULL; } ; DropPLangStmt: --- 2341,2347 ---- opt_lancompiler: LANCOMPILER Sconst { $$ = $2; } ! | /*EMPTY*/ { $$ = NULL; } ; DropPLangStmt: *** src/bin/scripts/createlang.c.orig Mon Aug 15 17:02:26 2005 --- src/bin/scripts/createlang.c Fri Sep 2 20:14:32 2005 *************** *** 9,16 **** * *------------------------------------------------------------------------- */ - #include "postgres_fe.h" #include "common.h" #include "print.h" --- 9,16 ---- * *------------------------------------------------------------------------- */ #include "postgres_fe.h" + #include "common.h" #include "print.h" *************** *** 48,59 **** char *langname = NULL; char *p; - bool handlerexists; - bool validatorexists; - bool trusted; - char *handler; - char *validator = NULL; - char *object; PQExpBufferData sql; --- 48,53 ---- *************** *** 88,94 **** dbname = optarg; break; case 'L': ! pglib = optarg; break; case 'e': echo = true; --- 82,88 ---- dbname = optarg; break; case 'L': ! pglib = optarg; /* obsolete, ignored */ break; case 'e': echo = true; *************** *** 165,239 **** exit(1); } - if (!pglib) - pglib = "$libdir"; - for (p = langname; *p; p++) if (*p >= 'A' && *p <= 'Z') *p += ('a' - 'A'); - if (strcmp(langname, "plpgsql") == 0) - { - trusted = true; - handler = "plpgsql_call_handler"; - validator = "plpgsql_validator"; - object = "plpgsql"; - } - else if (strcmp(langname, "pltcl") == 0) - { - trusted = true; - handler = "pltcl_call_handler"; - object = "pltcl"; - } - else if (strcmp(langname, "pltclu") == 0) - { - trusted = false; - handler = "pltclu_call_handler"; - object = "pltcl"; - } - else if (strcmp(langname, "plperl") == 0) - { - trusted = true; - handler = "plperl_call_handler"; - validator = "plperl_validator"; - object = "plperl"; - } - else if (strcmp(langname, "plperlu") == 0) - { - trusted = false; - handler = "plperl_call_handler"; - validator = "plperl_validator"; - object = "plperl"; - } - else if (strcmp(langname, "plpythonu") == 0) - { - trusted = false; - handler = "plpython_call_handler"; - object = "plpython"; - } - else - { - fprintf(stderr, _("%s: unsupported language \"%s\"\n"), - progname, langname); - fprintf(stderr, _("Supported languages are plpgsql, pltcl, pltclu, " - "plperl, plperlu, and plpythonu.\n")); - exit(1); - } - conn = connectDatabase(dbname, host, port, username, password, progname); /* - * Force schema search path to be just pg_catalog, so that we don't - * have to be paranoid about search paths below. - */ - executeCommand(conn, "SET search_path = pg_catalog;", - progname, echo); - - /* * Make sure the language isn't already installed */ printfPQExpBuffer(&sql, ! "SELECT oid FROM pg_language WHERE lanname = '%s';", langname); result = executeQuery(conn, sql.data, progname, echo); if (PQntuples(result) > 0) --- 159,175 ---- exit(1); } for (p = langname; *p; p++) if (*p >= 'A' && *p <= 'Z') *p += ('a' - 'A'); conn = connectDatabase(dbname, host, port, username, password, progname); /* * Make sure the language isn't already installed */ printfPQExpBuffer(&sql, ! "SELECT oid FROM pg_catalog.pg_language WHERE lanname = '%s';", langname); result = executeQuery(conn, sql.data, progname, echo); if (PQntuples(result) > 0) *************** *** 247,307 **** } PQclear(result); ! /* ! * Check whether the call handler exists ! */ ! printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' " ! "AND pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog') " ! "AND prorettype = 'language_handler'::regtype " ! "AND pronargs = 0;", handler); ! result = executeQuery(conn, sql.data, progname, echo); ! handlerexists = (PQntuples(result) > 0); ! PQclear(result); ! ! /* ! * Check whether the validator exists ! */ ! if (validator) ! { ! printfPQExpBuffer(&sql, "SELECT oid FROM pg_proc WHERE proname = '%s' " ! "AND pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog') " ! "AND proargtypes[0] = 'oid'::regtype " ! "AND pronargs = 1;", validator); ! result = executeQuery(conn, sql.data, progname, echo); ! validatorexists = (PQntuples(result) > 0); ! PQclear(result); ! } ! else ! validatorexists = true; /* don't try to create it */ ! ! /* ! * Create the function(s) and the language ! * ! * NOTE: the functions will be created in pg_catalog because ! * of our previous "SET search_path". ! */ ! resetPQExpBuffer(&sql); ! ! if (!handlerexists) ! appendPQExpBuffer(&sql, ! "CREATE FUNCTION \"%s\" () RETURNS language_handler " ! "AS '%s/%s' LANGUAGE C;\n", ! handler, pglib, object); ! ! if (!validatorexists) ! appendPQExpBuffer(&sql, ! "CREATE FUNCTION \"%s\" (oid) RETURNS void " ! "AS '%s/%s' LANGUAGE C;\n", ! validator, pglib, object); ! ! appendPQExpBuffer(&sql, ! "CREATE %sLANGUAGE \"%s\" HANDLER \"%s\"", ! (trusted ? "TRUSTED " : ""), langname, handler); ! ! if (validator) ! appendPQExpBuffer(&sql, " VALIDATOR \"%s\"", validator); ! ! appendPQExpBuffer(&sql, ";\n"); if (echo) printf("%s", sql.data); --- 183,189 ---- } PQclear(result); ! printfPQExpBuffer(&sql, "CREATE LANGUAGE \"%s\";\n", langname); if (echo) printf("%s", sql.data); *************** *** 330,336 **** printf(_(" -d, --dbname=DBNAME database to install language in\n")); printf(_(" -e, --echo show the commands being sent to the server\n")); printf(_(" -l, --list show a list of currently installed languages\n")); - printf(_(" -L, --pglib=DIRECTORY find language interpreter file in DIRECTORY\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); printf(_(" -p, --port=PORT database server port\n")); printf(_(" -U, --username=USERNAME user name to connect as\n")); --- 212,217 ----