|
19 | 19 | #include"access/detoast.h"
|
20 | 20 | #include"access/htup_details.h"
|
21 | 21 | #include"catalog/pg_type.h"
|
| 22 | +#include"common/hashfn.h" |
22 | 23 | #include"funcapi.h"
|
23 | 24 | #include"libpq/pqformat.h"
|
24 | 25 | #include"miscadmin.h"
|
@@ -1766,3 +1767,251 @@ btrecordimagecmp(PG_FUNCTION_ARGS)
|
1766 | 1767 | {
|
1767 | 1768 | PG_RETURN_INT32(record_image_cmp(fcinfo));
|
1768 | 1769 | }
|
| 1770 | + |
| 1771 | + |
| 1772 | +/* |
| 1773 | + * Row type hash functions |
| 1774 | + */ |
| 1775 | + |
| 1776 | +Datum |
| 1777 | +hash_record(PG_FUNCTION_ARGS) |
| 1778 | +{ |
| 1779 | +HeapTupleHeaderrecord=PG_GETARG_HEAPTUPLEHEADER(0); |
| 1780 | +uint32result=0; |
| 1781 | +OidtupType; |
| 1782 | +int32tupTypmod; |
| 1783 | +TupleDesctupdesc; |
| 1784 | +HeapTupleDatatuple; |
| 1785 | +intncolumns; |
| 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 (inti=0;i<ncolumns;i++) |
| 1836 | +{ |
| 1837 | +Form_pg_attributeatt; |
| 1838 | +TypeCacheEntry*typentry; |
| 1839 | +uint32element_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 | +HeapTupleHeaderrecord=PG_GETARG_HEAPTUPLEHEADER(0); |
| 1900 | +uint64seed=PG_GETARG_INT64(1); |
| 1901 | +uint64result=0; |
| 1902 | +OidtupType; |
| 1903 | +int32tupTypmod; |
| 1904 | +TupleDesctupdesc; |
| 1905 | +HeapTupleDatatuple; |
| 1906 | +intncolumns; |
| 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 (inti=0;i<ncolumns;i++) |
| 1957 | +{ |
| 1958 | +Form_pg_attributeatt; |
| 1959 | +TypeCacheEntry*typentry; |
| 1960 | +uint64element_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 | +} |