Commit a8ae19ec authored by Tom Lane's avatar Tom Lane

aggregate(DISTINCT ...) works, per SQL spec.

Note this forces initdb because of change of Aggref node in stored rules.
parent efb36d2b
This diff is collapsed.
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.97 1999/11/23 20:06:52 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.98 1999/12/13 01:26:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -832,6 +832,8 @@ _copyAggref(Aggref *from)
newnode->aggtype = from->aggtype;
Node_Copy(from, newnode, target);
newnode->usenulls = from->usenulls;
newnode->aggstar = from->aggstar;
newnode->aggdistinct = from->aggdistinct;
newnode->aggno = from->aggno; /* probably not needed */
return newnode;
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.52 1999/11/23 20:06:52 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.53 1999/12/13 01:26:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -219,6 +219,10 @@ _equalAggref(Aggref *a, Aggref *b)
return false;
if (a->usenulls != b->usenulls)
return false;
if (a->aggstar != b->aggstar)
return false;
if (a->aggdistinct != b->aggdistinct)
return false;
/* ignore aggno, which is only a private field for the executor */
return true;
}
......
......@@ -5,7 +5,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: outfuncs.c,v 1.99 1999/12/10 07:37:31 tgl Exp $
* $Id: outfuncs.c,v 1.100 1999/12/13 01:26:53 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
......@@ -686,8 +686,11 @@ _outAggref(StringInfo str, Aggref *node)
node->aggtype);
_outNode(str, node->target);
appendStringInfo(str, " :usenulls %s ",
node->usenulls ? "true" : "false");
appendStringInfo(str, " :usenulls %s :aggstar %s :aggdistinct %s ",
node->usenulls ? "true" : "false",
node->aggstar ? "true" : "false",
node->aggdistinct ? "true" : "false");
/* aggno is not dumped */
}
/*
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.75 1999/11/23 20:06:53 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.76 1999/12/13 01:26:54 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
......@@ -1190,6 +1190,14 @@ _readAggref()
token = lsptok(NULL, &length); /* get usenulls */
local_node->usenulls = (token[0] == 't') ? true : false;
token = lsptok(NULL, &length); /* eat :aggstar */
token = lsptok(NULL, &length); /* get aggstar */
local_node->aggstar = (token[0] == 't') ? true : false;
token = lsptok(NULL, &length); /* eat :aggdistinct */
token = lsptok(NULL, &length); /* get aggdistinct */
local_node->aggdistinct = (token[0] == 't') ? true : false;
return local_node;
}
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.56 1999/12/09 05:58:53 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.57 1999/12/13 01:26:55 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -45,6 +45,7 @@ typedef struct {
List *targetList;
} check_subplans_for_ungrouped_vars_context;
static bool contain_agg_clause_walker(Node *node, void *context);
static bool pull_agg_clause_walker(Node *node, List **listptr);
static bool check_subplans_for_ungrouped_vars_walker(Node *node,
check_subplans_for_ungrouped_vars_context *context);
......@@ -393,12 +394,36 @@ pull_constant_clauses(List *quals, List **constantQual)
return restqual;
}
/*
* contain_agg_clause
* Recursively search for Aggref nodes within a clause.
*
* Returns true if any aggregate found.
*/
bool
contain_agg_clause(Node *clause)
{
return contain_agg_clause_walker(clause, NULL);
}
static bool
contain_agg_clause_walker(Node *node, void *context)
{
if (node == NULL)
return false;
if (IsA(node, Aggref))
return true; /* abort the tree traversal and return true */
return expression_tree_walker(node, contain_agg_clause_walker, context);
}
/*
* pull_agg_clause
* Recursively pulls all Aggref nodes from an expression tree.
*
* Returns list of Aggref nodes found. Note the nodes themselves are not
* copied, only referenced.
*
* Note: this also checks for nested aggregates, which are an error.
*/
List *
pull_agg_clause(Node *clause)
......@@ -417,9 +442,16 @@ pull_agg_clause_walker(Node *node, List **listptr)
if (IsA(node, Aggref))
{
*listptr = lappend(*listptr, node);
/* continue, to iterate over agg's arg as well (do nested aggregates
* actually work?)
/*
* Complain if the aggregate's argument contains any aggregates;
* nested agg functions are semantically nonsensical.
*/
if (contain_agg_clause(((Aggref *) node)->target))
elog(ERROR, "Aggregate function calls may not be nested");
/*
* Having checked that, we need not recurse into the argument.
*/
return false;
}
return expression_tree_walker(node, pull_agg_clause_walker,
(void *) listptr);
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.31 1999/12/10 07:37:35 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.32 1999/12/13 01:26:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -28,38 +28,11 @@ typedef struct {
List *groupClauses;
} check_ungrouped_columns_context;
static bool contain_agg_clause(Node *clause);
static bool contain_agg_clause_walker(Node *node, void *context);
static void check_ungrouped_columns(Node *node, ParseState *pstate,
List *groupClauses);
static bool check_ungrouped_columns_walker(Node *node,
check_ungrouped_columns_context *context);
/*
* contain_agg_clause
* Recursively find aggref nodes within a clause.
*
* Returns true if any aggregate found.
*
* NOTE: we assume that the given clause has been transformed suitably for
* parser output. This means we can use the planner's expression_tree_walker.
*/
static bool
contain_agg_clause(Node *clause)
{
return contain_agg_clause_walker(clause, NULL);
}
static bool
contain_agg_clause_walker(Node *node, void *context)
{
if (node == NULL)
return false;
if (IsA(node, Aggref))
return true; /* abort the tree traversal and return true */
return expression_tree_walker(node, contain_agg_clause_walker, context);
}
/*
* check_ungrouped_columns -
* Scan the given expression tree for ungrouped variables (variables
......@@ -232,7 +205,8 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
* Since "1" never evaluates as null, we currently have no need of
* the "usenulls" flag, but it should be kept around; in fact, we should
* extend the pg_aggregate table to let usenulls be specified as an
* attribute of user-defined aggregates.
* attribute of user-defined aggregates. In the meantime, usenulls
* is just always set to "false".
*/
aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple);
......@@ -264,14 +238,8 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
aggref->aggtype = fintype;
aggref->target = lfirst(args);
aggref->usenulls = usenulls;
/*
* We should store agg_star and agg_distinct into the Aggref node,
* and let downstream processing deal with them. Currently, agg_star
* is ignored and agg_distinct is not implemented...
*/
if (agg_distinct)
elog(ERROR, "aggregate(DISTINCT ...) is not implemented yet");
aggref->aggstar = agg_star;
aggref->aggdistinct = agg_distinct;
pstate->p_hasAggs = true;
......
......@@ -3,7 +3,7 @@
* out of it's tuple
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.34 1999/12/06 02:37:17 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.35 1999/12/13 01:27:01 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
......@@ -1352,8 +1352,12 @@ get_rule_expr(Node *node, deparse_context *context)
{
Aggref *aggref = (Aggref *) node;
appendStringInfo(buf, "%s(",
quote_identifier(aggref->aggname));
appendStringInfo(buf, "%s(%s",
quote_identifier(aggref->aggname),
aggref->aggdistinct ? "DISTINCT " : "");
if (aggref->aggstar)
appendStringInfo(buf, "*");
else
get_rule_expr(aggref->target, context);
appendStringInfo(buf, ")");
}
......
......@@ -3,8 +3,8 @@
* tuplesort.c
* Generalized tuple sorting routines.
*
* This module handles sorting of either heap tuples or index tuples
* (and could fairly easily support other kinds of sortable objects,
* This module handles sorting of heap tuples, index tuples, or single
* Datums (and could easily support other kinds of sortable objects,
* if necessary). It works efficiently for both small and large amounts
* of data. Small amounts are sorted in-memory using qsort(). Large
* amounts are sorted using temporary files and a standard external sort
......@@ -77,7 +77,7 @@
* Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.2 1999/10/30 17:27:15 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.3 1999/12/13 01:27:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -87,7 +87,9 @@
#include "access/heapam.h"
#include "access/nbtree.h"
#include "miscadmin.h"
#include "parser/parse_type.h"
#include "utils/logtape.h"
#include "utils/lsyscache.h"
#include "utils/tuplesort.h"
/*
......@@ -251,6 +253,17 @@ struct Tuplesortstate
*/
Relation indexRel;
bool enforceUnique; /* complain if we find duplicate tuples */
/*
* These variables are specific to the Datum case; they are set
* by tuplesort_begin_datum and used only by the DatumTuple routines.
*/
Oid datumType;
Oid sortOperator;
FmgrInfo sortOpFn; /* cached lookup data for sortOperator */
/* we need typelen and byval in order to know how to copy the Datums. */
int datumTypeLen;
bool datumTypeByVal;
};
#define COMPARETUP(state,a,b) ((*(state)->comparetup) (state, a, b))
......@@ -321,7 +334,22 @@ struct Tuplesortstate
*--------------------
*/
/*
* For sorting single Datums, we build "pseudo tuples" that just carry
* the datum's value and null flag. For pass-by-reference data types,
* the actual data value appears after the DatumTupleHeader (MAXALIGNed,
* of course), and the value field in the header is just a pointer to it.
*/
typedef struct
{
Datum val;
bool isNull;
} DatumTuple;
static Tuplesortstate *tuplesort_begin_common(bool randomAccess);
static void puttuple_common(Tuplesortstate *state, void *tuple);
static void inittapes(Tuplesortstate *state);
static void selectnewtape(Tuplesortstate *state);
static void mergeruns(Tuplesortstate *state);
......@@ -349,6 +377,13 @@ static void writetup_index(Tuplesortstate *state, int tapenum, void *tup);
static void *readtup_index(Tuplesortstate *state, int tapenum,
unsigned int len);
static unsigned int tuplesize_index(Tuplesortstate *state, void *tup);
static int comparetup_datum(Tuplesortstate *state,
const void *a, const void *b);
static void *copytup_datum(Tuplesortstate *state, void *tup);
static void writetup_datum(Tuplesortstate *state, int tapenum, void *tup);
static void *readtup_datum(Tuplesortstate *state, int tapenum,
unsigned int len);
static unsigned int tuplesize_datum(Tuplesortstate *state, void *tup);
/*
* Since qsort(3) will not pass any context info to qsort_comparetup(),
......@@ -369,6 +404,7 @@ static Tuplesortstate *qsort_tuplesortstate;
* have been supplied. After performsort, retrieve the tuples in sorted
* order by calling tuplesort_gettuple until it returns NULL. (If random
* access was requested, rescan, markpos, and restorepos can also be called.)
* For Datum sorts, putdatum/getdatum are used instead of puttuple/gettuple.
* Call tuplesort_end to terminate the operation and release memory/disk space.
*/
......@@ -444,6 +480,32 @@ tuplesort_begin_index(Relation indexRel,
return state;
}
Tuplesortstate *
tuplesort_begin_datum(Oid datumType,
Oid sortOperator,
bool randomAccess)
{
Tuplesortstate *state = tuplesort_begin_common(randomAccess);
Type typeInfo;
state->comparetup = comparetup_datum;
state->copytup = copytup_datum;
state->writetup = writetup_datum;
state->readtup = readtup_datum;
state->tuplesize = tuplesize_datum;
state->datumType = datumType;
state->sortOperator = sortOperator;
/* lookup the function that implements the sort operator */
fmgr_info(get_opcode(sortOperator), &state->sortOpFn);
/* lookup necessary attributes of the datum type */
typeInfo = typeidType(datumType);
state->datumTypeLen = typeLen(typeInfo);
state->datumTypeByVal = typeByVal(typeInfo);
return state;
}
/*
* tuplesort_end
*
......@@ -476,9 +538,60 @@ tuplesort_puttuple(Tuplesortstate *state, void *tuple)
{
/*
* Copy the given tuple into memory we control, and decrease availMem.
* Then call the code shared with the Datum case.
*/
tuple = COPYTUP(state, tuple);
puttuple_common(state, tuple);
}
/*
* Accept one Datum while collecting input data for sort.
*
* If the Datum is pass-by-ref type, the value will be copied.
*/
void
tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
{
DatumTuple *tuple;
/*
* Build pseudo-tuple carrying the datum, and decrease availMem.
*/
if (isNull || state->datumTypeByVal)
{
USEMEM(state, sizeof(DatumTuple));
tuple = (DatumTuple *) palloc(sizeof(DatumTuple));
tuple->val = val;
tuple->isNull = isNull;
}
else
{
int datalen = state->datumTypeLen;
int tuplelen;
char *newVal;
if (datalen == -1) /* variable length type? */
datalen = VARSIZE((struct varlena *) DatumGetPointer(val));
tuplelen = datalen + MAXALIGN(sizeof(DatumTuple));
USEMEM(state, tuplelen);
newVal = (char *) palloc(tuplelen);
tuple = (DatumTuple *) newVal;
newVal += MAXALIGN(sizeof(DatumTuple));
memcpy(newVal, DatumGetPointer(val), datalen);
tuple->val = PointerGetDatum(newVal);
tuple->isNull = false;
}
puttuple_common(state, (void *) tuple);
}
/*
* Shared code for tuple and datum cases.
*/
static void
puttuple_common(Tuplesortstate *state, void *tuple)
{
switch (state->status)
{
case TSS_INITIAL:
......@@ -753,6 +866,50 @@ tuplesort_gettuple(Tuplesortstate *state, bool forward,
}
}
/*
* Fetch the next Datum in either forward or back direction.
* Returns FALSE if no more datums.
*
* If the Datum is pass-by-ref type, the returned value is freshly palloc'd
* and is now owned by the caller.
*/
bool
tuplesort_getdatum(Tuplesortstate *state, bool forward,
Datum *val, bool *isNull)
{
DatumTuple *tuple;
bool should_free;
tuple = (DatumTuple *) tuplesort_gettuple(state, forward, &should_free);
if (tuple == NULL)
return false;
if (tuple->isNull || state->datumTypeByVal)
{
*val = tuple->val;
*isNull = tuple->isNull;
}
else
{
int datalen = state->datumTypeLen;
char *newVal;
if (datalen == -1) /* variable length type? */
datalen = VARSIZE((struct varlena *) DatumGetPointer(tuple->val));
newVal = (char *) palloc(datalen);
memcpy(newVal, DatumGetPointer(tuple->val), datalen);
*val = PointerGetDatum(newVal);
*isNull = false;
}
if (should_free)
pfree(tuple);
return true;
}
/*
* inittapes - initialize for tape sorting.
*
......@@ -1695,3 +1852,103 @@ tuplesize_index(Tuplesortstate *state, void *tup)
return tuplen;
}
/*
* Routines specialized for DatumTuple case
*/
static int
comparetup_datum(Tuplesortstate *state, const void *a, const void *b)
{
DatumTuple *ltup = (DatumTuple *) a;
DatumTuple *rtup = (DatumTuple *) b;
if (ltup->isNull)
{
if (!rtup->isNull)
return 1; /* NULL sorts after non-NULL */
return 0;
}
else if (rtup->isNull)
return -1;
else
{
int result;
if (!(result = - (int) (*fmgr_faddr(&state->sortOpFn)) (ltup->val,
rtup->val)))
result = (int) (*fmgr_faddr(&state->sortOpFn)) (rtup->val,
ltup->val);
return result;
}
}
static void *
copytup_datum(Tuplesortstate *state, void *tup)
{
/* Not currently needed */
elog(ERROR, "copytup_datum() should not be called");
return NULL;
}
static void
writetup_datum(Tuplesortstate *state, int tapenum, void *tup)
{
DatumTuple *tuple = (DatumTuple *) tup;
unsigned int tuplen = tuplesize_datum(state, tup);
unsigned int writtenlen = tuplen + sizeof(unsigned int);
LogicalTapeWrite(state->tapeset, tapenum,
(void*) &writtenlen, sizeof(writtenlen));
LogicalTapeWrite(state->tapeset, tapenum,
(void*) tuple, tuplen);
if (state->randomAccess) /* need trailing length word? */
LogicalTapeWrite(state->tapeset, tapenum,
(void*) &writtenlen, sizeof(writtenlen));
FREEMEM(state, tuplen);
pfree(tuple);
}
static void *
readtup_datum(Tuplesortstate *state, int tapenum, unsigned int len)
{
unsigned int tuplen = len - sizeof(unsigned int);
DatumTuple *tuple = (DatumTuple *) palloc(tuplen);
USEMEM(state, tuplen);
if (LogicalTapeRead(state->tapeset, tapenum, (void *) tuple,
tuplen) != tuplen)
elog(ERROR, "tuplesort: unexpected end of data");
if (state->randomAccess) /* need trailing length word? */
if (LogicalTapeRead(state->tapeset, tapenum, (void *) &tuplen,
sizeof(tuplen)) != sizeof(tuplen))
elog(ERROR, "tuplesort: unexpected end of data");
if (!tuple->isNull && !state->datumTypeByVal)
tuple->val = PointerGetDatum(((char *) tuple) +
MAXALIGN(sizeof(DatumTuple)));
return (void *) tuple;
}
static unsigned int
tuplesize_datum(Tuplesortstate *state, void *tup)
{
DatumTuple *tuple = (DatumTuple *) tup;
if (tuple->isNull || state->datumTypeByVal)
{
return (unsigned int) sizeof(DatumTuple);
}
else
{
int datalen = state->datumTypeLen;
int tuplelen;
if (datalen == -1) /* variable length type? */
datalen = VARSIZE((struct varlena *) DatumGetPointer(tuple->val));
tuplelen = datalen + MAXALIGN(sizeof(DatumTuple));
return (unsigned int) tuplelen;
}
}
......@@ -36,7 +36,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.4 1999/11/24 16:52:48 momjian Exp $
* $Id: catversion.h,v 1.5 1999/12/13 01:27:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -51,6 +51,6 @@
* catalog changes on the same day...)
*/
#define CATALOG_VERSION_NO 199911241
#define CATALOG_VERSION_NO 199912121
#endif
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: primnodes.h,v 1.37 1999/11/15 02:00:15 tgl Exp $
* $Id: primnodes.h,v 1.38 1999/12/13 01:27:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -297,10 +297,12 @@ typedef struct Iter
/* ----------------
* Aggref
* aggname - name of the aggregate
* basetype - base type Oid of the aggregate
* basetype - base type Oid of the aggregate (ie, input type)
* aggtype - type Oid of final result of the aggregate
* target - attribute or expression we are aggregating on
* usenulls - TRUE to accept null values as inputs
* aggstar - TRUE if argument was really '*'
* aggdistinct - TRUE if arguments were labeled DISTINCT
* aggno - workspace for nodeAgg.c executor
* ----------------
*/
......@@ -312,6 +314,8 @@ typedef struct Aggref
Oid aggtype;
Node *target;
bool usenulls;
bool aggstar;
bool aggdistinct;
int aggno;
} Aggref;
......
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: clauses.h,v 1.31 1999/12/09 05:58:55 tgl Exp $
* $Id: clauses.h,v 1.32 1999/12/13 01:27:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -38,6 +38,7 @@ extern Expr *make_ands_explicit(List *andclauses);
extern List *make_ands_implicit(Expr *clause);
extern List *pull_constant_clauses(List *quals, List **constantQual);
extern bool contain_agg_clause(Node *clause);
extern List *pull_agg_clause(Node *clause);
extern void check_subplans_for_ungrouped_vars(Node *clause,
Query *query,
......
......@@ -3,8 +3,8 @@
* tuplesort.h
* Generalized tuple sorting routines.
*
* This module handles sorting of either heap tuples or index tuples
* (and could fairly easily support other kinds of sortable objects,
* This module handles sorting of heap tuples, index tuples, or single
* Datums (and could easily support other kinds of sortable objects,
* if necessary). It works efficiently for both small and large amounts
* of data. Small amounts are sorted in-memory using qsort(). Large
* amounts are sorted using temporary files and a standard external sort
......@@ -12,7 +12,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: tuplesort.h,v 1.1 1999/10/17 22:15:09 tgl Exp $
* $Id: tuplesort.h,v 1.2 1999/12/13 01:27:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -34,6 +34,7 @@ typedef struct Tuplesortstate Tuplesortstate;
* code: one for sorting HeapTuples and one for sorting IndexTuples.
* They differ primarily in the way that the sort key information is
* supplied.
* Yet a third slightly different interface supports sorting bare Datums.
*/
extern Tuplesortstate *tuplesort_begin_heap(TupleDesc tupDesc,
......@@ -42,9 +43,15 @@ extern Tuplesortstate *tuplesort_begin_heap(TupleDesc tupDesc,
extern Tuplesortstate *tuplesort_begin_index(Relation indexRel,
bool enforceUnique,
bool randomAccess);
extern Tuplesortstate *tuplesort_begin_datum(Oid datumType,
Oid sortOperator,
bool randomAccess);
extern void tuplesort_puttuple(Tuplesortstate *state, void *tuple);
extern void tuplesort_putdatum(Tuplesortstate *state, Datum val,
bool isNull);
extern void tuplesort_performsort(Tuplesortstate *state);
extern void *tuplesort_gettuple(Tuplesortstate *state, bool forward,
......@@ -54,11 +61,15 @@ extern void *tuplesort_gettuple(Tuplesortstate *state, bool forward,
#define tuplesort_getindextuple(state, forward, should_free) \
((IndexTuple) tuplesort_gettuple(state, forward, should_free))
extern bool tuplesort_getdatum(Tuplesortstate *state, bool forward,
Datum *val, bool *isNull);
extern void tuplesort_end(Tuplesortstate *state);
/*
* These routines may only be called if randomAccess was specified 'true'.
* Backwards scan in gettuple is likewise only allowed if randomAccess.
* Likewise, backwards scan in gettuple/getdatum is only allowed if
* randomAccess was specified.
*/
extern void tuplesort_rescan(Tuplesortstate *state);
......
......@@ -76,6 +76,42 @@ cnt_1000
1000
(1 row)
QUERY: SELECT count(DISTINCT four) AS cnt_4 FROM onek;
cnt_4
-----
4
(1 row)
QUERY: select ten, count(*), sum(four) from onek group by ten;
ten|count|sum
---+-----+---
0| 100|100
1| 100|200
2| 100|100
3| 100|200
4| 100|100
5| 100|200
6| 100|100
7| 100|200
8| 100|100
9| 100|200
(10 rows)
QUERY: select ten, count(four), sum(DISTINCT four) from onek group by ten;
ten|count|sum
---+-----+---
0| 100| 2
1| 100| 4
2| 100| 2
3| 100| 4
4| 100| 2
5| 100| 4
6| 100| 2
7| 100| 4
8| 100| 2
9| 100| 4
(10 rows)
QUERY: SELECT newavg(four) AS avg_1 FROM onek;
avg_1
-----
......
......@@ -1075,9 +1075,9 @@ pg_user |SELECT pg_shadow.usename, pg_shadow.usesysid, pg_shadow.usecr
pg_views |SELECT c.relname AS viewname, pg_get_userbyid(c.relowner) AS viewowner, pg_get_viewdef(c.relname) AS definition FROM pg_class c WHERE (c.relhasrules AND (EXISTS (SELECT r.rulename FROM pg_rewrite r WHERE ((r.ev_class = c.oid) AND (r.ev_type = '1'::"char")))));
rtest_v1 |SELECT rtest_t1.a, rtest_t1.b FROM rtest_t1;
rtest_vcomp |SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit);
rtest_vview1 |SELECT x.a, x.b FROM rtest_view1 x WHERE (0 < (SELECT count(1) AS count FROM rtest_view2 y WHERE (y.a = x.a)));
rtest_vview1 |SELECT x.a, x.b FROM rtest_view1 x WHERE (0 < (SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a)));
rtest_vview2 |SELECT rtest_view1.a, rtest_view1.b FROM rtest_view1 WHERE rtest_view1.v;
rtest_vview3 |SELECT x.a, x.b FROM rtest_vview2 x WHERE (0 < (SELECT count(1) AS count FROM rtest_view2 y WHERE (y.a = x.a)));
rtest_vview3 |SELECT x.a, x.b FROM rtest_vview2 x WHERE (0 < (SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a)));
rtest_vview4 |SELECT x.a, x.b, count(y.a) AS refcount FROM rtest_view1 x, rtest_view2 y WHERE (x.a = y.a) GROUP BY x.a, x.b;
rtest_vview5 |SELECT rtest_view1.a, rtest_view1.b, rtest_viewfunc1(rtest_view1.a) AS refcount FROM rtest_view1;
shoe |SELECT sh.shoename, sh.sh_avail, sh.slcolor, sh.slminlen, (sh.slminlen * un.un_fact) AS slminlen_cm, sh.slmaxlen, (sh.slmaxlen * un.un_fact) AS slmaxlen_cm, sh.slunit FROM shoe_data sh, unit un WHERE (sh.slunit = un.un_name);
......
......@@ -30,6 +30,12 @@ SELECT max(student.gpa) AS max_3_7 FROM student;
SELECT count(four) AS cnt_1000 FROM onek;
SELECT count(DISTINCT four) AS cnt_4 FROM onek;
select ten, count(*), sum(four) from onek group by ten;
select ten, count(four), sum(DISTINCT four) from onek group by ten;
SELECT newavg(four) AS avg_1 FROM onek;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment