Thanks to visit codestin.com
Credit goes to github.com

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 <[email protected]> Discussion: https://www.postgresql.org/message-id/flat/38eccd35-4e2d-6767-1b3c-dada1eac3124%402ndquadrant.com
1 parent 7888b09 commit 01e658f

File tree

19 files changed

+462
-75
lines changed

19 files changed

+462
-75
lines changed

doc/src/sgml/queries.sgml

Lines changed: 0 additions & 9 deletions
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

Lines changed: 249 additions & 0 deletions
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

Lines changed: 6 additions & 1 deletion
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)