Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 01e658f

Browse files
committed
Hash support for row types
Add hash functions for the record type as well as a hash operator family and operator class for the record type. This enables all the hash functionality for the record type such as hash-based plans for UNION/INTERSECT/EXCEPT DISTINCT, recursive queries using UNION DISTINCT, hash joins, and hash partitioning. Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://www.postgresql.org/message-id/flat/38eccd35-4e2d-6767-1b3c-dada1eac3124%402ndquadrant.com
1 parent 7888b09 commit 01e658f

19 files changed

+462
-75
lines changed

doc/src/sgml/queries.sgml

-9
Original file line numberDiff line numberDiff line change
@@ -2182,15 +2182,6 @@ SELECT * FROM search_tree <emphasis>ORDER BY path</emphasis>;
21822182
</programlisting>
21832183
</para>
21842184

2185-
<note>
2186-
<para>
2187-
The queries shown in this and the following section involving
2188-
<literal>ROW</literal> constructors in the target list only support
2189-
<literal>UNION ALL</literal> (not plain <literal>UNION</literal>) in the
2190-
current implementation.
2191-
</para>
2192-
</note>
2193-
21942185
<tip>
21952186
<para>
21962187
Omit the <literal>ROW()</literal> syntax in the common case where only one

src/backend/utils/adt/rowtypes.c

+249
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "access/detoast.h"
2020
#include "access/htup_details.h"
2121
#include "catalog/pg_type.h"
22+
#include "common/hashfn.h"
2223
#include "funcapi.h"
2324
#include "libpq/pqformat.h"
2425
#include "miscadmin.h"
@@ -1766,3 +1767,251 @@ btrecordimagecmp(PG_FUNCTION_ARGS)
17661767
{
17671768
PG_RETURN_INT32(record_image_cmp(fcinfo));
17681769
}
1770+
1771+
1772+
/*
1773+
* Row type hash functions
1774+
*/
1775+
1776+
Datum
1777+
hash_record(PG_FUNCTION_ARGS)
1778+
{
1779+
HeapTupleHeader record = PG_GETARG_HEAPTUPLEHEADER(0);
1780+
uint32 result = 0;
1781+
Oid tupType;
1782+
int32 tupTypmod;
1783+
TupleDesc tupdesc;
1784+
HeapTupleData tuple;
1785+
int ncolumns;
1786+
RecordCompareData *my_extra;
1787+
Datum *values;
1788+
bool *nulls;
1789+
1790+
check_stack_depth(); /* recurses for record-type columns */
1791+
1792+
/* Extract type info from tuple */
1793+
tupType = HeapTupleHeaderGetTypeId(record);
1794+
tupTypmod = HeapTupleHeaderGetTypMod(record);
1795+
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
1796+
ncolumns = tupdesc->natts;
1797+
1798+
/* Build temporary HeapTuple control structure */
1799+
tuple.t_len = HeapTupleHeaderGetDatumLength(record);
1800+
ItemPointerSetInvalid(&(tuple.t_self));
1801+
tuple.t_tableOid = InvalidOid;
1802+
tuple.t_data = record;
1803+
1804+
/*
1805+
* We arrange to look up the needed hashing info just once per series
1806+
* of calls, assuming the record type doesn't change underneath us.
1807+
*/
1808+
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1809+
if (my_extra == NULL ||
1810+
my_extra->ncolumns < ncolumns)
1811+
{
1812+
fcinfo->flinfo->fn_extra =
1813+
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1814+
offsetof(RecordCompareData, columns) +
1815+
ncolumns * sizeof(ColumnCompareData));
1816+
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1817+
my_extra->ncolumns = ncolumns;
1818+
my_extra->record1_type = InvalidOid;
1819+
my_extra->record1_typmod = 0;
1820+
}
1821+
1822+
if (my_extra->record1_type != tupType ||
1823+
my_extra->record1_typmod != tupTypmod)
1824+
{
1825+
MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
1826+
my_extra->record1_type = tupType;
1827+
my_extra->record1_typmod = tupTypmod;
1828+
}
1829+
1830+
/* Break down the tuple into fields */
1831+
values = (Datum *) palloc(ncolumns * sizeof(Datum));
1832+
nulls = (bool *) palloc(ncolumns * sizeof(bool));
1833+
heap_deform_tuple(&tuple, tupdesc, values, nulls);
1834+
1835+
for (int i = 0; i < ncolumns; i++)
1836+
{
1837+
Form_pg_attribute att;
1838+
TypeCacheEntry *typentry;
1839+
uint32 element_hash;
1840+
1841+
att = TupleDescAttr(tupdesc, i);
1842+
1843+
if (att->attisdropped)
1844+
continue;
1845+
1846+
/*
1847+
* Lookup the hash function if not done already
1848+
*/
1849+
typentry = my_extra->columns[i].typentry;
1850+
if (typentry == NULL ||
1851+
typentry->type_id != att->atttypid)
1852+
{
1853+
typentry = lookup_type_cache(att->atttypid,
1854+
TYPECACHE_HASH_PROC_FINFO);
1855+
if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
1856+
ereport(ERROR,
1857+
(errcode(ERRCODE_UNDEFINED_FUNCTION),
1858+
errmsg("could not identify a hash function for type %s",
1859+
format_type_be(typentry->type_id))));
1860+
my_extra->columns[i].typentry = typentry;
1861+
}
1862+
1863+
/* Compute hash of element */
1864+
if (nulls[i])
1865+
{
1866+
element_hash = 0;
1867+
}
1868+
else
1869+
{
1870+
LOCAL_FCINFO(locfcinfo, 1);
1871+
1872+
InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
1873+
att->attcollation, NULL, NULL);
1874+
locfcinfo->args[0].value = values[i];
1875+
locfcinfo->args[0].isnull = false;
1876+
element_hash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
1877+
1878+
/* We don't expect hash support functions to return null */
1879+
Assert(!locfcinfo->isnull);
1880+
}
1881+
1882+
/* see hash_array() */
1883+
result = (result << 5) - result + element_hash;
1884+
}
1885+
1886+
pfree(values);
1887+
pfree(nulls);
1888+
ReleaseTupleDesc(tupdesc);
1889+
1890+
/* Avoid leaking memory when handed toasted input. */
1891+
PG_FREE_IF_COPY(record, 0);
1892+
1893+
PG_RETURN_UINT32(result);
1894+
}
1895+
1896+
Datum
1897+
hash_record_extended(PG_FUNCTION_ARGS)
1898+
{
1899+
HeapTupleHeader record = PG_GETARG_HEAPTUPLEHEADER(0);
1900+
uint64 seed = PG_GETARG_INT64(1);
1901+
uint64 result = 0;
1902+
Oid tupType;
1903+
int32 tupTypmod;
1904+
TupleDesc tupdesc;
1905+
HeapTupleData tuple;
1906+
int ncolumns;
1907+
RecordCompareData *my_extra;
1908+
Datum *values;
1909+
bool *nulls;
1910+
1911+
check_stack_depth(); /* recurses for record-type columns */
1912+
1913+
/* Extract type info from tuple */
1914+
tupType = HeapTupleHeaderGetTypeId(record);
1915+
tupTypmod = HeapTupleHeaderGetTypMod(record);
1916+
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
1917+
ncolumns = tupdesc->natts;
1918+
1919+
/* Build temporary HeapTuple control structure */
1920+
tuple.t_len = HeapTupleHeaderGetDatumLength(record);
1921+
ItemPointerSetInvalid(&(tuple.t_self));
1922+
tuple.t_tableOid = InvalidOid;
1923+
tuple.t_data = record;
1924+
1925+
/*
1926+
* We arrange to look up the needed hashing info just once per series
1927+
* of calls, assuming the record type doesn't change underneath us.
1928+
*/
1929+
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1930+
if (my_extra == NULL ||
1931+
my_extra->ncolumns < ncolumns)
1932+
{
1933+
fcinfo->flinfo->fn_extra =
1934+
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1935+
offsetof(RecordCompareData, columns) +
1936+
ncolumns * sizeof(ColumnCompareData));
1937+
my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
1938+
my_extra->ncolumns = ncolumns;
1939+
my_extra->record1_type = InvalidOid;
1940+
my_extra->record1_typmod = 0;
1941+
}
1942+
1943+
if (my_extra->record1_type != tupType ||
1944+
my_extra->record1_typmod != tupTypmod)
1945+
{
1946+
MemSet(my_extra->columns, 0, ncolumns * sizeof(ColumnCompareData));
1947+
my_extra->record1_type = tupType;
1948+
my_extra->record1_typmod = tupTypmod;
1949+
}
1950+
1951+
/* Break down the tuple into fields */
1952+
values = (Datum *) palloc(ncolumns * sizeof(Datum));
1953+
nulls = (bool *) palloc(ncolumns * sizeof(bool));
1954+
heap_deform_tuple(&tuple, tupdesc, values, nulls);
1955+
1956+
for (int i = 0; i < ncolumns; i++)
1957+
{
1958+
Form_pg_attribute att;
1959+
TypeCacheEntry *typentry;
1960+
uint64 element_hash;
1961+
1962+
att = TupleDescAttr(tupdesc, i);
1963+
1964+
if (att->attisdropped)
1965+
continue;
1966+
1967+
/*
1968+
* Lookup the hash function if not done already
1969+
*/
1970+
typentry = my_extra->columns[i].typentry;
1971+
if (typentry == NULL ||
1972+
typentry->type_id != att->atttypid)
1973+
{
1974+
typentry = lookup_type_cache(att->atttypid,
1975+
TYPECACHE_HASH_EXTENDED_PROC_FINFO);
1976+
if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
1977+
ereport(ERROR,
1978+
(errcode(ERRCODE_UNDEFINED_FUNCTION),
1979+
errmsg("could not identify an extended hash function for type %s",
1980+
format_type_be(typentry->type_id))));
1981+
my_extra->columns[i].typentry = typentry;
1982+
}
1983+
1984+
/* Compute hash of element */
1985+
if (nulls[i])
1986+
{
1987+
element_hash = 0;
1988+
}
1989+
else
1990+
{
1991+
LOCAL_FCINFO(locfcinfo, 2);
1992+
1993+
InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
1994+
att->attcollation, NULL, NULL);
1995+
locfcinfo->args[0].value = values[i];
1996+
locfcinfo->args[0].isnull = false;
1997+
locfcinfo->args[1].value = Int64GetDatum(seed);
1998+
locfcinfo->args[0].isnull = false;
1999+
element_hash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
2000+
2001+
/* We don't expect hash support functions to return null */
2002+
Assert(!locfcinfo->isnull);
2003+
}
2004+
2005+
/* see hash_array_extended() */
2006+
result = (result << 5) - result + element_hash;
2007+
}
2008+
2009+
pfree(values);
2010+
pfree(nulls);
2011+
ReleaseTupleDesc(tupdesc);
2012+
2013+
/* Avoid leaking memory when handed toasted input. */
2014+
PG_FREE_IF_COPY(record, 0);
2015+
2016+
PG_RETURN_UINT64(result);
2017+
}

src/backend/utils/cache/lsyscache.c

+6-1
Original file line numberDiff line numberDiff line change
@@ -1358,13 +1358,18 @@ op_hashjoinable(Oid opno, Oid inputtype)
13581358
TypeCacheEntry *typentry;
13591359

13601360
/* As in op_mergejoinable, let the typcache handle the hard cases */
1361-
/* Eventually we'll need a similar case for record_eq ... */
13621361
if (opno == ARRAY_EQ_OP)
13631362
{
13641363
typentry = lookup_type_cache(inputtype, TYPECACHE_HASH_PROC);
13651364
if (typentry->hash_proc == F_HASH_ARRAY)
13661365
result = true;
13671366
}
1367+
else if (opno == RECORD_EQ_OP)
1368+
{
1369+
typentry = lookup_type_cache(inputtype, TYPECACHE_HASH_PROC);
1370+
if (typentry->hash_proc == F_HASH_RECORD)
1371+
result = true;
1372+
}
13681373
else
13691374
{
13701375
/* For all other operators, rely on pg_operator.oprcanhash */

0 commit comments

Comments
 (0)