Redesign DISTINCT ON as discussed in pgsql-sql 1/25/00: syntax is now

SELECT DISTINCT ON (expr [, expr ...]) targetlist ...
and there is a check to make sure that the user didn't specify an ORDER BY
that's incompatible with the DISTINCT operation.
Reimplement nodeUnique and nodeGroup to use the proper datatype-specific
equality function for each column being compared --- they used to do
bitwise comparisons or convert the data to text strings and strcmp().
(To add insult to injury, they'd look up the conversion functions once
for each tuple...)  Parse/plan representation of DISTINCT is now a list
of SortClause nodes.
initdb forced by querytree change...
This commit is contained in:
Tom Lane 2000-01-27 18:11:50 +00:00
parent 3f0074e403
commit dd979f66be
32 changed files with 638 additions and 576 deletions

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/select.sgml,v 1.23 1999/12/13 17:39:38 tgl Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/select.sgml,v 1.24 2000/01/27 18:11:25 tgl Exp $
Postgres documentation
-->
@ -22,7 +22,7 @@ Postgres documentation
<date>1999-07-20</date>
</refsynopsisdivinfo>
<synopsis>
SELECT [ ALL | DISTINCT [ ON <replaceable class="PARAMETER">column</replaceable> ] ]
SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replaceable> [, ...] ) ] ]
<replaceable class="PARAMETER">expression</replaceable> [ AS <replaceable class="PARAMETER">name</replaceable> ] [, ...]
[ INTO [ TEMPORARY | TEMP ] [ TABLE ] <replaceable class="PARAMETER">new_table</replaceable> ]
[ FROM <replaceable class="PARAMETER">table</replaceable> [ <replaceable class="PARAMETER">alias</replaceable> ] [, ...] ]
@ -201,16 +201,29 @@ SELECT [ ALL | DISTINCT [ ON <replaceable class="PARAMETER">column</replaceable>
</para>
<para>
<command>DISTINCT</command> will eliminate all duplicate rows from the
<command>DISTINCT</command> will eliminate duplicate rows from the
result.
<command>DISTINCT ON <replaceable class="PARAMETER">column</replaceable></command>
will eliminate all duplicates in the specified column; this is
similar to using
<command>GROUP BY <replaceable class="PARAMETER">column</replaceable></command>.
<command>ALL</command> will return all candidate rows,
<command>ALL</command> (the default) will return all candidate rows,
including duplicates.
</para>
<para>
<command>DISTINCT ON</command> eliminates rows that match on all the
specified expressions, keeping only the first row of each set of
duplicates. Note that "the first row" of each set is unpredictable
unless <command>ORDER BY</command> is used to ensure that the desired
row appears first. For example,
<programlisting>
SELECT DISTINCT ON (location) location, time, report
FROM weatherReports
ORDER BY location, time DESC;
</programlisting>
retrieves the most recent weather report for each location. But if
we had not used ORDER BY to force descending order of time values
for each location, we'd have gotten a report of unpredictable age
for each location.
</para>
<para>
The GROUP BY clause allows a user to divide a table
conceptually into groups.

View File

@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.35 2000/01/26 05:56:22 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.36 2000/01/27 18:11:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -753,7 +753,7 @@ NodeGetResultTupleSlot(Plan *node)
{
UniqueState *uniquestate = ((Unique *) node)->uniquestate;
slot = uniquestate->cs_ResultTupleSlot;
slot = uniquestate->cstate.cs_ResultTupleSlot;
}
break;

View File

@ -9,12 +9,13 @@
*
* DESCRIPTION
* The Group node is designed for handling queries with a GROUP BY clause.
* It's outer plan must be a sort node. It assumes that the tuples it gets
* back from the outer plan is sorted in the order specified by the group
* columns. (ie. tuples from the same group are consecutive)
* Its outer plan must deliver tuples that are sorted in the order
* specified by the grouping columns (ie. tuples from the same group are
* consecutive). That way, we just have to compare adjacent tuples to
* locate group boundaries.
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.32 2000/01/26 05:56:22 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.33 2000/01/27 18:11:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,13 +24,14 @@
#include "access/heapam.h"
#include "access/printtup.h"
#include "catalog/pg_operator.h"
#include "executor/executor.h"
#include "executor/nodeGroup.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
static TupleTableSlot *ExecGroupEveryTuple(Group *node);
static TupleTableSlot *ExecGroupOneTuple(Group *node);
static bool sameGroup(HeapTuple oldslot, HeapTuple newslot,
int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
/* ---------------------------------------
* ExecGroup -
@ -38,11 +40,11 @@ static bool sameGroup(HeapTuple oldslot, HeapTuple newslot,
* tuplePerGroup is TRUE, every tuple from the same group will be
* returned, followed by a NULL at the end of each group. This is
* useful for Agg node which needs to aggregate over tuples of the same
* group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
* group. (eg. SELECT salary, count(*) FROM emp GROUP BY salary)
*
* If tuplePerGroup is FALSE, only one tuple per group is returned. The
* tuple returned contains only the group columns. NULL is returned only
* at the end when no more groups is present. This is useful when
* at the end when no more groups are present. This is useful when
* the query does not involve aggregates. (eg. SELECT salary FROM emp
* GROUP BY salary)
* ------------------------------------------
@ -66,6 +68,7 @@ ExecGroupEveryTuple(Group *node)
GroupState *grpstate;
EState *estate;
ExprContext *econtext;
TupleDesc tupdesc;
HeapTuple outerTuple = NULL;
HeapTuple firsttuple;
@ -87,6 +90,8 @@ ExecGroupEveryTuple(Group *node)
econtext = grpstate->csstate.cstate.cs_ExprContext;
tupdesc = ExecGetScanType(&grpstate->csstate);
/* if we haven't returned first tuple of new group yet ... */
if (grpstate->grp_useFirstTuple)
{
@ -110,20 +115,25 @@ ExecGroupEveryTuple(Group *node)
outerTuple = outerslot->val;
firsttuple = grpstate->grp_firstTuple;
/* this should occur on the first call only */
if (firsttuple == NULL)
{
/* this should occur on the first call only */
grpstate->grp_firstTuple = heap_copytuple(outerTuple);
}
else
{
/*
* Compare with first tuple and see if this tuple is of the
* same group.
*/
if (!sameGroup(firsttuple, outerTuple,
node->numCols, node->grpColIdx,
ExecGetScanType(&grpstate->csstate)))
if (! execTuplesMatch(firsttuple, outerTuple,
tupdesc,
node->numCols, node->grpColIdx,
grpstate->eqfunctions))
{
/*
* No; save the tuple to return it next time, and return NULL
*/
grpstate->grp_useFirstTuple = TRUE;
heap_freetuple(firsttuple);
grpstate->grp_firstTuple = heap_copytuple(outerTuple);
@ -164,6 +174,7 @@ ExecGroupOneTuple(Group *node)
GroupState *grpstate;
EState *estate;
ExprContext *econtext;
TupleDesc tupdesc;
HeapTuple outerTuple = NULL;
HeapTuple firsttuple;
@ -185,10 +196,12 @@ ExecGroupOneTuple(Group *node)
econtext = node->grpstate->csstate.cstate.cs_ExprContext;
tupdesc = ExecGetScanType(&grpstate->csstate);
firsttuple = grpstate->grp_firstTuple;
/* this should occur on the first call only */
if (firsttuple == NULL)
{
/* this should occur on the first call only */
outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
if (TupIsNull(outerslot))
{
@ -213,14 +226,14 @@ ExecGroupOneTuple(Group *node)
}
outerTuple = outerslot->val;
/* ----------------
* Compare with first tuple and see if this tuple is of
* the same group.
* ----------------
/*
* Compare with first tuple and see if this tuple is of the
* same group.
*/
if ((!sameGroup(firsttuple, outerTuple,
node->numCols, node->grpColIdx,
ExecGetScanType(&grpstate->csstate))))
if (! execTuplesMatch(firsttuple, outerTuple,
tupdesc,
node->numCols, node->grpColIdx,
grpstate->eqfunctions))
break;
}
@ -311,6 +324,14 @@ ExecInitGroup(Group *node, EState *estate, Plan *parent)
ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
/*
* Precompute fmgr lookup data for inner loop
*/
grpstate->eqfunctions =
execTuplesMatchPrepare(ExecGetScanType(&grpstate->csstate),
node->numCols,
node->grpColIdx);
return TRUE;
}
@ -347,80 +368,6 @@ ExecEndGroup(Group *node)
}
}
/*****************************************************************************
*
*****************************************************************************/
/*
* code swiped from nodeUnique.c
*/
static bool
sameGroup(HeapTuple oldtuple,
HeapTuple newtuple,
int numCols,
AttrNumber *grpColIdx,
TupleDesc tupdesc)
{
bool isNull1,
isNull2;
Datum attr1,
attr2;
char *val1,
*val2;
int i;
AttrNumber att;
Oid typoutput,
typelem;
for (i = 0; i < numCols; i++)
{
att = grpColIdx[i];
getTypeOutAndElem((Oid) tupdesc->attrs[att - 1]->atttypid,
&typoutput, &typelem);
attr1 = heap_getattr(oldtuple,
att,
tupdesc,
&isNull1);
attr2 = heap_getattr(newtuple,
att,
tupdesc,
&isNull2);
if (isNull1 == isNull2)
{
if (isNull1) /* both are null, they are equal */
continue;
val1 = fmgr(typoutput, attr1, typelem,
tupdesc->attrs[att - 1]->atttypmod);
val2 = fmgr(typoutput, attr2, typelem,
tupdesc->attrs[att - 1]->atttypmod);
/*
* now, val1 and val2 are ascii representations so we can use
* strcmp for comparison
*/
if (strcmp(val1, val2) != 0)
{
pfree(val1);
pfree(val2);
return FALSE;
}
pfree(val1);
pfree(val2);
}
else
{
/* one is null and the other isn't, they aren't equal */
return FALSE;
}
}
return TRUE;
}
void
ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
{
@ -438,3 +385,104 @@ ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
((Plan *) node)->lefttree->chgParam == NULL)
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
}
/*****************************************************************************
* Code shared with nodeUnique.c
*****************************************************************************/
/*
* execTuplesMatch
* Return true if two tuples match in all the indicated fields.
* This is used to detect group boundaries in nodeGroup, and to
* decide whether two tuples are distinct or not in nodeUnique.
*
* tuple1, tuple2: the tuples to compare
* tupdesc: tuple descriptor applying to both tuples
* numCols: the number of attributes to be examined
* matchColIdx: array of attribute column numbers
* eqFunctions: array of fmgr lookup info for the equality functions to use
*/
bool
execTuplesMatch(HeapTuple tuple1,
HeapTuple tuple2,
TupleDesc tupdesc,
int numCols,
AttrNumber *matchColIdx,
FmgrInfo *eqfunctions)
{
int i;
/*
* We cannot report a match without checking all the fields, but we
* can report a non-match as soon as we find unequal fields. So,
* start comparing at the last field (least significant sort key).
* That's the most likely to be different...
*/
for (i = numCols; --i >= 0; )
{
AttrNumber att = matchColIdx[i];
Datum attr1,
attr2;
bool isNull1,
isNull2;
Datum equal;
attr1 = heap_getattr(tuple1,
att,
tupdesc,
&isNull1);
attr2 = heap_getattr(tuple2,
att,
tupdesc,
&isNull2);
if (isNull1 != isNull2)
return FALSE; /* one null and one not; they aren't equal */
if (isNull1)
continue; /* both are null, treat as equal */
/* Apply the type-specific equality function */
equal = (Datum) (*fmgr_faddr(& eqfunctions[i])) (attr1, attr2);
if (DatumGetInt32(equal) == 0)
return FALSE;
}
return TRUE;
}
/*
* execTuplesMatchPrepare
* Look up the equality functions needed for execTuplesMatch.
* The result is a palloc'd array.
*/
FmgrInfo *
execTuplesMatchPrepare(TupleDesc tupdesc,
int numCols,
AttrNumber *matchColIdx)
{
FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
int i;
for (i = 0; i < numCols; i++)
{
AttrNumber att = matchColIdx[i];
Oid typid = tupdesc->attrs[att - 1]->atttypid;
Operator eq_operator;
Form_pg_operator pgopform;
eq_operator = oper("=", typid, typid, true);
if (!HeapTupleIsValid(eq_operator))
{
elog(ERROR, "Unable to identify an equality operator for type '%s'",
typeidTypeName(typid));
}
pgopform = (Form_pg_operator) GETSTRUCT(eq_operator);
fmgr_info(pgopform->oprcode, & eqfunctions[i]);
}
return eqfunctions;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.26 2000/01/26 05:56:24 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.27 2000/01/27 18:11:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -29,79 +29,14 @@
#include "access/heapam.h"
#include "access/printtup.h"
#include "executor/executor.h"
#include "executor/nodeGroup.h"
#include "executor/nodeUnique.h"
/* ----------------------------------------------------------------
* ExecIdenticalTuples
*
* This is a hack function used by ExecUnique to see if
* two tuples are identical. This should be provided
* by the heap tuple code but isn't. The real problem
* is that we assume we can byte compare tuples to determine
* if they are "equal". In fact, if we have user defined
* types there may be problems because it's possible that
* an ADT may have multiple representations with the
* same ADT value. -cim
* ----------------------------------------------------------------
*/
static bool /* true if tuples are identical, false
* otherwise */
ExecIdenticalTuples(TupleTableSlot *t1, TupleTableSlot *t2)
{
HeapTuple h1;
HeapTuple h2;
char *d1;
char *d2;
int len;
h1 = t1->val;
h2 = t2->val;
/* ----------------
* if tuples aren't the same length then they are
* obviously different (one may have null attributes).
* ----------------
*/
if (h1->t_len != h2->t_len)
return false;
/* ----------------
* if the tuples have different header offsets then
* they are different. This will prevent us from returning
* true when comparing tuples of one attribute where one of
* two we're looking at is null (t_len - t_hoff == 0).
* THE t_len FIELDS CAN BE THE SAME IN THIS CASE!!
* ----------------
*/
if (h1->t_data->t_hoff != h2->t_data->t_hoff)
return false;
/* ----------------
* ok, now get the pointers to the data and the
* size of the attribute portion of the tuple.
* ----------------
*/
d1 = (char *) GETSTRUCT(h1);
d2 = (char *) GETSTRUCT(h2);
len = (int) h1->t_len - (int) h1->t_data->t_hoff;
/* ----------------
* byte compare the data areas and return the result.
* ----------------
*/
if (memcmp(d1, d2, len) != 0)
return false;
return true;
}
/* ----------------------------------------------------------------
* ExecUnique
*
* This is a very simple node which filters out duplicate
* tuples from a stream of sorted tuples from a subplan.
*
* XXX see comments below regarding freeing tuples.
* ----------------------------------------------------------------
*/
TupleTableSlot * /* return: a tuple or NULL */
@ -111,11 +46,7 @@ ExecUnique(Unique *node)
TupleTableSlot *resultTupleSlot;
TupleTableSlot *slot;
Plan *outerPlan;
char *uniqueAttr;
AttrNumber uniqueAttrNum;
TupleDesc tupDesc;
Oid typoutput,
typelem;
/* ----------------
* get information from the node
@ -123,22 +54,8 @@ ExecUnique(Unique *node)
*/
uniquestate = node->uniquestate;
outerPlan = outerPlan((Plan *) node);
resultTupleSlot = uniquestate->cs_ResultTupleSlot;
uniqueAttr = node->uniqueAttr;
uniqueAttrNum = node->uniqueAttrNum;
if (uniqueAttr)
{
tupDesc = ExecGetResultType(uniquestate);
getTypeOutAndElem((Oid) tupDesc->attrs[uniqueAttrNum - 1]->atttypid,
&typoutput, &typelem);
}
else
{ /* keep compiler quiet */
tupDesc = NULL;
typoutput = InvalidOid;
typelem = InvalidOid;
}
resultTupleSlot = uniquestate->cstate.cs_ResultTupleSlot;
tupDesc = ExecGetResultType(& uniquestate->cstate);
/* ----------------
* now loop, returning only non-duplicate tuples.
@ -157,83 +74,38 @@ ExecUnique(Unique *node)
return NULL;
/* ----------------
* we use the result tuple slot to hold our saved tuples.
* if we haven't a saved tuple to compare our new tuple with,
* then we exit the loop. This new tuple as the saved tuple
* the next time we get here.
* Always return the first tuple from the subplan.
* ----------------
*/
if (TupIsNull(resultTupleSlot))
if (uniquestate->priorTuple == NULL)
break;
/* ----------------
* now test if the new tuple and the previous
* Else test if the new tuple and the previously returned
* tuple match. If so then we loop back and fetch
* another new tuple from the subplan.
* ----------------
*/
if (uniqueAttr)
{
/*
* to check equality, we check to see if the typoutput of the
* attributes are equal
*/
bool isNull1,
isNull2;
Datum attr1,
attr2;
char *val1,
*val2;
attr1 = heap_getattr(slot->val,
uniqueAttrNum, tupDesc, &isNull1);
attr2 = heap_getattr(resultTupleSlot->val,
uniqueAttrNum, tupDesc, &isNull2);
if (isNull1 == isNull2)
{
if (isNull1) /* both are null, they are equal */
continue;
val1 = fmgr(typoutput, attr1, typelem,
tupDesc->attrs[uniqueAttrNum - 1]->atttypmod);
val2 = fmgr(typoutput, attr2, typelem,
tupDesc->attrs[uniqueAttrNum - 1]->atttypmod);
/*
* now, val1 and val2 are ascii representations so we can
* use strcmp for comparison
*/
if (strcmp(val1, val2) == 0) /* they are equal */
{
pfree(val1);
pfree(val2);
continue;
}
pfree(val1);
pfree(val2);
break;
}
else
/* one is null and the other isn't, they aren't equal */
break;
}
else
{
if (!ExecIdenticalTuples(slot, resultTupleSlot))
break;
}
if (! execTuplesMatch(slot->val, uniquestate->priorTuple,
tupDesc,
node->numCols, node->uniqColIdx,
uniquestate->eqfunctions))
break;
}
/* ----------------
* we have a new tuple different from the previous saved tuple
* so we save it in the saved tuple slot. We copy the tuple
* so we don't increment the buffer ref count.
* We have a new tuple different from the previous saved tuple (if any).
* Save it and return it. Note that we make two copies of the tuple:
* one to keep for our own future comparisons, and one to return to the
* caller. We need to copy the tuple returned by the subplan to avoid
* holding buffer refcounts, and we need our own copy because the caller
* may alter the resultTupleSlot (eg via ExecRemoveJunk).
* ----------------
*/
if (uniquestate->priorTuple != NULL)
heap_freetuple(uniquestate->priorTuple);
uniquestate->priorTuple = heap_copytuple(slot->val);
ExecStoreTuple(heap_copytuple(slot->val),
resultTupleSlot,
InvalidBuffer,
@ -254,7 +126,6 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
{
UniqueState *uniquestate;
Plan *outerPlan;
char *uniqueAttr;
/* ----------------
* assign execution state to node
@ -268,10 +139,10 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
*/
uniquestate = makeNode(UniqueState);
node->uniquestate = uniquestate;
uniqueAttr = node->uniqueAttr;
uniquestate->priorTuple = NULL;
/* ----------------
* Miscellanious initialization
* Miscellaneous initialization
*
* + assign node's base_id
* + assign debugging hooks and
@ -280,14 +151,14 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
* they never call ExecQual or ExecTargetList.
* ----------------
*/
ExecAssignNodeBaseInfo(estate, uniquestate, parent);
ExecAssignNodeBaseInfo(estate, & uniquestate->cstate, parent);
#define UNIQUE_NSLOTS 1
/* ------------
* Tuple table initialization
* ------------
*/
ExecInitResultTupleSlot(estate, uniquestate);
ExecInitResultTupleSlot(estate, & uniquestate->cstate);
/* ----------------
* then initialize outer plan
@ -301,31 +172,17 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
* projection info for this node appropriately
* ----------------
*/
ExecAssignResultTypeFromOuterPlan((Plan *) node, uniquestate);
uniquestate->cs_ProjInfo = NULL;
ExecAssignResultTypeFromOuterPlan((Plan *) node, & uniquestate->cstate);
uniquestate->cstate.cs_ProjInfo = NULL;
if (uniqueAttr)
{
TupleDesc tupDesc;
int i = 0;
tupDesc = ExecGetResultType(uniquestate);
/*
* the parser should have ensured that uniqueAttr is a legal
* attribute name
*/
while (strcmp(NameStr(tupDesc->attrs[i]->attname), uniqueAttr) != 0)
i++;
node->uniqueAttrNum = i + 1; /* attribute numbers start from 1 */
}
else
node->uniqueAttrNum = InvalidAttrNumber;
/* ----------------
* all done.
* ----------------
/*
* Precompute fmgr lookup data for inner loop
*/
uniquestate->eqfunctions =
execTuplesMatchPrepare(ExecGetResultType(& uniquestate->cstate),
node->numCols,
node->uniqColIdx);
return TRUE;
}
@ -347,11 +204,17 @@ ExecCountSlotsUnique(Unique *node)
void
ExecEndUnique(Unique *node)
{
UniqueState *uniquestate;
UniqueState *uniquestate = node->uniquestate;
uniquestate = node->uniquestate;
ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
ExecClearTuple(uniquestate->cs_ResultTupleSlot);
/* clean up tuple table */
ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
if (uniquestate->priorTuple != NULL)
{
heap_freetuple(uniquestate->priorTuple);
uniquestate->priorTuple = NULL;
}
}
@ -360,7 +223,12 @@ ExecReScanUnique(Unique *node, ExprContext *exprCtxt, Plan *parent)
{
UniqueState *uniquestate = node->uniquestate;
ExecClearTuple(uniquestate->cs_ResultTupleSlot);
ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
if (uniquestate->priorTuple != NULL)
{
heap_freetuple(uniquestate->priorTuple);
uniquestate->priorTuple = NULL;
}
/*
* if chgParam of subnode is not null then plan will be re-scanned by

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.102 2000/01/26 05:56:31 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.103 2000/01/27 18:11:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -532,11 +532,9 @@ _copyUnique(Unique *from)
* copy remainder of node
* ----------------
*/
if (from->uniqueAttr)
newnode->uniqueAttr = pstrdup(from->uniqueAttr);
else
newnode->uniqueAttr = NULL;
newnode->uniqueAttrNum = from->uniqueAttrNum;
newnode->numCols = from->numCols;
newnode->uniqColIdx = palloc(from->numCols * sizeof(AttrNumber));
memcpy(newnode->uniqColIdx, from->uniqColIdx, from->numCols * sizeof(AttrNumber));
return newnode;
}
@ -1427,8 +1425,7 @@ _copyQuery(Query *from)
Node_Copy(from, newnode, qual);
Node_Copy(from, newnode, rowMark);
if (from->uniqueFlag)
newnode->uniqueFlag = pstrdup(from->uniqueFlag);
Node_Copy(from, newnode, distinctClause);
Node_Copy(from, newnode, sortClause);
Node_Copy(from, newnode, groupClause);
Node_Copy(from, newnode, havingQual);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.56 2000/01/26 05:56:31 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.57 2000/01/27 18:11:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -593,16 +593,8 @@ _equalQuery(Query *a, Query *b)
return false;
if (!equal(a->rowMark, b->rowMark))
return false;
if (a->uniqueFlag && b->uniqueFlag)
{
if (strcmp(a->uniqueFlag, b->uniqueFlag) != 0)
return false;
}
else
{
if (a->uniqueFlag != b->uniqueFlag)
return false;
}
if (!equal(a->distinctClause, b->distinctClause))
return false;
if (!equal(a->sortClause, b->sortClause))
return false;
if (!equal(a->groupClause, b->groupClause))

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.32 2000/01/26 05:56:31 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.33 2000/01/27 18:11:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -427,8 +427,7 @@ _freeUnique(Unique *node)
* free remainder of node
* ----------------
*/
if (node->uniqueAttr)
pfree(node->uniqueAttr);
pfree(node->uniqColIdx);
pfree(node);
}
@ -1072,9 +1071,7 @@ _freeQuery(Query *node)
freeObject(node->targetList);
freeObject(node->qual);
freeObject(node->rowMark);
if (node->uniqueFlag)
pfree(node->uniqueFlag);
freeObject(node->distinctClause);
freeObject(node->sortClause);
freeObject(node->groupClause);
freeObject(node->havingQual);

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.104 2000/01/26 05:56:31 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.105 2000/01/27 18:11:28 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@ -256,12 +256,13 @@ _outQuery(StringInfo str, Query *node)
_outToken(str, node->into);
appendStringInfo(str,
" :isPortal %s :isBinary %s :isTemp %s :unionall %s :unique ",
" :isPortal %s :isBinary %s :isTemp %s :unionall %s :distinctClause ",
node->isPortal ? "true" : "false",
node->isBinary ? "true" : "false",
node->isTemp ? "true" : "false",
node->unionall ? "true" : "false");
_outToken(str, node->uniqueFlag);
_outNode(str, node->distinctClause);
appendStringInfo(str, " :sortClause ");
_outNode(str, node->sortClause);
@ -584,9 +585,10 @@ _outUnique(StringInfo str, Unique *node)
appendStringInfo(str, " UNIQUE ");
_outPlanInfo(str, (Plan *) node);
appendStringInfo(str, " :nonameid %u :keycount %d ",
appendStringInfo(str, " :nonameid %u :keycount %d :numCols %d ",
node->nonameid,
node->keycount);
node->keycount,
node->numCols);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.80 2000/01/26 05:56:32 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.81 2000/01/27 18:11:28 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
@ -111,12 +111,8 @@ _readQuery()
token = lsptok(NULL, &length); /* get unionall */
local_node->unionall = (token[0] == 't') ? true : false;
token = lsptok(NULL, &length); /* skip :uniqueFlag */
token = lsptok(NULL, &length); /* get uniqueFlag */
if (length == 0)
local_node->uniqueFlag = NULL;
else
local_node->uniqueFlag = debackslash(token, length);
token = lsptok(NULL, &length); /* skip :distinctClause */
local_node->distinctClause = nodeRead(true);
token = lsptok(NULL, &length); /* skip :sortClause */
local_node->sortClause = nodeRead(true);
@ -624,33 +620,6 @@ _readAgg()
return local_node;
}
/* ----------------
* _readUnique
*
* For some reason, unique is a subclass of Noname.
*/
static Unique *
_readUnique()
{
Unique *local_node;
char *token;
int length;
local_node = makeNode(Unique);
_getPlan((Plan *) local_node);
token = lsptok(NULL, &length); /* eat :nonameid */
token = lsptok(NULL, &length); /* get :nonameid */
local_node->nonameid = atol(token);
token = lsptok(NULL, &length); /* eat :keycount */
token = lsptok(NULL, &length); /* get :keycount */
local_node->keycount = atoi(token);
return local_node;
}
/* ----------------
* _readHash
*
@ -1847,8 +1816,6 @@ parsePlanString(void)
return_value = _readSubLink();
else if (length == 3 && strncmp(token, "AGG", length) == 0)
return_value = _readAgg();
else if (length == 6 && strncmp(token, "UNIQUE", length) == 0)
return_value = _readUnique();
else if (length == 4 && strncmp(token, "HASH", length) == 0)
return_value = _readHash();
else if (length == 6 && strncmp(token, "RESDOM", length) == 0)

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.81 2000/01/26 05:56:37 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.82 2000/01/27 18:11:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1342,16 +1342,19 @@ make_group(List *tlist,
}
/*
* The uniqueAttr argument must be a null-terminated string,
* either the name of the attribute to select unique on
* or "*"
* distinctList is a list of SortClauses, identifying the targetlist items
* that should be considered by the Unique filter.
*/
Unique *
make_unique(List *tlist, Plan *lefttree, char *uniqueAttr)
make_unique(List *tlist, Plan *lefttree, List *distinctList)
{
Unique *node = makeNode(Unique);
Plan *plan = &node->plan;
int numCols = length(distinctList);
int keyno = 0;
AttrNumber *uniqColIdx;
List *slitem;
copy_plan_costsize(plan, lefttree);
plan->state = (EState *) NULL;
@ -1361,10 +1364,22 @@ make_unique(List *tlist, Plan *lefttree, char *uniqueAttr)
plan->righttree = NULL;
node->nonameid = _NONAME_RELATION_ID_;
node->keycount = 0;
if (strcmp(uniqueAttr, "*") == 0)
node->uniqueAttr = NULL;
else
node->uniqueAttr = pstrdup(uniqueAttr);
/* convert SortClause list into array of attr indexes, as wanted by exec */
Assert(numCols > 0);
uniqColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
foreach(slitem, distinctList)
{
SortClause *sortcl = (SortClause *) lfirst(slitem);
TargetEntry *tle = get_sortgroupclause_tle(sortcl, tlist);
uniqColIdx[keyno++] = tle->resdom->resno;
}
node->numCols = numCols;
node->uniqColIdx = uniqColIdx;
return node;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.73 2000/01/26 05:56:37 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.74 2000/01/27 18:11:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -381,12 +381,12 @@ union_planner(Query *parse)
}
/*
* Finally, if there is a UNIQUE clause, add the UNIQUE node.
* Finally, if there is a DISTINCT clause, add the UNIQUE node.
*/
if (parse->uniqueFlag)
if (parse->distinctClause)
{
result_plan = (Plan *) make_unique(tlist, result_plan,
parse->uniqueFlag);
parse->distinctClause);
}
return result_plan;
@ -583,20 +583,8 @@ make_sortplan(List *tlist, List *sortcls, Plan *plannode)
foreach(i, sortcls)
{
SortClause *sortcl = (SortClause *) lfirst(i);
Index refnumber = sortcl->tleSortGroupRef;
TargetEntry *tle = NULL;
Resdom *resdom;
List *l;
foreach(l, temp_tlist)
{
tle = (TargetEntry *) lfirst(l);
if (tle->resdom->ressortgroupref == refnumber)
break;
}
if (l == NIL)
elog(ERROR, "make_sortplan: ORDER BY expression not found in targetlist");
resdom = tle->resdom;
TargetEntry *tle = get_sortgroupclause_tle(sortcl, temp_tlist);
Resdom *resdom = tle->resdom;
/*
* Check for the possibility of duplicate order-by clauses --- the

View File

@ -104,9 +104,7 @@ transformKeySetQuery(Query *origNode)
unionNode->isPortal = origNode->isPortal;
unionNode->isBinary = origNode->isBinary;
if (origNode->uniqueFlag)
unionNode->uniqueFlag = pstrdup(origNode->uniqueFlag);
Node_Copy(origNode, unionNode, distinctClause);
Node_Copy(origNode, unionNode, sortClause);
Node_Copy(origNode, unionNode, rtable);
Node_Copy(origNode, unionNode, targetList);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.41 2000/01/26 05:56:39 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.42 2000/01/27 18:11:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -21,6 +21,7 @@
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "optimizer/tlist.h"
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
@ -131,7 +132,7 @@ plan_union_queries(Query *parse)
!last_union_all_flag)
{
parse->sortClause = NIL;
parse->uniqueFlag = NULL;
parse->distinctClause = NIL;
}
parse->unionClause = NIL; /* prevent recursion */
@ -183,17 +184,28 @@ plan_union_queries(Query *parse)
if (!last_union_all_flag)
{
/* Need SELECT DISTINCT behavior to implement UNION.
* Set uniqueFlag properly, put back the held sortClause,
* and add any missing columns to the sort clause.
* Put back the held sortClause, add any missing columns to the
* sort clause, and set distinctClause properly.
*/
parse->uniqueFlag = "*";
List *slitem;
parse->sortClause = addAllTargetsToSortList(hold_sortClause,
parse->targetList);
parse->distinctClause = NIL;
foreach(slitem, parse->sortClause)
{
SortClause *scl = (SortClause *) lfirst(slitem);
TargetEntry *tle = get_sortgroupclause_tle(scl, parse->targetList);
if (! tle->resdom->resjunk)
parse->distinctClause = lappend(parse->distinctClause,
copyObject(scl));
}
}
else
{
/* needed so we don't take the flag from the first query */
parse->uniqueFlag = NULL;
/* needed so we don't take SELECT DISTINCT from the first query */
parse->distinctClause = NIL;
}
/* Make sure we don't try to apply the first query's grouping stuff
@ -314,9 +326,9 @@ plan_inherit_query(Relids relids,
* Clear the sorting and grouping qualifications in the subquery,
* so that sorting will only be done once after append
*/
new_root->uniqueFlag = NULL;
new_root->sortClause = NULL;
new_root->groupClause = NULL;
new_root->distinctClause = NIL;
new_root->sortClause = NIL;
new_root->groupClause = NIL;
new_root->havingQual = NULL;
new_root->hasAggs = false; /* shouldn't be any left ... */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.42 2000/01/26 05:56:40 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.43 2000/01/27 18:11:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -216,6 +216,33 @@ get_expr(TargetEntry *tle)
return (Var *) tle->expr;
}
/*
* get_sortgroupclause_tle
* Find the targetlist entry matching the given SortClause
* (or GroupClause) by ressortgroupref, and return it.
*
* Because GroupClause is typedef'd as SortClause, either kind of
* node can be passed without casting.
*/
TargetEntry *
get_sortgroupclause_tle(SortClause *sortClause,
List *targetList)
{
Index refnumber = sortClause->tleSortGroupRef;
List *l;
foreach(l, targetList)
{
TargetEntry *tle = (TargetEntry *) lfirst(l);
if (tle->resdom->ressortgroupref == refnumber)
return tle;
}
elog(ERROR, "get_sortgroupclause_tle: ORDER/GROUP BY expression not found in targetlist");
return NULL; /* keep compiler quiet */
}
/*
* get_sortgroupclause_expr
* Find the targetlist entry matching the given SortClause
@ -227,17 +254,7 @@ get_expr(TargetEntry *tle)
Node *
get_sortgroupclause_expr(SortClause *sortClause, List *targetList)
{
Index refnumber = sortClause->tleSortGroupRef;
List *l;
TargetEntry *tle = get_sortgroupclause_tle(sortClause, targetList);
foreach(l, targetList)
{
TargetEntry *tle = (TargetEntry *) lfirst(l);
if (tle->resdom->ressortgroupref == refnumber)
return tle->expr;
}
elog(ERROR, "get_sortgroupclause_expr: ORDER/GROUP BY expression not found in targetlist");
return NULL; /* keep compiler quiet */
return tle->expr;
}

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: analyze.c,v 1.133 2000/01/26 05:56:41 momjian Exp $
* $Id: analyze.c,v 1.134 2000/01/27 18:11:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -247,7 +247,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
makeRangeTable(pstate, NULL, NULL);
setTargetTable(pstate, stmt->relname);
qry->uniqueFlag = NULL;
qry->distinctClause = NIL;
/* fix where clause */
qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
@ -296,8 +296,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
/* set up a range table --- note INSERT target is not in it yet */
makeRangeTable(pstate, stmt->fromClause, &fromQual);
qry->uniqueFlag = stmt->unique;
qry->targetList = transformTargetList(pstate, stmt->targetList);
qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
@ -311,13 +309,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
stmt->groupClause,
qry->targetList);
/* An InsertStmt has no sortClause, but we still call
* transformSortClause because it also handles uniqueFlag.
*/
qry->sortClause = transformSortClause(pstate,
NIL,
qry->targetList,
qry->uniqueFlag);
/* An InsertStmt has no sortClause */
qry->sortClause = NIL;
qry->distinctClause = transformDistinctClause(pstate,
stmt->distinctClause,
qry->targetList,
& qry->sortClause);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
@ -1312,8 +1310,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
/* set up a range table */
makeRangeTable(pstate, stmt->fromClause, &fromQual);
qry->uniqueFlag = stmt->unique;
qry->into = stmt->into;
qry->isTemp = stmt->istemp;
qry->isPortal = FALSE;
@ -1333,8 +1329,12 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->sortClause = transformSortClause(pstate,
stmt->sortClause,
qry->targetList,
qry->uniqueFlag);
qry->targetList);
qry->distinctClause = transformDistinctClause(pstate,
stmt->distinctClause,
qry->targetList,
& qry->sortClause);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
@ -1558,9 +1558,9 @@ CheckSelectForUpdate(Query *qry)
{
if (qry->unionClause != NULL)
elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT clause");
if (qry->uniqueFlag != NULL)
if (qry->distinctClause != NIL)
elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause");
if (qry->groupClause != NULL)
if (qry->groupClause != NIL)
elog(ERROR, "SELECT FOR UPDATE is not allowed with GROUP BY clause");
if (qry->hasAggs)
elog(ERROR, "SELECT FOR UPDATE is not allowed with AGGREGATE");

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.135 2000/01/26 05:56:41 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.136 2000/01/27 18:11:35 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -159,7 +159,7 @@ static Node *doNegate(Node *n);
class, index_name, name, func_name, file_name, aggr_argtype
%type <str> opt_id,
all_Op, MathOp, opt_name, opt_unique,
all_Op, MathOp, opt_name,
OptUseOp, opt_class, SpecialRuleRelation
%type <str> opt_level, opt_encoding
@ -168,7 +168,7 @@ static Node *doNegate(Node *n);
%type <list> stmtblock, stmtmulti,
result, relation_name_list, OptTableElementList,
OptInherit, definition,
OptInherit, definition, opt_distinct,
opt_with, func_args, func_args_list, func_as,
oper_argtypes, RuleActionList, RuleActionMulti,
opt_column_list, columnList, opt_va_list, va_list,
@ -2843,7 +2843,7 @@ insert_rest: VALUES '(' target_list ')'
{
$$ = makeNode(InsertStmt);
$$->cols = NULL;
$$->unique = NULL;
$$->distinctClause = NIL;
$$->targetList = $3;
$$->fromClause = NIL;
$$->whereClause = NULL;
@ -2854,7 +2854,7 @@ insert_rest: VALUES '(' target_list ')'
| DEFAULT VALUES
{
$$ = makeNode(InsertStmt);
$$->unique = NULL;
$$->distinctClause = NIL;
$$->targetList = NIL;
$$->fromClause = NIL;
$$->whereClause = NULL;
@ -2873,7 +2873,7 @@ insert_rest: VALUES '(' target_list ')'
elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
$$ = makeNode(InsertStmt);
$$->cols = NIL;
$$->unique = n->unique;
$$->distinctClause = n->distinctClause;
$$->targetList = n->targetList;
$$->fromClause = n->fromClause;
$$->whereClause = n->whereClause;
@ -2888,7 +2888,7 @@ insert_rest: VALUES '(' target_list ')'
{
$$ = makeNode(InsertStmt);
$$->cols = $2;
$$->unique = NULL;
$$->distinctClause = NIL;
$$->targetList = $6;
$$->fromClause = NIL;
$$->whereClause = NULL;
@ -2904,7 +2904,7 @@ insert_rest: VALUES '(' target_list ')'
elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
$$ = makeNode(InsertStmt);
$$->cols = $2;
$$->unique = n->unique;
$$->distinctClause = n->distinctClause;
$$->targetList = n->targetList;
$$->fromClause = n->fromClause;
$$->whereClause = n->whereClause;
@ -3189,12 +3189,12 @@ select_clause: '(' select_clause ')'
}
;
SubSelect: SELECT opt_unique target_list
SubSelect: SELECT opt_distinct target_list
result from_clause where_clause
group_clause having_clause
{
SelectStmt *n = makeNode(SelectStmt);
n->unique = $2;
n->distinctClause = $2;
n->unionall = FALSE;
n->targetList = $3;
/* This is new: Subselects support the INTO clause
@ -3230,10 +3230,13 @@ opt_all: ALL { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
;
opt_unique: DISTINCT { $$ = "*"; }
| DISTINCT ON ColId { $$ = $3; }
| ALL { $$ = NULL; }
| /*EMPTY*/ { $$ = NULL; }
/* We use (NIL) as a placeholder to indicate that all target expressions
* should be placed in the DISTINCT list during parsetree analysis.
*/
opt_distinct: DISTINCT { $$ = lcons(NIL,NIL); }
| DISTINCT ON '(' expr_list ')' { $$ = $4; }
| ALL { $$ = NIL; }
| /*EMPTY*/ { $$ = NIL; }
;
sort_clause: ORDER BY sortby_list { $$ = $3; }

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.50 2000/01/26 05:56:42 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.51 2000/01/27 18:11:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -28,12 +28,12 @@
#define ORDER_CLAUSE 0
#define GROUP_CLAUSE 1
#define DISTINCT_ON_CLAUSE 2
static char *clauseText[] = {"ORDER", "GROUP"};
static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"};
static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
List *tlist, int clause,
char *uniqFlag);
List *tlist, int clause);
static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
static char *transformTableEntry(ParseState *pstate, RangeVar *r);
static List *addTargetToSortList(TargetEntry *tle, List *sortlist,
@ -359,14 +359,13 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
* If no matching entry exists, one is created and appended to the target
* list as a "resjunk" node.
*
* node the ORDER BY or GROUP BY expression to be matched
* node the ORDER BY, GROUP BY, or DISTINCT ON expression to be matched
* tlist the existing target list (NB: this cannot be NIL, which is a
* good thing since we'd be unable to append to it...)
* clause identifies clause type for error messages.
*/
static TargetEntry *
findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
char *uniqueFlag)
findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
{
TargetEntry *target_result = NULL;
List *tl;
@ -407,7 +406,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
if (target_result != NULL)
{
if (! equal(target_result->expr, tle->expr))
elog(ERROR, "%s BY '%s' is ambiguous",
elog(ERROR, "%s '%s' is ambiguous",
clauseText[clause], name);
}
else
@ -424,8 +423,8 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
int targetlist_pos = 0;
int target_pos;
if (nodeTag(val) != T_Integer)
elog(ERROR, "Non-integer constant in %s BY", clauseText[clause]);
if (! IsA(val, Integer))
elog(ERROR, "Non-integer constant in %s", clauseText[clause]);
target_pos = intVal(val);
foreach(tl, tlist)
{
@ -438,7 +437,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
return tle; /* return the unique match */
}
}
elog(ERROR, "%s BY position %d is not in target list",
elog(ERROR, "%s position %d is not in target list",
clauseText[clause], target_pos);
}
@ -462,13 +461,9 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
/*
* If no matches, construct a new target entry which is appended to
* the end of the target list. This target is set to be resjunk =
* TRUE so that it will not be projected into the final tuple.
* the end of the target list. This target is given resjunk = TRUE
* so that it will not be projected into the final tuple.
*/
if(clause == ORDER_CLAUSE && uniqueFlag) {
elog(ERROR, "ORDER BY columns must appear in SELECT DISTINCT target list");
}
target_result = transformTargetEntry(pstate, node, expr, NULL, true);
lappend(tlist, target_result);
@ -492,7 +487,7 @@ transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
TargetEntry *tle;
tle = findTargetlistEntry(pstate, lfirst(gl),
targetlist, GROUP_CLAUSE, NULL);
targetlist, GROUP_CLAUSE);
/* avoid making duplicate grouplist entries */
if (! exprIsInSortList(tle->expr, glist, targetlist))
@ -514,74 +509,149 @@ transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
/*
* transformSortClause -
* transform an Order By clause
*
* transform an ORDER BY clause
*/
List *
transformSortClause(ParseState *pstate,
List *orderlist,
List *targetlist,
char *uniqueFlag)
List *targetlist)
{
List *sortlist = NIL;
List *olitem;
/* Transform all the explicit ORDER BY clauses */
foreach(olitem, orderlist)
{
SortGroupBy *sortby = lfirst(olitem);
TargetEntry *tle;
tle = findTargetlistEntry(pstate, sortby->node,
targetlist, ORDER_CLAUSE, uniqueFlag);
targetlist, ORDER_CLAUSE);
sortlist = addTargetToSortList(tle, sortlist, targetlist,
sortby->useOp);
}
/* If we have a DISTINCT clause, add any necessary entries to
* the sortlist to ensure that all the DISTINCT columns will be
* sorted. A subsequent UNIQUE pass will then do the right thing.
*/
if (uniqueFlag)
{
if (uniqueFlag[0] == '*')
{
/*
* concatenate all elements from target list that are not
* already in the sortby list
*/
sortlist = addAllTargetsToSortList(sortlist, targetlist);
}
else
{
TargetEntry *tle = NULL;
char *uniqueAttrName = uniqueFlag;
List *i;
/* only create sort clause with the specified unique attribute */
foreach(i, targetlist)
{
tle = (TargetEntry *) lfirst(i);
if (strcmp(tle->resdom->resname, uniqueAttrName) == 0)
break;
}
if (i == NIL)
elog(ERROR, "All fields in the UNIQUE ON clause must appear in the target list");
sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL);
}
}
return sortlist;
}
/*
* transformDistinctClause -
* transform a DISTINCT or DISTINCT ON clause
*
* Since we may need to add items to the query's sortClause list, that list
* is passed by reference. We might also need to add items to the query's
* targetlist, but we assume that cannot be empty initially, so we can
* lappend to it even though the pointer is passed by value.
*/
List *
transformDistinctClause(ParseState *pstate, List *distinctlist,
List *targetlist, List **sortClause)
{
List *result = NIL;
List *slitem;
List *dlitem;
/* No work if there was no DISTINCT clause */
if (distinctlist == NIL)
return NIL;
if (lfirst(distinctlist) == NIL)
{
/* We had SELECT DISTINCT */
/*
* All non-resjunk elements from target list that are not already
* in the sort list should be added to it. (We don't really care
* what order the DISTINCT fields are checked in, so we can leave
* the user's ORDER BY spec alone, and just add additional sort keys
* to it to ensure that all targetlist items get sorted.)
*/
*sortClause = addAllTargetsToSortList(*sortClause, targetlist);
/*
* Now, DISTINCT list consists of all non-resjunk sortlist items.
* Actually, all the sortlist items had better be non-resjunk!
* Otherwise, user wrote SELECT DISTINCT with an ORDER BY item
* that does not appear anywhere in the SELECT targetlist, and
* we can't implement that with only one sorting pass...
*/
foreach(slitem, *sortClause)
{
SortClause *scl = (SortClause *) lfirst(slitem);
TargetEntry *tle = get_sortgroupclause_tle(scl, targetlist);
if (tle->resdom->resjunk)
elog(ERROR, "For SELECT DISTINCT, ORDER BY expressions must appear in target list");
else
result = lappend(result, copyObject(scl));
}
}
else
{
/* We had SELECT DISTINCT ON (expr, ...) */
/*
* If the user writes both DISTINCT ON and ORDER BY, then the two
* expression lists must match (until one or the other runs out).
* Otherwise the ORDER BY requires a different sort order than the
* DISTINCT does, and we can't implement that with only one sort pass
* (and if we do two passes, the results will be rather unpredictable).
* However, it's OK to have more DISTINCT ON expressions than ORDER BY
* expressions; we can just add the extra DISTINCT values to the sort
* list, much as we did above for ordinary DISTINCT fields.
*
* Actually, it'd be OK for the common prefixes of the two lists to
* match in any order, but implementing that check seems like more
* trouble than it's worth.
*/
List *nextsortlist = *sortClause;
foreach(dlitem, distinctlist)
{
TargetEntry *tle;
tle = findTargetlistEntry(pstate, lfirst(dlitem),
targetlist, DISTINCT_ON_CLAUSE);
if (nextsortlist != NIL)
{
SortClause *scl = (SortClause *) lfirst(nextsortlist);
if (tle->resdom->ressortgroupref != scl->tleSortGroupRef)
elog(ERROR, "SELECT DISTINCT ON expressions must match initial ORDER BY expressions");
result = lappend(result, copyObject(scl));
nextsortlist = lnext(nextsortlist);
}
else
{
*sortClause = addTargetToSortList(tle, *sortClause,
targetlist, NULL);
/* Probably, the tle should always have been added at the
* end of the sort list ... but search to be safe.
*/
foreach(slitem, *sortClause)
{
SortClause *scl = (SortClause *) lfirst(slitem);
if (tle->resdom->ressortgroupref == scl->tleSortGroupRef)
{
result = lappend(result, copyObject(scl));
break;
}
}
if (slitem == NIL)
elog(ERROR, "transformDistinctClause: failed to add DISTINCT ON clause to target list");
}
}
}
return result;
}
/*
* addAllTargetsToSortList
* Make sure all targets in the targetlist are in the ORDER BY list,
* adding the not-yet-sorted ones to the end of the list.
* Make sure all non-resjunk targets in the targetlist are in the
* ORDER BY list, adding the not-yet-sorted ones to the end of the list.
* This is typically used to help implement SELECT DISTINCT.
*
* Returns the updated ORDER BY list.
@ -595,7 +665,8 @@ addAllTargetsToSortList(List *sortlist, List *targetlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(i);
sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL);
if (! tle->resdom->resjunk)
sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL);
}
return sortlist;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.41 2000/01/26 05:56:49 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.42 2000/01/27 18:11:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -312,7 +312,7 @@ DefineQueryRewrite(RuleStmt *stmt)
/*
* DISTINCT on view is not supported
*/
if (query->uniqueFlag != NULL)
if (query->distinctClause != NIL)
elog(ERROR, "DISTINCT not supported in views");
/*

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.66 2000/01/26 05:56:49 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.67 2000/01/27 18:11:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -536,8 +536,8 @@ modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree)
subquery->isBinary = FALSE;
subquery->isTemp = FALSE;
subquery->unionall = FALSE;
subquery->uniqueFlag = NULL;
subquery->sortClause = NULL;
subquery->distinctClause = NIL;
subquery->sortClause = NIL;
subquery->rtable = lcons(copyObject(rte), NIL);
subquery->targetList = lcons(tle, NIL);
subquery->qual = modifyAggrefDropQual((Node *) parsetree->qual,
@ -1725,7 +1725,7 @@ check_targetlists_are_compatible(List *prev_target, List *current_target)
* The operator tree is attached to 'intersectClause' (see rule
* 'SelectStmt' in gram.y) of the 'parsetree' given as an
* argument. First we remember some clauses (the sortClause, the
* unique flag etc.) Then we translate the operator tree to DNF
* distinctClause etc.) Then we translate the operator tree to DNF
* (disjunctive normal form) by 'cnfify'. (Note that 'cnfify' produces
* CNF but as we exchanged ANDs with ORs in function A_Expr_to_Expr()
* earlier we get DNF after exchanging ANDs and ORs again in the
@ -1736,8 +1736,8 @@ check_targetlists_are_compatible(List *prev_target, List *current_target)
* union list is handed back but before that the remembered clauses
* (sortClause etc) are attached to the new top Node (Note that the
* new top Node can differ from the parsetree given as argument because of
* the translation to DNF. That's why we have to remember the sortClause or
* unique flag!) */
* the translation to DNF. That's why we have to remember the sortClause
* and so on!) */
static Query *
Except_Intersect_Rewrite(Query *parsetree)
{
@ -1750,12 +1750,12 @@ Except_Intersect_Rewrite(Query *parsetree)
*intersect,
*intersectClause;
List *union_list = NIL,
*sortClause;
*sortClause,
*distinctClause;
List *left_expr,
*right_expr,
*resnames = NIL;
char *op,
*uniqueFlag,
*into;
bool isBinary,
isPortal,
@ -1806,7 +1806,7 @@ Except_Intersect_Rewrite(Query *parsetree)
* node at the end of the function
*/
sortClause = parsetree->sortClause;
uniqueFlag = parsetree->uniqueFlag;
distinctClause = parsetree->distinctClause;
into = parsetree->into;
isBinary = parsetree->isBinary;
isPortal = parsetree->isPortal;
@ -2009,7 +2009,7 @@ Except_Intersect_Rewrite(Query *parsetree)
result->unionClause = lnext(union_list);
/* Attach all the items remembered in the beginning of the function */
result->sortClause = sortClause;
result->uniqueFlag = uniqueFlag;
result->distinctClause = distinctClause;
result->into = into;
result->isPortal = isPortal;
result->isBinary = isBinary;

View File

@ -7,13 +7,14 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.43 2000/01/26 05:56:49 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.44 2000/01/27 18:11:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "parser/parse_clause.h"
#include "rewrite/rewriteManip.h"
@ -286,22 +287,10 @@ AddGroupClause(Query *parsetree, List *group_by, List *tlist)
foreach(l, group_by)
{
GroupClause *groupclause = (GroupClause *) copyObject(lfirst(l));
Index refnumber = groupclause->tleSortGroupRef;
TargetEntry *tle = NULL;
List *tl;
TargetEntry *tle = get_sortgroupclause_tle(groupclause, tlist);
/* Find and copy the groupclause's TLE in the old tlist */
foreach(tl, tlist)
{
if (((TargetEntry *) lfirst(tl))->resdom->ressortgroupref ==
refnumber)
{
tle = (TargetEntry *) copyObject(lfirst(tl));
break;
}
}
if (tle == NULL)
elog(ERROR, "AddGroupClause(): GROUP BY entry not found in rules targetlist");
/* copy the groupclause's TLE from the old tlist */
tle = (TargetEntry *) copyObject(tle);
/* The ressortgroupref number in the old tlist might be already
* taken in the new tlist, so force assignment of a new number.

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.12 2000/01/26 05:57:56 momjian Exp $
* $Id: catversion.h,v 1.13 2000/01/27 18:11:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200001241
#define CATALOG_VERSION_NO 200001271
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: nodeGroup.h,v 1.14 2000/01/26 23:48:05 tgl Exp $
* $Id: nodeGroup.h,v 1.15 2000/01/27 18:11:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -22,4 +22,14 @@ extern int ExecCountSlotsGroup(Group *node);
extern void ExecEndGroup(Group *node);
extern void ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent);
extern bool execTuplesMatch(HeapTuple tuple1,
HeapTuple tuple2,
TupleDesc tupdesc,
int numCols,
AttrNumber *matchColIdx,
FmgrInfo *eqfunctions);
extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc,
int numCols,
AttrNumber *matchColIdx);
#endif /* NODEGROUP_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: execnodes.h,v 1.39 2000/01/26 05:58:15 momjian Exp $
* $Id: execnodes.h,v 1.40 2000/01/27 18:11:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -621,6 +621,7 @@ typedef struct AggState
typedef struct GroupState
{
CommonScanState csstate; /* its first field is NodeTag */
FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */
bool grp_useFirstTuple; /* first tuple not processed yet */
bool grp_done;
HeapTuple grp_firstTuple;
@ -663,9 +664,9 @@ typedef struct SortState
* Unique nodes are used "on top of" sort nodes to discard
* duplicate tuples returned from the sort phase. Basically
* all it does is compare the current tuple from the subplan
* with the previously fetched tuple stored in OuterTuple and
* if the two are identical, then we just fetch another tuple
* from the sort and try again.
* with the previously fetched tuple stored in priorTuple.
* If the two are identical in all interesting fields, then
* we just fetch another tuple from the sort and try again.
*
* CommonState information
*
@ -677,7 +678,12 @@ typedef struct SortState
* ScanAttributes attribute numbers of interest in this tuple
* ----------------
*/
typedef CommonState UniqueState;
typedef struct UniqueState
{
CommonState cstate; /* its first field is NodeTag */
FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */
HeapTuple priorTuple; /* most recently returned tuple, or NULL */
} UniqueState;
/* ----------------

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.96 2000/01/26 05:58:16 momjian Exp $
* $Id: parsenodes.h,v 1.97 2000/01/27 18:11:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -54,7 +54,8 @@ typedef struct Query
Node *qual; /* qualifications applied to tuples */
List *rowMark; /* list of RowMark entries */
char *uniqueFlag; /* NULL, '*', or Unique attribute name */
List *distinctClause; /* a list of SortClause's */
List *sortClause; /* a list of SortClause's */
List *groupClause; /* a list of GroupClause's */
@ -733,7 +734,8 @@ typedef struct InsertStmt
{
NodeTag type;
char *relname; /* relation to insert into */
char *unique; /* NULL, '*', or unique attribute name */
List *distinctClause; /* NULL, list of DISTINCT ON exprs, or
* lcons(NIL,NIL) for all (SELECT DISTINCT) */
List *cols; /* names of the columns */
List *targetList; /* the target list (of ResTarget) */
List *fromClause; /* the from clause */
@ -777,7 +779,8 @@ typedef struct UpdateStmt
typedef struct SelectStmt
{
NodeTag type;
char *unique; /* NULL, '*', or unique attribute name */
List *distinctClause; /* NULL, list of DISTINCT ON exprs, or
* lcons(NIL,NIL) for all (SELECT DISTINCT) */
char *into; /* name of table (for select into table) */
List *targetList; /* the target list (of ResTarget) */
List *fromClause; /* the from clause */
@ -1135,6 +1138,13 @@ typedef struct RangeTblEntry
* tleSortGroupRef must match ressortgroupref of exactly one Resdom of the
* associated targetlist; that is the expression to be sorted (or grouped) by.
* sortop is the OID of the ordering operator.
*
* SortClauses are also used to identify Resdoms that we will do a "Unique"
* filter step on (for SELECT DISTINCT and SELECT DISTINCT ON). The
* distinctClause list is simply a copy of the relevant members of the
* sortClause list. Note that distinctClause can be a subset of sortClause,
* but cannot have members not present in sortClause; and the members that
* do appear must be in the same order as in sortClause.
*/
typedef struct SortClause
{
@ -1148,7 +1158,7 @@ typedef struct SortClause
* representation of GROUP BY clauses
*
* GroupClause is exactly like SortClause except for the nodetag value
* (and it's probably not even really necessary to have two different
* (it's probably not even really necessary to have two different
* nodetags...). We have routines that operate interchangeably on both.
*/
typedef SortClause GroupClause;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: plannodes.h,v 1.36 2000/01/26 05:58:16 momjian Exp $
* $Id: plannodes.h,v 1.37 2000/01/27 18:11:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -265,7 +265,7 @@ typedef struct Group
Plan plan;
bool tuplePerGroup; /* what tuples to return (see above) */
int numCols; /* number of group columns */
AttrNumber *grpColIdx; /* index into the target list */
AttrNumber *grpColIdx; /* indexes into the target list */
GroupState *grpstate;
} Group;
@ -314,10 +314,8 @@ typedef struct Unique
Plan plan; /* noname node flattened out */
Oid nonameid;
int keycount;
char *uniqueAttr; /* NULL if all attrs, or unique attribute
* name */
AttrNumber uniqueAttrNum; /* attribute number of attribute to select
* distinct on */
int numCols; /* number of columns to check for uniqueness */
AttrNumber *uniqColIdx; /* indexes into the target list */
UniqueState *uniquestate;
} Unique;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: planmain.h,v 1.36 2000/01/26 05:58:20 momjian Exp $
* $Id: planmain.h,v 1.37 2000/01/27 18:11:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -33,7 +33,7 @@ extern Agg *make_agg(List *tlist, Plan *lefttree);
extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
AttrNumber *grpColIdx, Plan *lefttree);
extern Noname *make_noname(List *tlist, List *pathkeys, Plan *subplan);
extern Unique *make_unique(List *tlist, Plan *lefttree, char *uniqueAttr);
extern Unique *make_unique(List *tlist, Plan *lefttree, List *distinctList);
extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
/*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: tlist.h,v 1.23 2000/01/26 05:58:21 momjian Exp $
* $Id: tlist.h,v 1.24 2000/01/27 18:11:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -28,6 +28,9 @@ extern List *flatten_tlist(List *tlist);
extern List *add_to_flat_tlist(List *tlist, List *vars);
extern Var *get_expr(TargetEntry *tle);
extern TargetEntry *get_sortgroupclause_tle(SortClause *sortClause,
List *targetList);
extern Node *get_sortgroupclause_expr(SortClause *sortClause,
List *targetList);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_clause.h,v 1.14 2000/01/26 05:58:26 momjian Exp $
* $Id: parse_clause.h,v 1.15 2000/01/27 18:11:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,7 +23,9 @@ extern Node *transformWhereClause(ParseState *pstate, Node *where,
extern List *transformGroupClause(ParseState *pstate, List *grouplist,
List *targetlist);
extern List *transformSortClause(ParseState *pstate, List *orderlist,
List *targetlist, char *uniqueFlag);
List *targetlist);
extern List *transformDistinctClause(ParseState *pstate, List *distinctlist,
List *targetlist, List **sortClause);
extern List *addAllTargetsToSortList(List *sortlist, List *targetlist);
extern Index assignSortGroupRef(TargetEntry *tle, List *tlist);

View File

@ -29,12 +29,12 @@ ERROR: attribute 'nonesuch' not found
-- bad attribute name on rhs of operator
select * from pg_database where pg_database.datname = nonesuch;
ERROR: attribute 'nonesuch' not found
-- bad select distinct on syntax, distinct attribute missing
select distinct on foobar from pg_database;
-- bad select distinct on syntax, distinct attribute missing
select distinct on (foobar) from pg_database;
ERROR: parser: parse error at or near "from"
-- bad select distinct on syntax, distinct attribute not in target list
select distinct on foobar * from pg_database;
ERROR: All fields in the UNIQUE ON clause must appear in the target list
select distinct on (foobar) * from pg_database;
ERROR: attribute 'foobar' not found
--
-- DELETE

View File

@ -1,18 +1,66 @@
--
-- SELECT_DISTINCT_ON
--
SELECT DISTINCT ON string4 two, string4, ten
FROM tmp
ORDER BY two using <, string4 using <, ten using <;
two | string4 | ten
-----+---------+-----
0 | AAAAxx | 0
0 | HHHHxx | 0
0 | OOOOxx | 0
0 | VVVVxx | 0
1 | AAAAxx | 1
1 | HHHHxx | 1
1 | OOOOxx | 1
1 | VVVVxx | 1
(8 rows)
SELECT DISTINCT ON (string4) string4, two, ten
FROM tmp
ORDER BY string4 using <, two using >, ten using <;
string4 | two | ten
---------+-----+-----
AAAAxx | 1 | 1
HHHHxx | 1 | 1
OOOOxx | 1 | 1
VVVVxx | 1 | 1
(4 rows)
-- this will fail due to conflict of ordering requirements
SELECT DISTINCT ON (string4, ten) string4, two, ten
FROM tmp
ORDER BY string4 using <, two using <, ten using <;
ERROR: SELECT DISTINCT ON expressions must match initial ORDER BY expressions
SELECT DISTINCT ON (string4, ten) string4, ten, two
FROM tmp
ORDER BY string4 using <, ten using >, two using <;
string4 | ten | two
---------+-----+-----
AAAAxx | 9 | 1
AAAAxx | 8 | 0
AAAAxx | 7 | 1
AAAAxx | 6 | 0
AAAAxx | 5 | 1
AAAAxx | 4 | 0
AAAAxx | 3 | 1
AAAAxx | 2 | 0
AAAAxx | 1 | 1
AAAAxx | 0 | 0
HHHHxx | 9 | 1
HHHHxx | 8 | 0
HHHHxx | 7 | 1
HHHHxx | 6 | 0
HHHHxx | 5 | 1
HHHHxx | 4 | 0
HHHHxx | 3 | 1
HHHHxx | 2 | 0
HHHHxx | 1 | 1
HHHHxx | 0 | 0
OOOOxx | 9 | 1
OOOOxx | 8 | 0
OOOOxx | 7 | 1
OOOOxx | 6 | 0
OOOOxx | 5 | 1
OOOOxx | 4 | 0
OOOOxx | 3 | 1
OOOOxx | 2 | 0
OOOOxx | 1 | 1
OOOOxx | 0 | 0
VVVVxx | 9 | 1
VVVVxx | 8 | 0
VVVVxx | 7 | 1
VVVVxx | 6 | 0
VVVVxx | 5 | 1
VVVVxx | 4 | 0
VVVVxx | 3 | 1
VVVVxx | 2 | 0
VVVVxx | 1 | 1
VVVVxx | 0 | 0
(40 rows)

View File

@ -34,12 +34,12 @@ select * from pg_database where nonesuch = pg_database.datname;
select * from pg_database where pg_database.datname = nonesuch;
-- bad select distinct on syntax, distinct attribute missing
select distinct on foobar from pg_database;
-- bad select distinct on syntax, distinct attribute missing
select distinct on (foobar) from pg_database;
-- bad select distinct on syntax, distinct attribute not in target list
select distinct on foobar * from pg_database;
select distinct on (foobar) * from pg_database;
--

View File

@ -2,7 +2,15 @@
-- SELECT_DISTINCT_ON
--
SELECT DISTINCT ON string4 two, string4, ten
FROM tmp
ORDER BY two using <, string4 using <, ten using <;
SELECT DISTINCT ON (string4) string4, two, ten
FROM tmp
ORDER BY string4 using <, two using >, ten using <;
-- this will fail due to conflict of ordering requirements
SELECT DISTINCT ON (string4, ten) string4, two, ten
FROM tmp
ORDER BY string4 using <, two using <, ten using <;
SELECT DISTINCT ON (string4, ten) string4, ten, two
FROM tmp
ORDER BY string4 using <, ten using >, two using <;