deparse: Support CREATE AGGREGATE
authorAbhijit Menon-Sen <ams@2ndQuadrant.com>
Tue, 6 May 2014 08:16:01 +0000 (13:46 +0530)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 18 Jun 2014 15:15:37 +0000 (11:15 -0400)
src/backend/tcop/deparse_utility.c

index bfe0eee458ed6e5baa289771049a0832d2ce16ab..ec457ca256556aaffa2e300bb0a49878fd910758 100644 (file)
@@ -637,6 +637,242 @@ get_persistence_str(char persistence)
    }
 }
 
+static ObjTree *
+deparse_DefineStmt_Aggregate(Oid objectId, DefineStmt *define)
+{
+   HeapTuple   aggTup;
+   HeapTuple   procTup;
+   ObjTree    *stmt;
+   ObjTree    *tmp;
+   List       *list;
+   Datum       initval;
+   bool        isnull;
+   Form_pg_aggregate agg;
+   Form_pg_proc proc;
+
+   aggTup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(objectId));
+   if (!HeapTupleIsValid(aggTup))
+       elog(ERROR, "cache lookup failed for aggregate with OID %u", objectId);
+   agg = (Form_pg_aggregate) GETSTRUCT(aggTup);
+
+   procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(agg->aggfnoid));
+   if (!HeapTupleIsValid(procTup))
+       elog(ERROR, "cache lookup failed for procedure with OID %u",
+            agg->aggfnoid);
+   proc = (Form_pg_proc) GETSTRUCT(procTup);
+
+   stmt = new_objtree_VA("CREATE AGGREGATE %{identity}D (%{types:, }s) "
+                         "(%{elems:, }s)", 0);
+
+   append_object_object(stmt, "identity",
+                        new_objtree_for_qualname(proc->pronamespace,
+                                                 NameStr(proc->proname)));
+
+   list = NIL;
+
+   /*
+    * An aggregate may have no arguments, in which case its signature
+    * is (*), to match count(*). If it's not an ordered-set aggregate,
+    * it may have a non-zero number of arguments. Otherwise it may have
+    * zero or more direct arguments and zero or more ordered arguments.
+    * There are no defaults or table parameters, and the only mode that
+    * we need to consider is VARIADIC.
+    */
+
+   if (proc->pronargs == 0)
+       list = lappend(list, new_object_object(NULL, new_objtree_VA("*", 0)));
+   else
+   {
+       int         i;
+       int         nargs;
+       Oid        *types;
+       char       *modes;
+       char      **names;
+       int         insertorderbyat = -1;
+
+       nargs = get_func_arg_info(procTup, &types, &names, &modes);
+
+       if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
+           insertorderbyat = agg->aggnumdirectargs;
+
+       for (i = 0; i < nargs; i++)
+       {
+           tmp = new_objtree_VA("%{order}s%{mode}s%{name}s%{type}T", 0);
+
+           if (i == insertorderbyat)
+               append_string_object(tmp, "order", "ORDER BY ");
+           else
+               append_string_object(tmp, "order", "");
+
+           if (modes)
+               append_string_object(tmp, "mode",
+                                    modes[i] == 'v' ? "VARIADIC " : "");
+           else
+               append_string_object(tmp, "mode", "");
+
+           if (names)
+               append_string_object(tmp, "name", names[i]);
+           else
+               append_string_object(tmp, "name", " ");
+
+           append_object_object(tmp, "type",
+                                new_objtree_for_type(types[i], -1));
+
+           list = lappend(list, new_object_object(NULL, tmp));
+
+           /*
+            * For variadic ordered-set aggregates, we have to repeat
+            * the last argument. This nasty hack is copied from
+            * print_function_arguments in ruleutils.c
+            */
+           if (i == insertorderbyat && i == nargs-1)
+               list = lappend(list, new_object_object(NULL, tmp));
+       }
+   }
+
+   append_array_object(stmt, "types", list);
+
+   list = NIL;
+
+   tmp = new_objtree_VA("SFUNC=%{procedure}D", 0);
+   append_object_object(tmp, "procedure",
+                        new_objtree_for_qualname_id(ProcedureRelationId,
+                                                    agg->aggtransfn));
+   list = lappend(list, new_object_object(NULL, tmp));
+
+   tmp = new_objtree_VA("STYPE=%{type}T", 0);
+   append_object_object(tmp, "type",
+                        new_objtree_for_type(agg->aggtranstype, -1));
+   list = lappend(list, new_object_object(NULL, tmp));
+
+   if (agg->aggtransspace != 0)
+   {
+       tmp = new_objtree_VA("SSPACE=%{space}s", 1,
+                            "space", ObjTypeString,
+                            psprintf("%d", agg->aggtransspace));
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   if (OidIsValid(agg->aggfinalfn))
+   {
+       tmp = new_objtree_VA("FINALFUNC=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        agg->aggfinalfn));
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   if (agg->aggfinalextra)
+   {
+       tmp = new_objtree_VA("FINALFUNC_EXTRA=true", 0);
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   initval = SysCacheGetAttr(AGGFNOID, aggTup,
+                             Anum_pg_aggregate_agginitval,
+                             &isnull);
+   if (!isnull)
+   {
+       tmp = new_objtree_VA("INITCOND=%{initval}L",
+                            1, "initval", ObjTypeString,
+                            TextDatumGetCString(initval));
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   if (OidIsValid(agg->aggmtransfn))
+   {
+       tmp = new_objtree_VA("MSFUNC=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        agg->aggmtransfn));
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   if (OidIsValid(agg->aggmtranstype))
+   {
+       tmp = new_objtree_VA("MSTYPE=%{type}T", 0);
+       append_object_object(tmp, "type",
+                            new_objtree_for_type(agg->aggmtranstype, -1));
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   if (agg->aggmtransspace != 0)
+   {
+       tmp = new_objtree_VA("SSPACE=%{space}s", 1,
+                            "space", ObjTypeString,
+                            psprintf("%d", agg->aggmtransspace));
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   if (OidIsValid(agg->aggminvtransfn))
+   {
+       tmp = new_objtree_VA("MINVFUNC=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        agg->aggminvtransfn));
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   if (OidIsValid(agg->aggmfinalfn))
+   {
+       tmp = new_objtree_VA("MFINALFUNC=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        agg->aggmfinalfn));
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   if (agg->aggmfinalextra)
+   {
+       tmp = new_objtree_VA("MFINALFUNC_EXTRA=true", 0);
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   initval = SysCacheGetAttr(AGGFNOID, aggTup,
+                             Anum_pg_aggregate_aggminitval,
+                             &isnull);
+   if (!isnull)
+   {
+       tmp = new_objtree_VA("MINITCOND=%{initval}L",
+                            1, "initval", ObjTypeString,
+                            TextDatumGetCString(initval));
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   if (agg->aggkind == AGGKIND_HYPOTHETICAL)
+   {
+       tmp = new_objtree_VA("HYPOTHETICAL=true", 0);
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+
+   if (OidIsValid(agg->aggsortop))
+   {
+       Oid sortop = agg->aggsortop;
+       Form_pg_operator op;
+       HeapTuple tup;
+
+       tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(sortop));
+       if (!HeapTupleIsValid(tup))
+           elog(ERROR, "cache lookup failed for operator with OID %u", sortop);
+       op = (Form_pg_operator) GETSTRUCT(tup);
+
+       tmp = new_objtree_VA("SORTOP=%{operator}O", 0);
+       append_object_object(tmp, "operator",
+                            new_objtree_for_qualname(op->oprnamespace,
+                                                     NameStr(op->oprname)));
+       list = lappend(list, new_object_object(NULL, tmp));
+
+       ReleaseSysCache(tup);
+   }
+
+   append_array_object(stmt, "elems", list);
+
+   ReleaseSysCache(procTup);
+   ReleaseSysCache(aggTup);
+
+   return stmt;
+}
+
 static ObjTree *
 deparse_DefineStmt_Collation(Oid objectId, DefineStmt *define)
 {
@@ -1210,6 +1446,10 @@ deparse_DefineStmt(Oid objectId, Node *parsetree)
 
    switch (define->kind)
    {
+       case OBJECT_AGGREGATE:
+           defStmt = deparse_DefineStmt_Aggregate(objectId, define);
+           break;
+
        case OBJECT_COLLATION:
            defStmt = deparse_DefineStmt_Collation(objectId, define);
            break;
@@ -1239,7 +1479,6 @@ deparse_DefineStmt(Oid objectId, Node *parsetree)
            break;
 
        default:
-       case OBJECT_AGGREGATE:
            elog(ERROR, "unsupported object kind");
            return NULL;
    }