diff options
-rw-r--r-- | contrib/earthdistance/earthdistance.c | 22 | ||||
-rw-r--r-- | doc/src/sgml/xfunc.sgml | 269 | ||||
-rw-r--r-- | src/backend/utils/fmgr/README | 254 | ||||
-rw-r--r-- | src/backend/utils/fmgr/fmgr.c | 377 | ||||
-rw-r--r-- | src/include/fmgr.h | 8 | ||||
-rw-r--r-- | src/test/regress/input/create_function_2.source | 5 | ||||
-rw-r--r-- | src/test/regress/input/misc.source | 13 | ||||
-rw-r--r-- | src/test/regress/output/create_function_2.source | 4 | ||||
-rw-r--r-- | src/test/regress/output/misc.source | 18 | ||||
-rw-r--r-- | src/test/regress/regress.c | 47 |
10 files changed, 106 insertions, 911 deletions
diff --git a/contrib/earthdistance/earthdistance.c b/contrib/earthdistance/earthdistance.c index 861b1663739..6ad6d87ce8c 100644 --- a/contrib/earthdistance/earthdistance.c +++ b/contrib/earthdistance/earthdistance.c @@ -88,16 +88,8 @@ geo_distance_internal(Point *pt1, Point *pt2) * * returns: float8 * distance between the points in miles on earth's surface - * - * If float8 is passed-by-value, the oldstyle version-0 calling convention - * is unportable, so we use version-1. However, if it's passed-by-reference, - * continue to use oldstyle. This is just because we'd like earthdistance - * to serve as a canary for any unintentional breakage of version-0 functions - * with float8 results. ******************************************************/ -#ifdef USE_FLOAT8_BYVAL - PG_FUNCTION_INFO_V1(geo_distance); Datum @@ -110,17 +102,3 @@ geo_distance(PG_FUNCTION_ARGS) result = geo_distance_internal(pt1, pt2); PG_RETURN_FLOAT8(result); } -#else /* !USE_FLOAT8_BYVAL */ - -double *geo_distance(Point *pt1, Point *pt2); - -double * -geo_distance(Point *pt1, Point *pt2) -{ - double *resultp = palloc(sizeof(double)); - - *resultp = geo_distance_internal(pt1, pt2); - return resultp; -} - -#endif /* USE_FLOAT8_BYVAL */ diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 94a7ad747da..e6313ddd59d 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -1610,14 +1610,10 @@ CREATE FUNCTION square_root(double precision) RETURNS double precision </para> <para> - Two different calling conventions are currently used for C functions. - The newer <quote>version 1</quote> calling convention is indicated by writing - a <literal>PG_FUNCTION_INFO_V1()</literal> macro call for the function, - as illustrated below. Lack of such a macro indicates an old-style - (<quote>version 0</quote>) function. The language name specified in <command>CREATE FUNCTION</command> - is <literal>C</literal> in either case. Old-style functions are now deprecated - because of portability problems and lack of functionality, but they - are still supported for compatibility reasons. + Currently only one calling convention is used for C functions + (<quote>version 1</quote>). Support for that calling convention is + indicated by writing a <literal>PG_FUNCTION_INFO_V1()</literal> macro + call for the function, as illustrated below. </para> <sect2 id="xfunc-c-dynload"> @@ -2138,160 +2134,6 @@ memcpy(destination->data, buffer, 40); </sect2> <sect2> - <title>Version 0 Calling Conventions</title> - - <para> - We present the <quote>old style</quote> calling convention first — although - this approach is now deprecated, it's easier to get a handle on - initially. In the version-0 method, the arguments and result - of the C function are just declared in normal C style, but being - careful to use the C representation of each SQL data type as shown - above. - </para> - - <para> - Here are some examples: - -<programlisting><![CDATA[ -#include "postgres.h" -#include <string.h> -#include "utils/geo_decls.h" - -#ifdef PG_MODULE_MAGIC -PG_MODULE_MAGIC; -#endif - -/* by value */ - -int -add_one(int arg) -{ - return arg + 1; -} - -/* by reference, fixed length */ - -float8 * -add_one_float8(float8 *arg) -{ - float8 *result = (float8 *) palloc(sizeof(float8)); - - *result = *arg + 1.0; - - return result; -} - -Point * -makepoint(Point *pointx, Point *pointy) -{ - Point *new_point = (Point *) palloc(sizeof(Point)); - - new_point->x = pointx->x; - new_point->y = pointy->y; - - return new_point; -} - -/* by reference, variable length */ - -text * -copytext(text *t) -{ - /* - * VARSIZE is the total size of the struct in bytes. - */ - text *new_t = (text *) palloc(VARSIZE(t)); - SET_VARSIZE(new_t, VARSIZE(t)); - /* - * VARDATA is a pointer to the data region of the struct. - */ - memcpy((void *) VARDATA(new_t), /* destination */ - (void *) VARDATA(t), /* source */ - VARSIZE(t) - VARHDRSZ); /* how many bytes */ - return new_t; -} - -text * -concat_text(text *arg1, text *arg2) -{ - int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ; - text *new_text = (text *) palloc(new_text_size); - - SET_VARSIZE(new_text, new_text_size); - memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ); - memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ), - VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ); - return new_text; -} -]]> -</programlisting> - </para> - - <para> - Supposing that the above code has been prepared in file - <filename>funcs.c</filename> and compiled into a shared object, - we could define the functions to <productname>PostgreSQL</productname> - with commands like this: - -<programlisting> -CREATE FUNCTION add_one(integer) RETURNS integer - AS '<replaceable>DIRECTORY</replaceable>/funcs', 'add_one' - LANGUAGE C STRICT; - --- note overloading of SQL function name "add_one" -CREATE FUNCTION add_one(double precision) RETURNS double precision - AS '<replaceable>DIRECTORY</replaceable>/funcs', 'add_one_float8' - LANGUAGE C STRICT; - -CREATE FUNCTION makepoint(point, point) RETURNS point - AS '<replaceable>DIRECTORY</replaceable>/funcs', 'makepoint' - LANGUAGE C STRICT; - -CREATE FUNCTION copytext(text) RETURNS text - AS '<replaceable>DIRECTORY</replaceable>/funcs', 'copytext' - LANGUAGE C STRICT; - -CREATE FUNCTION concat_text(text, text) RETURNS text - AS '<replaceable>DIRECTORY</replaceable>/funcs', 'concat_text' - LANGUAGE C STRICT; -</programlisting> - </para> - - <para> - Here, <replaceable>DIRECTORY</replaceable> stands for the - directory of the shared library file (for instance the - <productname>PostgreSQL</productname> tutorial directory, which - contains the code for the examples used in this section). - (Better style would be to use just <literal>'funcs'</> in the - <literal>AS</> clause, after having added - <replaceable>DIRECTORY</replaceable> to the search path. In any - case, we can omit the system-specific extension for a shared - library, commonly <literal>.so</literal> or - <literal>.sl</literal>.) - </para> - - <para> - Notice that we have specified the functions as <quote>strict</quote>, - meaning that - the system should automatically assume a null result if any input - value is null. By doing this, we avoid having to check for null inputs - in the function code. Without this, we'd have to check for null values - explicitly, by checking for a null pointer for each - pass-by-reference argument. (For pass-by-value arguments, we don't - even have a way to check!) - </para> - - <para> - Although this calling convention is simple to use, - it is not very portable; on some architectures there are problems - with passing data types that are smaller than <type>int</type> this way. Also, there is - no simple way to return a null result, nor to cope with null arguments - in any way other than making the function strict. The version-1 - convention, presented next, overcomes these objections. - </para> - </sect2> - - <sect2> <title>Version 1 Calling Conventions</title> <para> @@ -2316,8 +2158,10 @@ PG_FUNCTION_INFO_V1(funcname); <para> In a version-1 function, each actual argument is fetched using a <function>PG_GETARG_<replaceable>xxx</replaceable>()</function> - macro that corresponds to the argument's data type, and the - result is returned using a + macro that corresponds to the argument's data type. In non-strict + functions there needs to be a previous check about argument null-ness + using <function>PG_ARGNULL_<replaceable>xxx</replaceable>()</function>. + The result is returned using a <function>PG_RETURN_<replaceable>xxx</replaceable>()</function> macro for the return type. <function>PG_GETARG_<replaceable>xxx</replaceable>()</function> @@ -2328,7 +2172,7 @@ PG_FUNCTION_INFO_V1(funcname); </para> <para> - Here we show the same functions as above, coded in version-1 style: + Here are some examples using the version-1 calling convention: <programlisting><![CDATA[ #include "postgres.h" @@ -2427,27 +2271,67 @@ concat_text(PG_FUNCTION_ARGS) } ]]> </programlisting> + + <para> + Supposing that the above code has been prepared in file + <filename>funcs.c</filename> and compiled into a shared object, + we could define the functions to <productname>PostgreSQL</productname> + with commands like this: + +<programlisting> +CREATE FUNCTION add_one(integer) RETURNS integer + AS '<replaceable>DIRECTORY</replaceable>/funcs', 'add_one' + LANGUAGE C STRICT; + +-- note overloading of SQL function name "add_one" +CREATE FUNCTION add_one(double precision) RETURNS double precision + AS '<replaceable>DIRECTORY</replaceable>/funcs', 'add_one_float8' + LANGUAGE C STRICT; + +CREATE FUNCTION makepoint(point, point) RETURNS point + AS '<replaceable>DIRECTORY</replaceable>/funcs', 'makepoint' + LANGUAGE C STRICT; + +CREATE FUNCTION copytext(text) RETURNS text + AS '<replaceable>DIRECTORY</replaceable>/funcs', 'copytext' + LANGUAGE C STRICT; + +CREATE FUNCTION concat_text(text, text) RETURNS text + AS '<replaceable>DIRECTORY</replaceable>/funcs', 'concat_text' + LANGUAGE C STRICT; +</programlisting> + + <para> + Here, <replaceable>DIRECTORY</replaceable> stands for the + directory of the shared library file (for instance the + <productname>PostgreSQL</productname> tutorial directory, which + contains the code for the examples used in this section). + (Better style would be to use just <literal>'funcs'</> in the + <literal>AS</> clause, after having added + <replaceable>DIRECTORY</replaceable> to the search path. In any + case, we can omit the system-specific extension for a shared + library, commonly <literal>.so</literal>.) </para> <para> - The <command>CREATE FUNCTION</command> commands are the same as - for the version-0 equivalents. + Notice that we have specified the functions as <quote>strict</quote>, + meaning that + the system should automatically assume a null result if any input + value is null. By doing this, we avoid having to check for null inputs + in the function code. Without this, we'd have to check for null values + explicitly, using PG_ARGISNULL(). </para> <para> - At first glance, the version-1 coding conventions might appear to - be just pointless obscurantism. They do, however, offer a number - of improvements, because the macros can hide unnecessary detail. - An example is that in coding <function>add_one_float8</>, we no longer need to - be aware that <type>float8</type> is a pass-by-reference type. Another - example is that the <literal>GETARG</> macros for variable-length types allow - for more efficient fetching of <quote>toasted</quote> (compressed or + At first glance, the version-1 coding conventions might appear to be just + pointless obscurantism, over using plain <literal>C</> calling + conventions. They do however allow to deal with <literal>NULL</>able + arguments/return values, and <quote>toasted</quote> (compressed or out-of-line) values. </para> <para> - One big improvement in version-1 functions is better handling of null - inputs and results. The macro <function>PG_ARGISNULL(<replaceable>n</>)</function> + The macro <function>PG_ARGISNULL(<replaceable>n</>)</function> allows a function to test whether each input is null. (Of course, doing this is only necessary in functions not declared <quote>strict</>.) As with the @@ -2461,7 +2345,7 @@ concat_text(PG_FUNCTION_ARGS) </para> <para> - Other options provided in the new-style interface are two + Other options provided by the version-1 interface are two variants of the <function>PG_GETARG_<replaceable>xxx</replaceable>()</function> macros. The first of these, @@ -2493,9 +2377,7 @@ concat_text(PG_FUNCTION_ARGS) to return set results (<xref linkend="xfunc-c-return-set">) and implement trigger functions (<xref linkend="triggers">) and procedural-language call handlers (<xref - linkend="plhandler">). Version-1 code is also more - portable than version-0, because it does not break restrictions - on function call protocol in the C standard. For more details + linkend="plhandler">). For more details see <filename>src/backend/utils/fmgr/README</filename> in the source distribution. </para> @@ -2630,7 +2512,7 @@ SELECT name, c_overpaid(emp, 1500) AS overpaid WHERE name = 'Bill' OR name = 'Sam'; </programlisting> - Using call conventions version 0, we can define + Using the version-1 calling conventions, we can define <function>c_overpaid</> as: <programlisting><![CDATA[ @@ -2641,31 +2523,6 @@ SELECT name, c_overpaid(emp, 1500) AS overpaid PG_MODULE_MAGIC; #endif -bool -c_overpaid(HeapTupleHeader t, /* the current row of emp */ - int32 limit) -{ - bool isnull; - int32 salary; - - salary = DatumGetInt32(GetAttributeByName(t, "salary", &isnull)); - if (isnull) - return false; - return salary > limit; -} -]]> -</programlisting> - - In version-1 coding, the above would look like this: - -<programlisting><![CDATA[ -#include "postgres.h" -#include "executor/executor.h" /* for GetAttributeByName() */ - -#ifdef PG_MODULE_MAGIC -PG_MODULE_MAGIC; -#endif - PG_FUNCTION_INFO_V1(c_overpaid); Datum diff --git a/src/backend/utils/fmgr/README b/src/backend/utils/fmgr/README index e7e7ae9c6e8..5a2331ff159 100644 --- a/src/backend/utils/fmgr/README +++ b/src/backend/utils/fmgr/README @@ -3,62 +3,19 @@ src/backend/utils/fmgr/README Function Manager ================ -Proposal For Function-Manager Redesign 19-Nov-2000 --------------------------------------- - -We know that the existing mechanism for calling Postgres functions needs -to be redesigned. It has portability problems because it makes -assumptions about parameter passing that violate ANSI C; it fails to -handle NULL arguments and results cleanly; and "function handlers" that -support a class of functions (such as fmgr_pl) can only be done via a -really ugly, non-reentrant kluge. (Global variable set during every -function call, forsooth.) Here is a proposal for fixing these problems. - -In the past, the major objections to redoing the function-manager -interface have been (a) it'll be quite tedious to implement, since every -built-in function and everyplace that calls such functions will need to -be touched; (b) such wide-ranging changes will be difficult to make in -parallel with other development work; (c) it will break existing -user-written loadable modules that define "C language" functions. While -I have no solution to the "tedium" aspect, I believe I see an answer to -the other problems: by use of function handlers, we can support both old -and new interfaces in parallel for both callers and callees, at some -small efficiency cost for the old styles. That way, most of the changes -can be done on an incremental file-by-file basis --- we won't need a -"big bang" where everything changes at once. Support for callees -written in the old style can be left in place indefinitely, to provide -backward compatibility for user-written C functions. - - -Changes In pg_proc (System Data About a Function) -------------------------------------------------- - -A new column "proisstrict" will be added to the system pg_proc table. -This is a boolean value which will be TRUE if the function is "strict", -that is it always returns NULL when any of its inputs are NULL. The -function manager will check this field and skip calling the function when -it's TRUE and there are NULL inputs. This allows us to remove explicit -NULL-value tests from many functions that currently need them (not to -mention fixing many more that need them but don't have them). A function -that is not marked "strict" is responsible for checking whether its inputs -are NULL or not. Most builtin functions will be marked "strict". - -An optional WITH parameter will be added to CREATE FUNCTION to allow -specification of whether user-defined functions are strict or not. I am -inclined to make the default be "not strict", since that seems to be the -more useful case for functions expressed in SQL or a PL language, but -am open to arguments for the other choice. - - -The New Function-Manager Interface ----------------------------------- - -The core of the new design is revised data structures for representing -the result of a function lookup and for representing the parameters -passed to a specific function invocation. (We want to keep function -lookup separate from function call, since many parts of the system apply -the same function over and over; the lookup overhead should be paid once -per query, not once per tuple.) +[This file originally explained the transition from the V0 to the V1 +interface. Now it just explains some internals and rationale for the V1 +interface, while the V0 interface has been removed.] + +The V1 Function-Manager Interface +--------------------------------- + +The core of the design is data structures for representing the result of a +function lookup and for representing the parameters passed to a specific +function invocation. (We want to keep function lookup separate from +function call, since many parts of the system apply the same function over +and over; the lookup overhead should be paid once per query, not once per +tuple.) When a function is looked up in pg_proc, the result is represented as @@ -183,50 +140,6 @@ should have no portability or optimization problems. Function Coding Conventions --------------------------- -As an example, int4 addition goes from old-style - -int32 -int4pl(int32 arg1, int32 arg2) -{ - return arg1 + arg2; -} - -to new-style - -Datum -int4pl(FunctionCallInfo fcinfo) -{ - /* we assume the function is marked "strict", so we can ignore - * NULL-value handling */ - - return Int32GetDatum(DatumGetInt32(fcinfo->arg[0]) + - DatumGetInt32(fcinfo->arg[1])); -} - -This is, of course, much uglier than the old-style code, but we can -improve matters with some well-chosen macros for the boilerplate parts. -I propose below macros that would make the code look like - -Datum -int4pl(PG_FUNCTION_ARGS) -{ - int32 arg1 = PG_GETARG_INT32(0); - int32 arg2 = PG_GETARG_INT32(1); - - PG_RETURN_INT32( arg1 + arg2 ); -} - -This is still more code than before, but it's fairly readable, and it's -also amenable to machine processing --- for example, we could probably -write a script that scans code like this and extracts argument and result -type info for comparison to the pg_proc table. - -For the standard data types float4, float8, and int8, these macros should hide -whether the types are pass-by-value or pass-by reference, by incorporating -indirection and space allocation if needed. This will offer a considerable -gain in readability, and it also opens up the opportunity to make these types -be pass-by-value on machines where it's feasible to do so. - Here are the proposed macros and coding conventions: The definition of an fmgr-callable function will always look like @@ -291,67 +204,6 @@ fields of FunctionCallInfo, it should just do it. I doubt that providing syntactic-sugar macros for these cases is useful. -Call-Site Coding Conventions ----------------------------- - -There are many places in the system that call either a specific function -(for example, the parser invokes "textin" by name in places) or a -particular group of functions that have a common argument list (for -example, the optimizer invokes selectivity estimation functions with -a fixed argument list). These places will need to change, but we should -try to avoid making them significantly uglier than before. - -Places that invoke an arbitrary function with an arbitrary argument list -can simply be changed to fill a FunctionCallInfoData structure directly; -that'll be no worse and possibly cleaner than what they do now. - -When invoking a specific built-in function by name, we have generally -just written something like - result = textin ( ... args ... ) -which will not work after textin() is converted to the new call style. -I suggest that code like this be converted to use "helper" functions -that will create and fill in a FunctionCallInfoData struct. For -example, if textin is being called with one argument, it'd look -something like - result = DirectFunctionCall1(textin, PointerGetDatum(argument)); -These helper routines will have declarations like - Datum DirectFunctionCall2(PGFunction func, Datum arg1, Datum arg2); -Note it will be the caller's responsibility to convert to and from -Datum; appropriate conversion macros should be used. - -The DirectFunctionCallN routines will not bother to fill in -fcinfo->flinfo (indeed cannot, since they have no idea about an OID for -the target function); they will just set it NULL. This is unlikely to -bother any built-in function that could be called this way. Note also -that this style of coding cannot pass a NULL input value nor cope with -a NULL result (it couldn't before, either!). We can make the helper -routines ereport an error if they see that the function returns a NULL. - -When invoking a function that has a known argument signature, we have -usually written either - result = fmgr(targetfuncOid, ... args ... ); -or - result = fmgr_ptr(FmgrInfo *finfo, ... args ... ); -depending on whether an FmgrInfo lookup has been done yet or not. -This kind of code can be recast using helper routines, in the same -style as above: - result = OidFunctionCall1(funcOid, PointerGetDatum(argument)); - result = FunctionCall2(funcCallInfo, - PointerGetDatum(argument), - Int32GetDatum(argument)); -Again, this style of coding does not allow for expressing NULL inputs -or receiving a NULL result. - -As with the callee-side situation, I propose adding argument conversion -macros that hide whether int8, float4, and float8 are pass-by-value or -pass-by-reference. - -The existing helper functions fmgr(), fmgr_c(), etc will be left in -place until all uses of them are gone. Of course their internals will -have to change in the first step of implementation, but they can -continue to support the same external appearance. - - Support for TOAST-Able Data Types --------------------------------- @@ -474,83 +326,3 @@ context. fn_mcxt normally points at the context that was CurrentMemoryContext at the time the FmgrInfo structure was created; in any case it is required to be a context at least as long-lived as the FmgrInfo itself. - - -Telling the Difference Between Old- and New-Style Functions ------------------------------------------------------------ - -During the conversion process, we carried two different pg_language -entries, "internal" and "newinternal", for internal functions. The -function manager used the language code to distinguish which calling -convention to use. (Old-style internal functions were supported via -a function handler.) As of Nov. 2000, no old-style internal functions -remain, so we can drop support for them. We will remove the old "internal" -pg_language entry and rename "newinternal" to "internal". - -The interim solution for dynamically-loaded compiled functions has been -similar: two pg_language entries "C" and "newC". This naming convention -is not desirable for the long run, and yet we cannot stop supporting -old-style user functions. Instead, it seems better to use just one -pg_language entry "C", and require the dynamically-loaded library to -provide additional information that identifies new-style functions. -This avoids compatibility problems --- for example, existing dump -scripts will identify PL language handlers as being in language "C", -which would be wrong under the "newC" convention. Also, this approach -should generalize more conveniently for future extensions to the function -interface specification. - -Given a dynamically loaded function named "foo" (note that the name being -considered here is the link-symbol name, not the SQL-level function name), -the function manager will look for another function in the same dynamically -loaded library named "pg_finfo_foo". If this second function does not -exist, then foo is assumed to be called old-style, thus ensuring backwards -compatibility with existing libraries. If the info function does exist, -it is expected to have the signature - - Pg_finfo_record * pg_finfo_foo (void); - -The info function will be called by the fmgr, and must return a pointer -to a Pg_finfo_record struct. (The returned struct will typically be a -statically allocated constant in the dynamic-link library.) The current -definition of the struct is just - - typedef struct { - int api_version; - } Pg_finfo_record; - -where api_version is 0 to indicate old-style or 1 to indicate new-style -calling convention. In future releases, additional fields may be defined -after api_version, but these additional fields will only be used if -api_version is greater than 1. - -These details will be hidden from the author of a dynamically loaded -function by using a macro. To define a new-style dynamically loaded -function named foo, write - - PG_FUNCTION_INFO_V1(foo); - - Datum - foo(PG_FUNCTION_ARGS) - { - ... - } - -The function itself is written using the same conventions as for new-style -internal functions; you just need to add the PG_FUNCTION_INFO_V1() macro. -Note that old-style and new-style functions can be intermixed in the same -library, depending on whether or not you write a PG_FUNCTION_INFO_V1() for -each one. - -The SQL declaration for a dynamically-loaded function is CREATE FUNCTION -foo ... LANGUAGE C regardless of whether it is old- or new-style. - -New-style dynamic functions will be invoked directly by fmgr, and will -therefore have the same performance as internal functions after the initial -pg_proc lookup overhead. Old-style dynamic functions will be invoked via -a handler, and will therefore have a small performance penalty. - -To allow old-style dynamic functions to work safely on toastable datatypes, -the handler for old-style functions will automatically detoast toastable -arguments before passing them to the old-style function. A new-style -function is expected to take care of toasted arguments by using the -standard argument access macros defined above. diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 9fb695279bb..68d2110890a 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -37,37 +37,6 @@ PGDLLIMPORT needs_fmgr_hook_type needs_fmgr_hook = NULL; PGDLLIMPORT fmgr_hook_type fmgr_hook = NULL; /* - * Declaration for old-style function pointer type. This is now used only - * in fmgr_oldstyle() and is no longer exported. - * - * The m68k SVR4 ABI defines that pointers are returned in %a0 instead of - * %d0. So if a function pointer is declared to return a pointer, the - * compiler may look only into %a0, but if the called function was declared - * to return an integer type, it puts its value only into %d0. So the - * caller doesn't pick up the correct return value. The solution is to - * declare the function pointer to return int, so the compiler picks up the - * return value from %d0. (Functions returning pointers put their value - * *additionally* into %d0 for compatibility.) The price is that there are - * some warnings about int->pointer conversions ... which we can suppress - * with suitably ugly casts in fmgr_oldstyle(). - */ -#if (defined(__mc68000__) || (defined(__m68k__))) && defined(__ELF__) -typedef int32 (*func_ptr) (); -#else -typedef char *(*func_ptr) (); -#endif - -/* - * For an oldstyle function, fn_extra points to a record like this: - */ -typedef struct -{ - func_ptr func; /* Address of the oldstyle function */ - bool arg_toastable[FUNC_MAX_ARGS]; /* is n'th arg of a toastable - * datatype? */ -} Oldstyle_fnextra; - -/* * Hashtable for fast lookup of external C functions */ typedef struct @@ -90,7 +59,6 @@ static void fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple proc static CFuncHashTabEntry *lookup_C_func(HeapTuple procedureTuple); static void record_C_func(HeapTuple procedureTuple, PGFunction user_fn, const Pg_finfo_record *inforec); -static Datum fmgr_oldstyle(PG_FUNCTION_ARGS); static Datum fmgr_security_definer(PG_FUNCTION_ARGS); @@ -304,13 +272,10 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, static void fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) { - Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); CFuncHashTabEntry *hashentry; PGFunction user_fn; const Pg_finfo_record *inforec; - Oldstyle_fnextra *fnextra; bool isnull; - int i; /* * See if we have the function address cached already @@ -362,20 +327,6 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) switch (inforec->api_version) { - case 0: - /* Old style: need to use a handler */ - finfo->fn_addr = fmgr_oldstyle; - fnextra = (Oldstyle_fnextra *) - MemoryContextAllocZero(finfo->fn_mcxt, - sizeof(Oldstyle_fnextra)); - finfo->fn_extra = (void *) fnextra; - fnextra->func = (func_ptr) user_fn; - for (i = 0; i < procedureStruct->pronargs; i++) - { - fnextra->arg_toastable[i] = - TypeIsToastable(procedureStruct->proargtypes.values[i]); - } - break; case 1: /* New style: call directly */ finfo->fn_addr = user_fn; @@ -415,14 +366,6 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) CurrentMemoryContext, true); finfo->fn_addr = plfinfo.fn_addr; - /* - * If lookup of the PL handler function produced nonnull fn_extra, - * complain --- it must be an oldstyle function! We no longer support - * oldstyle PL handlers. - */ - if (plfinfo.fn_extra != NULL) - elog(ERROR, "language %u has old-style handler", language); - ReleaseSysCache(languageTuple); } @@ -431,10 +374,7 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) * The function is specified by a handle for the containing library * (obtained from load_external_function) as well as the function name. * - * If no info function exists for the given name, it is not an error. - * Instead we return a default info record for a version-0 function. - * We want to raise an error here only if the info function returns - * something bogus. + * If no info function exists for the given name an error is raised. * * This function is broken out of fmgr_info_C_lang so that fmgr_c_validator * can validate the information record for a function not yet entered into @@ -446,7 +386,6 @@ fetch_finfo_record(void *filehandle, char *funcname) char *infofuncname; PGFInfoFunction infofunc; const Pg_finfo_record *inforec; - static Pg_finfo_record default_inforec = {0}; infofuncname = psprintf("pg_finfo_%s", funcname); @@ -455,9 +394,12 @@ fetch_finfo_record(void *filehandle, char *funcname) infofuncname); if (infofunc == NULL) { - /* Not found --- assume version 0 */ - pfree(infofuncname); - return &default_inforec; + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not find function information for function \"%s\"", + funcname), + errhint("SQL-callable functions need an accompanying PG_FUNCTION_INFO_V1(funcname)."))); + return NULL; /* silence compiler */ } /* Found, so call it */ @@ -468,7 +410,6 @@ fetch_finfo_record(void *filehandle, char *funcname) elog(ERROR, "null result from info function \"%s\"", infofuncname); switch (inforec->api_version) { - case 0: case 1: /* OK, no additional fields to validate */ break; @@ -585,18 +526,7 @@ fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, { memcpy(dstinfo, srcinfo, sizeof(FmgrInfo)); dstinfo->fn_mcxt = destcxt; - if (dstinfo->fn_addr == fmgr_oldstyle) - { - /* For oldstyle functions we must copy fn_extra */ - Oldstyle_fnextra *fnextra; - - fnextra = (Oldstyle_fnextra *) - MemoryContextAlloc(destcxt, sizeof(Oldstyle_fnextra)); - memcpy(fnextra, srcinfo->fn_extra, sizeof(Oldstyle_fnextra)); - dstinfo->fn_extra = (void *) fnextra; - } - else - dstinfo->fn_extra = NULL; + dstinfo->fn_extra = NULL; } @@ -617,245 +547,6 @@ fmgr_internal_function(const char *proname) /* - * Handler for old-style "C" language functions - */ -static Datum -fmgr_oldstyle(PG_FUNCTION_ARGS) -{ - Oldstyle_fnextra *fnextra; - int n_arguments = fcinfo->nargs; - int i; - bool isnull; - func_ptr user_fn; - char *returnValue; - - if (fcinfo->flinfo == NULL || fcinfo->flinfo->fn_extra == NULL) - elog(ERROR, "fmgr_oldstyle received NULL pointer"); - fnextra = (Oldstyle_fnextra *) fcinfo->flinfo->fn_extra; - - /* - * Result is NULL if any argument is NULL, but we still call the function - * (peculiar, but that's the way it worked before, and after all this is a - * backwards-compatibility wrapper). Note, however, that we'll never get - * here with NULL arguments if the function is marked strict. - * - * We also need to detoast any TOAST-ed inputs, since it's unlikely that - * an old-style function knows about TOASTing. - */ - isnull = false; - for (i = 0; i < n_arguments; i++) - { - if (PG_ARGISNULL(i)) - isnull = true; - else if (fnextra->arg_toastable[i]) - fcinfo->arg[i] = PointerGetDatum(PG_DETOAST_DATUM(fcinfo->arg[i])); - } - fcinfo->isnull = isnull; - - user_fn = fnextra->func; - - switch (n_arguments) - { - case 0: - returnValue = (char *) (*user_fn) (); - break; - case 1: - - /* - * nullvalue() used to use isNull to check if arg is NULL; perhaps - * there are other functions still out there that also rely on - * this undocumented hack? - */ - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - &fcinfo->isnull); - break; - case 2: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1]); - break; - case 3: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2]); - break; - case 4: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3]); - break; - case 5: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4]); - break; - case 6: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5]); - break; - case 7: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5], - fcinfo->arg[6]); - break; - case 8: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5], - fcinfo->arg[6], - fcinfo->arg[7]); - break; - case 9: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5], - fcinfo->arg[6], - fcinfo->arg[7], - fcinfo->arg[8]); - break; - case 10: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5], - fcinfo->arg[6], - fcinfo->arg[7], - fcinfo->arg[8], - fcinfo->arg[9]); - break; - case 11: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5], - fcinfo->arg[6], - fcinfo->arg[7], - fcinfo->arg[8], - fcinfo->arg[9], - fcinfo->arg[10]); - break; - case 12: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5], - fcinfo->arg[6], - fcinfo->arg[7], - fcinfo->arg[8], - fcinfo->arg[9], - fcinfo->arg[10], - fcinfo->arg[11]); - break; - case 13: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5], - fcinfo->arg[6], - fcinfo->arg[7], - fcinfo->arg[8], - fcinfo->arg[9], - fcinfo->arg[10], - fcinfo->arg[11], - fcinfo->arg[12]); - break; - case 14: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5], - fcinfo->arg[6], - fcinfo->arg[7], - fcinfo->arg[8], - fcinfo->arg[9], - fcinfo->arg[10], - fcinfo->arg[11], - fcinfo->arg[12], - fcinfo->arg[13]); - break; - case 15: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5], - fcinfo->arg[6], - fcinfo->arg[7], - fcinfo->arg[8], - fcinfo->arg[9], - fcinfo->arg[10], - fcinfo->arg[11], - fcinfo->arg[12], - fcinfo->arg[13], - fcinfo->arg[14]); - break; - case 16: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5], - fcinfo->arg[6], - fcinfo->arg[7], - fcinfo->arg[8], - fcinfo->arg[9], - fcinfo->arg[10], - fcinfo->arg[11], - fcinfo->arg[12], - fcinfo->arg[13], - fcinfo->arg[14], - fcinfo->arg[15]); - break; - default: - - /* - * Increasing FUNC_MAX_ARGS doesn't automatically add cases to the - * above code, so mention the actual value in this error not - * FUNC_MAX_ARGS. You could add cases to the above if you needed - * to support old-style functions with many arguments, but making - * 'em be new-style is probably a better idea. - */ - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), - errmsg("function %u has too many arguments (%d, maximum is %d)", - fcinfo->flinfo->fn_oid, n_arguments, 16))); - returnValue = NULL; /* keep compiler quiet */ - break; - } - - return PointerGetDatum(returnValue); -} - - -/* * Support for security-definer and proconfig-using functions. We support * both of these features using the same call handler, because they are * often used together and it would be inefficient (as well as notationally @@ -2081,58 +1772,6 @@ OidSendFunctionCall(Oid functionId, Datum val) } -/* - * !!! OLD INTERFACE !!! - * - * fmgr() is the only remaining vestige of the old-style caller support - * functions. It's no longer used anywhere in the Postgres distribution, - * but we should leave it around for a release or two to ease the transition - * for user-supplied C functions. OidFunctionCallN() replaces it for new - * code. - * - * DEPRECATED, DO NOT USE IN NEW CODE - */ -char * -fmgr(Oid procedureId,...) -{ - FmgrInfo flinfo; - FunctionCallInfoData fcinfo; - int n_arguments; - Datum result; - - fmgr_info(procedureId, &flinfo); - - MemSet(&fcinfo, 0, sizeof(fcinfo)); - fcinfo.flinfo = &flinfo; - fcinfo.nargs = flinfo.fn_nargs; - n_arguments = fcinfo.nargs; - - if (n_arguments > 0) - { - va_list pvar; - int i; - - if (n_arguments > FUNC_MAX_ARGS) - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), - errmsg("function %u has too many arguments (%d, maximum is %d)", - flinfo.fn_oid, n_arguments, FUNC_MAX_ARGS))); - va_start(pvar, procedureId); - for (i = 0; i < n_arguments; i++) - fcinfo.arg[i] = PointerGetDatum(va_arg(pvar, char *)); - va_end(pvar); - } - - result = FunctionCallInvoke(&fcinfo); - - /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", flinfo.fn_oid); - - return DatumGetPointer(result); -} - - /*------------------------------------------------------------------------- * Support routines for standard maybe-pass-by-reference datatypes * diff --git a/src/include/fmgr.h b/src/include/fmgr.h index 6128752ab19..0c695e246a5 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -336,10 +336,10 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena * datum); /*------------------------------------------------------------------------- * Support for detecting call convention of dynamically-loaded functions * - * Dynamically loaded functions may use either the version-1 ("new style") - * or version-0 ("old style") calling convention. Version 1 is the call - * convention defined in this header file; version 0 is the old "plain C" - * convention. A version-1 function must be accompanied by the macro call + * Dynamically loaded functions currently can only use the version-1 ("new + * style") calling convention. Version-0 ("old style") is not supported + * anymore. Version 1 is the call convention defined in this header file, and + * must be accompanied by the macro call * * PG_FUNCTION_INFO_V1(function_name); * diff --git a/src/test/regress/input/create_function_2.source b/src/test/regress/input/create_function_2.source index 3c26b2fec6a..b167c8ac6d8 100644 --- a/src/test/regress/input/create_function_2.source +++ b/src/test/regress/input/create_function_2.source @@ -87,11 +87,6 @@ CREATE FUNCTION reverse_name(name) AS '@libdir@/regress@DLSUFFIX@' LANGUAGE C STRICT; -CREATE FUNCTION oldstyle_length(int4, text) - RETURNS int4 - AS '@libdir@/regress@DLSUFFIX@' - LANGUAGE C; -- intentionally not strict - -- -- Function dynamic loading -- diff --git a/src/test/regress/input/misc.source b/src/test/regress/input/misc.source index dd2d1b20337..b1dbc573c9b 100644 --- a/src/test/regress/input/misc.source +++ b/src/test/regress/input/misc.source @@ -250,19 +250,6 @@ SELECT *, name(equipment(h.*)) FROM hobbies_r h; SELECT *, (equipment(CAST((h.*) AS hobbies_r))).name FROM hobbies_r h; -- --- check that old-style C functions work properly with TOASTed values --- -create table oldstyle_test(i int4, t text); -insert into oldstyle_test values(null,null); -insert into oldstyle_test values(0,'12'); -insert into oldstyle_test values(1000,'12'); -insert into oldstyle_test values(0, repeat('x', 50000)); - -select i, length(t), octet_length(t), oldstyle_length(i,t) from oldstyle_test; - -drop table oldstyle_test; - --- -- functional joins -- diff --git a/src/test/regress/output/create_function_2.source b/src/test/regress/output/create_function_2.source index bdd1b1bec56..8f28bff298a 100644 --- a/src/test/regress/output/create_function_2.source +++ b/src/test/regress/output/create_function_2.source @@ -67,10 +67,6 @@ CREATE FUNCTION reverse_name(name) RETURNS name AS '@libdir@/regress@DLSUFFIX@' LANGUAGE C STRICT; -CREATE FUNCTION oldstyle_length(int4, text) - RETURNS int4 - AS '@libdir@/regress@DLSUFFIX@' - LANGUAGE C; -- intentionally not strict -- -- Function dynamic loading -- diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source index 574ef0d2e34..b9595cc2391 100644 --- a/src/test/regress/output/misc.source +++ b/src/test/regress/output/misc.source @@ -682,24 +682,6 @@ SELECT *, (equipment(CAST((h.*) AS hobbies_r))).name FROM hobbies_r h; (7 rows) -- --- check that old-style C functions work properly with TOASTed values --- -create table oldstyle_test(i int4, t text); -insert into oldstyle_test values(null,null); -insert into oldstyle_test values(0,'12'); -insert into oldstyle_test values(1000,'12'); -insert into oldstyle_test values(0, repeat('x', 50000)); -select i, length(t), octet_length(t), oldstyle_length(i,t) from oldstyle_test; - i | length | octet_length | oldstyle_length -------+--------+--------------+----------------- - | | | - 0 | 2 | 2 | 2 - 1000 | 2 | 2 | 1002 - 0 | 50000 | 50000 | 50000 -(4 rows) - -drop table oldstyle_test; --- -- functional joins -- -- diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c index 986d54ce2fa..d7fb8498d86 100644 --- a/src/test/regress/regress.c +++ b/src/test/regress/regress.c @@ -45,8 +45,6 @@ extern PATH *poly2path(POLYGON *poly); extern void regress_lseg_construct(LSEG *lseg, Point *pt1, Point *pt2); -extern char *reverse_name(char *string); -extern int oldstyle_length(int n, text *t); #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; @@ -240,14 +238,15 @@ typedef struct double radius; } WIDGET; -WIDGET *widget_in(char *str); -char *widget_out(WIDGET *widget); +PG_FUNCTION_INFO_V1(widget_in); +PG_FUNCTION_INFO_V1(widget_out); #define NARGS 3 -WIDGET * -widget_in(char *str) +Datum +widget_in(PG_FUNCTION_ARGS) { + char *str = PG_GETARG_CSTRING(0); char *p, *coord[NARGS]; int i; @@ -270,14 +269,16 @@ widget_in(char *str) result->center.y = atof(coord[1]); result->radius = atof(coord[2]); - return result; + PG_RETURN_POINTER(result); } -char * -widget_out(WIDGET *widget) +Datum +widget_out(PG_FUNCTION_ARGS) { - return psprintf("(%g,%g,%g)", - widget->center.x, widget->center.y, widget->radius); + WIDGET *widget = (WIDGET *) PG_GETARG_POINTER(0); + char *str = psprintf("(%g,%g,%g)", + widget->center.x, widget->center.y, widget->radius); + PG_RETURN_CSTRING(str); } PG_FUNCTION_INFO_V1(pt_in_widget); @@ -305,9 +306,12 @@ boxarea(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(width * height); } -char * -reverse_name(char *string) +PG_FUNCTION_INFO_V1(reverse_name); + +Datum +reverse_name(PG_FUNCTION_ARGS) { + char *string = PG_GETARG_CSTRING(0); int i; int len; char *new_string; @@ -320,22 +324,7 @@ reverse_name(char *string) len = i; for (; i >= 0; --i) new_string[len - i] = string[i]; - return new_string; -} - -/* - * This rather silly function is just to test that oldstyle functions - * work correctly on toast-able inputs. - */ -int -oldstyle_length(int n, text *t) -{ - int len = 0; - - if (t) - len = VARSIZE(t) - VARHDRSZ; - - return n + len; + PG_RETURN_CSTRING(new_string); } |