mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-06 15:24:56 +08:00
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:
parent
3f0074e403
commit
dd979f66be
@ -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
|
Postgres documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ Postgres documentation
|
|||||||
<date>1999-07-20</date>
|
<date>1999-07-20</date>
|
||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<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> ] [, ...]
|
<replaceable class="PARAMETER">expression</replaceable> [ AS <replaceable class="PARAMETER">name</replaceable> ] [, ...]
|
||||||
[ INTO [ TEMPORARY | TEMP ] [ TABLE ] <replaceable class="PARAMETER">new_table</replaceable> ]
|
[ INTO [ TEMPORARY | TEMP ] [ TABLE ] <replaceable class="PARAMETER">new_table</replaceable> ]
|
||||||
[ FROM <replaceable class="PARAMETER">table</replaceable> [ <replaceable class="PARAMETER">alias</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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<command>DISTINCT</command> will eliminate all duplicate rows from the
|
<command>DISTINCT</command> will eliminate duplicate rows from the
|
||||||
result.
|
result.
|
||||||
<command>DISTINCT ON <replaceable class="PARAMETER">column</replaceable></command>
|
<command>ALL</command> (the default) will return all candidate rows,
|
||||||
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,
|
|
||||||
including duplicates.
|
including duplicates.
|
||||||
</para>
|
</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>
|
<para>
|
||||||
The GROUP BY clause allows a user to divide a table
|
The GROUP BY clause allows a user to divide a table
|
||||||
conceptually into groups.
|
conceptually into groups.
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
UniqueState *uniquestate = ((Unique *) node)->uniquestate;
|
||||||
|
|
||||||
slot = uniquestate->cs_ResultTupleSlot;
|
slot = uniquestate->cstate.cs_ResultTupleSlot;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -9,12 +9,13 @@
|
|||||||
*
|
*
|
||||||
* DESCRIPTION
|
* DESCRIPTION
|
||||||
* The Group node is designed for handling queries with a GROUP BY clause.
|
* 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
|
* Its outer plan must deliver tuples that are sorted in the order
|
||||||
* back from the outer plan is sorted in the order specified by the group
|
* specified by the grouping columns (ie. tuples from the same group are
|
||||||
* columns. (ie. tuples from the same group are consecutive)
|
* consecutive). That way, we just have to compare adjacent tuples to
|
||||||
|
* locate group boundaries.
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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/heapam.h"
|
||||||
#include "access/printtup.h"
|
#include "access/printtup.h"
|
||||||
|
#include "catalog/pg_operator.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "executor/nodeGroup.h"
|
#include "executor/nodeGroup.h"
|
||||||
|
#include "parser/parse_oper.h"
|
||||||
|
#include "parser/parse_type.h"
|
||||||
|
|
||||||
static TupleTableSlot *ExecGroupEveryTuple(Group *node);
|
static TupleTableSlot *ExecGroupEveryTuple(Group *node);
|
||||||
static TupleTableSlot *ExecGroupOneTuple(Group *node);
|
static TupleTableSlot *ExecGroupOneTuple(Group *node);
|
||||||
static bool sameGroup(HeapTuple oldslot, HeapTuple newslot,
|
|
||||||
int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
|
|
||||||
|
|
||||||
/* ---------------------------------------
|
/* ---------------------------------------
|
||||||
* ExecGroup -
|
* ExecGroup -
|
||||||
@ -38,11 +40,11 @@ static bool sameGroup(HeapTuple oldslot, HeapTuple newslot,
|
|||||||
* tuplePerGroup is TRUE, every tuple from the same group will be
|
* tuplePerGroup is TRUE, every tuple from the same group will be
|
||||||
* returned, followed by a NULL at the end of each group. This is
|
* 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
|
* 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
|
* If tuplePerGroup is FALSE, only one tuple per group is returned. The
|
||||||
* tuple returned contains only the group columns. NULL is returned only
|
* 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
|
* the query does not involve aggregates. (eg. SELECT salary FROM emp
|
||||||
* GROUP BY salary)
|
* GROUP BY salary)
|
||||||
* ------------------------------------------
|
* ------------------------------------------
|
||||||
@ -66,6 +68,7 @@ ExecGroupEveryTuple(Group *node)
|
|||||||
GroupState *grpstate;
|
GroupState *grpstate;
|
||||||
EState *estate;
|
EState *estate;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
|
TupleDesc tupdesc;
|
||||||
|
|
||||||
HeapTuple outerTuple = NULL;
|
HeapTuple outerTuple = NULL;
|
||||||
HeapTuple firsttuple;
|
HeapTuple firsttuple;
|
||||||
@ -87,6 +90,8 @@ ExecGroupEveryTuple(Group *node)
|
|||||||
|
|
||||||
econtext = grpstate->csstate.cstate.cs_ExprContext;
|
econtext = grpstate->csstate.cstate.cs_ExprContext;
|
||||||
|
|
||||||
|
tupdesc = ExecGetScanType(&grpstate->csstate);
|
||||||
|
|
||||||
/* if we haven't returned first tuple of new group yet ... */
|
/* if we haven't returned first tuple of new group yet ... */
|
||||||
if (grpstate->grp_useFirstTuple)
|
if (grpstate->grp_useFirstTuple)
|
||||||
{
|
{
|
||||||
@ -110,20 +115,25 @@ ExecGroupEveryTuple(Group *node)
|
|||||||
outerTuple = outerslot->val;
|
outerTuple = outerslot->val;
|
||||||
|
|
||||||
firsttuple = grpstate->grp_firstTuple;
|
firsttuple = grpstate->grp_firstTuple;
|
||||||
/* this should occur on the first call only */
|
|
||||||
if (firsttuple == NULL)
|
if (firsttuple == NULL)
|
||||||
|
{
|
||||||
|
/* this should occur on the first call only */
|
||||||
grpstate->grp_firstTuple = heap_copytuple(outerTuple);
|
grpstate->grp_firstTuple = heap_copytuple(outerTuple);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compare with first tuple and see if this tuple is of the
|
* Compare with first tuple and see if this tuple is of the
|
||||||
* same group.
|
* same group.
|
||||||
*/
|
*/
|
||||||
if (!sameGroup(firsttuple, outerTuple,
|
if (! execTuplesMatch(firsttuple, outerTuple,
|
||||||
node->numCols, node->grpColIdx,
|
tupdesc,
|
||||||
ExecGetScanType(&grpstate->csstate)))
|
node->numCols, node->grpColIdx,
|
||||||
|
grpstate->eqfunctions))
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* No; save the tuple to return it next time, and return NULL
|
||||||
|
*/
|
||||||
grpstate->grp_useFirstTuple = TRUE;
|
grpstate->grp_useFirstTuple = TRUE;
|
||||||
heap_freetuple(firsttuple);
|
heap_freetuple(firsttuple);
|
||||||
grpstate->grp_firstTuple = heap_copytuple(outerTuple);
|
grpstate->grp_firstTuple = heap_copytuple(outerTuple);
|
||||||
@ -164,6 +174,7 @@ ExecGroupOneTuple(Group *node)
|
|||||||
GroupState *grpstate;
|
GroupState *grpstate;
|
||||||
EState *estate;
|
EState *estate;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
|
TupleDesc tupdesc;
|
||||||
|
|
||||||
HeapTuple outerTuple = NULL;
|
HeapTuple outerTuple = NULL;
|
||||||
HeapTuple firsttuple;
|
HeapTuple firsttuple;
|
||||||
@ -185,10 +196,12 @@ ExecGroupOneTuple(Group *node)
|
|||||||
|
|
||||||
econtext = node->grpstate->csstate.cstate.cs_ExprContext;
|
econtext = node->grpstate->csstate.cstate.cs_ExprContext;
|
||||||
|
|
||||||
|
tupdesc = ExecGetScanType(&grpstate->csstate);
|
||||||
|
|
||||||
firsttuple = grpstate->grp_firstTuple;
|
firsttuple = grpstate->grp_firstTuple;
|
||||||
/* this should occur on the first call only */
|
|
||||||
if (firsttuple == NULL)
|
if (firsttuple == NULL)
|
||||||
{
|
{
|
||||||
|
/* this should occur on the first call only */
|
||||||
outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
|
outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
|
||||||
if (TupIsNull(outerslot))
|
if (TupIsNull(outerslot))
|
||||||
{
|
{
|
||||||
@ -213,14 +226,14 @@ ExecGroupOneTuple(Group *node)
|
|||||||
}
|
}
|
||||||
outerTuple = outerslot->val;
|
outerTuple = outerslot->val;
|
||||||
|
|
||||||
/* ----------------
|
/*
|
||||||
* Compare with first tuple and see if this tuple is of
|
* Compare with first tuple and see if this tuple is of the
|
||||||
* the same group.
|
* same group.
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
if ((!sameGroup(firsttuple, outerTuple,
|
if (! execTuplesMatch(firsttuple, outerTuple,
|
||||||
node->numCols, node->grpColIdx,
|
tupdesc,
|
||||||
ExecGetScanType(&grpstate->csstate))))
|
node->numCols, node->grpColIdx,
|
||||||
|
grpstate->eqfunctions))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,6 +324,14 @@ ExecInitGroup(Group *node, EState *estate, Plan *parent)
|
|||||||
ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
|
ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
|
||||||
ExecAssignProjectionInfo((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;
|
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
|
void
|
||||||
ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
|
ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
{
|
{
|
||||||
@ -438,3 +385,104 @@ ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
|
|||||||
((Plan *) node)->lefttree->chgParam == NULL)
|
((Plan *) node)->lefttree->chgParam == NULL)
|
||||||
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
|
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;
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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/heapam.h"
|
||||||
#include "access/printtup.h"
|
#include "access/printtup.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
|
#include "executor/nodeGroup.h"
|
||||||
#include "executor/nodeUnique.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
|
* ExecUnique
|
||||||
*
|
*
|
||||||
* This is a very simple node which filters out duplicate
|
* This is a very simple node which filters out duplicate
|
||||||
* tuples from a stream of sorted tuples from a subplan.
|
* tuples from a stream of sorted tuples from a subplan.
|
||||||
*
|
|
||||||
* XXX see comments below regarding freeing tuples.
|
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
TupleTableSlot * /* return: a tuple or NULL */
|
TupleTableSlot * /* return: a tuple or NULL */
|
||||||
@ -111,11 +46,7 @@ ExecUnique(Unique *node)
|
|||||||
TupleTableSlot *resultTupleSlot;
|
TupleTableSlot *resultTupleSlot;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
Plan *outerPlan;
|
Plan *outerPlan;
|
||||||
char *uniqueAttr;
|
|
||||||
AttrNumber uniqueAttrNum;
|
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
Oid typoutput,
|
|
||||||
typelem;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* get information from the node
|
* get information from the node
|
||||||
@ -123,22 +54,8 @@ ExecUnique(Unique *node)
|
|||||||
*/
|
*/
|
||||||
uniquestate = node->uniquestate;
|
uniquestate = node->uniquestate;
|
||||||
outerPlan = outerPlan((Plan *) node);
|
outerPlan = outerPlan((Plan *) node);
|
||||||
resultTupleSlot = uniquestate->cs_ResultTupleSlot;
|
resultTupleSlot = uniquestate->cstate.cs_ResultTupleSlot;
|
||||||
uniqueAttr = node->uniqueAttr;
|
tupDesc = ExecGetResultType(& uniquestate->cstate);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* now loop, returning only non-duplicate tuples.
|
* now loop, returning only non-duplicate tuples.
|
||||||
@ -157,83 +74,38 @@ ExecUnique(Unique *node)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* we use the result tuple slot to hold our saved tuples.
|
* Always return the first tuple from the subplan.
|
||||||
* 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.
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
if (TupIsNull(resultTupleSlot))
|
if (uniquestate->priorTuple == NULL)
|
||||||
break;
|
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
|
* tuple match. If so then we loop back and fetch
|
||||||
* another new tuple from the subplan.
|
* another new tuple from the subplan.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
|
if (! execTuplesMatch(slot->val, uniquestate->priorTuple,
|
||||||
if (uniqueAttr)
|
tupDesc,
|
||||||
{
|
node->numCols, node->uniqColIdx,
|
||||||
|
uniquestate->eqfunctions))
|
||||||
/*
|
break;
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* we have a new tuple different from the previous saved tuple
|
* We have a new tuple different from the previous saved tuple (if any).
|
||||||
* so we save it in the saved tuple slot. We copy the tuple
|
* Save it and return it. Note that we make two copies of the tuple:
|
||||||
* so we don't increment the buffer ref count.
|
* 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),
|
ExecStoreTuple(heap_copytuple(slot->val),
|
||||||
resultTupleSlot,
|
resultTupleSlot,
|
||||||
InvalidBuffer,
|
InvalidBuffer,
|
||||||
@ -254,7 +126,6 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
|
|||||||
{
|
{
|
||||||
UniqueState *uniquestate;
|
UniqueState *uniquestate;
|
||||||
Plan *outerPlan;
|
Plan *outerPlan;
|
||||||
char *uniqueAttr;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* assign execution state to node
|
* assign execution state to node
|
||||||
@ -268,10 +139,10 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
|
|||||||
*/
|
*/
|
||||||
uniquestate = makeNode(UniqueState);
|
uniquestate = makeNode(UniqueState);
|
||||||
node->uniquestate = uniquestate;
|
node->uniquestate = uniquestate;
|
||||||
uniqueAttr = node->uniqueAttr;
|
uniquestate->priorTuple = NULL;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Miscellanious initialization
|
* Miscellaneous initialization
|
||||||
*
|
*
|
||||||
* + assign node's base_id
|
* + assign node's base_id
|
||||||
* + assign debugging hooks and
|
* + assign debugging hooks and
|
||||||
@ -280,14 +151,14 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
|
|||||||
* they never call ExecQual or ExecTargetList.
|
* they never call ExecQual or ExecTargetList.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecAssignNodeBaseInfo(estate, uniquestate, parent);
|
ExecAssignNodeBaseInfo(estate, & uniquestate->cstate, parent);
|
||||||
|
|
||||||
#define UNIQUE_NSLOTS 1
|
#define UNIQUE_NSLOTS 1
|
||||||
/* ------------
|
/* ------------
|
||||||
* Tuple table initialization
|
* Tuple table initialization
|
||||||
* ------------
|
* ------------
|
||||||
*/
|
*/
|
||||||
ExecInitResultTupleSlot(estate, uniquestate);
|
ExecInitResultTupleSlot(estate, & uniquestate->cstate);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* then initialize outer plan
|
* then initialize outer plan
|
||||||
@ -301,31 +172,17 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
|
|||||||
* projection info for this node appropriately
|
* projection info for this node appropriately
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecAssignResultTypeFromOuterPlan((Plan *) node, uniquestate);
|
ExecAssignResultTypeFromOuterPlan((Plan *) node, & uniquestate->cstate);
|
||||||
uniquestate->cs_ProjInfo = NULL;
|
uniquestate->cstate.cs_ProjInfo = NULL;
|
||||||
|
|
||||||
if (uniqueAttr)
|
/*
|
||||||
{
|
* Precompute fmgr lookup data for inner loop
|
||||||
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.
|
|
||||||
* ----------------
|
|
||||||
*/
|
*/
|
||||||
|
uniquestate->eqfunctions =
|
||||||
|
execTuplesMatchPrepare(ExecGetResultType(& uniquestate->cstate),
|
||||||
|
node->numCols,
|
||||||
|
node->uniqColIdx);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,11 +204,17 @@ ExecCountSlotsUnique(Unique *node)
|
|||||||
void
|
void
|
||||||
ExecEndUnique(Unique *node)
|
ExecEndUnique(Unique *node)
|
||||||
{
|
{
|
||||||
UniqueState *uniquestate;
|
UniqueState *uniquestate = node->uniquestate;
|
||||||
|
|
||||||
uniquestate = node->uniquestate;
|
|
||||||
ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
|
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;
|
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
|
* if chgParam of subnode is not null then plan will be re-scanned by
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* copy remainder of node
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
if (from->uniqueAttr)
|
newnode->numCols = from->numCols;
|
||||||
newnode->uniqueAttr = pstrdup(from->uniqueAttr);
|
newnode->uniqColIdx = palloc(from->numCols * sizeof(AttrNumber));
|
||||||
else
|
memcpy(newnode->uniqColIdx, from->uniqColIdx, from->numCols * sizeof(AttrNumber));
|
||||||
newnode->uniqueAttr = NULL;
|
|
||||||
newnode->uniqueAttrNum = from->uniqueAttrNum;
|
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
@ -1427,8 +1425,7 @@ _copyQuery(Query *from)
|
|||||||
Node_Copy(from, newnode, qual);
|
Node_Copy(from, newnode, qual);
|
||||||
Node_Copy(from, newnode, rowMark);
|
Node_Copy(from, newnode, rowMark);
|
||||||
|
|
||||||
if (from->uniqueFlag)
|
Node_Copy(from, newnode, distinctClause);
|
||||||
newnode->uniqueFlag = pstrdup(from->uniqueFlag);
|
|
||||||
Node_Copy(from, newnode, sortClause);
|
Node_Copy(from, newnode, sortClause);
|
||||||
Node_Copy(from, newnode, groupClause);
|
Node_Copy(from, newnode, groupClause);
|
||||||
Node_Copy(from, newnode, havingQual);
|
Node_Copy(from, newnode, havingQual);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
return false;
|
||||||
if (!equal(a->rowMark, b->rowMark))
|
if (!equal(a->rowMark, b->rowMark))
|
||||||
return false;
|
return false;
|
||||||
if (a->uniqueFlag && b->uniqueFlag)
|
if (!equal(a->distinctClause, b->distinctClause))
|
||||||
{
|
return false;
|
||||||
if (strcmp(a->uniqueFlag, b->uniqueFlag) != 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (a->uniqueFlag != b->uniqueFlag)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!equal(a->sortClause, b->sortClause))
|
if (!equal(a->sortClause, b->sortClause))
|
||||||
return false;
|
return false;
|
||||||
if (!equal(a->groupClause, b->groupClause))
|
if (!equal(a->groupClause, b->groupClause))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* free remainder of node
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
if (node->uniqueAttr)
|
pfree(node->uniqColIdx);
|
||||||
pfree(node->uniqueAttr);
|
|
||||||
|
|
||||||
pfree(node);
|
pfree(node);
|
||||||
}
|
}
|
||||||
@ -1072,9 +1071,7 @@ _freeQuery(Query *node)
|
|||||||
freeObject(node->targetList);
|
freeObject(node->targetList);
|
||||||
freeObject(node->qual);
|
freeObject(node->qual);
|
||||||
freeObject(node->rowMark);
|
freeObject(node->rowMark);
|
||||||
if (node->uniqueFlag)
|
freeObject(node->distinctClause);
|
||||||
pfree(node->uniqueFlag);
|
|
||||||
|
|
||||||
freeObject(node->sortClause);
|
freeObject(node->sortClause);
|
||||||
freeObject(node->groupClause);
|
freeObject(node->groupClause);
|
||||||
freeObject(node->havingQual);
|
freeObject(node->havingQual);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* NOTES
|
||||||
* Every (plan) node in POSTGRES has an associated "out" routine which
|
* Every (plan) node in POSTGRES has an associated "out" routine which
|
||||||
@ -256,12 +256,13 @@ _outQuery(StringInfo str, Query *node)
|
|||||||
_outToken(str, node->into);
|
_outToken(str, node->into);
|
||||||
|
|
||||||
appendStringInfo(str,
|
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->isPortal ? "true" : "false",
|
||||||
node->isBinary ? "true" : "false",
|
node->isBinary ? "true" : "false",
|
||||||
node->isTemp ? "true" : "false",
|
node->isTemp ? "true" : "false",
|
||||||
node->unionall ? "true" : "false");
|
node->unionall ? "true" : "false");
|
||||||
_outToken(str, node->uniqueFlag);
|
_outNode(str, node->distinctClause);
|
||||||
|
|
||||||
appendStringInfo(str, " :sortClause ");
|
appendStringInfo(str, " :sortClause ");
|
||||||
_outNode(str, node->sortClause);
|
_outNode(str, node->sortClause);
|
||||||
|
|
||||||
@ -584,9 +585,10 @@ _outUnique(StringInfo str, Unique *node)
|
|||||||
appendStringInfo(str, " UNIQUE ");
|
appendStringInfo(str, " UNIQUE ");
|
||||||
_outPlanInfo(str, (Plan *) node);
|
_outPlanInfo(str, (Plan *) node);
|
||||||
|
|
||||||
appendStringInfo(str, " :nonameid %u :keycount %d ",
|
appendStringInfo(str, " :nonameid %u :keycount %d :numCols %d ",
|
||||||
node->nonameid,
|
node->nonameid,
|
||||||
node->keycount);
|
node->keycount,
|
||||||
|
node->numCols);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* Most of the read functions for plan nodes are tested. (In fact, they
|
* Most of the read functions for plan nodes are tested. (In fact, they
|
||||||
@ -111,12 +111,8 @@ _readQuery()
|
|||||||
token = lsptok(NULL, &length); /* get unionall */
|
token = lsptok(NULL, &length); /* get unionall */
|
||||||
local_node->unionall = (token[0] == 't') ? true : false;
|
local_node->unionall = (token[0] == 't') ? true : false;
|
||||||
|
|
||||||
token = lsptok(NULL, &length); /* skip :uniqueFlag */
|
token = lsptok(NULL, &length); /* skip :distinctClause */
|
||||||
token = lsptok(NULL, &length); /* get uniqueFlag */
|
local_node->distinctClause = nodeRead(true);
|
||||||
if (length == 0)
|
|
||||||
local_node->uniqueFlag = NULL;
|
|
||||||
else
|
|
||||||
local_node->uniqueFlag = debackslash(token, length);
|
|
||||||
|
|
||||||
token = lsptok(NULL, &length); /* skip :sortClause */
|
token = lsptok(NULL, &length); /* skip :sortClause */
|
||||||
local_node->sortClause = nodeRead(true);
|
local_node->sortClause = nodeRead(true);
|
||||||
@ -624,33 +620,6 @@ _readAgg()
|
|||||||
return local_node;
|
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
|
* _readHash
|
||||||
*
|
*
|
||||||
@ -1847,8 +1816,6 @@ parsePlanString(void)
|
|||||||
return_value = _readSubLink();
|
return_value = _readSubLink();
|
||||||
else if (length == 3 && strncmp(token, "AGG", length) == 0)
|
else if (length == 3 && strncmp(token, "AGG", length) == 0)
|
||||||
return_value = _readAgg();
|
return_value = _readAgg();
|
||||||
else if (length == 6 && strncmp(token, "UNIQUE", length) == 0)
|
|
||||||
return_value = _readUnique();
|
|
||||||
else if (length == 4 && strncmp(token, "HASH", length) == 0)
|
else if (length == 4 && strncmp(token, "HASH", length) == 0)
|
||||||
return_value = _readHash();
|
return_value = _readHash();
|
||||||
else if (length == 6 && strncmp(token, "RESDOM", length) == 0)
|
else if (length == 6 && strncmp(token, "RESDOM", length) == 0)
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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,
|
* distinctList is a list of SortClauses, identifying the targetlist items
|
||||||
* either the name of the attribute to select unique on
|
* that should be considered by the Unique filter.
|
||||||
* or "*"
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Unique *
|
Unique *
|
||||||
make_unique(List *tlist, Plan *lefttree, char *uniqueAttr)
|
make_unique(List *tlist, Plan *lefttree, List *distinctList)
|
||||||
{
|
{
|
||||||
Unique *node = makeNode(Unique);
|
Unique *node = makeNode(Unique);
|
||||||
Plan *plan = &node->plan;
|
Plan *plan = &node->plan;
|
||||||
|
int numCols = length(distinctList);
|
||||||
|
int keyno = 0;
|
||||||
|
AttrNumber *uniqColIdx;
|
||||||
|
List *slitem;
|
||||||
|
|
||||||
copy_plan_costsize(plan, lefttree);
|
copy_plan_costsize(plan, lefttree);
|
||||||
plan->state = (EState *) NULL;
|
plan->state = (EState *) NULL;
|
||||||
@ -1361,10 +1364,22 @@ make_unique(List *tlist, Plan *lefttree, char *uniqueAttr)
|
|||||||
plan->righttree = NULL;
|
plan->righttree = NULL;
|
||||||
node->nonameid = _NONAME_RELATION_ID_;
|
node->nonameid = _NONAME_RELATION_ID_;
|
||||||
node->keycount = 0;
|
node->keycount = 0;
|
||||||
if (strcmp(uniqueAttr, "*") == 0)
|
|
||||||
node->uniqueAttr = NULL;
|
/* convert SortClause list into array of attr indexes, as wanted by exec */
|
||||||
else
|
Assert(numCols > 0);
|
||||||
node->uniqueAttr = pstrdup(uniqueAttr);
|
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;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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,
|
result_plan = (Plan *) make_unique(tlist, result_plan,
|
||||||
parse->uniqueFlag);
|
parse->distinctClause);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result_plan;
|
return result_plan;
|
||||||
@ -583,20 +583,8 @@ make_sortplan(List *tlist, List *sortcls, Plan *plannode)
|
|||||||
foreach(i, sortcls)
|
foreach(i, sortcls)
|
||||||
{
|
{
|
||||||
SortClause *sortcl = (SortClause *) lfirst(i);
|
SortClause *sortcl = (SortClause *) lfirst(i);
|
||||||
Index refnumber = sortcl->tleSortGroupRef;
|
TargetEntry *tle = get_sortgroupclause_tle(sortcl, temp_tlist);
|
||||||
TargetEntry *tle = NULL;
|
Resdom *resdom = tle->resdom;
|
||||||
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;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for the possibility of duplicate order-by clauses --- the
|
* Check for the possibility of duplicate order-by clauses --- the
|
||||||
|
@ -104,9 +104,7 @@ transformKeySetQuery(Query *origNode)
|
|||||||
unionNode->isPortal = origNode->isPortal;
|
unionNode->isPortal = origNode->isPortal;
|
||||||
unionNode->isBinary = origNode->isBinary;
|
unionNode->isBinary = origNode->isBinary;
|
||||||
|
|
||||||
if (origNode->uniqueFlag)
|
Node_Copy(origNode, unionNode, distinctClause);
|
||||||
unionNode->uniqueFlag = pstrdup(origNode->uniqueFlag);
|
|
||||||
|
|
||||||
Node_Copy(origNode, unionNode, sortClause);
|
Node_Copy(origNode, unionNode, sortClause);
|
||||||
Node_Copy(origNode, unionNode, rtable);
|
Node_Copy(origNode, unionNode, rtable);
|
||||||
Node_Copy(origNode, unionNode, targetList);
|
Node_Copy(origNode, unionNode, targetList);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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/planmain.h"
|
||||||
#include "optimizer/planner.h"
|
#include "optimizer/planner.h"
|
||||||
#include "optimizer/prep.h"
|
#include "optimizer/prep.h"
|
||||||
|
#include "optimizer/tlist.h"
|
||||||
#include "parser/parse_clause.h"
|
#include "parser/parse_clause.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
@ -131,7 +132,7 @@ plan_union_queries(Query *parse)
|
|||||||
!last_union_all_flag)
|
!last_union_all_flag)
|
||||||
{
|
{
|
||||||
parse->sortClause = NIL;
|
parse->sortClause = NIL;
|
||||||
parse->uniqueFlag = NULL;
|
parse->distinctClause = NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse->unionClause = NIL; /* prevent recursion */
|
parse->unionClause = NIL; /* prevent recursion */
|
||||||
@ -183,17 +184,28 @@ plan_union_queries(Query *parse)
|
|||||||
if (!last_union_all_flag)
|
if (!last_union_all_flag)
|
||||||
{
|
{
|
||||||
/* Need SELECT DISTINCT behavior to implement UNION.
|
/* Need SELECT DISTINCT behavior to implement UNION.
|
||||||
* Set uniqueFlag properly, put back the held sortClause,
|
* Put back the held sortClause, add any missing columns to the
|
||||||
* and add any missing columns to the sort clause.
|
* sort clause, and set distinctClause properly.
|
||||||
*/
|
*/
|
||||||
parse->uniqueFlag = "*";
|
List *slitem;
|
||||||
|
|
||||||
parse->sortClause = addAllTargetsToSortList(hold_sortClause,
|
parse->sortClause = addAllTargetsToSortList(hold_sortClause,
|
||||||
parse->targetList);
|
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
|
else
|
||||||
{
|
{
|
||||||
/* needed so we don't take the flag from the first query */
|
/* needed so we don't take SELECT DISTINCT from the first query */
|
||||||
parse->uniqueFlag = NULL;
|
parse->distinctClause = NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure we don't try to apply the first query's grouping stuff
|
/* 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,
|
* Clear the sorting and grouping qualifications in the subquery,
|
||||||
* so that sorting will only be done once after append
|
* so that sorting will only be done once after append
|
||||||
*/
|
*/
|
||||||
new_root->uniqueFlag = NULL;
|
new_root->distinctClause = NIL;
|
||||||
new_root->sortClause = NULL;
|
new_root->sortClause = NIL;
|
||||||
new_root->groupClause = NULL;
|
new_root->groupClause = NIL;
|
||||||
new_root->havingQual = NULL;
|
new_root->havingQual = NULL;
|
||||||
new_root->hasAggs = false; /* shouldn't be any left ... */
|
new_root->hasAggs = false; /* shouldn't be any left ... */
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
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
|
* get_sortgroupclause_expr
|
||||||
* Find the targetlist entry matching the given SortClause
|
* Find the targetlist entry matching the given SortClause
|
||||||
@ -227,17 +254,7 @@ get_expr(TargetEntry *tle)
|
|||||||
Node *
|
Node *
|
||||||
get_sortgroupclause_expr(SortClause *sortClause, List *targetList)
|
get_sortgroupclause_expr(SortClause *sortClause, List *targetList)
|
||||||
{
|
{
|
||||||
Index refnumber = sortClause->tleSortGroupRef;
|
TargetEntry *tle = get_sortgroupclause_tle(sortClause, targetList);
|
||||||
List *l;
|
|
||||||
|
|
||||||
foreach(l, targetList)
|
return tle->expr;
|
||||||
{
|
|
||||||
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 */
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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);
|
makeRangeTable(pstate, NULL, NULL);
|
||||||
setTargetTable(pstate, stmt->relname);
|
setTargetTable(pstate, stmt->relname);
|
||||||
|
|
||||||
qry->uniqueFlag = NULL;
|
qry->distinctClause = NIL;
|
||||||
|
|
||||||
/* fix where clause */
|
/* fix where clause */
|
||||||
qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
|
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 */
|
/* set up a range table --- note INSERT target is not in it yet */
|
||||||
makeRangeTable(pstate, stmt->fromClause, &fromQual);
|
makeRangeTable(pstate, stmt->fromClause, &fromQual);
|
||||||
|
|
||||||
qry->uniqueFlag = stmt->unique;
|
|
||||||
|
|
||||||
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
||||||
|
|
||||||
qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
|
qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
|
||||||
@ -311,13 +309,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
|||||||
stmt->groupClause,
|
stmt->groupClause,
|
||||||
qry->targetList);
|
qry->targetList);
|
||||||
|
|
||||||
/* An InsertStmt has no sortClause, but we still call
|
/* An InsertStmt has no sortClause */
|
||||||
* transformSortClause because it also handles uniqueFlag.
|
qry->sortClause = NIL;
|
||||||
*/
|
|
||||||
qry->sortClause = transformSortClause(pstate,
|
qry->distinctClause = transformDistinctClause(pstate,
|
||||||
NIL,
|
stmt->distinctClause,
|
||||||
qry->targetList,
|
qry->targetList,
|
||||||
qry->uniqueFlag);
|
& qry->sortClause);
|
||||||
|
|
||||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||||
qry->hasAggs = pstate->p_hasAggs;
|
qry->hasAggs = pstate->p_hasAggs;
|
||||||
@ -1312,8 +1310,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
|||||||
/* set up a range table */
|
/* set up a range table */
|
||||||
makeRangeTable(pstate, stmt->fromClause, &fromQual);
|
makeRangeTable(pstate, stmt->fromClause, &fromQual);
|
||||||
|
|
||||||
qry->uniqueFlag = stmt->unique;
|
|
||||||
|
|
||||||
qry->into = stmt->into;
|
qry->into = stmt->into;
|
||||||
qry->isTemp = stmt->istemp;
|
qry->isTemp = stmt->istemp;
|
||||||
qry->isPortal = FALSE;
|
qry->isPortal = FALSE;
|
||||||
@ -1333,8 +1329,12 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
|||||||
|
|
||||||
qry->sortClause = transformSortClause(pstate,
|
qry->sortClause = transformSortClause(pstate,
|
||||||
stmt->sortClause,
|
stmt->sortClause,
|
||||||
qry->targetList,
|
qry->targetList);
|
||||||
qry->uniqueFlag);
|
|
||||||
|
qry->distinctClause = transformDistinctClause(pstate,
|
||||||
|
stmt->distinctClause,
|
||||||
|
qry->targetList,
|
||||||
|
& qry->sortClause);
|
||||||
|
|
||||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||||
qry->hasAggs = pstate->p_hasAggs;
|
qry->hasAggs = pstate->p_hasAggs;
|
||||||
@ -1558,9 +1558,9 @@ CheckSelectForUpdate(Query *qry)
|
|||||||
{
|
{
|
||||||
if (qry->unionClause != NULL)
|
if (qry->unionClause != NULL)
|
||||||
elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT clause");
|
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");
|
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");
|
elog(ERROR, "SELECT FOR UPDATE is not allowed with GROUP BY clause");
|
||||||
if (qry->hasAggs)
|
if (qry->hasAggs)
|
||||||
elog(ERROR, "SELECT FOR UPDATE is not allowed with AGGREGATE");
|
elog(ERROR, "SELECT FOR UPDATE is not allowed with AGGREGATE");
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -159,7 +159,7 @@ static Node *doNegate(Node *n);
|
|||||||
class, index_name, name, func_name, file_name, aggr_argtype
|
class, index_name, name, func_name, file_name, aggr_argtype
|
||||||
|
|
||||||
%type <str> opt_id,
|
%type <str> opt_id,
|
||||||
all_Op, MathOp, opt_name, opt_unique,
|
all_Op, MathOp, opt_name,
|
||||||
OptUseOp, opt_class, SpecialRuleRelation
|
OptUseOp, opt_class, SpecialRuleRelation
|
||||||
|
|
||||||
%type <str> opt_level, opt_encoding
|
%type <str> opt_level, opt_encoding
|
||||||
@ -168,7 +168,7 @@ static Node *doNegate(Node *n);
|
|||||||
|
|
||||||
%type <list> stmtblock, stmtmulti,
|
%type <list> stmtblock, stmtmulti,
|
||||||
result, relation_name_list, OptTableElementList,
|
result, relation_name_list, OptTableElementList,
|
||||||
OptInherit, definition,
|
OptInherit, definition, opt_distinct,
|
||||||
opt_with, func_args, func_args_list, func_as,
|
opt_with, func_args, func_args_list, func_as,
|
||||||
oper_argtypes, RuleActionList, RuleActionMulti,
|
oper_argtypes, RuleActionList, RuleActionMulti,
|
||||||
opt_column_list, columnList, opt_va_list, va_list,
|
opt_column_list, columnList, opt_va_list, va_list,
|
||||||
@ -2843,7 +2843,7 @@ insert_rest: VALUES '(' target_list ')'
|
|||||||
{
|
{
|
||||||
$$ = makeNode(InsertStmt);
|
$$ = makeNode(InsertStmt);
|
||||||
$$->cols = NULL;
|
$$->cols = NULL;
|
||||||
$$->unique = NULL;
|
$$->distinctClause = NIL;
|
||||||
$$->targetList = $3;
|
$$->targetList = $3;
|
||||||
$$->fromClause = NIL;
|
$$->fromClause = NIL;
|
||||||
$$->whereClause = NULL;
|
$$->whereClause = NULL;
|
||||||
@ -2854,7 +2854,7 @@ insert_rest: VALUES '(' target_list ')'
|
|||||||
| DEFAULT VALUES
|
| DEFAULT VALUES
|
||||||
{
|
{
|
||||||
$$ = makeNode(InsertStmt);
|
$$ = makeNode(InsertStmt);
|
||||||
$$->unique = NULL;
|
$$->distinctClause = NIL;
|
||||||
$$->targetList = NIL;
|
$$->targetList = NIL;
|
||||||
$$->fromClause = NIL;
|
$$->fromClause = NIL;
|
||||||
$$->whereClause = NULL;
|
$$->whereClause = NULL;
|
||||||
@ -2873,7 +2873,7 @@ insert_rest: VALUES '(' target_list ')'
|
|||||||
elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
|
elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
|
||||||
$$ = makeNode(InsertStmt);
|
$$ = makeNode(InsertStmt);
|
||||||
$$->cols = NIL;
|
$$->cols = NIL;
|
||||||
$$->unique = n->unique;
|
$$->distinctClause = n->distinctClause;
|
||||||
$$->targetList = n->targetList;
|
$$->targetList = n->targetList;
|
||||||
$$->fromClause = n->fromClause;
|
$$->fromClause = n->fromClause;
|
||||||
$$->whereClause = n->whereClause;
|
$$->whereClause = n->whereClause;
|
||||||
@ -2888,7 +2888,7 @@ insert_rest: VALUES '(' target_list ')'
|
|||||||
{
|
{
|
||||||
$$ = makeNode(InsertStmt);
|
$$ = makeNode(InsertStmt);
|
||||||
$$->cols = $2;
|
$$->cols = $2;
|
||||||
$$->unique = NULL;
|
$$->distinctClause = NIL;
|
||||||
$$->targetList = $6;
|
$$->targetList = $6;
|
||||||
$$->fromClause = NIL;
|
$$->fromClause = NIL;
|
||||||
$$->whereClause = NULL;
|
$$->whereClause = NULL;
|
||||||
@ -2904,7 +2904,7 @@ insert_rest: VALUES '(' target_list ')'
|
|||||||
elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
|
elog(ERROR, "INSERT ... SELECT can't have ORDER BY");
|
||||||
$$ = makeNode(InsertStmt);
|
$$ = makeNode(InsertStmt);
|
||||||
$$->cols = $2;
|
$$->cols = $2;
|
||||||
$$->unique = n->unique;
|
$$->distinctClause = n->distinctClause;
|
||||||
$$->targetList = n->targetList;
|
$$->targetList = n->targetList;
|
||||||
$$->fromClause = n->fromClause;
|
$$->fromClause = n->fromClause;
|
||||||
$$->whereClause = n->whereClause;
|
$$->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
|
result from_clause where_clause
|
||||||
group_clause having_clause
|
group_clause having_clause
|
||||||
{
|
{
|
||||||
SelectStmt *n = makeNode(SelectStmt);
|
SelectStmt *n = makeNode(SelectStmt);
|
||||||
n->unique = $2;
|
n->distinctClause = $2;
|
||||||
n->unionall = FALSE;
|
n->unionall = FALSE;
|
||||||
n->targetList = $3;
|
n->targetList = $3;
|
||||||
/* This is new: Subselects support the INTO clause
|
/* This is new: Subselects support the INTO clause
|
||||||
@ -3230,10 +3230,13 @@ opt_all: ALL { $$ = TRUE; }
|
|||||||
| /*EMPTY*/ { $$ = FALSE; }
|
| /*EMPTY*/ { $$ = FALSE; }
|
||||||
;
|
;
|
||||||
|
|
||||||
opt_unique: DISTINCT { $$ = "*"; }
|
/* We use (NIL) as a placeholder to indicate that all target expressions
|
||||||
| DISTINCT ON ColId { $$ = $3; }
|
* should be placed in the DISTINCT list during parsetree analysis.
|
||||||
| ALL { $$ = NULL; }
|
*/
|
||||||
| /*EMPTY*/ { $$ = NULL; }
|
opt_distinct: DISTINCT { $$ = lcons(NIL,NIL); }
|
||||||
|
| DISTINCT ON '(' expr_list ')' { $$ = $4; }
|
||||||
|
| ALL { $$ = NIL; }
|
||||||
|
| /*EMPTY*/ { $$ = NIL; }
|
||||||
;
|
;
|
||||||
|
|
||||||
sort_clause: ORDER BY sortby_list { $$ = $3; }
|
sort_clause: ORDER BY sortby_list { $$ = $3; }
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 ORDER_CLAUSE 0
|
||||||
#define GROUP_CLAUSE 1
|
#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,
|
static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
|
||||||
List *tlist, int clause,
|
List *tlist, int clause);
|
||||||
char *uniqFlag);
|
|
||||||
static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
|
static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
|
||||||
static char *transformTableEntry(ParseState *pstate, RangeVar *r);
|
static char *transformTableEntry(ParseState *pstate, RangeVar *r);
|
||||||
static List *addTargetToSortList(TargetEntry *tle, List *sortlist,
|
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
|
* If no matching entry exists, one is created and appended to the target
|
||||||
* list as a "resjunk" node.
|
* 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
|
* tlist the existing target list (NB: this cannot be NIL, which is a
|
||||||
* good thing since we'd be unable to append to it...)
|
* good thing since we'd be unable to append to it...)
|
||||||
* clause identifies clause type for error messages.
|
* clause identifies clause type for error messages.
|
||||||
*/
|
*/
|
||||||
static TargetEntry *
|
static TargetEntry *
|
||||||
findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
|
findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
|
||||||
char *uniqueFlag)
|
|
||||||
{
|
{
|
||||||
TargetEntry *target_result = NULL;
|
TargetEntry *target_result = NULL;
|
||||||
List *tl;
|
List *tl;
|
||||||
@ -407,7 +406,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
|
|||||||
if (target_result != NULL)
|
if (target_result != NULL)
|
||||||
{
|
{
|
||||||
if (! equal(target_result->expr, tle->expr))
|
if (! equal(target_result->expr, tle->expr))
|
||||||
elog(ERROR, "%s BY '%s' is ambiguous",
|
elog(ERROR, "%s '%s' is ambiguous",
|
||||||
clauseText[clause], name);
|
clauseText[clause], name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -424,8 +423,8 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
|
|||||||
int targetlist_pos = 0;
|
int targetlist_pos = 0;
|
||||||
int target_pos;
|
int target_pos;
|
||||||
|
|
||||||
if (nodeTag(val) != T_Integer)
|
if (! IsA(val, Integer))
|
||||||
elog(ERROR, "Non-integer constant in %s BY", clauseText[clause]);
|
elog(ERROR, "Non-integer constant in %s", clauseText[clause]);
|
||||||
target_pos = intVal(val);
|
target_pos = intVal(val);
|
||||||
foreach(tl, tlist)
|
foreach(tl, tlist)
|
||||||
{
|
{
|
||||||
@ -438,7 +437,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause,
|
|||||||
return tle; /* return the unique match */
|
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);
|
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
|
* 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 =
|
* the end of the target list. This target is given resjunk = TRUE
|
||||||
* TRUE so that it will not be projected into the final tuple.
|
* 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);
|
target_result = transformTargetEntry(pstate, node, expr, NULL, true);
|
||||||
lappend(tlist, target_result);
|
lappend(tlist, target_result);
|
||||||
|
|
||||||
@ -492,7 +487,7 @@ transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
|
|||||||
TargetEntry *tle;
|
TargetEntry *tle;
|
||||||
|
|
||||||
tle = findTargetlistEntry(pstate, lfirst(gl),
|
tle = findTargetlistEntry(pstate, lfirst(gl),
|
||||||
targetlist, GROUP_CLAUSE, NULL);
|
targetlist, GROUP_CLAUSE);
|
||||||
|
|
||||||
/* avoid making duplicate grouplist entries */
|
/* avoid making duplicate grouplist entries */
|
||||||
if (! exprIsInSortList(tle->expr, glist, targetlist))
|
if (! exprIsInSortList(tle->expr, glist, targetlist))
|
||||||
@ -514,74 +509,149 @@ transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* transformSortClause -
|
* transformSortClause -
|
||||||
* transform an Order By clause
|
* transform an ORDER BY clause
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
transformSortClause(ParseState *pstate,
|
transformSortClause(ParseState *pstate,
|
||||||
List *orderlist,
|
List *orderlist,
|
||||||
List *targetlist,
|
List *targetlist)
|
||||||
char *uniqueFlag)
|
|
||||||
{
|
{
|
||||||
List *sortlist = NIL;
|
List *sortlist = NIL;
|
||||||
List *olitem;
|
List *olitem;
|
||||||
|
|
||||||
/* Transform all the explicit ORDER BY clauses */
|
|
||||||
|
|
||||||
foreach(olitem, orderlist)
|
foreach(olitem, orderlist)
|
||||||
{
|
{
|
||||||
SortGroupBy *sortby = lfirst(olitem);
|
SortGroupBy *sortby = lfirst(olitem);
|
||||||
TargetEntry *tle;
|
TargetEntry *tle;
|
||||||
|
|
||||||
tle = findTargetlistEntry(pstate, sortby->node,
|
tle = findTargetlistEntry(pstate, sortby->node,
|
||||||
targetlist, ORDER_CLAUSE, uniqueFlag);
|
targetlist, ORDER_CLAUSE);
|
||||||
|
|
||||||
sortlist = addTargetToSortList(tle, sortlist, targetlist,
|
sortlist = addTargetToSortList(tle, sortlist, targetlist,
|
||||||
sortby->useOp);
|
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;
|
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
|
* addAllTargetsToSortList
|
||||||
* Make sure all targets in the targetlist are in the ORDER BY list,
|
* Make sure all non-resjunk targets in the targetlist are in the
|
||||||
* adding the not-yet-sorted ones to the end of the list.
|
* ORDER BY list, adding the not-yet-sorted ones to the end of the list.
|
||||||
* This is typically used to help implement SELECT DISTINCT.
|
* This is typically used to help implement SELECT DISTINCT.
|
||||||
*
|
*
|
||||||
* Returns the updated ORDER BY list.
|
* Returns the updated ORDER BY list.
|
||||||
@ -595,7 +665,8 @@ addAllTargetsToSortList(List *sortlist, List *targetlist)
|
|||||||
{
|
{
|
||||||
TargetEntry *tle = (TargetEntry *) lfirst(i);
|
TargetEntry *tle = (TargetEntry *) lfirst(i);
|
||||||
|
|
||||||
sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL);
|
if (! tle->resdom->resjunk)
|
||||||
|
sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL);
|
||||||
}
|
}
|
||||||
return sortlist;
|
return sortlist;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* DISTINCT on view is not supported
|
||||||
*/
|
*/
|
||||||
if (query->uniqueFlag != NULL)
|
if (query->distinctClause != NIL)
|
||||||
elog(ERROR, "DISTINCT not supported in views");
|
elog(ERROR, "DISTINCT not supported in views");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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->isBinary = FALSE;
|
||||||
subquery->isTemp = FALSE;
|
subquery->isTemp = FALSE;
|
||||||
subquery->unionall = FALSE;
|
subquery->unionall = FALSE;
|
||||||
subquery->uniqueFlag = NULL;
|
subquery->distinctClause = NIL;
|
||||||
subquery->sortClause = NULL;
|
subquery->sortClause = NIL;
|
||||||
subquery->rtable = lcons(copyObject(rte), NIL);
|
subquery->rtable = lcons(copyObject(rte), NIL);
|
||||||
subquery->targetList = lcons(tle, NIL);
|
subquery->targetList = lcons(tle, NIL);
|
||||||
subquery->qual = modifyAggrefDropQual((Node *) parsetree->qual,
|
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
|
* The operator tree is attached to 'intersectClause' (see rule
|
||||||
* 'SelectStmt' in gram.y) of the 'parsetree' given as an
|
* 'SelectStmt' in gram.y) of the 'parsetree' given as an
|
||||||
* argument. First we remember some clauses (the sortClause, the
|
* 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
|
* (disjunctive normal form) by 'cnfify'. (Note that 'cnfify' produces
|
||||||
* CNF but as we exchanged ANDs with ORs in function A_Expr_to_Expr()
|
* 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
|
* 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
|
* union list is handed back but before that the remembered clauses
|
||||||
* (sortClause etc) are attached to the new top Node (Note that the
|
* (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
|
* 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
|
* the translation to DNF. That's why we have to remember the sortClause
|
||||||
* unique flag!) */
|
* and so on!) */
|
||||||
static Query *
|
static Query *
|
||||||
Except_Intersect_Rewrite(Query *parsetree)
|
Except_Intersect_Rewrite(Query *parsetree)
|
||||||
{
|
{
|
||||||
@ -1750,12 +1750,12 @@ Except_Intersect_Rewrite(Query *parsetree)
|
|||||||
*intersect,
|
*intersect,
|
||||||
*intersectClause;
|
*intersectClause;
|
||||||
List *union_list = NIL,
|
List *union_list = NIL,
|
||||||
*sortClause;
|
*sortClause,
|
||||||
|
*distinctClause;
|
||||||
List *left_expr,
|
List *left_expr,
|
||||||
*right_expr,
|
*right_expr,
|
||||||
*resnames = NIL;
|
*resnames = NIL;
|
||||||
char *op,
|
char *op,
|
||||||
*uniqueFlag,
|
|
||||||
*into;
|
*into;
|
||||||
bool isBinary,
|
bool isBinary,
|
||||||
isPortal,
|
isPortal,
|
||||||
@ -1806,7 +1806,7 @@ Except_Intersect_Rewrite(Query *parsetree)
|
|||||||
* node at the end of the function
|
* node at the end of the function
|
||||||
*/
|
*/
|
||||||
sortClause = parsetree->sortClause;
|
sortClause = parsetree->sortClause;
|
||||||
uniqueFlag = parsetree->uniqueFlag;
|
distinctClause = parsetree->distinctClause;
|
||||||
into = parsetree->into;
|
into = parsetree->into;
|
||||||
isBinary = parsetree->isBinary;
|
isBinary = parsetree->isBinary;
|
||||||
isPortal = parsetree->isPortal;
|
isPortal = parsetree->isPortal;
|
||||||
@ -2009,7 +2009,7 @@ Except_Intersect_Rewrite(Query *parsetree)
|
|||||||
result->unionClause = lnext(union_list);
|
result->unionClause = lnext(union_list);
|
||||||
/* Attach all the items remembered in the beginning of the function */
|
/* Attach all the items remembered in the beginning of the function */
|
||||||
result->sortClause = sortClause;
|
result->sortClause = sortClause;
|
||||||
result->uniqueFlag = uniqueFlag;
|
result->distinctClause = distinctClause;
|
||||||
result->into = into;
|
result->into = into;
|
||||||
result->isPortal = isPortal;
|
result->isPortal = isPortal;
|
||||||
result->isBinary = isBinary;
|
result->isBinary = isBinary;
|
||||||
|
@ -7,13 +7,14 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "postgres.h"
|
||||||
|
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
|
#include "optimizer/tlist.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
#include "parser/parse_clause.h"
|
#include "parser/parse_clause.h"
|
||||||
#include "rewrite/rewriteManip.h"
|
#include "rewrite/rewriteManip.h"
|
||||||
@ -286,22 +287,10 @@ AddGroupClause(Query *parsetree, List *group_by, List *tlist)
|
|||||||
foreach(l, group_by)
|
foreach(l, group_by)
|
||||||
{
|
{
|
||||||
GroupClause *groupclause = (GroupClause *) copyObject(lfirst(l));
|
GroupClause *groupclause = (GroupClause *) copyObject(lfirst(l));
|
||||||
Index refnumber = groupclause->tleSortGroupRef;
|
TargetEntry *tle = get_sortgroupclause_tle(groupclause, tlist);
|
||||||
TargetEntry *tle = NULL;
|
|
||||||
List *tl;
|
|
||||||
|
|
||||||
/* Find and copy the groupclause's TLE in the old tlist */
|
/* copy the groupclause's TLE from the old tlist */
|
||||||
foreach(tl, tlist)
|
tle = (TargetEntry *) copyObject(tle);
|
||||||
{
|
|
||||||
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");
|
|
||||||
|
|
||||||
/* The ressortgroupref number in the old tlist might be already
|
/* The ressortgroupref number in the old tlist might be already
|
||||||
* taken in the new tlist, so force assignment of a new number.
|
* taken in the new tlist, so force assignment of a new number.
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200001241
|
#define CATALOG_VERSION_NO 200001271
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 ExecEndGroup(Group *node);
|
||||||
extern void ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent);
|
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 */
|
#endif /* NODEGROUP_H */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
typedef struct GroupState
|
||||||
{
|
{
|
||||||
CommonScanState csstate; /* its first field is NodeTag */
|
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_useFirstTuple; /* first tuple not processed yet */
|
||||||
bool grp_done;
|
bool grp_done;
|
||||||
HeapTuple grp_firstTuple;
|
HeapTuple grp_firstTuple;
|
||||||
@ -663,9 +664,9 @@ typedef struct SortState
|
|||||||
* Unique nodes are used "on top of" sort nodes to discard
|
* Unique nodes are used "on top of" sort nodes to discard
|
||||||
* duplicate tuples returned from the sort phase. Basically
|
* duplicate tuples returned from the sort phase. Basically
|
||||||
* all it does is compare the current tuple from the subplan
|
* all it does is compare the current tuple from the subplan
|
||||||
* with the previously fetched tuple stored in OuterTuple and
|
* with the previously fetched tuple stored in priorTuple.
|
||||||
* if the two are identical, then we just fetch another tuple
|
* If the two are identical in all interesting fields, then
|
||||||
* from the sort and try again.
|
* we just fetch another tuple from the sort and try again.
|
||||||
*
|
*
|
||||||
* CommonState information
|
* CommonState information
|
||||||
*
|
*
|
||||||
@ -677,7 +678,12 @@ typedef struct SortState
|
|||||||
* ScanAttributes attribute numbers of interest in this tuple
|
* 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;
|
||||||
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 */
|
Node *qual; /* qualifications applied to tuples */
|
||||||
List *rowMark; /* list of RowMark entries */
|
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 *sortClause; /* a list of SortClause's */
|
||||||
|
|
||||||
List *groupClause; /* a list of GroupClause's */
|
List *groupClause; /* a list of GroupClause's */
|
||||||
@ -733,7 +734,8 @@ typedef struct InsertStmt
|
|||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
char *relname; /* relation to insert into */
|
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 *cols; /* names of the columns */
|
||||||
List *targetList; /* the target list (of ResTarget) */
|
List *targetList; /* the target list (of ResTarget) */
|
||||||
List *fromClause; /* the from clause */
|
List *fromClause; /* the from clause */
|
||||||
@ -777,7 +779,8 @@ typedef struct UpdateStmt
|
|||||||
typedef struct SelectStmt
|
typedef struct SelectStmt
|
||||||
{
|
{
|
||||||
NodeTag type;
|
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) */
|
char *into; /* name of table (for select into table) */
|
||||||
List *targetList; /* the target list (of ResTarget) */
|
List *targetList; /* the target list (of ResTarget) */
|
||||||
List *fromClause; /* the from clause */
|
List *fromClause; /* the from clause */
|
||||||
@ -1135,6 +1138,13 @@ typedef struct RangeTblEntry
|
|||||||
* tleSortGroupRef must match ressortgroupref of exactly one Resdom of the
|
* tleSortGroupRef must match ressortgroupref of exactly one Resdom of the
|
||||||
* associated targetlist; that is the expression to be sorted (or grouped) by.
|
* associated targetlist; that is the expression to be sorted (or grouped) by.
|
||||||
* sortop is the OID of the ordering operator.
|
* 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
|
typedef struct SortClause
|
||||||
{
|
{
|
||||||
@ -1148,7 +1158,7 @@ typedef struct SortClause
|
|||||||
* representation of GROUP BY clauses
|
* representation of GROUP BY clauses
|
||||||
*
|
*
|
||||||
* GroupClause is exactly like SortClause except for the nodetag value
|
* 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.
|
* nodetags...). We have routines that operate interchangeably on both.
|
||||||
*/
|
*/
|
||||||
typedef SortClause GroupClause;
|
typedef SortClause GroupClause;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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;
|
Plan plan;
|
||||||
bool tuplePerGroup; /* what tuples to return (see above) */
|
bool tuplePerGroup; /* what tuples to return (see above) */
|
||||||
int numCols; /* number of group columns */
|
int numCols; /* number of group columns */
|
||||||
AttrNumber *grpColIdx; /* index into the target list */
|
AttrNumber *grpColIdx; /* indexes into the target list */
|
||||||
GroupState *grpstate;
|
GroupState *grpstate;
|
||||||
} Group;
|
} Group;
|
||||||
|
|
||||||
@ -314,10 +314,8 @@ typedef struct Unique
|
|||||||
Plan plan; /* noname node flattened out */
|
Plan plan; /* noname node flattened out */
|
||||||
Oid nonameid;
|
Oid nonameid;
|
||||||
int keycount;
|
int keycount;
|
||||||
char *uniqueAttr; /* NULL if all attrs, or unique attribute
|
int numCols; /* number of columns to check for uniqueness */
|
||||||
* name */
|
AttrNumber *uniqColIdx; /* indexes into the target list */
|
||||||
AttrNumber uniqueAttrNum; /* attribute number of attribute to select
|
|
||||||
* distinct on */
|
|
||||||
UniqueState *uniquestate;
|
UniqueState *uniquestate;
|
||||||
} Unique;
|
} Unique;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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,
|
extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
|
||||||
AttrNumber *grpColIdx, Plan *lefttree);
|
AttrNumber *grpColIdx, Plan *lefttree);
|
||||||
extern Noname *make_noname(List *tlist, List *pathkeys, Plan *subplan);
|
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);
|
extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 List *add_to_flat_tlist(List *tlist, List *vars);
|
||||||
|
|
||||||
extern Var *get_expr(TargetEntry *tle);
|
extern Var *get_expr(TargetEntry *tle);
|
||||||
|
|
||||||
|
extern TargetEntry *get_sortgroupclause_tle(SortClause *sortClause,
|
||||||
|
List *targetList);
|
||||||
extern Node *get_sortgroupclause_expr(SortClause *sortClause,
|
extern Node *get_sortgroupclause_expr(SortClause *sortClause,
|
||||||
List *targetList);
|
List *targetList);
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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,
|
extern List *transformGroupClause(ParseState *pstate, List *grouplist,
|
||||||
List *targetlist);
|
List *targetlist);
|
||||||
extern List *transformSortClause(ParseState *pstate, List *orderlist,
|
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 List *addAllTargetsToSortList(List *sortlist, List *targetlist);
|
||||||
extern Index assignSortGroupRef(TargetEntry *tle, List *tlist);
|
extern Index assignSortGroupRef(TargetEntry *tle, List *tlist);
|
||||||
|
@ -29,12 +29,12 @@ ERROR: attribute 'nonesuch' not found
|
|||||||
-- bad attribute name on rhs of operator
|
-- bad attribute name on rhs of operator
|
||||||
select * from pg_database where pg_database.datname = nonesuch;
|
select * from pg_database where pg_database.datname = nonesuch;
|
||||||
ERROR: attribute 'nonesuch' not found
|
ERROR: attribute 'nonesuch' not found
|
||||||
-- bad select distinct on syntax, distinct attribute missing
|
-- bad select distinct on syntax, distinct attribute missing
|
||||||
select distinct on foobar from pg_database;
|
select distinct on (foobar) from pg_database;
|
||||||
ERROR: parser: parse error at or near "from"
|
ERROR: parser: parse error at or near "from"
|
||||||
-- bad select distinct on syntax, distinct attribute not in target list
|
-- 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;
|
||||||
ERROR: All fields in the UNIQUE ON clause must appear in the target list
|
ERROR: attribute 'foobar' not found
|
||||||
--
|
--
|
||||||
-- DELETE
|
-- DELETE
|
||||||
|
|
||||||
|
@ -1,18 +1,66 @@
|
|||||||
--
|
--
|
||||||
-- SELECT_DISTINCT_ON
|
-- SELECT_DISTINCT_ON
|
||||||
--
|
--
|
||||||
SELECT DISTINCT ON string4 two, string4, ten
|
SELECT DISTINCT ON (string4) string4, two, ten
|
||||||
FROM tmp
|
FROM tmp
|
||||||
ORDER BY two using <, string4 using <, ten using <;
|
ORDER BY string4 using <, two using >, ten using <;
|
||||||
two | string4 | ten
|
string4 | two | ten
|
||||||
-----+---------+-----
|
---------+-----+-----
|
||||||
0 | AAAAxx | 0
|
AAAAxx | 1 | 1
|
||||||
0 | HHHHxx | 0
|
HHHHxx | 1 | 1
|
||||||
0 | OOOOxx | 0
|
OOOOxx | 1 | 1
|
||||||
0 | VVVVxx | 0
|
VVVVxx | 1 | 1
|
||||||
1 | AAAAxx | 1
|
(4 rows)
|
||||||
1 | HHHHxx | 1
|
|
||||||
1 | OOOOxx | 1
|
-- this will fail due to conflict of ordering requirements
|
||||||
1 | VVVVxx | 1
|
SELECT DISTINCT ON (string4, ten) string4, two, ten
|
||||||
(8 rows)
|
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)
|
||||||
|
|
||||||
|
@ -34,12 +34,12 @@ select * from pg_database where nonesuch = pg_database.datname;
|
|||||||
select * from pg_database where pg_database.datname = nonesuch;
|
select * from pg_database where pg_database.datname = nonesuch;
|
||||||
|
|
||||||
|
|
||||||
-- bad select distinct on syntax, distinct attribute missing
|
-- bad select distinct on syntax, distinct attribute missing
|
||||||
select distinct on foobar from pg_database;
|
select distinct on (foobar) from pg_database;
|
||||||
|
|
||||||
|
|
||||||
-- bad select distinct on syntax, distinct attribute not in target list
|
-- 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;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -2,7 +2,15 @@
|
|||||||
-- SELECT_DISTINCT_ON
|
-- SELECT_DISTINCT_ON
|
||||||
--
|
--
|
||||||
|
|
||||||
SELECT DISTINCT ON string4 two, string4, ten
|
SELECT DISTINCT ON (string4) string4, two, ten
|
||||||
FROM tmp
|
FROM tmp
|
||||||
ORDER BY two using <, string4 using <, ten using <;
|
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 <;
|
||||||
|
Loading…
Reference in New Issue
Block a user