]> granicus.if.org Git - postgresql/blobdiff - src/backend/commands/aggregatecmds.c
Update copyright via script for 2017
[postgresql] / src / backend / commands / aggregatecmds.c
index a2122c1d8b6dab36647d898667ddc4665e740947..02ea254cd498f02d7ac0a47f3a0b5b57d50183bb 100644 (file)
@@ -4,7 +4,7 @@
  *
  *       Routines for aggregate-manipulation commands
  *
- * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  */
 #include "postgres.h"
 
-#include "access/heapam.h"
+#include "access/htup_details.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_aggregate.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
+#include "commands/alter.h"
 #include "commands/defrem.h"
 #include "miscadmin.h"
 #include "parser/parse_func.h"
  *
  * "oldstyle" signals the old (pre-8.2) style where the aggregate input type
  * is specified by a BASETYPE element in the parameters.  Otherwise,
- * "args" defines the input type(s).
+ * "args" is a pair, whose first element is a list of FunctionParameter structs
+ * defining the agg's arguments (both direct and aggregated), and whose second
+ * element is an Integer node with the number of direct args, or -1 if this
+ * isn't an ordered-set aggregate.
+ * "parameters" is a list of DefElem representing the agg's definition clauses.
  */
-void
-DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
+ObjectAddress
+DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle, List *parameters)
 {
        char       *aggName;
        Oid                     aggNamespace;
        AclResult       aclresult;
+       char            aggKind = AGGKIND_NORMAL;
        List       *transfuncName = NIL;
        List       *finalfuncName = NIL;
+       List       *combinefuncName = NIL;
+       List       *serialfuncName = NIL;
+       List       *deserialfuncName = NIL;
+       List       *mtransfuncName = NIL;
+       List       *minvtransfuncName = NIL;
+       List       *mfinalfuncName = NIL;
+       bool            finalfuncExtraArgs = false;
+       bool            mfinalfuncExtraArgs = false;
        List       *sortoperatorName = NIL;
        TypeName   *baseType = NULL;
        TypeName   *transType = NULL;
+       TypeName   *mtransType = NULL;
+       int32           transSpace = 0;
+       int32           mtransSpace = 0;
        char       *initval = NULL;
-       Oid                *aggArgTypes;
+       char       *minitval = NULL;
+       char       *parallel = NULL;
        int                     numArgs;
+       int                     numDirectArgs = 0;
+       oidvector  *parameterTypes;
+       ArrayType  *allParameterTypes;
+       ArrayType  *parameterModes;
+       ArrayType  *parameterNames;
+       List       *parameterDefaults;
+       Oid                     variadicArgType;
        Oid                     transTypeId;
+       Oid                     mtransTypeId = InvalidOid;
+       char            transTypeType;
+       char            mtransTypeType = 0;
+       char            proparallel = PROPARALLEL_UNSAFE;
        ListCell   *pl;
 
        /* Convert list of names to a name and namespace */
@@ -71,6 +100,19 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
                aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
                                           get_namespace_name(aggNamespace));
 
+       /* Deconstruct the output of the aggr_args grammar production */
+       if (!oldstyle)
+       {
+               Assert(list_length(args) == 2);
+               numDirectArgs = intVal(lsecond(args));
+               if (numDirectArgs >= 0)
+                       aggKind = AGGKIND_ORDERED_SET;
+               else
+                       numDirectArgs = 0;
+               args = (List *) linitial(args);
+       }
+
+       /* Examine aggregate's definition clauses */
        foreach(pl, parameters)
        {
                DefElem    *defel = (DefElem *) lfirst(pl);
@@ -85,18 +127,55 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
                        transfuncName = defGetQualifiedName(defel);
                else if (pg_strcasecmp(defel->defname, "finalfunc") == 0)
                        finalfuncName = defGetQualifiedName(defel);
+               else if (pg_strcasecmp(defel->defname, "combinefunc") == 0)
+                       combinefuncName = defGetQualifiedName(defel);
+               else if (pg_strcasecmp(defel->defname, "serialfunc") == 0)
+                       serialfuncName = defGetQualifiedName(defel);
+               else if (pg_strcasecmp(defel->defname, "deserialfunc") == 0)
+                       deserialfuncName = defGetQualifiedName(defel);
+               else if (pg_strcasecmp(defel->defname, "msfunc") == 0)
+                       mtransfuncName = defGetQualifiedName(defel);
+               else if (pg_strcasecmp(defel->defname, "minvfunc") == 0)
+                       minvtransfuncName = defGetQualifiedName(defel);
+               else if (pg_strcasecmp(defel->defname, "mfinalfunc") == 0)
+                       mfinalfuncName = defGetQualifiedName(defel);
+               else if (pg_strcasecmp(defel->defname, "finalfunc_extra") == 0)
+                       finalfuncExtraArgs = defGetBoolean(defel);
+               else if (pg_strcasecmp(defel->defname, "mfinalfunc_extra") == 0)
+                       mfinalfuncExtraArgs = defGetBoolean(defel);
                else if (pg_strcasecmp(defel->defname, "sortop") == 0)
                        sortoperatorName = defGetQualifiedName(defel);
                else if (pg_strcasecmp(defel->defname, "basetype") == 0)
                        baseType = defGetTypeName(defel);
+               else if (pg_strcasecmp(defel->defname, "hypothetical") == 0)
+               {
+                       if (defGetBoolean(defel))
+                       {
+                               if (aggKind == AGGKIND_NORMAL)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                                        errmsg("only ordered-set aggregates can be hypothetical")));
+                               aggKind = AGGKIND_HYPOTHETICAL;
+                       }
+               }
                else if (pg_strcasecmp(defel->defname, "stype") == 0)
                        transType = defGetTypeName(defel);
                else if (pg_strcasecmp(defel->defname, "stype1") == 0)
                        transType = defGetTypeName(defel);
+               else if (pg_strcasecmp(defel->defname, "sspace") == 0)
+                       transSpace = defGetInt32(defel);
+               else if (pg_strcasecmp(defel->defname, "mstype") == 0)
+                       mtransType = defGetTypeName(defel);
+               else if (pg_strcasecmp(defel->defname, "msspace") == 0)
+                       mtransSpace = defGetInt32(defel);
                else if (pg_strcasecmp(defel->defname, "initcond") == 0)
                        initval = defGetString(defel);
                else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
                        initval = defGetString(defel);
+               else if (pg_strcasecmp(defel->defname, "minitcond") == 0)
+                       minitval = defGetString(defel);
+               else if (pg_strcasecmp(defel->defname, "parallel") == 0)
+                       parallel = defGetString(defel);
                else
                        ereport(WARNING,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
@@ -116,6 +195,46 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
                                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                                 errmsg("aggregate sfunc must be specified")));
 
+       /*
+        * if mtransType is given, mtransfuncName and minvtransfuncName must be as
+        * well; if not, then none of the moving-aggregate options should have
+        * been given.
+        */
+       if (mtransType != NULL)
+       {
+               if (mtransfuncName == NIL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("aggregate msfunc must be specified when mstype is specified")));
+               if (minvtransfuncName == NIL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("aggregate minvfunc must be specified when mstype is specified")));
+       }
+       else
+       {
+               if (mtransfuncName != NIL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                       errmsg("aggregate msfunc must not be specified without mstype")));
+               if (minvtransfuncName != NIL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("aggregate minvfunc must not be specified without mstype")));
+               if (mfinalfuncName != NIL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("aggregate mfinalfunc must not be specified without mstype")));
+               if (mtransSpace != 0)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("aggregate msspace must not be specified without mstype")));
+               if (minitval != NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("aggregate minitcond must not be specified without mstype")));
+       }
+
        /*
         * look up the aggregate's input datatype(s).
         */
@@ -128,6 +247,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
                 * Historically we allowed the command to look like basetype = 'ANY'
                 * so we must do a case-insensitive comparison for the name ANY. Ugh.
                 */
+               Oid                     aggArgTypes[1];
+
                if (baseType == NULL)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
@@ -136,22 +257,27 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
                if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0)
                {
                        numArgs = 0;
-                       aggArgTypes = NULL;
+                       aggArgTypes[0] = InvalidOid;
                }
                else
                {
                        numArgs = 1;
-                       aggArgTypes = (Oid *) palloc(sizeof(Oid));
                        aggArgTypes[0] = typenameTypeId(NULL, baseType);
                }
+               parameterTypes = buildoidvector(aggArgTypes, numArgs);
+               allParameterTypes = NULL;
+               parameterModes = NULL;
+               parameterNames = NULL;
+               parameterDefaults = NIL;
+               variadicArgType = InvalidOid;
        }
        else
        {
                /*
-                * New style: args is a list of TypeNames (possibly zero of 'em).
+                * New style: args is a list of FunctionParameters (possibly zero of
+                * 'em).  We share functioncmds.c's code for processing them.
                 */
-               ListCell   *lc;
-               int                     i = 0;
+               Oid                     requiredResultType;
 
                if (baseType != NULL)
                        ereport(ERROR,
@@ -159,13 +285,21 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
                                         errmsg("basetype is redundant with aggregate input type specification")));
 
                numArgs = list_length(args);
-               aggArgTypes = (Oid *) palloc(sizeof(Oid) * numArgs);
-               foreach(lc, args)
-               {
-                       TypeName   *curTypeName = (TypeName *) lfirst(lc);
-
-                       aggArgTypes[i++] = typenameTypeId(NULL, curTypeName);
-               }
+               interpret_function_parameter_list(pstate,
+                                                                                 args,
+                                                                                 InvalidOid,
+                                                                                 true, /* is an aggregate */
+                                                                                 &parameterTypes,
+                                                                                 &allParameterTypes,
+                                                                                 &parameterModes,
+                                                                                 &parameterNames,
+                                                                                 &parameterDefaults,
+                                                                                 &variadicArgType,
+                                                                                 &requiredResultType);
+               /* Parameter defaults are not currently allowed by the grammar */
+               Assert(parameterDefaults == NIL);
+               /* There shouldn't have been any OUT parameters, either */
+               Assert(requiredResultType == InvalidOid);
        }
 
        /*
@@ -173,14 +307,15 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
         *
         * transtype can't be a pseudo-type, since we need to be able to store
         * values of the transtype.  However, we can allow polymorphic transtype
-        * in some cases (AggregateCreate will check).  Also, we allow "internal"
+        * in some cases (AggregateCreate will check).  Also, we allow "internal"
         * for functions that want to pass pointers to private data structures;
         * but allow that only to superusers, since you could crash the system (or
         * worse) by connecting up incompatible internal-using functions in an
         * aggregate.
         */
        transTypeId = typenameTypeId(NULL, transType);
-       if (get_typtype(transTypeId) == TYPTYPE_PSEUDO &&
+       transTypeType = get_typtype(transTypeId);
+       if (transTypeType == TYPTYPE_PSEUDO &&
                !IsPolymorphicType(transTypeId))
        {
                if (transTypeId == INTERNALOID && superuser())
@@ -192,141 +327,122 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
                                                        format_type_be(transTypeId))));
        }
 
+       if (serialfuncName && deserialfuncName)
+       {
+               /*
+                * Serialization is only needed/allowed for transtype INTERNAL.
+                */
+               if (transTypeId != INTERNALOID)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("serialization functions may be specified only when the aggregate transition data type is %s",
+                                                       format_type_be(INTERNALOID))));
+       }
+       else if (serialfuncName || deserialfuncName)
+       {
+               /*
+                * Cannot specify one function without the other.
+                */
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                errmsg("must specify both or neither of serialization and deserialization functions")));
+       }
+
        /*
-        * Most of the argument-checking is done inside of AggregateCreate
+        * If a moving-aggregate transtype is specified, look that up.  Same
+        * restrictions as for transtype.
         */
-       AggregateCreate(aggName,        /* aggregate name */
-                                       aggNamespace,           /* namespace */
-                                       aggArgTypes,    /* input data type(s) */
-                                       numArgs,
-                                       transfuncName,          /* step function name */
-                                       finalfuncName,          /* final function name */
-                                       sortoperatorName,       /* sort operator name */
-                                       transTypeId,    /* transition data type */
-                                       initval);       /* initial condition */
-}
-
-
-/*
- * RemoveAggregate
- *             Deletes an aggregate.
- */
-void
-RemoveAggregate(RemoveFuncStmt *stmt)
-{
-       List       *aggName = stmt->name;
-       List       *aggArgs = stmt->args;
-       Oid                     procOid;
-       HeapTuple       tup;
-       ObjectAddress object;
-
-       /* Look up function and make sure it's an aggregate */
-       procOid = LookupAggNameTypeNames(aggName, aggArgs, stmt->missing_ok);
-
-       if (!OidIsValid(procOid))
+       if (mtransType)
        {
-               /* we only get here if stmt->missing_ok is true */
-               ereport(NOTICE,
-                               (errmsg("aggregate %s(%s) does not exist, skipping",
-                                               NameListToString(aggName),
-                                               TypeNameListToString(aggArgs))));
-               return;
+               mtransTypeId = typenameTypeId(NULL, mtransType);
+               mtransTypeType = get_typtype(mtransTypeId);
+               if (mtransTypeType == TYPTYPE_PSEUDO &&
+                       !IsPolymorphicType(mtransTypeId))
+               {
+                       if (mtransTypeId == INTERNALOID && superuser())
+                                /* okay */ ;
+                       else
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                                errmsg("aggregate transition data type cannot be %s",
+                                                               format_type_be(mtransTypeId))));
+               }
        }
 
        /*
-        * Find the function tuple, do permissions and validity checks
+        * If we have an initval, and it's not for a pseudotype (particularly a
+        * polymorphic type), make sure it's acceptable to the type's input
+        * function.  We will store the initval as text, because the input
+        * function isn't necessarily immutable (consider "now" for timestamp),
+        * and we want to use the runtime not creation-time interpretation of the
+        * value.  However, if it's an incorrect value it seems much more
+        * user-friendly to complain at CREATE AGGREGATE time.
         */
-       tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procOid));
-       if (!HeapTupleIsValid(tup)) /* should not happen */
-               elog(ERROR, "cache lookup failed for function %u", procOid);
-
-       /* Permission check: must own agg or its namespace */
-       if (!pg_proc_ownercheck(procOid, GetUserId()) &&
-         !pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
-                                                          GetUserId()))
-               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
-                                          NameListToString(aggName));
+       if (initval && transTypeType != TYPTYPE_PSEUDO)
+       {
+               Oid                     typinput,
+                                       typioparam;
 
-       ReleaseSysCache(tup);
+               getTypeInputInfo(transTypeId, &typinput, &typioparam);
+               (void) OidInputFunctionCall(typinput, initval, typioparam, -1);
+       }
 
        /*
-        * Do the deletion
+        * Likewise for moving-aggregate initval.
         */
-       object.classId = ProcedureRelationId;
-       object.objectId = procOid;
-       object.objectSubId = 0;
-
-       performDeletion(&object, stmt->behavior);
-}
-
-
-void
-RenameAggregate(List *name, List *args, const char *newname)
-{
-       Oid                     procOid;
-       Oid                     namespaceOid;
-       HeapTuple       tup;
-       Form_pg_proc procForm;
-       Relation        rel;
-       AclResult       aclresult;
-
-       rel = heap_open(ProcedureRelationId, RowExclusiveLock);
-
-       /* Look up function and make sure it's an aggregate */
-       procOid = LookupAggNameTypeNames(name, args, false);
-
-       tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));
-       if (!HeapTupleIsValid(tup)) /* should not happen */
-               elog(ERROR, "cache lookup failed for function %u", procOid);
-       procForm = (Form_pg_proc) GETSTRUCT(tup);
-
-       namespaceOid = procForm->pronamespace;
-
-       /* make sure the new name doesn't exist */
-       if (SearchSysCacheExists3(PROCNAMEARGSNSP,
-                                                         CStringGetDatum(newname),
-                                                         PointerGetDatum(&procForm->proargtypes),
-                                                         ObjectIdGetDatum(namespaceOid)))
-               ereport(ERROR,
-                               (errcode(ERRCODE_DUPLICATE_FUNCTION),
-                                errmsg("function %s already exists in schema \"%s\"",
-                                               funcname_signature_string(newname,
-                                                                                                 procForm->pronargs,
-                                                                                                 NIL,
-                                                                                          procForm->proargtypes.values),
-                                               get_namespace_name(namespaceOid))));
-
-       /* must be owner */
-       if (!pg_proc_ownercheck(procOid, GetUserId()))
-               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
-                                          NameListToString(name));
-
-       /* must have CREATE privilege on namespace */
-       aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
-       if (aclresult != ACLCHECK_OK)
-               aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
-                                          get_namespace_name(namespaceOid));
-
-       /* rename */
-       namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname);
-       simple_heap_update(rel, &tup->t_self, tup);
-       CatalogUpdateIndexes(rel, tup);
-
-       heap_close(rel, NoLock);
-       heap_freetuple(tup);
-}
+       if (minitval && mtransTypeType != TYPTYPE_PSEUDO)
+       {
+               Oid                     typinput,
+                                       typioparam;
 
-/*
- * Change aggregate owner
- */
-void
-AlterAggregateOwner(List *name, List *args, Oid newOwnerId)
-{
-       Oid                     procOid;
+               getTypeInputInfo(mtransTypeId, &typinput, &typioparam);
+               (void) OidInputFunctionCall(typinput, minitval, typioparam, -1);
+       }
 
-       /* Look up function and make sure it's an aggregate */
-       procOid = LookupAggNameTypeNames(name, args, false);
+       if (parallel)
+       {
+               if (pg_strcasecmp(parallel, "safe") == 0)
+                       proparallel = PROPARALLEL_SAFE;
+               else if (pg_strcasecmp(parallel, "restricted") == 0)
+                       proparallel = PROPARALLEL_RESTRICTED;
+               else if (pg_strcasecmp(parallel, "unsafe") == 0)
+                       proparallel = PROPARALLEL_UNSAFE;
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("parameter \"parallel\" must be SAFE, RESTRICTED, or UNSAFE")));
+       }
 
-       /* The rest is just like a function */
-       AlterFunctionOwner_oid(procOid, newOwnerId);
+       /*
+        * Most of the argument-checking is done inside of AggregateCreate
+        */
+       return AggregateCreate(aggName,         /* aggregate name */
+                                                  aggNamespace,                /* namespace */
+                                                  aggKind,
+                                                  numArgs,
+                                                  numDirectArgs,
+                                                  parameterTypes,
+                                                  PointerGetDatum(allParameterTypes),
+                                                  PointerGetDatum(parameterModes),
+                                                  PointerGetDatum(parameterNames),
+                                                  parameterDefaults,
+                                                  variadicArgType,
+                                                  transfuncName,               /* step function name */
+                                                  finalfuncName,               /* final function name */
+                                                  combinefuncName,             /* combine function name */
+                                                  serialfuncName,              /* serial function name */
+                                                  deserialfuncName,    /* deserial function name */
+                                                  mtransfuncName,              /* fwd trans function name */
+                                                  minvtransfuncName,   /* inv trans function name */
+                                                  mfinalfuncName,              /* final function name */
+                                                  finalfuncExtraArgs,
+                                                  mfinalfuncExtraArgs,
+                                                  sortoperatorName,    /* sort operator name */
+                                                  transTypeId, /* transition data type */
+                                                  transSpace,  /* transition space */
+                                                  mtransTypeId,                /* transition data type */
+                                                  mtransSpace, /* transition space */
+                                                  initval,             /* initial condition */
+                                                  minitval,    /* initial condition */
+                                                  proparallel);                /* parallel safe? */
 }