mirror of
https://github.com/postgres/postgres.git
synced 2025-06-25 01:02:05 +03:00
Fix incorrect slot type in BuildTupleHashTableExt
0f5738202
adjusted the execGrouping.c code so it made use of ExprStates to
generate hash values. That commit made a wrong assumption that the slot
type to pass to ExecBuildHash32FromAttrs() is always &TTSOpsMinimalTuple.
That's not the case as the slot type depends on the slot type passed to
LookupTupleHashEntry(), which for nodeRecursiveunion.c, could be any of
the current slot types.
Here we fix this by adding a new parameter to BuildTupleHashTableExt()
to allow the slot type to be passed in. In the case of nodeSubplan.c
and nodeAgg.c the slot type is always &TTSOpsVirtual, so for both of
those cases, it's beneficial to pass the known slot type as that allows
ExecBuildHash32FromAttrs() to skip adding the tuple deform step to the
resulting ExprState. Another possible fix would have been to have
ExecBuildHash32FromAttrs() set "fetch.kind" to NULL so that
ExecComputeSlotInfo() always determines the EEOP_INNER_FETCHSOME is
required, however, that option isn't favorable as slows down aggregation
and hashed subplan evaluation due to the extra (needless) deform step.
Thanks to Nathan Bossart for bisecting to find the offending commit
based on Paul's report.
Reported-by: Paul Ramsey <pramsey@cleverelephant.ca>
Discussion: https://postgr.es/m/99F064C1-B3EB-4BE7-97D2-D2A0AA487A71@cleverelephant.ca
This commit is contained in:
@ -135,6 +135,7 @@ execTuplesHashPrepare(int numCols,
|
||||
/*
|
||||
* Construct an empty TupleHashTable
|
||||
*
|
||||
* inputOps: slot ops for input hash values, or NULL if unknown or not fixed
|
||||
* numCols, keyColIdx: identify the tuple fields to use as lookup key
|
||||
* eqfunctions: equality comparison functions to use
|
||||
* hashfunctions: datatype-specific hashing functions to use
|
||||
@ -154,6 +155,7 @@ execTuplesHashPrepare(int numCols,
|
||||
TupleHashTable
|
||||
BuildTupleHashTableExt(PlanState *parent,
|
||||
TupleDesc inputDesc,
|
||||
const TupleTableSlotOps *inputOps,
|
||||
int numCols, AttrNumber *keyColIdx,
|
||||
const Oid *eqfuncoids,
|
||||
FmgrInfo *hashfunctions,
|
||||
@ -225,7 +227,7 @@ BuildTupleHashTableExt(PlanState *parent,
|
||||
|
||||
/* build hash ExprState for all columns */
|
||||
hashtable->tab_hash_expr = ExecBuildHash32FromAttrs(inputDesc,
|
||||
&TTSOpsMinimalTuple,
|
||||
inputOps,
|
||||
hashfunctions,
|
||||
collations,
|
||||
numCols,
|
||||
@ -274,6 +276,7 @@ BuildTupleHashTable(PlanState *parent,
|
||||
{
|
||||
return BuildTupleHashTableExt(parent,
|
||||
inputDesc,
|
||||
NULL,
|
||||
numCols, keyColIdx,
|
||||
eqfuncoids,
|
||||
hashfunctions,
|
||||
|
@ -1520,6 +1520,7 @@ build_hash_table(AggState *aggstate, int setno, long nbuckets)
|
||||
|
||||
perhash->hashtable = BuildTupleHashTableExt(&aggstate->ss.ps,
|
||||
perhash->hashslot->tts_tupleDescriptor,
|
||||
perhash->hashslot->tts_ops,
|
||||
perhash->numCols,
|
||||
perhash->hashGrpColIdxHash,
|
||||
perhash->eqfuncoids,
|
||||
|
@ -37,8 +37,10 @@ build_hash_table(RecursiveUnionState *rustate)
|
||||
Assert(node->numCols > 0);
|
||||
Assert(node->numGroups > 0);
|
||||
|
||||
/* XXX is it worth working a bit harder to determine the inputOps here? */
|
||||
rustate->hashtable = BuildTupleHashTableExt(&rustate->ps,
|
||||
desc,
|
||||
NULL,
|
||||
node->numCols,
|
||||
node->dupColIdx,
|
||||
rustate->eqfuncoids,
|
||||
|
@ -128,6 +128,7 @@ build_hash_table(SetOpState *setopstate)
|
||||
|
||||
setopstate->hashtable = BuildTupleHashTableExt(&setopstate->ps,
|
||||
desc,
|
||||
NULL,
|
||||
node->numCols,
|
||||
node->dupColIdx,
|
||||
setopstate->eqfuncoids,
|
||||
|
@ -519,6 +519,11 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
|
||||
*
|
||||
* If it's not necessary to distinguish FALSE and UNKNOWN, then we don't
|
||||
* need to store subplan output rows that contain NULL.
|
||||
*
|
||||
* Because the input slot for each hash table is always the slot resulting
|
||||
* from an ExecProject(), we can use TTSOpsVirtual for the input ops. This
|
||||
* saves a needless fetch inner op step for the hashing ExprState created
|
||||
* in BuildTupleHashTableExt().
|
||||
*/
|
||||
MemoryContextReset(node->hashtablecxt);
|
||||
node->havehashrows = false;
|
||||
@ -533,6 +538,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
|
||||
else
|
||||
node->hashtable = BuildTupleHashTableExt(node->parent,
|
||||
node->descRight,
|
||||
&TTSOpsVirtual,
|
||||
ncols,
|
||||
node->keyColIdx,
|
||||
node->tab_eq_funcoids,
|
||||
@ -561,6 +567,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
|
||||
else
|
||||
node->hashnulls = BuildTupleHashTableExt(node->parent,
|
||||
node->descRight,
|
||||
&TTSOpsVirtual,
|
||||
ncols,
|
||||
node->keyColIdx,
|
||||
node->tab_eq_funcoids,
|
||||
|
@ -140,6 +140,7 @@ extern TupleHashTable BuildTupleHashTable(PlanState *parent,
|
||||
MemoryContext tempcxt, bool use_variable_hash_iv);
|
||||
extern TupleHashTable BuildTupleHashTableExt(PlanState *parent,
|
||||
TupleDesc inputDesc,
|
||||
const TupleTableSlotOps *inputOps,
|
||||
int numCols, AttrNumber *keyColIdx,
|
||||
const Oid *eqfuncoids,
|
||||
FmgrInfo *hashfunctions,
|
||||
|
@ -329,6 +329,26 @@ SELECT * FROM subdepartment ORDER BY name;
|
||||
1 | 0 | A
|
||||
(1 row)
|
||||
|
||||
-- exercise the deduplication code of a UNION with mixed input slot types
|
||||
WITH RECURSIVE subdepartment AS
|
||||
(
|
||||
-- select all columns to prevent projection
|
||||
SELECT id, parent_department, name FROM department WHERE name = 'A'
|
||||
UNION
|
||||
-- joins do projection
|
||||
SELECT d.id, d.parent_department, d.name FROM department AS d
|
||||
INNER JOIN subdepartment AS sd ON d.parent_department = sd.id
|
||||
)
|
||||
SELECT * FROM subdepartment ORDER BY name;
|
||||
id | parent_department | name
|
||||
----+-------------------+------
|
||||
1 | 0 | A
|
||||
2 | 1 | B
|
||||
3 | 2 | C
|
||||
4 | 2 | D
|
||||
6 | 4 | F
|
||||
(5 rows)
|
||||
|
||||
-- inside subqueries
|
||||
SELECT count(*) FROM (
|
||||
WITH RECURSIVE t(n) AS (
|
||||
|
@ -216,6 +216,20 @@ WITH RECURSIVE subdepartment AS
|
||||
)
|
||||
SELECT * FROM subdepartment ORDER BY name;
|
||||
|
||||
-- exercise the deduplication code of a UNION with mixed input slot types
|
||||
WITH RECURSIVE subdepartment AS
|
||||
(
|
||||
-- select all columns to prevent projection
|
||||
SELECT id, parent_department, name FROM department WHERE name = 'A'
|
||||
|
||||
UNION
|
||||
|
||||
-- joins do projection
|
||||
SELECT d.id, d.parent_department, d.name FROM department AS d
|
||||
INNER JOIN subdepartment AS sd ON d.parent_department = sd.id
|
||||
)
|
||||
SELECT * FROM subdepartment ORDER BY name;
|
||||
|
||||
-- inside subqueries
|
||||
SELECT count(*) FROM (
|
||||
WITH RECURSIVE t(n) AS (
|
||||
|
Reference in New Issue
Block a user