Add hash support functions and hash opclass for contrib/ltree.
This also enables hash join and hash aggregation on ltree columns. Tommy Pavlicek, reviewed by jian he Discussion: https://postgr.es/m/CAEhP-W9ZEoHeaP_nKnPCVd_o1c3BAUvq1gWHrq8EbkNRiS9CvQ@mail.gmail.com
This commit is contained in:
parent
0997e0af27
commit
485f0aa859
|
@ -14,7 +14,8 @@ OBJS = \
|
||||||
ltxtquery_op.o
|
ltxtquery_op.o
|
||||||
|
|
||||||
EXTENSION = ltree
|
EXTENSION = ltree
|
||||||
DATA = ltree--1.1--1.2.sql ltree--1.1.sql ltree--1.0--1.1.sql
|
DATA = ltree--1.2--1.3.sql ltree--1.1--1.2.sql ltree--1.1.sql \
|
||||||
|
ltree--1.0--1.1.sql
|
||||||
PGFILEDESC = "ltree - hierarchical label data type"
|
PGFILEDESC = "ltree - hierarchical label data type"
|
||||||
|
|
||||||
HEADERS = ltree.h
|
HEADERS = ltree.h
|
||||||
|
|
|
@ -1433,8 +1433,27 @@ SELECT '{j.k.l.m, g.b.c.d.e}'::ltree[] ?~ 'A*@|g.b.c.d.e';
|
||||||
g.b.c.d.e
|
g.b.c.d.e
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- Check that the hash_ltree() and hash_ltree_extended() function's lower
|
||||||
|
-- 32 bits match when the seed is 0 and do not match when the seed != 0
|
||||||
|
SELECT v as value, hash_ltree(v)::bit(32) as standard,
|
||||||
|
hash_ltree_extended(v, 0)::bit(32) as extended0,
|
||||||
|
hash_ltree_extended(v, 1)::bit(32) as extended1
|
||||||
|
FROM (VALUES (NULL::ltree), (''::ltree), ('0'::ltree), ('0.1'::ltree),
|
||||||
|
('0.1.2'::ltree), ('0'::ltree), ('0_asd.1_ASD'::ltree)) x(v)
|
||||||
|
WHERE hash_ltree(v)::bit(32) != hash_ltree_extended(v, 0)::bit(32)
|
||||||
|
OR hash_ltree(v)::bit(32) = hash_ltree_extended(v, 1)::bit(32);
|
||||||
|
value | standard | extended0 | extended1
|
||||||
|
-------+----------+-----------+-----------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
CREATE TABLE ltreetest (t ltree);
|
CREATE TABLE ltreetest (t ltree);
|
||||||
\copy ltreetest FROM 'data/ltree.data'
|
\copy ltreetest FROM 'data/ltree.data'
|
||||||
|
SELECT count(*) from ltreetest;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
1006
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT * FROM ltreetest WHERE t < '12.3' order by t asc;
|
SELECT * FROM ltreetest WHERE t < '12.3' order by t asc;
|
||||||
t
|
t
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
@ -7833,6 +7852,55 @@ SELECT * FROM ltreetest WHERE t ? '{23.*.1,23.*.2}' order by t asc;
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
drop index tstidx;
|
drop index tstidx;
|
||||||
|
--- test hash index
|
||||||
|
create index tstidx on ltreetest using hash (t);
|
||||||
|
set enable_seqscan=off;
|
||||||
|
set enable_bitmapscan=off;
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT * FROM ltreetest WHERE t = '12.3' order by t asc;
|
||||||
|
QUERY PLAN
|
||||||
|
--------------------------------------
|
||||||
|
Index Scan using tstidx on ltreetest
|
||||||
|
Index Cond: (t = '12.3'::ltree)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM ltreetest WHERE t = '12.3' order by t asc;
|
||||||
|
t
|
||||||
|
------
|
||||||
|
12.3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
reset enable_seqscan;
|
||||||
|
reset enable_bitmapscan;
|
||||||
|
-- test hash aggregate
|
||||||
|
set enable_hashagg=on;
|
||||||
|
set enable_sort=off;
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM (
|
||||||
|
SELECT t FROM (SELECT * FROM ltreetest UNION ALL SELECT * FROM ltreetest) t1 GROUP BY t
|
||||||
|
) t2;
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> HashAggregate
|
||||||
|
Group Key: ltreetest.t
|
||||||
|
-> Append
|
||||||
|
-> Seq Scan on ltreetest
|
||||||
|
-> Seq Scan on ltreetest ltreetest_1
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
SELECT count(*) FROM (
|
||||||
|
SELECT t FROM (SELECT * FROM ltreetest UNION ALL SELECT * FROM ltreetest) t1 GROUP BY t
|
||||||
|
) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
1006
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
reset enable_hashagg;
|
||||||
|
reset enable_sort;
|
||||||
|
drop index tstidx;
|
||||||
|
-- test gist index
|
||||||
create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=0));
|
create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=0));
|
||||||
ERROR: value 0 out of bounds for option "siglen"
|
ERROR: value 0 out of bounds for option "siglen"
|
||||||
DETAIL: Valid values are between "4" and "2024".
|
DETAIL: Valid values are between "4" and "2024".
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/* contrib/ltree/ltree--1.2--1.3.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION ltree UPDATE TO '1.3'" to load this file. \quit
|
||||||
|
|
||||||
|
CREATE FUNCTION hash_ltree(ltree)
|
||||||
|
RETURNS integer
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION hash_ltree_extended(ltree, bigint)
|
||||||
|
RETURNS bigint
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS hash_ltree_ops
|
||||||
|
DEFAULT FOR TYPE ltree USING hash
|
||||||
|
AS
|
||||||
|
OPERATOR 1 = ,
|
||||||
|
FUNCTION 1 hash_ltree(ltree),
|
||||||
|
FUNCTION 2 hash_ltree_extended(ltree, bigint);
|
||||||
|
|
||||||
|
ALTER OPERATOR =(ltree, ltree) SET (HASHES);
|
|
@ -1,6 +1,6 @@
|
||||||
# ltree extension
|
# ltree extension
|
||||||
comment = 'data type for hierarchical tree-like structures'
|
comment = 'data type for hierarchical tree-like structures'
|
||||||
default_version = '1.2'
|
default_version = '1.3'
|
||||||
module_pathname = '$libdir/ltree'
|
module_pathname = '$libdir/ltree'
|
||||||
relocatable = true
|
relocatable = true
|
||||||
trusted = true
|
trusted = true
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "access/htup_details.h"
|
#include "access/htup_details.h"
|
||||||
#include "catalog/pg_statistic.h"
|
#include "catalog/pg_statistic.h"
|
||||||
|
#include "common/hashfn.h"
|
||||||
#include "ltree.h"
|
#include "ltree.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
@ -24,6 +25,8 @@ PG_FUNCTION_INFO_V1(ltree_eq);
|
||||||
PG_FUNCTION_INFO_V1(ltree_ne);
|
PG_FUNCTION_INFO_V1(ltree_ne);
|
||||||
PG_FUNCTION_INFO_V1(ltree_ge);
|
PG_FUNCTION_INFO_V1(ltree_ge);
|
||||||
PG_FUNCTION_INFO_V1(ltree_gt);
|
PG_FUNCTION_INFO_V1(ltree_gt);
|
||||||
|
PG_FUNCTION_INFO_V1(hash_ltree);
|
||||||
|
PG_FUNCTION_INFO_V1(hash_ltree_extended);
|
||||||
PG_FUNCTION_INFO_V1(nlevel);
|
PG_FUNCTION_INFO_V1(nlevel);
|
||||||
PG_FUNCTION_INFO_V1(ltree_isparent);
|
PG_FUNCTION_INFO_V1(ltree_isparent);
|
||||||
PG_FUNCTION_INFO_V1(ltree_risparent);
|
PG_FUNCTION_INFO_V1(ltree_risparent);
|
||||||
|
@ -129,6 +132,72 @@ ltree_ne(PG_FUNCTION_ARGS)
|
||||||
PG_RETURN_BOOL(res != 0);
|
PG_RETURN_BOOL(res != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Compute a hash for the ltree */
|
||||||
|
Datum
|
||||||
|
hash_ltree(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ltree *a = PG_GETARG_LTREE_P(0);
|
||||||
|
uint32 result = 1;
|
||||||
|
int an = a->numlevel;
|
||||||
|
ltree_level *al = LTREE_FIRST(a);
|
||||||
|
|
||||||
|
while (an > 0)
|
||||||
|
{
|
||||||
|
uint32 levelHash = DatumGetUInt32(hash_any((unsigned char *) al->name, al->len));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Combine hash values of successive elements by multiplying the
|
||||||
|
* current value by 31 and adding on the new element's hash value.
|
||||||
|
*
|
||||||
|
* This method is borrowed from hash_array(), which see for further
|
||||||
|
* commentary.
|
||||||
|
*/
|
||||||
|
result = (result << 5) - result + levelHash;
|
||||||
|
|
||||||
|
an--;
|
||||||
|
al = LEVEL_NEXT(al);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FREE_IF_COPY(a, 0);
|
||||||
|
PG_RETURN_UINT32(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute an extended hash for the ltree */
|
||||||
|
Datum
|
||||||
|
hash_ltree_extended(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ltree *a = PG_GETARG_LTREE_P(0);
|
||||||
|
const uint64 seed = PG_GETARG_INT64(1);
|
||||||
|
uint64 result = 1;
|
||||||
|
int an = a->numlevel;
|
||||||
|
ltree_level *al = LTREE_FIRST(a);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the path has length zero, return 1 + seed to ensure that the low 32
|
||||||
|
* bits of the result match hash_ltree when the seed is 0, as required by
|
||||||
|
* the hash index support functions, but to also return a different value
|
||||||
|
* when there is a seed.
|
||||||
|
*/
|
||||||
|
if (an == 0)
|
||||||
|
{
|
||||||
|
PG_FREE_IF_COPY(a, 0);
|
||||||
|
PG_RETURN_UINT64(result + seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (an > 0)
|
||||||
|
{
|
||||||
|
uint64 levelHash = DatumGetUInt64(hash_any_extended((unsigned char *) al->name, al->len, seed));
|
||||||
|
|
||||||
|
result = (result << 5) - result + levelHash;
|
||||||
|
|
||||||
|
an--;
|
||||||
|
al = LEVEL_NEXT(al);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FREE_IF_COPY(a, 0);
|
||||||
|
PG_RETURN_UINT64(result);
|
||||||
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
nlevel(PG_FUNCTION_ARGS)
|
nlevel(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,3 +19,4 @@ INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Galaxies');
|
||||||
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Astronauts');
|
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Astronauts');
|
||||||
CREATE INDEX path_gist_idx ON test USING gist(path);
|
CREATE INDEX path_gist_idx ON test USING gist(path);
|
||||||
CREATE INDEX path_idx ON test USING btree(path);
|
CREATE INDEX path_idx ON test USING btree(path);
|
||||||
|
CREATE INDEX path_hash_idx ON test USING hash(path);
|
||||||
|
|
|
@ -30,8 +30,9 @@ contrib_targets += ltree
|
||||||
install_data(
|
install_data(
|
||||||
'ltree.control',
|
'ltree.control',
|
||||||
'ltree--1.0--1.1.sql',
|
'ltree--1.0--1.1.sql',
|
||||||
'ltree--1.1--1.2.sql',
|
|
||||||
'ltree--1.1.sql',
|
'ltree--1.1.sql',
|
||||||
|
'ltree--1.1--1.2.sql',
|
||||||
|
'ltree--1.2--1.3.sql',
|
||||||
kwargs: contrib_data_args,
|
kwargs: contrib_data_args,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -282,9 +282,21 @@ SELECT ('{3456,1.2.3.4}'::ltree[] ?<@ '1.2.5') is null;
|
||||||
SELECT '{ltree.asd, tree.awdfg}'::ltree[] ?@ 'tree & aWdfg@'::ltxtquery;
|
SELECT '{ltree.asd, tree.awdfg}'::ltree[] ?@ 'tree & aWdfg@'::ltxtquery;
|
||||||
SELECT '{j.k.l.m, g.b.c.d.e}'::ltree[] ?~ 'A*@|g.b.c.d.e';
|
SELECT '{j.k.l.m, g.b.c.d.e}'::ltree[] ?~ 'A*@|g.b.c.d.e';
|
||||||
|
|
||||||
|
-- Check that the hash_ltree() and hash_ltree_extended() function's lower
|
||||||
|
-- 32 bits match when the seed is 0 and do not match when the seed != 0
|
||||||
|
SELECT v as value, hash_ltree(v)::bit(32) as standard,
|
||||||
|
hash_ltree_extended(v, 0)::bit(32) as extended0,
|
||||||
|
hash_ltree_extended(v, 1)::bit(32) as extended1
|
||||||
|
FROM (VALUES (NULL::ltree), (''::ltree), ('0'::ltree), ('0.1'::ltree),
|
||||||
|
('0.1.2'::ltree), ('0'::ltree), ('0_asd.1_ASD'::ltree)) x(v)
|
||||||
|
WHERE hash_ltree(v)::bit(32) != hash_ltree_extended(v, 0)::bit(32)
|
||||||
|
OR hash_ltree(v)::bit(32) = hash_ltree_extended(v, 1)::bit(32);
|
||||||
|
|
||||||
CREATE TABLE ltreetest (t ltree);
|
CREATE TABLE ltreetest (t ltree);
|
||||||
\copy ltreetest FROM 'data/ltree.data'
|
\copy ltreetest FROM 'data/ltree.data'
|
||||||
|
|
||||||
|
SELECT count(*) from ltreetest;
|
||||||
|
|
||||||
SELECT * FROM ltreetest WHERE t < '12.3' order by t asc;
|
SELECT * FROM ltreetest WHERE t < '12.3' order by t asc;
|
||||||
SELECT * FROM ltreetest WHERE t <= '12.3' order by t asc;
|
SELECT * FROM ltreetest WHERE t <= '12.3' order by t asc;
|
||||||
SELECT * FROM ltreetest WHERE t = '12.3' order by t asc;
|
SELECT * FROM ltreetest WHERE t = '12.3' order by t asc;
|
||||||
|
@ -329,6 +341,41 @@ SELECT * FROM ltreetest WHERE t ~ '23.*.2' order by t asc;
|
||||||
SELECT * FROM ltreetest WHERE t ? '{23.*.1,23.*.2}' order by t asc;
|
SELECT * FROM ltreetest WHERE t ? '{23.*.1,23.*.2}' order by t asc;
|
||||||
|
|
||||||
drop index tstidx;
|
drop index tstidx;
|
||||||
|
|
||||||
|
--- test hash index
|
||||||
|
|
||||||
|
create index tstidx on ltreetest using hash (t);
|
||||||
|
set enable_seqscan=off;
|
||||||
|
set enable_bitmapscan=off;
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT * FROM ltreetest WHERE t = '12.3' order by t asc;
|
||||||
|
SELECT * FROM ltreetest WHERE t = '12.3' order by t asc;
|
||||||
|
|
||||||
|
reset enable_seqscan;
|
||||||
|
reset enable_bitmapscan;
|
||||||
|
|
||||||
|
-- test hash aggregate
|
||||||
|
|
||||||
|
set enable_hashagg=on;
|
||||||
|
set enable_sort=off;
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM (
|
||||||
|
SELECT t FROM (SELECT * FROM ltreetest UNION ALL SELECT * FROM ltreetest) t1 GROUP BY t
|
||||||
|
) t2;
|
||||||
|
|
||||||
|
SELECT count(*) FROM (
|
||||||
|
SELECT t FROM (SELECT * FROM ltreetest UNION ALL SELECT * FROM ltreetest) t1 GROUP BY t
|
||||||
|
) t2;
|
||||||
|
|
||||||
|
reset enable_hashagg;
|
||||||
|
reset enable_sort;
|
||||||
|
|
||||||
|
drop index tstidx;
|
||||||
|
|
||||||
|
-- test gist index
|
||||||
|
|
||||||
create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=0));
|
create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=0));
|
||||||
create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2025));
|
create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2025));
|
||||||
create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2028));
|
create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2028));
|
||||||
|
|
|
@ -623,6 +623,13 @@ Europe & Russia*@ & !Transportation
|
||||||
<literal>>=</literal>, <literal>></literal>
|
<literal>>=</literal>, <literal>></literal>
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Hash index over <type>ltree</type>:
|
||||||
|
<literal>=</literal>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
GiST index over <type>ltree</type> (<literal>gist_ltree_ops</literal>
|
GiST index over <type>ltree</type> (<literal>gist_ltree_ops</literal>
|
||||||
|
@ -712,6 +719,7 @@ INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Galaxies');
|
||||||
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Astronauts');
|
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Astronauts');
|
||||||
CREATE INDEX path_gist_idx ON test USING GIST (path);
|
CREATE INDEX path_gist_idx ON test USING GIST (path);
|
||||||
CREATE INDEX path_idx ON test USING BTREE (path);
|
CREATE INDEX path_idx ON test USING BTREE (path);
|
||||||
|
CREATE INDEX path_hash_idx ON test USING HASH (path);
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
|
Loading…
Reference in New Issue