1996-07-09 06:22:35 +00:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-13 23:22:53 +00:00
|
|
|
* define.c
|
1996-10-31 09:08:10 +00:00
|
|
|
*
|
1997-09-07 05:04:48 +00:00
|
|
|
* These routines execute some of the CREATE statements. In an earlier
|
|
|
|
* version of Postgres, these were "define" statements.
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
2001-01-24 19:43:33 +00:00
|
|
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
2000-01-26 05:58:53 +00:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2002-04-11 20:00:18 +00:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.75 2002/04/11 19:59:57 tgl Exp $
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
* DESCRIPTION
|
1997-09-07 05:04:48 +00:00
|
|
|
* The "DefineFoo" routines take the parse tree and pick out the
|
|
|
|
* appropriate arguments/flags, passing the results to the
|
|
|
|
* corresponding "FooDefine" routines (in src/catalog) that do
|
|
|
|
* the actual catalog-munging. These routines also verify permission
|
|
|
|
* of the user to execute the command.
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
* NOTES
|
1997-09-07 05:04:48 +00:00
|
|
|
* These things must be defined and committed in the following order:
|
|
|
|
* "create function":
|
|
|
|
* input/output, recv/send procedures
|
|
|
|
* "create type":
|
|
|
|
* type
|
|
|
|
* "create operator":
|
|
|
|
* operators
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
1997-09-07 05:04:48 +00:00
|
|
|
* Most of the parse-tree manipulation routines are defined in
|
|
|
|
* commands/manip.c.
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
2001-06-13 21:44:41 +00:00
|
|
|
#include "postgres.h"
|
|
|
|
|
1996-07-09 06:22:35 +00:00
|
|
|
#include <ctype.h>
|
1996-11-06 08:21:43 +00:00
|
|
|
#include <math.h>
|
1996-07-09 06:22:35 +00:00
|
|
|
|
1999-07-15 23:04:24 +00:00
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/catname.h"
|
2002-03-19 02:18:25 +00:00
|
|
|
#include "catalog/heap.h"
|
2002-03-29 19:06:29 +00:00
|
|
|
#include "catalog/namespace.h"
|
1999-07-15 23:04:24 +00:00
|
|
|
#include "catalog/pg_aggregate.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "catalog/pg_language.h"
|
1999-07-15 23:04:24 +00:00
|
|
|
#include "catalog/pg_operator.h"
|
|
|
|
#include "catalog/pg_proc.h"
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "commands/defrem.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "fmgr.h"
|
2001-06-13 21:44:41 +00:00
|
|
|
#include "miscadmin.h"
|
1999-07-15 23:04:24 +00:00
|
|
|
#include "optimizer/cost.h"
|
2002-03-29 19:06:29 +00:00
|
|
|
#include "parser/parse_func.h"
|
|
|
|
#include "parser/parse_type.h"
|
2002-02-18 23:11:58 +00:00
|
|
|
#include "utils/acl.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "utils/builtins.h"
|
2002-03-29 19:06:29 +00:00
|
|
|
#include "utils/fmgroids.h"
|
|
|
|
#include "utils/lsyscache.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "utils/syscache.h"
|
1996-11-03 23:57:43 +00:00
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
|
2002-04-09 20:35:55 +00:00
|
|
|
static Oid findTypeIOFunction(List *procname, bool isOutput);
|
1997-09-08 21:56:23 +00:00
|
|
|
static char *defGetString(DefElem *def);
|
1999-10-02 21:33:33 +00:00
|
|
|
static double defGetNumeric(DefElem *def);
|
2002-04-09 20:35:55 +00:00
|
|
|
static List *defGetQualifiedName(DefElem *def);
|
2002-03-29 19:06:29 +00:00
|
|
|
static TypeName *defGetTypeName(DefElem *def);
|
1997-09-08 21:56:23 +00:00
|
|
|
static int defGetTypeLength(DefElem *def);
|
1996-07-09 06:22:35 +00:00
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
#define DEFAULT_TYPDELIM ','
|
1996-10-31 09:08:10 +00:00
|
|
|
|
|
|
|
|
2002-02-18 23:11:58 +00:00
|
|
|
/*
|
|
|
|
* Translate the input language name to lower case.
|
|
|
|
*/
|
1996-11-10 03:06:38 +00:00
|
|
|
static void
|
1997-09-07 05:04:48 +00:00
|
|
|
case_translate_language_name(const char *input, char *output)
|
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
int i;
|
1996-10-31 09:08:10 +00:00
|
|
|
|
2001-03-22 04:01:46 +00:00
|
|
|
for (i = 0; i < NAMEDATALEN - 1 && input[i]; ++i)
|
2000-12-03 20:45:40 +00:00
|
|
|
output[i] = tolower((unsigned char) input[i]);
|
1996-10-31 09:08:10 +00:00
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
output[i] = '\0';
|
|
|
|
}
|
1996-10-31 09:08:10 +00:00
|
|
|
|
|
|
|
|
2001-06-21 18:25:54 +00:00
|
|
|
/*
|
2001-10-25 05:50:21 +00:00
|
|
|
* Examine the "returns" clause returnType of the CREATE FUNCTION statement
|
|
|
|
* and return information about it as *prorettype_p and *returnsSet.
|
2002-03-29 19:06:29 +00:00
|
|
|
*
|
|
|
|
* This is more complex than the average typename lookup because we want to
|
|
|
|
* allow a shell type to be used, or even created if the specified return type
|
|
|
|
* doesn't exist yet. (Without this, there's no way to define the I/O procs
|
|
|
|
* for a new type.) But SQL function creation won't cope, so error out if
|
|
|
|
* the target language is SQL.
|
2001-06-21 18:25:54 +00:00
|
|
|
*/
|
2002-03-29 19:06:29 +00:00
|
|
|
static void
|
|
|
|
compute_return_type(TypeName *returnType, Oid languageOid,
|
|
|
|
Oid *prorettype_p, bool *returnsSet_p)
|
|
|
|
{
|
|
|
|
Oid rettype;
|
|
|
|
|
|
|
|
rettype = LookupTypeName(returnType);
|
|
|
|
|
|
|
|
if (OidIsValid(rettype))
|
|
|
|
{
|
|
|
|
if (!get_typisdefined(rettype))
|
|
|
|
{
|
|
|
|
if (languageOid == SQLlanguageId)
|
|
|
|
elog(ERROR, "SQL functions cannot return shell types");
|
|
|
|
else
|
|
|
|
elog(WARNING, "Return type \"%s\" is only a shell",
|
|
|
|
TypeNameToString(returnType));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *typnam = TypeNameToString(returnType);
|
|
|
|
|
|
|
|
if (strcmp(typnam, "opaque") == 0)
|
|
|
|
rettype = InvalidOid;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Oid namespaceId;
|
|
|
|
char *typname;
|
|
|
|
|
|
|
|
if (languageOid == SQLlanguageId)
|
|
|
|
elog(ERROR, "Type \"%s\" does not exist", typnam);
|
|
|
|
elog(WARNING, "ProcedureCreate: type %s is not yet defined",
|
|
|
|
typnam);
|
|
|
|
namespaceId = QualifiedNameGetCreationNamespace(returnType->names,
|
|
|
|
&typname);
|
|
|
|
rettype = TypeShellMake(typname, namespaceId);
|
|
|
|
if (!OidIsValid(rettype))
|
|
|
|
elog(ERROR, "could not create type %s", typnam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*prorettype_p = rettype;
|
2000-10-07 00:58:23 +00:00
|
|
|
*returnsSet_p = returnType->setof;
|
1996-10-31 09:08:10 +00:00
|
|
|
}
|
|
|
|
|
2002-04-11 20:00:18 +00:00
|
|
|
/*
|
|
|
|
* Interpret the argument-types list of the CREATE FUNCTION statement.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
compute_parameter_types(List *argTypes, Oid languageOid,
|
|
|
|
Oid *parameterTypes)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2002-04-11 20:00:18 +00:00
|
|
|
int parameterCount = 0;
|
|
|
|
List *x;
|
|
|
|
|
|
|
|
MemSet(parameterTypes, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
|
|
|
foreach(x, argTypes)
|
|
|
|
{
|
|
|
|
TypeName *t = (TypeName *) lfirst(x);
|
|
|
|
Oid toid;
|
|
|
|
|
|
|
|
if (parameterCount >= FUNC_MAX_ARGS)
|
|
|
|
elog(ERROR, "functions cannot have more than %d arguments",
|
|
|
|
FUNC_MAX_ARGS);
|
|
|
|
|
|
|
|
toid = LookupTypeName(t);
|
|
|
|
if (OidIsValid(toid))
|
|
|
|
{
|
|
|
|
if (!get_typisdefined(toid))
|
|
|
|
elog(WARNING, "Argument type \"%s\" is only a shell",
|
|
|
|
TypeNameToString(t));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *typnam = TypeNameToString(t);
|
|
|
|
|
|
|
|
if (strcmp(typnam, "opaque") == 0)
|
|
|
|
{
|
|
|
|
if (languageOid == SQLlanguageId)
|
|
|
|
elog(ERROR, "SQL functions cannot have arguments of type \"opaque\"");
|
|
|
|
toid = InvalidOid;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
elog(ERROR, "Type \"%s\" does not exist", typnam);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t->setof)
|
|
|
|
elog(ERROR, "functions cannot accept set arguments");
|
|
|
|
|
|
|
|
parameterTypes[parameterCount++] = toid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return parameterCount;
|
|
|
|
}
|
|
|
|
|
2001-06-21 18:25:54 +00:00
|
|
|
/*-------------
|
2001-10-25 05:50:21 +00:00
|
|
|
* Interpret the parameters *parameters and return their contents as
|
|
|
|
* *byte_pct_p, etc.
|
2001-06-21 18:25:54 +00:00
|
|
|
*
|
2001-10-25 05:50:21 +00:00
|
|
|
* These parameters supply optional information about a function.
|
|
|
|
* All have defaults if not specified.
|
2001-06-21 18:25:54 +00:00
|
|
|
*
|
2002-04-11 20:00:18 +00:00
|
|
|
* Note: currently, only three of these parameters actually do anything:
|
|
|
|
*
|
|
|
|
* * isImplicit means the function may be used as an implicit type
|
|
|
|
* coercion.
|
2001-06-21 18:25:54 +00:00
|
|
|
*
|
2001-10-25 05:50:21 +00:00
|
|
|
* * isStrict means the function should not be called when any NULL
|
|
|
|
* inputs are present; instead a NULL result value should be assumed.
|
2001-06-21 18:25:54 +00:00
|
|
|
*
|
2002-04-05 00:31:36 +00:00
|
|
|
* * volatility tells the optimizer whether the function's result can
|
|
|
|
* be assumed to be repeatable over multiple evaluations.
|
|
|
|
*
|
2001-10-25 05:50:21 +00:00
|
|
|
* The other four parameters are not used anywhere. They used to be
|
|
|
|
* used in the "expensive functions" optimizer, but that's been dead code
|
|
|
|
* for a long time.
|
2001-06-21 18:25:54 +00:00
|
|
|
*------------
|
|
|
|
*/
|
2002-04-11 20:00:18 +00:00
|
|
|
static void
|
|
|
|
compute_full_attributes(List *parameters,
|
|
|
|
int32 *byte_pct_p, int32 *perbyte_cpu_p,
|
|
|
|
int32 *percall_cpu_p, int32 *outin_ratio_p,
|
|
|
|
bool *isImplicit_p, bool *isStrict_p,
|
|
|
|
char *volatility_p)
|
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
List *pl;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/* the defaults */
|
|
|
|
*byte_pct_p = BYTE_PCT;
|
|
|
|
*perbyte_cpu_p = PERBYTE_CPU;
|
|
|
|
*percall_cpu_p = PERCALL_CPU;
|
|
|
|
*outin_ratio_p = OUTIN_RATIO;
|
2002-04-11 20:00:18 +00:00
|
|
|
*isImplicit_p = false;
|
2000-05-28 17:56:29 +00:00
|
|
|
*isStrict_p = false;
|
2002-04-05 00:31:36 +00:00
|
|
|
*volatility_p = PROVOLATILE_VOLATILE;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-10-02 21:33:33 +00:00
|
|
|
foreach(pl, parameters)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2000-04-12 17:17:23 +00:00
|
|
|
DefElem *param = (DefElem *) lfirst(pl);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-04-11 20:00:18 +00:00
|
|
|
if (strcasecmp(param->defname, "implicitcoercion") == 0)
|
|
|
|
*isImplicit_p = true;
|
|
|
|
else if (strcasecmp(param->defname, "isstrict") == 0)
|
2000-05-28 17:56:29 +00:00
|
|
|
*isStrict_p = true;
|
2002-04-05 00:31:36 +00:00
|
|
|
else if (strcasecmp(param->defname, "isimmutable") == 0)
|
|
|
|
*volatility_p = PROVOLATILE_IMMUTABLE;
|
|
|
|
else if (strcasecmp(param->defname, "isstable") == 0)
|
|
|
|
*volatility_p = PROVOLATILE_STABLE;
|
|
|
|
else if (strcasecmp(param->defname, "isvolatile") == 0)
|
|
|
|
*volatility_p = PROVOLATILE_VOLATILE;
|
|
|
|
else if (strcasecmp(param->defname, "iscachable") == 0)
|
|
|
|
{
|
|
|
|
/* obsolete spelling of isImmutable */
|
|
|
|
*volatility_p = PROVOLATILE_IMMUTABLE;
|
|
|
|
}
|
1999-10-02 21:33:33 +00:00
|
|
|
else if (strcasecmp(param->defname, "trusted") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* we don't have untrusted functions any more. The 4.2
|
|
|
|
* implementation is lousy anyway so I took it out. -ay 10/94
|
|
|
|
*/
|
1998-01-05 16:40:20 +00:00
|
|
|
elog(ERROR, "untrusted function has been decommissioned.");
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
1999-10-02 21:33:33 +00:00
|
|
|
else if (strcasecmp(param->defname, "byte_pct") == 0)
|
|
|
|
*byte_pct_p = (int) defGetNumeric(param);
|
|
|
|
else if (strcasecmp(param->defname, "perbyte_cpu") == 0)
|
|
|
|
*perbyte_cpu_p = (int) defGetNumeric(param);
|
|
|
|
else if (strcasecmp(param->defname, "percall_cpu") == 0)
|
|
|
|
*percall_cpu_p = (int) defGetNumeric(param);
|
|
|
|
else if (strcasecmp(param->defname, "outin_ratio") == 0)
|
|
|
|
*outin_ratio_p = (int) defGetNumeric(param);
|
|
|
|
else
|
2002-03-06 06:10:59 +00:00
|
|
|
elog(WARNING, "Unrecognized function attribute '%s' ignored",
|
1999-10-02 21:33:33 +00:00
|
|
|
param->defname);
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
1996-10-31 09:08:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
I have been working with user defined types and user defined c
functions. One problem that I have encountered with the function
manager is that it does not allow the user to define type conversion
functions that convert between user types. For instance if mytype1,
mytype2, and mytype3 are three Postgresql user types, and if I wish to
define Postgresql conversion functions like
I run into problems, because the Postgresql dynamic loader would look
for a single link symbol, mytype3, for both pieces of object code. If
I just change the name of one of the Postgresql functions (to make the
symbols distinct), the automatic type conversion that Postgresql uses,
for example, when matching operators to arguments no longer finds the
type conversion function.
The solution that I propose, and have implemented in the attatched
patch extends the CREATE FUNCTION syntax as follows. In the first case
above I use the link symbol mytype2_to_mytype3 for the link object
that implements the first conversion function, and define the
Postgresql operator with the following syntax
The patch includes changes to the parser to include the altered
syntax, changes to the ProcedureStmt node in nodes/parsenodes.h,
changes to commands/define.c to handle the extra information in the AS
clause, and changes to utils/fmgr/dfmgr.c that alter the way that the
dynamic loader figures out what link symbol to use. I store the
string for the link symbol in the prosrc text attribute of the pg_proc
table which is currently unused in rows that reference dynamically
loaded
functions.
Bernie Frankpitt
1999-09-28 04:34:56 +00:00
|
|
|
/*
|
|
|
|
* For a dynamically linked C language object, the form of the clause is
|
|
|
|
*
|
|
|
|
* AS <object file name> [, <link symbol name> ]
|
|
|
|
*
|
|
|
|
* In all other cases
|
|
|
|
*
|
|
|
|
* AS <object reference, or sql code>
|
|
|
|
*
|
|
|
|
*/
|
1996-10-31 09:08:10 +00:00
|
|
|
|
1996-11-10 03:06:38 +00:00
|
|
|
static void
|
2002-02-18 23:11:58 +00:00
|
|
|
interpret_AS_clause(Oid languageOid, const char *languageName, const List *as,
|
1997-09-07 05:04:48 +00:00
|
|
|
char **prosrc_str_p, char **probin_str_p)
|
|
|
|
{
|
I have been working with user defined types and user defined c
functions. One problem that I have encountered with the function
manager is that it does not allow the user to define type conversion
functions that convert between user types. For instance if mytype1,
mytype2, and mytype3 are three Postgresql user types, and if I wish to
define Postgresql conversion functions like
I run into problems, because the Postgresql dynamic loader would look
for a single link symbol, mytype3, for both pieces of object code. If
I just change the name of one of the Postgresql functions (to make the
symbols distinct), the automatic type conversion that Postgresql uses,
for example, when matching operators to arguments no longer finds the
type conversion function.
The solution that I propose, and have implemented in the attatched
patch extends the CREATE FUNCTION syntax as follows. In the first case
above I use the link symbol mytype2_to_mytype3 for the link object
that implements the first conversion function, and define the
Postgresql operator with the following syntax
The patch includes changes to the parser to include the altered
syntax, changes to the ProcedureStmt node in nodes/parsenodes.h,
changes to commands/define.c to handle the extra information in the AS
clause, and changes to utils/fmgr/dfmgr.c that alter the way that the
dynamic loader figures out what link symbol to use. I store the
string for the link symbol in the prosrc text attribute of the pg_proc
table which is currently unused in rows that reference dynamically
loaded
functions.
Bernie Frankpitt
1999-09-28 04:34:56 +00:00
|
|
|
Assert(as != NIL);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-02-18 23:11:58 +00:00
|
|
|
if (languageOid == ClanguageId)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
I have been working with user defined types and user defined c
functions. One problem that I have encountered with the function
manager is that it does not allow the user to define type conversion
functions that convert between user types. For instance if mytype1,
mytype2, and mytype3 are three Postgresql user types, and if I wish to
define Postgresql conversion functions like
I run into problems, because the Postgresql dynamic loader would look
for a single link symbol, mytype3, for both pieces of object code. If
I just change the name of one of the Postgresql functions (to make the
symbols distinct), the automatic type conversion that Postgresql uses,
for example, when matching operators to arguments no longer finds the
type conversion function.
The solution that I propose, and have implemented in the attatched
patch extends the CREATE FUNCTION syntax as follows. In the first case
above I use the link symbol mytype2_to_mytype3 for the link object
that implements the first conversion function, and define the
Postgresql operator with the following syntax
The patch includes changes to the parser to include the altered
syntax, changes to the ProcedureStmt node in nodes/parsenodes.h,
changes to commands/define.c to handle the extra information in the AS
clause, and changes to utils/fmgr/dfmgr.c that alter the way that the
dynamic loader figures out what link symbol to use. I store the
string for the link symbol in the prosrc text attribute of the pg_proc
table which is currently unused in rows that reference dynamically
loaded
functions.
Bernie Frankpitt
1999-09-28 04:34:56 +00:00
|
|
|
/*
|
|
|
|
* For "C" language, store the file name in probin and, when
|
2000-05-28 17:56:29 +00:00
|
|
|
* given, the link symbol name in prosrc.
|
I have been working with user defined types and user defined c
functions. One problem that I have encountered with the function
manager is that it does not allow the user to define type conversion
functions that convert between user types. For instance if mytype1,
mytype2, and mytype3 are three Postgresql user types, and if I wish to
define Postgresql conversion functions like
I run into problems, because the Postgresql dynamic loader would look
for a single link symbol, mytype3, for both pieces of object code. If
I just change the name of one of the Postgresql functions (to make the
symbols distinct), the automatic type conversion that Postgresql uses,
for example, when matching operators to arguments no longer finds the
type conversion function.
The solution that I propose, and have implemented in the attatched
patch extends the CREATE FUNCTION syntax as follows. In the first case
above I use the link symbol mytype2_to_mytype3 for the link object
that implements the first conversion function, and define the
Postgresql operator with the following syntax
The patch includes changes to the parser to include the altered
syntax, changes to the ProcedureStmt node in nodes/parsenodes.h,
changes to commands/define.c to handle the extra information in the AS
clause, and changes to utils/fmgr/dfmgr.c that alter the way that the
dynamic loader figures out what link symbol to use. I store the
string for the link symbol in the prosrc text attribute of the pg_proc
table which is currently unused in rows that reference dynamically
loaded
functions.
Bernie Frankpitt
1999-09-28 04:34:56 +00:00
|
|
|
*/
|
|
|
|
*probin_str_p = strVal(lfirst(as));
|
|
|
|
if (lnext(as) == NULL)
|
|
|
|
*prosrc_str_p = "-";
|
|
|
|
else
|
|
|
|
*prosrc_str_p = strVal(lsecond(as));
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
I have been working with user defined types and user defined c
functions. One problem that I have encountered with the function
manager is that it does not allow the user to define type conversion
functions that convert between user types. For instance if mytype1,
mytype2, and mytype3 are three Postgresql user types, and if I wish to
define Postgresql conversion functions like
I run into problems, because the Postgresql dynamic loader would look
for a single link symbol, mytype3, for both pieces of object code. If
I just change the name of one of the Postgresql functions (to make the
symbols distinct), the automatic type conversion that Postgresql uses,
for example, when matching operators to arguments no longer finds the
type conversion function.
The solution that I propose, and have implemented in the attatched
patch extends the CREATE FUNCTION syntax as follows. In the first case
above I use the link symbol mytype2_to_mytype3 for the link object
that implements the first conversion function, and define the
Postgresql operator with the following syntax
The patch includes changes to the parser to include the altered
syntax, changes to the ProcedureStmt node in nodes/parsenodes.h,
changes to commands/define.c to handle the extra information in the AS
clause, and changes to utils/fmgr/dfmgr.c that alter the way that the
dynamic loader figures out what link symbol to use. I store the
string for the link symbol in the prosrc text attribute of the pg_proc
table which is currently unused in rows that reference dynamically
loaded
functions.
Bernie Frankpitt
1999-09-28 04:34:56 +00:00
|
|
|
/* Everything else wants the given string in prosrc. */
|
|
|
|
*prosrc_str_p = strVal(lfirst(as));
|
1997-09-07 05:04:48 +00:00
|
|
|
*probin_str_p = "-";
|
I have been working with user defined types and user defined c
functions. One problem that I have encountered with the function
manager is that it does not allow the user to define type conversion
functions that convert between user types. For instance if mytype1,
mytype2, and mytype3 are three Postgresql user types, and if I wish to
define Postgresql conversion functions like
I run into problems, because the Postgresql dynamic loader would look
for a single link symbol, mytype3, for both pieces of object code. If
I just change the name of one of the Postgresql functions (to make the
symbols distinct), the automatic type conversion that Postgresql uses,
for example, when matching operators to arguments no longer finds the
type conversion function.
The solution that I propose, and have implemented in the attatched
patch extends the CREATE FUNCTION syntax as follows. In the first case
above I use the link symbol mytype2_to_mytype3 for the link object
that implements the first conversion function, and define the
Postgresql operator with the following syntax
The patch includes changes to the parser to include the altered
syntax, changes to the ProcedureStmt node in nodes/parsenodes.h,
changes to commands/define.c to handle the extra information in the AS
clause, and changes to utils/fmgr/dfmgr.c that alter the way that the
dynamic loader figures out what link symbol to use. I store the
string for the link symbol in the prosrc text attribute of the pg_proc
table which is currently unused in rows that reference dynamically
loaded
functions.
Bernie Frankpitt
1999-09-28 04:34:56 +00:00
|
|
|
|
2000-10-07 00:58:23 +00:00
|
|
|
if (lnext(as) != NIL)
|
1999-10-02 21:33:33 +00:00
|
|
|
elog(ERROR, "CREATE FUNCTION: only one AS item needed for %s language",
|
|
|
|
languageName);
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
1996-10-31 09:08:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
1999-05-25 16:15:34 +00:00
|
|
|
* CreateFunction
|
1997-09-07 05:04:48 +00:00
|
|
|
* Execute a CREATE FUNCTION utility statement.
|
1996-10-31 09:08:10 +00:00
|
|
|
*/
|
|
|
|
void
|
2001-09-08 01:10:21 +00:00
|
|
|
CreateFunction(ProcedureStmt *stmt)
|
1996-10-31 09:08:10 +00:00
|
|
|
{
|
2002-02-18 23:11:58 +00:00
|
|
|
char *probin_str;
|
|
|
|
char *prosrc_str;
|
2002-03-29 19:06:29 +00:00
|
|
|
Oid prorettype;
|
2002-02-18 23:11:58 +00:00
|
|
|
bool returnsSet;
|
2002-03-29 19:06:29 +00:00
|
|
|
char languageName[NAMEDATALEN];
|
|
|
|
Oid languageOid;
|
|
|
|
char *funcname;
|
|
|
|
Oid namespaceId;
|
2002-04-11 20:00:18 +00:00
|
|
|
int parameterCount;
|
|
|
|
Oid parameterTypes[FUNC_MAX_ARGS];
|
1997-09-08 02:41:22 +00:00
|
|
|
int32 byte_pct,
|
|
|
|
perbyte_cpu,
|
|
|
|
percall_cpu,
|
|
|
|
outin_ratio;
|
2002-04-11 20:00:18 +00:00
|
|
|
bool isImplicit,
|
|
|
|
isStrict;
|
2002-04-05 00:31:36 +00:00
|
|
|
char volatility;
|
2002-02-18 23:11:58 +00:00
|
|
|
HeapTuple languageTuple;
|
|
|
|
Form_pg_language languageStruct;
|
2002-03-29 19:06:29 +00:00
|
|
|
|
|
|
|
/* Convert list of names to a name and namespace */
|
|
|
|
namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
|
|
|
|
&funcname);
|
2002-02-18 23:11:58 +00:00
|
|
|
|
2000-10-07 00:58:23 +00:00
|
|
|
/* Convert language name to canonical case */
|
1997-09-07 05:04:48 +00:00
|
|
|
case_translate_language_name(stmt->language, languageName);
|
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
/* Look up the language and validate permissions */
|
2002-02-18 23:11:58 +00:00
|
|
|
languageTuple = SearchSysCache(LANGNAME,
|
|
|
|
PointerGetDatum(languageName),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(languageTuple))
|
|
|
|
elog(ERROR, "language \"%s\" does not exist", languageName);
|
1997-10-28 15:11:45 +00:00
|
|
|
|
2002-02-18 23:11:58 +00:00
|
|
|
languageOid = languageTuple->t_data->t_oid;
|
|
|
|
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
|
1997-10-28 15:11:45 +00:00
|
|
|
|
2002-02-18 23:11:58 +00:00
|
|
|
if (!((languageStruct->lanpltrusted
|
|
|
|
&& pg_language_aclcheck(languageOid, GetUserId()) == ACLCHECK_OK)
|
|
|
|
|| superuser()))
|
|
|
|
elog(ERROR, "permission denied");
|
2000-11-16 22:30:52 +00:00
|
|
|
|
2002-02-18 23:11:58 +00:00
|
|
|
ReleaseSysCache(languageTuple);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2000-10-07 00:58:23 +00:00
|
|
|
/*
|
|
|
|
* Convert remaining parameters of CREATE to form wanted by
|
|
|
|
* ProcedureCreate.
|
|
|
|
*/
|
2002-03-29 19:06:29 +00:00
|
|
|
compute_return_type(stmt->returnType, languageOid,
|
2000-10-07 00:58:23 +00:00
|
|
|
&prorettype, &returnsSet);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-04-11 20:00:18 +00:00
|
|
|
parameterCount = compute_parameter_types(stmt->argTypes, languageOid,
|
|
|
|
parameterTypes);
|
|
|
|
|
1999-10-02 21:33:33 +00:00
|
|
|
compute_full_attributes(stmt->withClause,
|
|
|
|
&byte_pct, &perbyte_cpu, &percall_cpu,
|
2002-04-11 20:00:18 +00:00
|
|
|
&outin_ratio, &isImplicit, &isStrict,
|
|
|
|
&volatility);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
interpret_AS_clause(languageOid, languageName, stmt->as,
|
|
|
|
&prosrc_str, &probin_str);
|
1999-10-02 21:33:33 +00:00
|
|
|
|
|
|
|
/*
|
2000-04-12 17:17:23 +00:00
|
|
|
* And now that we have all the parameters, and know we're permitted
|
|
|
|
* to do so, go ahead and create the function.
|
1999-10-02 21:33:33 +00:00
|
|
|
*/
|
2002-03-29 19:06:29 +00:00
|
|
|
ProcedureCreate(funcname,
|
|
|
|
namespaceId,
|
2001-10-02 21:39:36 +00:00
|
|
|
stmt->replace,
|
1999-10-02 21:33:33 +00:00
|
|
|
returnsSet,
|
|
|
|
prorettype,
|
2002-02-18 23:11:58 +00:00
|
|
|
languageOid,
|
2000-04-12 17:17:23 +00:00
|
|
|
prosrc_str, /* converted to text later */
|
|
|
|
probin_str, /* converted to text later */
|
2002-04-11 20:00:18 +00:00
|
|
|
false, /* not an aggregate */
|
2000-04-12 17:17:23 +00:00
|
|
|
true, /* (obsolete "trusted") */
|
2002-04-11 20:00:18 +00:00
|
|
|
isImplicit,
|
2000-05-28 17:56:29 +00:00
|
|
|
isStrict,
|
2002-04-05 00:31:36 +00:00
|
|
|
volatility,
|
1999-10-02 21:33:33 +00:00
|
|
|
byte_pct,
|
|
|
|
perbyte_cpu,
|
|
|
|
percall_cpu,
|
|
|
|
outin_ratio,
|
2002-04-11 20:00:18 +00:00
|
|
|
parameterCount,
|
|
|
|
parameterTypes);
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1996-10-31 09:08:10 +00:00
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
/*
|
1999-02-13 23:22:53 +00:00
|
|
|
* DefineOperator
|
1997-09-07 05:04:48 +00:00
|
|
|
* this function extracts all the information from the
|
|
|
|
* parameter list generated by the parser and then has
|
|
|
|
* OperatorCreate() do all the actual work.
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
* 'parameters' is a list of DefElem
|
|
|
|
*/
|
|
|
|
void
|
2002-03-29 19:06:29 +00:00
|
|
|
DefineOperator(List *names, List *parameters)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
2002-03-29 19:06:29 +00:00
|
|
|
char *oprName;
|
|
|
|
Oid oprNamespace;
|
1997-09-08 02:41:22 +00:00
|
|
|
uint16 precedence = 0; /* operator precedence */
|
2001-10-28 06:26:15 +00:00
|
|
|
bool canHash = false; /* operator hashes */
|
1997-09-08 02:41:22 +00:00
|
|
|
bool isLeftAssociative = true; /* operator is left
|
1997-09-07 05:04:48 +00:00
|
|
|
* associative */
|
1997-09-08 02:41:22 +00:00
|
|
|
char *functionName = NULL; /* function for operator */
|
2002-03-29 19:06:29 +00:00
|
|
|
TypeName *typeName1 = NULL; /* first type name */
|
|
|
|
TypeName *typeName2 = NULL; /* second type name */
|
|
|
|
Oid typeId1 = InvalidOid; /* types converted to OID */
|
|
|
|
Oid typeId2 = InvalidOid;
|
1997-09-08 02:41:22 +00:00
|
|
|
char *commutatorName = NULL; /* optional commutator operator
|
1997-09-07 05:04:48 +00:00
|
|
|
* name */
|
1997-09-08 02:41:22 +00:00
|
|
|
char *negatorName = NULL; /* optional negator operator name */
|
|
|
|
char *restrictionName = NULL; /* optional restrict. sel.
|
|
|
|
* procedure */
|
2001-10-28 06:26:15 +00:00
|
|
|
char *joinName = NULL; /* optional join sel. procedure name */
|
1997-09-08 02:41:22 +00:00
|
|
|
char *sortName1 = NULL; /* optional first sort operator */
|
|
|
|
char *sortName2 = NULL; /* optional second sort operator */
|
|
|
|
List *pl;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
/* Convert list of names to a name and namespace */
|
|
|
|
oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
/*
|
|
|
|
* loop over the definition list and extract the information we need.
|
|
|
|
*/
|
|
|
|
foreach(pl, parameters)
|
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
DefElem *defel = (DefElem *) lfirst(pl);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2000-10-07 00:58:23 +00:00
|
|
|
if (strcasecmp(defel->defname, "leftarg") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2002-03-29 19:06:29 +00:00
|
|
|
typeName1 = defGetTypeName(defel);
|
|
|
|
if (typeName1->setof)
|
2000-10-07 00:58:23 +00:00
|
|
|
elog(ERROR, "setof type not implemented for leftarg");
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "rightarg") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
2002-03-29 19:06:29 +00:00
|
|
|
typeName2 = defGetTypeName(defel);
|
|
|
|
if (typeName2->setof)
|
2000-10-07 00:58:23 +00:00
|
|
|
elog(ERROR, "setof type not implemented for rightarg");
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "procedure") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
functionName = defGetString(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "precedence") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
|
|
|
/* NOT IMPLEMENTED (never worked in v4.2) */
|
2002-03-06 06:10:59 +00:00
|
|
|
elog(NOTICE, "CREATE OPERATOR: precedence not implemented");
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "associativity") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
|
|
|
/* NOT IMPLEMENTED (never worked in v4.2) */
|
2002-03-06 06:10:59 +00:00
|
|
|
elog(NOTICE, "CREATE OPERATOR: associativity not implemented");
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "commutator") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
commutatorName = defGetString(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "negator") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
negatorName = defGetString(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "restrict") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
restrictionName = defGetString(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "join") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
joinName = defGetString(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "hashes") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
canHash = TRUE;
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "sort1") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
sortName1 = defGetString(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "sort2") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
sortName2 = defGetString(defel);
|
|
|
|
else
|
|
|
|
{
|
2002-03-06 06:10:59 +00:00
|
|
|
elog(WARNING, "DefineOperator: attribute \"%s\" not recognized",
|
1997-09-07 05:04:48 +00:00
|
|
|
defel->defname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make sure we have our required definitions
|
|
|
|
*/
|
|
|
|
if (functionName == NULL)
|
1998-01-05 16:40:20 +00:00
|
|
|
elog(ERROR, "Define: \"procedure\" unspecified");
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
/* Transform type names to type OIDs */
|
|
|
|
if (typeName1)
|
|
|
|
typeId1 = typenameTypeId(typeName1);
|
|
|
|
if (typeName2)
|
|
|
|
typeId2 = typenameTypeId(typeName2);
|
|
|
|
|
2001-03-22 06:16:21 +00:00
|
|
|
/*
|
|
|
|
* now have OperatorCreate do all the work..
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
|
|
|
OperatorCreate(oprName, /* operator name */
|
2002-03-29 19:06:29 +00:00
|
|
|
typeId1, /* left type id */
|
|
|
|
typeId2, /* right type id */
|
2001-10-28 06:26:15 +00:00
|
|
|
functionName, /* function for operator */
|
1997-09-07 05:04:48 +00:00
|
|
|
precedence, /* operator precedence */
|
|
|
|
isLeftAssociative, /* operator is left associative */
|
|
|
|
commutatorName, /* optional commutator operator
|
|
|
|
* name */
|
|
|
|
negatorName, /* optional negator operator name */
|
|
|
|
restrictionName, /* optional restrict. sel.
|
|
|
|
* procedure */
|
|
|
|
joinName, /* optional join sel. procedure name */
|
|
|
|
canHash, /* operator hashes */
|
|
|
|
sortName1, /* optional first sort operator */
|
|
|
|
sortName2); /* optional second sort operator */
|
|
|
|
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
/*
|
1997-09-07 05:04:48 +00:00
|
|
|
* DefineAggregate
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
|
|
|
void
|
2002-03-29 19:06:29 +00:00
|
|
|
DefineAggregate(List *names, List *parameters)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
2002-03-29 19:06:29 +00:00
|
|
|
char *aggName;
|
|
|
|
Oid aggNamespace;
|
2002-04-09 20:35:55 +00:00
|
|
|
List *transfuncName = NIL;
|
|
|
|
List *finalfuncName = NIL;
|
2002-03-29 19:06:29 +00:00
|
|
|
TypeName *baseType = NULL;
|
|
|
|
TypeName *transType = NULL;
|
2000-07-17 03:05:41 +00:00
|
|
|
char *initval = NULL;
|
2002-03-29 19:06:29 +00:00
|
|
|
Oid baseTypeId;
|
|
|
|
Oid transTypeId;
|
1997-09-08 02:41:22 +00:00
|
|
|
List *pl;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
/* Convert list of names to a name and namespace */
|
|
|
|
aggNamespace = QualifiedNameGetCreationNamespace(names, &aggName);
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
foreach(pl, parameters)
|
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
DefElem *defel = (DefElem *) lfirst(pl);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
2001-03-22 04:01:46 +00:00
|
|
|
* sfunc1, stype1, and initcond1 are accepted as obsolete
|
|
|
|
* spellings for sfunc, stype, initcond.
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
2000-07-17 03:05:41 +00:00
|
|
|
if (strcasecmp(defel->defname, "sfunc") == 0)
|
2002-04-09 20:35:55 +00:00
|
|
|
transfuncName = defGetQualifiedName(defel);
|
2000-07-17 03:05:41 +00:00
|
|
|
else if (strcasecmp(defel->defname, "sfunc1") == 0)
|
2002-04-09 20:35:55 +00:00
|
|
|
transfuncName = defGetQualifiedName(defel);
|
2000-07-17 03:05:41 +00:00
|
|
|
else if (strcasecmp(defel->defname, "finalfunc") == 0)
|
2002-04-09 20:35:55 +00:00
|
|
|
finalfuncName = defGetQualifiedName(defel);
|
2000-07-17 03:05:41 +00:00
|
|
|
else if (strcasecmp(defel->defname, "basetype") == 0)
|
2002-03-29 19:06:29 +00:00
|
|
|
baseType = defGetTypeName(defel);
|
2000-07-17 03:05:41 +00:00
|
|
|
else if (strcasecmp(defel->defname, "stype") == 0)
|
2002-03-29 19:06:29 +00:00
|
|
|
transType = defGetTypeName(defel);
|
2000-07-17 03:05:41 +00:00
|
|
|
else if (strcasecmp(defel->defname, "stype1") == 0)
|
2002-03-29 19:06:29 +00:00
|
|
|
transType = defGetTypeName(defel);
|
2000-07-17 03:05:41 +00:00
|
|
|
else if (strcasecmp(defel->defname, "initcond") == 0)
|
|
|
|
initval = defGetString(defel);
|
|
|
|
else if (strcasecmp(defel->defname, "initcond1") == 0)
|
|
|
|
initval = defGetString(defel);
|
1997-09-07 05:04:48 +00:00
|
|
|
else
|
2002-03-06 06:10:59 +00:00
|
|
|
elog(WARNING, "DefineAggregate: attribute \"%s\" not recognized",
|
1997-09-07 05:04:48 +00:00
|
|
|
defel->defname);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make sure we have our required definitions
|
|
|
|
*/
|
|
|
|
if (baseType == NULL)
|
1998-01-05 16:40:20 +00:00
|
|
|
elog(ERROR, "Define: \"basetype\" unspecified");
|
2000-07-17 03:05:41 +00:00
|
|
|
if (transType == NULL)
|
|
|
|
elog(ERROR, "Define: \"stype\" unspecified");
|
2002-04-09 20:35:55 +00:00
|
|
|
if (transfuncName == NIL)
|
2000-07-17 03:05:41 +00:00
|
|
|
elog(ERROR, "Define: \"sfunc\" unspecified");
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
/*
|
|
|
|
* Handle the aggregate's base type (input data type). This can be
|
|
|
|
* specified as 'ANY' for a data-independent transition function, such
|
|
|
|
* as COUNT(*).
|
|
|
|
*/
|
|
|
|
baseTypeId = LookupTypeName(baseType);
|
|
|
|
if (OidIsValid(baseTypeId))
|
|
|
|
{
|
|
|
|
/* no need to allow aggregates on as-yet-undefined types */
|
|
|
|
if (!get_typisdefined(baseTypeId))
|
|
|
|
elog(ERROR, "Type \"%s\" is only a shell",
|
|
|
|
TypeNameToString(baseType));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *typnam = TypeNameToString(baseType);
|
|
|
|
|
|
|
|
if (strcasecmp(typnam, "ANY") != 0)
|
|
|
|
elog(ERROR, "Type \"%s\" does not exist", typnam);
|
|
|
|
baseTypeId = InvalidOid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle transtype --- no special cases here */
|
|
|
|
transTypeId = typenameTypeId(transType);
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
/*
|
|
|
|
* Most of the argument-checking is done inside of AggregateCreate
|
|
|
|
*/
|
2001-03-22 04:01:46 +00:00
|
|
|
AggregateCreate(aggName, /* aggregate name */
|
2002-03-29 19:06:29 +00:00
|
|
|
aggNamespace, /* namespace */
|
2001-03-22 04:01:46 +00:00
|
|
|
transfuncName, /* step function name */
|
|
|
|
finalfuncName, /* final function name */
|
2002-03-29 19:06:29 +00:00
|
|
|
baseTypeId, /* type of data being aggregated */
|
|
|
|
transTypeId, /* transition data type */
|
2001-03-22 04:01:46 +00:00
|
|
|
initval); /* initial condition */
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
2002-03-19 02:18:25 +00:00
|
|
|
/*
|
|
|
|
* DefineDomain
|
|
|
|
* Registers a new domain.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
DefineDomain(CreateDomainStmt *stmt)
|
|
|
|
{
|
2002-03-29 19:06:29 +00:00
|
|
|
char *domainName;
|
|
|
|
Oid domainNamespace;
|
2002-03-20 19:45:13 +00:00
|
|
|
int16 internalLength;
|
|
|
|
int16 externalLength;
|
2002-03-29 19:06:29 +00:00
|
|
|
Oid inputProcedure;
|
|
|
|
Oid outputProcedure;
|
|
|
|
Oid receiveProcedure;
|
|
|
|
Oid sendProcedure;
|
2002-03-20 19:45:13 +00:00
|
|
|
bool byValue;
|
|
|
|
char delimiter;
|
|
|
|
char alignment;
|
|
|
|
char storage;
|
2002-03-19 02:18:25 +00:00
|
|
|
char typtype;
|
|
|
|
Datum datum;
|
2002-03-20 19:45:13 +00:00
|
|
|
bool isnull;
|
|
|
|
char *defaultValue = NULL;
|
|
|
|
char *defaultValueBin = NULL;
|
2002-03-19 02:18:25 +00:00
|
|
|
bool typNotNull = false;
|
2002-03-20 19:45:13 +00:00
|
|
|
Oid basetypelem;
|
2002-03-29 19:06:29 +00:00
|
|
|
int32 typNDims = length(stmt->typename->arrayBounds);
|
2002-03-19 02:18:25 +00:00
|
|
|
HeapTuple typeTup;
|
|
|
|
List *schema = stmt->constraints;
|
2002-03-20 19:45:13 +00:00
|
|
|
List *listptr;
|
2002-03-19 02:18:25 +00:00
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
/* Convert list of names to a name and namespace */
|
|
|
|
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
|
|
|
|
&domainName);
|
|
|
|
|
2002-03-19 02:18:25 +00:00
|
|
|
/*
|
|
|
|
* Domainnames, unlike typenames don't need to account for the '_'
|
|
|
|
* prefix. So they can be one character longer.
|
|
|
|
*/
|
2002-03-29 19:06:29 +00:00
|
|
|
if (strlen(domainName) > (NAMEDATALEN - 1))
|
2002-03-19 02:18:25 +00:00
|
|
|
elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less",
|
|
|
|
NAMEDATALEN - 1);
|
|
|
|
|
|
|
|
/*
|
2002-03-29 19:06:29 +00:00
|
|
|
* Look up the base type.
|
2002-03-19 02:18:25 +00:00
|
|
|
*/
|
2002-03-29 19:06:29 +00:00
|
|
|
typeTup = typenameType(stmt->typename);
|
2002-03-19 02:18:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* What we really don't want is domains of domains. This could cause all sorts
|
|
|
|
* of neat issues if we allow that.
|
|
|
|
*
|
|
|
|
* With testing, we may determine complex types should be allowed
|
|
|
|
*/
|
2002-03-20 19:45:13 +00:00
|
|
|
typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype;
|
|
|
|
if (typtype != 'b')
|
|
|
|
elog(ERROR, "DefineDomain: %s is not a basetype",
|
2002-03-29 19:06:29 +00:00
|
|
|
TypeNameToString(stmt->typename));
|
2002-03-19 02:18:25 +00:00
|
|
|
|
|
|
|
/* passed by value */
|
|
|
|
byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval;
|
|
|
|
|
|
|
|
/* Required Alignment */
|
|
|
|
alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign;
|
|
|
|
|
2002-03-20 19:45:13 +00:00
|
|
|
/* TOAST Strategy */
|
|
|
|
storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage;
|
|
|
|
|
2002-03-19 02:18:25 +00:00
|
|
|
/* Storage Length */
|
|
|
|
internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen;
|
|
|
|
|
|
|
|
/* External Length (unused) */
|
|
|
|
externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen;
|
|
|
|
|
|
|
|
/* Array element Delimiter */
|
|
|
|
delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim;
|
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
/* I/O Functions */
|
|
|
|
inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
|
|
|
|
outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
|
|
|
|
receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive;
|
|
|
|
sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend;
|
2002-03-19 02:18:25 +00:00
|
|
|
|
|
|
|
/* Inherited default value */
|
2002-03-29 19:06:29 +00:00
|
|
|
datum = SysCacheGetAttr(TYPEOID, typeTup,
|
2002-03-20 19:45:13 +00:00
|
|
|
Anum_pg_type_typdefault, &isnull);
|
|
|
|
if (!isnull)
|
|
|
|
defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
|
2002-03-19 02:18:25 +00:00
|
|
|
|
|
|
|
/* Inherited default binary value */
|
2002-03-29 19:06:29 +00:00
|
|
|
datum = SysCacheGetAttr(TYPEOID, typeTup,
|
2002-03-20 19:45:13 +00:00
|
|
|
Anum_pg_type_typdefaultbin, &isnull);
|
|
|
|
if (!isnull)
|
|
|
|
defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
|
2002-03-19 02:18:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Pull out the typelem name of the parent OID.
|
|
|
|
*
|
|
|
|
* This is what enables us to make a domain of an array
|
|
|
|
*/
|
2002-03-20 19:45:13 +00:00
|
|
|
basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
|
2002-03-19 02:18:25 +00:00
|
|
|
|
|
|
|
/*
|
2002-03-20 19:45:13 +00:00
|
|
|
* Run through constraints manually to avoid the additional
|
2002-03-19 02:18:25 +00:00
|
|
|
* processing conducted by DefineRelation() and friends.
|
|
|
|
*
|
|
|
|
* Besides, we don't want any constraints to be cooked. We'll
|
|
|
|
* do that when the table is created via MergeDomainAttributes().
|
|
|
|
*/
|
|
|
|
foreach(listptr, schema)
|
|
|
|
{
|
2002-03-20 19:45:13 +00:00
|
|
|
Constraint *colDef = lfirst(listptr);
|
2002-03-19 02:18:25 +00:00
|
|
|
bool nullDefined = false;
|
|
|
|
Node *expr;
|
|
|
|
ParseState *pstate;
|
|
|
|
|
2002-03-20 19:45:13 +00:00
|
|
|
switch (colDef->contype)
|
|
|
|
{
|
2002-03-19 02:18:25 +00:00
|
|
|
/*
|
|
|
|
* The inherited default value may be overridden by the user
|
|
|
|
* with the DEFAULT <expr> statement.
|
|
|
|
*
|
|
|
|
* We have to search the entire constraint tree returned as we
|
|
|
|
* don't want to cook or fiddle too much.
|
|
|
|
*/
|
|
|
|
case CONSTR_DEFAULT:
|
2002-03-20 19:45:13 +00:00
|
|
|
/* Create a dummy ParseState for transformExpr */
|
|
|
|
pstate = make_parsestate(NULL);
|
2002-03-19 02:18:25 +00:00
|
|
|
/*
|
2002-03-20 19:45:13 +00:00
|
|
|
* Cook the colDef->raw_expr into an expression.
|
2002-03-19 02:18:25 +00:00
|
|
|
* Note: Name is strictly for error message
|
|
|
|
*/
|
2002-03-20 19:45:13 +00:00
|
|
|
expr = cookDefault(pstate, colDef->raw_expr,
|
|
|
|
typeTup->t_data->t_oid,
|
|
|
|
stmt->typename->typmod,
|
2002-03-29 19:06:29 +00:00
|
|
|
domainName);
|
2002-03-20 19:45:13 +00:00
|
|
|
/*
|
|
|
|
* Expression must be stored as a nodeToString result,
|
|
|
|
* but we also require a valid textual representation
|
|
|
|
* (mainly to make life easier for pg_dump).
|
|
|
|
*/
|
2002-03-19 02:18:25 +00:00
|
|
|
defaultValue = deparse_expression(expr,
|
2002-03-29 19:06:29 +00:00
|
|
|
deparse_context_for(domainName,
|
2002-03-19 02:18:25 +00:00
|
|
|
InvalidOid),
|
|
|
|
false);
|
|
|
|
defaultValueBin = nodeToString(expr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the NULL constraint.
|
|
|
|
*/
|
|
|
|
case CONSTR_NOTNULL:
|
|
|
|
if (nullDefined) {
|
|
|
|
elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
|
|
|
|
} else {
|
|
|
|
typNotNull = true;
|
|
|
|
nullDefined = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CONSTR_NULL:
|
|
|
|
if (nullDefined) {
|
|
|
|
elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
|
|
|
|
} else {
|
|
|
|
typNotNull = false;
|
|
|
|
nullDefined = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CONSTR_UNIQUE:
|
2002-03-20 19:45:13 +00:00
|
|
|
elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported");
|
2002-03-19 02:18:25 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CONSTR_PRIMARY:
|
2002-03-20 19:45:13 +00:00
|
|
|
elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
|
2002-03-19 02:18:25 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CONSTR_CHECK:
|
2002-03-20 19:45:13 +00:00
|
|
|
elog(ERROR, "DefineDomain: CHECK Constraints not supported");
|
2002-03-19 02:18:25 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CONSTR_ATTR_DEFERRABLE:
|
|
|
|
case CONSTR_ATTR_NOT_DEFERRABLE:
|
|
|
|
case CONSTR_ATTR_DEFERRED:
|
|
|
|
case CONSTR_ATTR_IMMEDIATE:
|
2002-03-20 19:45:13 +00:00
|
|
|
elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
|
2002-03-19 02:18:25 +00:00
|
|
|
break;
|
|
|
|
|
2002-03-20 19:45:13 +00:00
|
|
|
default:
|
|
|
|
elog(ERROR, "DefineDomain: unrecognized constraint node type");
|
|
|
|
break;
|
|
|
|
}
|
2002-03-19 02:18:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Have TypeCreate do all the real work.
|
|
|
|
*/
|
2002-03-29 19:06:29 +00:00
|
|
|
TypeCreate(domainName, /* type name */
|
|
|
|
domainNamespace, /* namespace */
|
2002-03-19 02:18:25 +00:00
|
|
|
InvalidOid, /* preassigned type oid (not done here) */
|
|
|
|
InvalidOid, /* relation oid (n/a here) */
|
|
|
|
internalLength, /* internal size */
|
|
|
|
externalLength, /* external size */
|
|
|
|
'd', /* type-type (domain type) */
|
|
|
|
delimiter, /* array element delimiter */
|
2002-03-29 19:06:29 +00:00
|
|
|
inputProcedure, /* input procedure */
|
|
|
|
outputProcedure, /* output procedure */
|
|
|
|
receiveProcedure, /* receive procedure */
|
|
|
|
sendProcedure, /* send procedure */
|
|
|
|
basetypelem, /* element type ID */
|
|
|
|
typeTup->t_data->t_oid, /* base type ID */
|
2002-03-20 19:45:13 +00:00
|
|
|
defaultValue, /* default type value (text) */
|
|
|
|
defaultValueBin, /* default type value (binary) */
|
2002-03-19 02:18:25 +00:00
|
|
|
byValue, /* passed by value */
|
|
|
|
alignment, /* required alignment */
|
|
|
|
storage, /* TOAST strategy */
|
|
|
|
stmt->typename->typmod, /* typeMod value */
|
|
|
|
typNDims, /* Array dimensions for base type */
|
2002-03-29 19:06:29 +00:00
|
|
|
typNotNull); /* Type NOT NULL */
|
2002-03-19 02:18:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now we can clean up.
|
|
|
|
*/
|
|
|
|
ReleaseSysCache(typeTup);
|
|
|
|
}
|
|
|
|
|
1996-07-09 06:22:35 +00:00
|
|
|
/*
|
1999-05-25 16:15:34 +00:00
|
|
|
* DefineType
|
1997-09-07 05:04:48 +00:00
|
|
|
* Registers a new type.
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
|
|
|
void
|
2002-03-29 19:06:29 +00:00
|
|
|
DefineType(List *names, List *parameters)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
2002-03-29 19:06:29 +00:00
|
|
|
char *typeName;
|
|
|
|
Oid typeNamespace;
|
2001-10-25 05:50:21 +00:00
|
|
|
int16 internalLength = -1; /* int2 */
|
|
|
|
int16 externalLength = -1; /* int2 */
|
2002-03-29 19:06:29 +00:00
|
|
|
Oid elemType = InvalidOid;
|
2002-04-09 20:35:55 +00:00
|
|
|
List *inputName = NIL;
|
|
|
|
List *outputName = NIL;
|
|
|
|
List *sendName = NIL;
|
|
|
|
List *receiveName = NIL;
|
2001-09-06 02:07:42 +00:00
|
|
|
char *defaultValue = NULL;
|
1997-09-08 02:41:22 +00:00
|
|
|
bool byValue = false;
|
|
|
|
char delimiter = DEFAULT_TYPDELIM;
|
2001-10-28 06:26:15 +00:00
|
|
|
char alignment = 'i'; /* default alignment */
|
2001-09-06 02:07:42 +00:00
|
|
|
char storage = 'p'; /* default TOAST storage method */
|
2002-03-29 19:06:29 +00:00
|
|
|
Oid inputOid;
|
|
|
|
Oid outputOid;
|
|
|
|
Oid sendOid;
|
|
|
|
Oid receiveOid;
|
|
|
|
char *shadow_type;
|
|
|
|
List *pl;
|
|
|
|
Oid typoid;
|
|
|
|
|
|
|
|
/* Convert list of names to a name and namespace */
|
|
|
|
typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
2001-03-22 04:01:46 +00:00
|
|
|
* Type names must be one character shorter than other names, allowing
|
|
|
|
* room to create the corresponding array type name with prepended
|
|
|
|
* "_".
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
2000-10-07 00:58:23 +00:00
|
|
|
if (strlen(typeName) > (NAMEDATALEN - 2))
|
1998-01-05 16:40:20 +00:00
|
|
|
elog(ERROR, "DefineType: type names must be %d characters or less",
|
2000-10-07 00:58:23 +00:00
|
|
|
NAMEDATALEN - 2);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
foreach(pl, parameters)
|
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
DefElem *defel = (DefElem *) lfirst(pl);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2000-10-07 00:58:23 +00:00
|
|
|
if (strcasecmp(defel->defname, "internallength") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
internalLength = defGetTypeLength(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "externallength") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
externalLength = defGetTypeLength(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "input") == 0)
|
2002-04-09 20:35:55 +00:00
|
|
|
inputName = defGetQualifiedName(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "output") == 0)
|
2002-04-09 20:35:55 +00:00
|
|
|
outputName = defGetQualifiedName(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "send") == 0)
|
2002-04-09 20:35:55 +00:00
|
|
|
sendName = defGetQualifiedName(defel);
|
2002-03-29 19:06:29 +00:00
|
|
|
else if (strcasecmp(defel->defname, "receive") == 0)
|
2002-04-09 20:35:55 +00:00
|
|
|
receiveName = defGetQualifiedName(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "delimiter") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
char *p = defGetString(defel);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
delimiter = p[0];
|
|
|
|
}
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "element") == 0)
|
2002-03-29 19:06:29 +00:00
|
|
|
elemType = typenameTypeId(defGetTypeName(defel));
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "default") == 0)
|
2002-03-20 19:45:13 +00:00
|
|
|
defaultValue = defGetString(defel);
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
byValue = true;
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "alignment") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
char *a = defGetString(defel);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2001-08-03 20:47:40 +00:00
|
|
|
/*
|
2001-10-25 05:50:21 +00:00
|
|
|
* Note: if argument was an unquoted identifier, parser will
|
|
|
|
* have applied xlateSqlType() to it, so be prepared to
|
|
|
|
* recognize translated type names as well as the nominal
|
|
|
|
* form.
|
2001-08-03 20:47:40 +00:00
|
|
|
*/
|
2000-10-07 00:58:23 +00:00
|
|
|
if (strcasecmp(a, "double") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
alignment = 'd';
|
2001-08-03 20:47:40 +00:00
|
|
|
else if (strcasecmp(a, "float8") == 0)
|
|
|
|
alignment = 'd';
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(a, "int4") == 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
alignment = 'i';
|
2001-08-03 20:47:40 +00:00
|
|
|
else if (strcasecmp(a, "int2") == 0)
|
|
|
|
alignment = 's';
|
|
|
|
else if (strcasecmp(a, "char") == 0)
|
|
|
|
alignment = 'c';
|
|
|
|
else if (strcasecmp(a, "bpchar") == 0)
|
|
|
|
alignment = 'c';
|
1997-09-07 05:04:48 +00:00
|
|
|
else
|
2000-10-07 00:58:23 +00:00
|
|
|
elog(ERROR, "DefineType: \"%s\" alignment not recognized",
|
1997-09-07 05:04:48 +00:00
|
|
|
a);
|
|
|
|
}
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(defel->defname, "storage") == 0)
|
2000-07-03 23:10:14 +00:00
|
|
|
{
|
|
|
|
char *a = defGetString(defel);
|
|
|
|
|
2000-10-07 00:58:23 +00:00
|
|
|
if (strcasecmp(a, "plain") == 0)
|
2000-07-03 23:10:14 +00:00
|
|
|
storage = 'p';
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(a, "external") == 0)
|
2000-07-03 23:10:14 +00:00
|
|
|
storage = 'e';
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(a, "extended") == 0)
|
2000-07-03 23:10:14 +00:00
|
|
|
storage = 'x';
|
2000-10-07 00:58:23 +00:00
|
|
|
else if (strcasecmp(a, "main") == 0)
|
2000-07-03 23:10:14 +00:00
|
|
|
storage = 'm';
|
|
|
|
else
|
|
|
|
elog(ERROR, "DefineType: \"%s\" storage not recognized",
|
|
|
|
a);
|
|
|
|
}
|
1997-09-07 05:04:48 +00:00
|
|
|
else
|
|
|
|
{
|
2002-03-06 06:10:59 +00:00
|
|
|
elog(WARNING, "DefineType: attribute \"%s\" not recognized",
|
1997-09-07 05:04:48 +00:00
|
|
|
defel->defname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make sure we have our required definitions
|
|
|
|
*/
|
2002-04-09 20:35:55 +00:00
|
|
|
if (inputName == NIL)
|
1998-01-05 16:40:20 +00:00
|
|
|
elog(ERROR, "Define: \"input\" unspecified");
|
2002-04-09 20:35:55 +00:00
|
|
|
if (outputName == NIL)
|
1998-01-05 16:40:20 +00:00
|
|
|
elog(ERROR, "Define: \"output\" unspecified");
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
/* Convert I/O proc names to OIDs */
|
|
|
|
inputOid = findTypeIOFunction(inputName, false);
|
|
|
|
outputOid = findTypeIOFunction(outputName, true);
|
|
|
|
if (sendName)
|
|
|
|
sendOid = findTypeIOFunction(sendName, true);
|
|
|
|
else
|
|
|
|
sendOid = outputOid;
|
|
|
|
if (receiveName)
|
|
|
|
receiveOid = findTypeIOFunction(receiveName, false);
|
|
|
|
else
|
|
|
|
receiveOid = inputOid;
|
|
|
|
|
2001-03-22 06:16:21 +00:00
|
|
|
/*
|
|
|
|
* now have TypeCreate do all the real work.
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
2002-03-29 19:06:29 +00:00
|
|
|
typoid =
|
|
|
|
TypeCreate(typeName, /* type name */
|
|
|
|
typeNamespace, /* namespace */
|
|
|
|
InvalidOid, /* preassigned type oid (not done here) */
|
|
|
|
InvalidOid, /* relation oid (n/a here) */
|
|
|
|
internalLength, /* internal size */
|
|
|
|
externalLength, /* external size */
|
|
|
|
'b', /* type-type (base type) */
|
|
|
|
delimiter, /* array element delimiter */
|
|
|
|
inputOid, /* input procedure */
|
|
|
|
outputOid, /* output procedure */
|
|
|
|
receiveOid, /* receive procedure */
|
|
|
|
sendOid, /* send procedure */
|
|
|
|
elemType, /* element type ID */
|
|
|
|
InvalidOid, /* base type ID (only for domains) */
|
|
|
|
defaultValue, /* default type value */
|
|
|
|
NULL, /* no binary form available */
|
|
|
|
byValue, /* passed by value */
|
|
|
|
alignment, /* required alignment */
|
|
|
|
storage, /* TOAST strategy */
|
|
|
|
-1, /* typMod (Domains only) */
|
|
|
|
0, /* Array Dimensions of typbasetype */
|
|
|
|
false); /* Type NOT NULL */
|
1997-09-07 05:04:48 +00:00
|
|
|
|
2001-03-22 06:16:21 +00:00
|
|
|
/*
|
|
|
|
* When we create a base type (as opposed to a complex type) we need
|
|
|
|
* to have an array entry for it in pg_type as well.
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
|
|
|
shadow_type = makeArrayTypeName(typeName);
|
|
|
|
|
2001-09-06 02:07:42 +00:00
|
|
|
/* alignment must be 'i' or 'd' for arrays */
|
|
|
|
alignment = (alignment == 'd') ? 'd' : 'i';
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
TypeCreate(shadow_type, /* type name */
|
2002-03-29 19:06:29 +00:00
|
|
|
typeNamespace, /* namespace */
|
2001-02-12 20:07:21 +00:00
|
|
|
InvalidOid, /* preassigned type oid (not done here) */
|
1997-09-07 05:04:48 +00:00
|
|
|
InvalidOid, /* relation oid (n/a here) */
|
|
|
|
-1, /* internal size */
|
|
|
|
-1, /* external size */
|
|
|
|
'b', /* type-type (base type) */
|
2001-10-28 06:26:15 +00:00
|
|
|
DEFAULT_TYPDELIM, /* array element delimiter */
|
2002-03-29 19:06:29 +00:00
|
|
|
F_ARRAY_IN, /* input procedure */
|
|
|
|
F_ARRAY_OUT, /* output procedure */
|
|
|
|
F_ARRAY_IN, /* receive procedure */
|
|
|
|
F_ARRAY_OUT, /* send procedure */
|
|
|
|
typoid, /* element type ID */
|
|
|
|
InvalidOid, /* base type ID */
|
2000-07-22 03:34:43 +00:00
|
|
|
NULL, /* never a default type value */
|
2002-03-19 02:18:25 +00:00
|
|
|
NULL, /* binary default isn't sent either */
|
1997-09-07 05:04:48 +00:00
|
|
|
false, /* never passed by value */
|
2001-09-06 02:07:42 +00:00
|
|
|
alignment, /* see above */
|
2002-03-19 02:18:25 +00:00
|
|
|
'x', /* ARRAY is always toastable */
|
|
|
|
-1, /* typMod (Domains only) */
|
|
|
|
0, /* Array dimensions of typbasetype */
|
2002-03-29 19:06:29 +00:00
|
|
|
false); /* Type NOT NULL */
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
pfree(shadow_type);
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
static Oid
|
2002-04-09 20:35:55 +00:00
|
|
|
findTypeIOFunction(List *procname, bool isOutput)
|
2002-03-29 19:06:29 +00:00
|
|
|
{
|
|
|
|
Oid argList[FUNC_MAX_ARGS];
|
|
|
|
int nargs;
|
|
|
|
Oid procOid;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First look for a 1-argument func with all argtypes 0. This is
|
|
|
|
* valid for all kinds of procedure.
|
|
|
|
*/
|
|
|
|
MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
|
|
|
|
2002-04-09 20:35:55 +00:00
|
|
|
procOid = LookupFuncName(procname, 1, argList);
|
2002-03-29 19:06:29 +00:00
|
|
|
|
|
|
|
if (!OidIsValid(procOid))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Alternatively, input procedures may take 3 args (data
|
|
|
|
* value, element OID, atttypmod); the pg_proc argtype
|
|
|
|
* signature is 0,OIDOID,INT4OID. Output procedures may
|
|
|
|
* take 2 args (data value, element OID).
|
|
|
|
*/
|
|
|
|
if (isOutput)
|
|
|
|
{
|
|
|
|
/* output proc */
|
|
|
|
nargs = 2;
|
|
|
|
argList[1] = OIDOID;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* input proc */
|
|
|
|
nargs = 3;
|
|
|
|
argList[1] = OIDOID;
|
|
|
|
argList[2] = INT4OID;
|
|
|
|
}
|
2002-04-09 20:35:55 +00:00
|
|
|
procOid = LookupFuncName(procname, nargs, argList);
|
2002-03-29 19:06:29 +00:00
|
|
|
|
|
|
|
if (!OidIsValid(procOid))
|
|
|
|
func_error("TypeCreate", procname, 1, argList, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return procOid;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-08 02:41:22 +00:00
|
|
|
static char *
|
1997-09-08 21:56:23 +00:00
|
|
|
defGetString(DefElem *def)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
2000-10-07 00:58:23 +00:00
|
|
|
if (def->arg == NULL)
|
|
|
|
elog(ERROR, "Define: \"%s\" requires a parameter",
|
|
|
|
def->defname);
|
|
|
|
switch (nodeTag(def->arg))
|
|
|
|
{
|
|
|
|
case T_Integer:
|
2001-03-22 04:01:46 +00:00
|
|
|
{
|
|
|
|
char *str = palloc(32);
|
2000-04-07 13:40:45 +00:00
|
|
|
|
2001-03-22 04:01:46 +00:00
|
|
|
snprintf(str, 32, "%ld", (long) intVal(def->arg));
|
|
|
|
return str;
|
|
|
|
}
|
2000-10-07 00:58:23 +00:00
|
|
|
case T_Float:
|
2001-03-22 04:01:46 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* T_Float values are kept in string form, so this type cheat
|
2000-10-07 00:58:23 +00:00
|
|
|
* works (and doesn't risk losing precision)
|
|
|
|
*/
|
|
|
|
return strVal(def->arg);
|
|
|
|
case T_String:
|
|
|
|
return strVal(def->arg);
|
|
|
|
case T_TypeName:
|
2002-03-29 19:06:29 +00:00
|
|
|
return TypeNameToString((TypeName *) def->arg);
|
2000-10-07 00:58:23 +00:00
|
|
|
default:
|
|
|
|
elog(ERROR, "Define: cannot interpret argument of \"%s\"",
|
|
|
|
def->defname);
|
|
|
|
}
|
|
|
|
return NULL; /* keep compiler quiet */
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1999-10-02 21:33:33 +00:00
|
|
|
static double
|
|
|
|
defGetNumeric(DefElem *def)
|
|
|
|
{
|
|
|
|
if (def->arg == NULL)
|
|
|
|
elog(ERROR, "Define: \"%s\" requires a numeric value",
|
|
|
|
def->defname);
|
|
|
|
switch (nodeTag(def->arg))
|
|
|
|
{
|
|
|
|
case T_Integer:
|
|
|
|
return (double) intVal(def->arg);
|
|
|
|
case T_Float:
|
|
|
|
return floatVal(def->arg);
|
|
|
|
default:
|
|
|
|
elog(ERROR, "Define: \"%s\" requires a numeric value",
|
|
|
|
def->defname);
|
|
|
|
}
|
|
|
|
return 0; /* keep compiler quiet */
|
|
|
|
}
|
|
|
|
|
2002-04-09 20:35:55 +00:00
|
|
|
static List *
|
|
|
|
defGetQualifiedName(DefElem *def)
|
|
|
|
{
|
|
|
|
if (def->arg == NULL)
|
|
|
|
elog(ERROR, "Define: \"%s\" requires a parameter",
|
|
|
|
def->defname);
|
|
|
|
switch (nodeTag(def->arg))
|
|
|
|
{
|
|
|
|
case T_TypeName:
|
|
|
|
return ((TypeName *) def->arg)->names;
|
|
|
|
case T_String:
|
|
|
|
/* Allow quoted name for backwards compatibility */
|
|
|
|
return makeList1(def->arg);
|
|
|
|
default:
|
|
|
|
elog(ERROR, "Define: argument of \"%s\" must be a name",
|
|
|
|
def->defname);
|
|
|
|
}
|
|
|
|
return NIL; /* keep compiler quiet */
|
|
|
|
}
|
|
|
|
|
2002-03-29 19:06:29 +00:00
|
|
|
static TypeName *
|
|
|
|
defGetTypeName(DefElem *def)
|
|
|
|
{
|
|
|
|
if (def->arg == NULL)
|
|
|
|
elog(ERROR, "Define: \"%s\" requires a parameter",
|
|
|
|
def->defname);
|
|
|
|
switch (nodeTag(def->arg))
|
|
|
|
{
|
|
|
|
case T_TypeName:
|
|
|
|
return (TypeName *) def->arg;
|
|
|
|
case T_String:
|
|
|
|
{
|
|
|
|
/* Allow quoted typename for backwards compatibility */
|
|
|
|
TypeName *n = makeNode(TypeName);
|
|
|
|
|
|
|
|
n->names = makeList1(def->arg);
|
|
|
|
n->typmod = -1;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
elog(ERROR, "Define: argument of \"%s\" must be a type name",
|
|
|
|
def->defname);
|
|
|
|
}
|
|
|
|
return NULL; /* keep compiler quiet */
|
|
|
|
}
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
static int
|
1997-09-08 21:56:23 +00:00
|
|
|
defGetTypeLength(DefElem *def)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
2000-10-07 00:58:23 +00:00
|
|
|
if (def->arg == NULL)
|
|
|
|
elog(ERROR, "Define: \"%s\" requires a parameter",
|
|
|
|
def->defname);
|
|
|
|
switch (nodeTag(def->arg))
|
|
|
|
{
|
|
|
|
case T_Integer:
|
|
|
|
return intVal(def->arg);
|
|
|
|
case T_Float:
|
|
|
|
elog(ERROR, "Define: \"%s\" requires an integral value",
|
|
|
|
def->defname);
|
|
|
|
break;
|
|
|
|
case T_String:
|
|
|
|
if (strcasecmp(strVal(def->arg), "variable") == 0)
|
|
|
|
return -1; /* variable length */
|
|
|
|
break;
|
|
|
|
case T_TypeName:
|
|
|
|
/* cope if grammar chooses to believe "variable" is a typename */
|
2002-03-29 19:06:29 +00:00
|
|
|
if (strcasecmp(TypeNameToString((TypeName *) def->arg),
|
2000-10-07 00:58:23 +00:00
|
|
|
"variable") == 0)
|
|
|
|
return -1; /* variable length */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
elog(ERROR, "Define: cannot interpret argument of \"%s\"",
|
|
|
|
def->defname);
|
|
|
|
}
|
|
|
|
elog(ERROR, "Define: invalid argument for \"%s\"",
|
|
|
|
def->defname);
|
|
|
|
return 0; /* keep compiler quiet */
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|