Header And Logo

PostgreSQL
| The world's most advanced open source database.

aggregatecmds.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * aggregatecmds.c
00004  *
00005  *    Routines for aggregate-manipulation commands
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  *
00011  * IDENTIFICATION
00012  *    src/backend/commands/aggregatecmds.c
00013  *
00014  * DESCRIPTION
00015  *    The "DefineFoo" routines take the parse tree and pick out the
00016  *    appropriate arguments/flags, passing the results to the
00017  *    corresponding "FooDefine" routines (in src/catalog) that do
00018  *    the actual catalog-munging.  These routines also verify permission
00019  *    of the user to execute the command.
00020  *
00021  *-------------------------------------------------------------------------
00022  */
00023 #include "postgres.h"
00024 
00025 #include "access/heapam.h"
00026 #include "access/htup_details.h"
00027 #include "catalog/dependency.h"
00028 #include "catalog/indexing.h"
00029 #include "catalog/pg_aggregate.h"
00030 #include "catalog/pg_proc.h"
00031 #include "catalog/pg_type.h"
00032 #include "commands/alter.h"
00033 #include "commands/defrem.h"
00034 #include "miscadmin.h"
00035 #include "parser/parse_func.h"
00036 #include "parser/parse_type.h"
00037 #include "utils/acl.h"
00038 #include "utils/builtins.h"
00039 #include "utils/lsyscache.h"
00040 #include "utils/syscache.h"
00041 
00042 
00043 /*
00044  *  DefineAggregate
00045  *
00046  * "oldstyle" signals the old (pre-8.2) style where the aggregate input type
00047  * is specified by a BASETYPE element in the parameters.  Otherwise,
00048  * "args" defines the input type(s).
00049  */
00050 Oid
00051 DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
00052 {
00053     char       *aggName;
00054     Oid         aggNamespace;
00055     AclResult   aclresult;
00056     List       *transfuncName = NIL;
00057     List       *finalfuncName = NIL;
00058     List       *sortoperatorName = NIL;
00059     TypeName   *baseType = NULL;
00060     TypeName   *transType = NULL;
00061     char       *initval = NULL;
00062     Oid        *aggArgTypes;
00063     int         numArgs;
00064     Oid         transTypeId;
00065     char        transTypeType;
00066     ListCell   *pl;
00067 
00068     /* Convert list of names to a name and namespace */
00069     aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);
00070 
00071     /* Check we have creation rights in target namespace */
00072     aclresult = pg_namespace_aclcheck(aggNamespace, GetUserId(), ACL_CREATE);
00073     if (aclresult != ACLCHECK_OK)
00074         aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
00075                        get_namespace_name(aggNamespace));
00076 
00077     foreach(pl, parameters)
00078     {
00079         DefElem    *defel = (DefElem *) lfirst(pl);
00080 
00081         /*
00082          * sfunc1, stype1, and initcond1 are accepted as obsolete spellings
00083          * for sfunc, stype, initcond.
00084          */
00085         if (pg_strcasecmp(defel->defname, "sfunc") == 0)
00086             transfuncName = defGetQualifiedName(defel);
00087         else if (pg_strcasecmp(defel->defname, "sfunc1") == 0)
00088             transfuncName = defGetQualifiedName(defel);
00089         else if (pg_strcasecmp(defel->defname, "finalfunc") == 0)
00090             finalfuncName = defGetQualifiedName(defel);
00091         else if (pg_strcasecmp(defel->defname, "sortop") == 0)
00092             sortoperatorName = defGetQualifiedName(defel);
00093         else if (pg_strcasecmp(defel->defname, "basetype") == 0)
00094             baseType = defGetTypeName(defel);
00095         else if (pg_strcasecmp(defel->defname, "stype") == 0)
00096             transType = defGetTypeName(defel);
00097         else if (pg_strcasecmp(defel->defname, "stype1") == 0)
00098             transType = defGetTypeName(defel);
00099         else if (pg_strcasecmp(defel->defname, "initcond") == 0)
00100             initval = defGetString(defel);
00101         else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
00102             initval = defGetString(defel);
00103         else
00104             ereport(WARNING,
00105                     (errcode(ERRCODE_SYNTAX_ERROR),
00106                      errmsg("aggregate attribute \"%s\" not recognized",
00107                             defel->defname)));
00108     }
00109 
00110     /*
00111      * make sure we have our required definitions
00112      */
00113     if (transType == NULL)
00114         ereport(ERROR,
00115                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00116                  errmsg("aggregate stype must be specified")));
00117     if (transfuncName == NIL)
00118         ereport(ERROR,
00119                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00120                  errmsg("aggregate sfunc must be specified")));
00121 
00122     /*
00123      * look up the aggregate's input datatype(s).
00124      */
00125     if (oldstyle)
00126     {
00127         /*
00128          * Old style: use basetype parameter.  This supports aggregates of
00129          * zero or one input, with input type ANY meaning zero inputs.
00130          *
00131          * Historically we allowed the command to look like basetype = 'ANY'
00132          * so we must do a case-insensitive comparison for the name ANY. Ugh.
00133          */
00134         if (baseType == NULL)
00135             ereport(ERROR,
00136                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00137                      errmsg("aggregate input type must be specified")));
00138 
00139         if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0)
00140         {
00141             numArgs = 0;
00142             aggArgTypes = NULL;
00143         }
00144         else
00145         {
00146             numArgs = 1;
00147             aggArgTypes = (Oid *) palloc(sizeof(Oid));
00148             aggArgTypes[0] = typenameTypeId(NULL, baseType);
00149         }
00150     }
00151     else
00152     {
00153         /*
00154          * New style: args is a list of TypeNames (possibly zero of 'em).
00155          */
00156         ListCell   *lc;
00157         int         i = 0;
00158 
00159         if (baseType != NULL)
00160             ereport(ERROR,
00161                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00162                      errmsg("basetype is redundant with aggregate input type specification")));
00163 
00164         numArgs = list_length(args);
00165         aggArgTypes = (Oid *) palloc(sizeof(Oid) * numArgs);
00166         foreach(lc, args)
00167         {
00168             TypeName   *curTypeName = (TypeName *) lfirst(lc);
00169 
00170             aggArgTypes[i++] = typenameTypeId(NULL, curTypeName);
00171         }
00172     }
00173 
00174     /*
00175      * look up the aggregate's transtype.
00176      *
00177      * transtype can't be a pseudo-type, since we need to be able to store
00178      * values of the transtype.  However, we can allow polymorphic transtype
00179      * in some cases (AggregateCreate will check).  Also, we allow "internal"
00180      * for functions that want to pass pointers to private data structures;
00181      * but allow that only to superusers, since you could crash the system (or
00182      * worse) by connecting up incompatible internal-using functions in an
00183      * aggregate.
00184      */
00185     transTypeId = typenameTypeId(NULL, transType);
00186     transTypeType = get_typtype(transTypeId);
00187     if (transTypeType == TYPTYPE_PSEUDO &&
00188         !IsPolymorphicType(transTypeId))
00189     {
00190         if (transTypeId == INTERNALOID && superuser())
00191              /* okay */ ;
00192         else
00193             ereport(ERROR,
00194                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
00195                      errmsg("aggregate transition data type cannot be %s",
00196                             format_type_be(transTypeId))));
00197     }
00198 
00199     /*
00200      * If we have an initval, and it's not for a pseudotype (particularly a
00201      * polymorphic type), make sure it's acceptable to the type's input
00202      * function.  We will store the initval as text, because the input
00203      * function isn't necessarily immutable (consider "now" for timestamp),
00204      * and we want to use the runtime not creation-time interpretation of the
00205      * value.  However, if it's an incorrect value it seems much more
00206      * user-friendly to complain at CREATE AGGREGATE time.
00207      */
00208     if (initval && transTypeType != TYPTYPE_PSEUDO)
00209     {
00210         Oid         typinput,
00211                     typioparam;
00212 
00213         getTypeInputInfo(transTypeId, &typinput, &typioparam);
00214         (void) OidInputFunctionCall(typinput, initval, typioparam, -1);
00215     }
00216 
00217     /*
00218      * Most of the argument-checking is done inside of AggregateCreate
00219      */
00220     return AggregateCreate(aggName, /* aggregate name */
00221                            aggNamespace,        /* namespace */
00222                            aggArgTypes, /* input data type(s) */
00223                            numArgs,
00224                            transfuncName,       /* step function name */
00225                            finalfuncName,       /* final function name */
00226                            sortoperatorName,    /* sort operator name */
00227                            transTypeId, /* transition data type */
00228                            initval);    /* initial condition */
00229 }