Skip to content

Commit 3bef830

Browse files
rafsun42Zainab-Saad
authored andcommitted
Extend EXPLAIN and add config param to switch transformation of property filter (apache#1262)
* Add explain options Explain command in the following format is supported now: `EXPLAIN (VERBOSE, COSTS OFF, FORMAT XML) ...` Note that, this is basically Postgres' EXPLAIN command, and the purpose of this is to support debugging and regression tests. * Add config param to switch transformation method of property filter When the `age.enable_containment` parameter is on, the agtype containment operator is used to transform property filter. When off, access operator is used instead. The former case is preferable for GIN index, and the later for BTREE expression index. The idea of replacing containment with access operator in order to support BTREE index is taken from a patch by Josh Innis. A note on regression testing- although there are test cases for the `age.enable_containment` parameter, sometimes it may be useful to set this parameter before running any tests (not just the ones related to it). For example, when the logic related to property transformation changes. It can be set in the `age_regression.conf` file.
1 parent 30d9399 commit 3bef830

File tree

9 files changed

+572
-8
lines changed

9 files changed

+572
-8
lines changed

Diff for: Makefile

+3-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ OBJS = src/backend/age.o \
6969
src/backend/utils/load/ag_load_edges.o \
7070
src/backend/utils/load/age_load.o \
7171
src/backend/utils/load/libcsv.o \
72-
src/backend/utils/name_validation.o
72+
src/backend/utils/name_validation.o \
73+
src/backend/utils/ag_guc.o
7374

7475
EXTENSION = age
7576

@@ -105,7 +106,7 @@ REGRESS = scan \
105106
srcdir=`pwd`
106107

107108
ag_regress_dir = $(srcdir)/regress
108-
REGRESS_OPTS = --load-extension=age --inputdir=$(ag_regress_dir) --outputdir=$(ag_regress_dir) --temp-instance=$(ag_regress_dir)/instance --port=61958 --encoding=UTF-8
109+
REGRESS_OPTS = --load-extension=age --inputdir=$(ag_regress_dir) --outputdir=$(ag_regress_dir) --temp-instance=$(ag_regress_dir)/instance --port=61958 --encoding=UTF-8 --temp-config $(ag_regress_dir)/age_regression.conf
109110

110111
ag_regress_out = instance/ log/ results/ regression.*
111112
EXTRA_CLEAN = $(addprefix $(ag_regress_dir)/, $(ag_regress_out)) src/backend/parser/cypher_gram.c src/include/parser/cypher_gram_def.h src/include/parser/cypher_kwlist_d.h

Diff for: regress/age_regression.conf

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#age.enable_containment = on

Diff for: regress/expected/cypher_match.out

+175
Original file line numberDiff line numberDiff line change
@@ -2419,6 +2419,170 @@ SELECT * FROM cypher('cypher_match', $$ MATCH p=(x:r)-[]->(x:R) RETURN p, x $$)
24192419
ERROR: multiple labels for variable 'x' are not supported
24202420
LINE 1: ...FROM cypher('cypher_match', $$ MATCH p=(x:r)-[]->(x:R) RETUR...
24212421
^
2422+
--
2423+
-- Test age.enable_containment configuration parameter
2424+
--
2425+
-- Test queries are run before and after switching off this parameter.
2426+
-- When on, the containment operator should be used to filter properties.
2427+
-- When off, the access operator should be used.
2428+
--
2429+
SELECT create_graph('test_enable_containment');
2430+
NOTICE: graph "test_enable_containment" has been created
2431+
create_graph
2432+
--------------
2433+
2434+
(1 row)
2435+
2436+
SELECT * FROM cypher('test_enable_containment',
2437+
$$
2438+
CREATE (x:Customer {
2439+
name: 'Bob',
2440+
school: {
2441+
name: 'XYZ College',
2442+
program: {
2443+
major: 'Psyc',
2444+
degree: 'BSc'
2445+
}
2446+
},
2447+
phone: [ 123456789, 987654321, 456987123 ],
2448+
addr: [
2449+
{city: 'Vancouver', street: 30},
2450+
{city: 'Toronto', street: 40}
2451+
]
2452+
})
2453+
RETURN x
2454+
$$) as (a agtype);
2455+
a
2456+
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2457+
{"id": 844424930131969, "label": "Customer", "properties": {"addr": [{"city": "Vancouver", "street": 30}, {"city": "Toronto", "street": 40}], "name": "Bob", "phone": [123456789, 987654321, 456987123], "school": {"name": "XYZ College", "program": {"major": "Psyc", "degree": "BSc"}}}}::vertex
2458+
(1 row)
2459+
2460+
-- With enable_containment on
2461+
SET age.enable_containment = on;
2462+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Toronto'}]}) RETURN x $$) as (a agtype);
2463+
count
2464+
-------
2465+
1
2466+
(1 row)
2467+
2468+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Toronto'}, {city: 'Vancouver'}]}) RETURN x $$) as (a agtype);
2469+
count
2470+
-------
2471+
1
2472+
(1 row)
2473+
2474+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Alberta'}]}) RETURN x $$) as (a agtype);
2475+
count
2476+
-------
2477+
0
2478+
(1 row)
2479+
2480+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {school:{program:{major:'Psyc'}}}) RETURN x $$) as (a agtype);
2481+
count
2482+
-------
2483+
1
2484+
(1 row)
2485+
2486+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {name:'Bob',school:{program:{degree:'BSc'}}}) RETURN x $$) as (a agtype);
2487+
count
2488+
-------
2489+
1
2490+
(1 row)
2491+
2492+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {school:{program:{major:'Cs'}}}) RETURN x $$) as (a agtype);
2493+
count
2494+
-------
2495+
0
2496+
(1 row)
2497+
2498+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {name:'Bob',school:{program:{degree:'PHd'}}}) RETURN x $$) as (a agtype);
2499+
count
2500+
-------
2501+
0
2502+
(1 row)
2503+
2504+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {phone:[987654321]}) RETURN x $$) as (a agtype);
2505+
count
2506+
-------
2507+
1
2508+
(1 row)
2509+
2510+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {phone:[654765876]}) RETURN x $$) as (a agtype);
2511+
count
2512+
-------
2513+
0
2514+
(1 row)
2515+
2516+
SELECT * FROM cypher('test_enable_containment', $$ EXPLAIN (COSTS OFF) MATCH (x:Customer {school:{name:'XYZ',program:{degree:'BSc'}},phone:[987654321],parents:{}}) RETURN x $$) as (a agtype);
2517+
QUERY PLAN
2518+
------------------------------------------------------------------------------------------------------------------------------------
2519+
Seq Scan on "Customer" x
2520+
Filter: (properties @> '{"phone": [987654321], "school": {"name": "XYZ", "program": {"degree": "BSc"}}, "parents": {}}'::agtype)
2521+
(2 rows)
2522+
2523+
-- Previous set of queries, with enable_containment off
2524+
SET age.enable_containment = off;
2525+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Toronto'}]}) RETURN x $$) as (a agtype);
2526+
count
2527+
-------
2528+
1
2529+
(1 row)
2530+
2531+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Toronto'}, {city: 'Vancouver'}]}) RETURN x $$) as (a agtype);
2532+
count
2533+
-------
2534+
1
2535+
(1 row)
2536+
2537+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Alberta'}]}) RETURN x $$) as (a agtype);
2538+
count
2539+
-------
2540+
0
2541+
(1 row)
2542+
2543+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {school:{program:{major:'Psyc'}}}) RETURN x $$) as (a agtype);
2544+
count
2545+
-------
2546+
1
2547+
(1 row)
2548+
2549+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {name:'Bob',school:{program:{degree:'BSc'}}}) RETURN x $$) as (a agtype);
2550+
count
2551+
-------
2552+
1
2553+
(1 row)
2554+
2555+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {school:{program:{major:'Cs'}}}) RETURN x $$) as (a agtype);
2556+
count
2557+
-------
2558+
0
2559+
(1 row)
2560+
2561+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {name:'Bob',school:{program:{degree:'PHd'}}}) RETURN x $$) as (a agtype);
2562+
count
2563+
-------
2564+
0
2565+
(1 row)
2566+
2567+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {phone:[987654321]}) RETURN x $$) as (a agtype);
2568+
count
2569+
-------
2570+
1
2571+
(1 row)
2572+
2573+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {phone:[654765876]}) RETURN x $$) as (a agtype);
2574+
count
2575+
-------
2576+
0
2577+
(1 row)
2578+
2579+
SELECT * FROM cypher('test_enable_containment', $$ EXPLAIN (COSTS OFF) MATCH (x:Customer {school:{name:'XYZ',program:{degree:'BSc'}},phone:[987654321],parents:{}}) RETURN x $$) as (a agtype);
2580+
QUERY PLAN
2581+
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2582+
Seq Scan on "Customer" x
2583+
Filter: ((agtype_access_operator(VARIADIC ARRAY[properties, '"school"'::agtype, '"name"'::agtype]) = '"XYZ"'::agtype) AND (agtype_access_operator(VARIADIC ARRAY[properties, '"school"'::agtype, '"program"'::agtype, '"degree"'::agtype]) = '"BSc"'::agtype) AND (agtype_access_operator(VARIADIC ARRAY[properties, '"phone"'::agtype]) @> '[987654321]'::agtype) AND (agtype_access_operator(VARIADIC ARRAY[properties, '"parents"'::agtype]) @> '{}'::agtype))
2584+
(2 rows)
2585+
24222586
--
24232587
-- Clean up
24242588
--
@@ -2460,6 +2624,17 @@ NOTICE: graph "test_retrieve_var" has been dropped
24602624

24612625
(1 row)
24622626

2627+
SELECT drop_graph('test_enable_containment', true);
2628+
NOTICE: drop cascades to 3 other objects
2629+
DETAIL: drop cascades to table test_enable_containment._ag_label_vertex
2630+
drop cascades to table test_enable_containment._ag_label_edge
2631+
drop cascades to table test_enable_containment."Customer"
2632+
NOTICE: graph "test_enable_containment" has been dropped
2633+
drop_graph
2634+
------------
2635+
2636+
(1 row)
2637+
24632638
--
24642639
-- End
24652640
--

Diff for: regress/sql/cypher_match.sql

+56
Original file line numberDiff line numberDiff line change
@@ -1077,11 +1077,67 @@ SELECT * FROM cypher('cypher_match', $$ CREATE () WITH * MATCH (x{n0:x.n1}) RETU
10771077
SELECT * FROM cypher('cypher_match', $$ MATCH p=(x)-[]->(x:R) RETURN p, x $$) AS (p agtype, x agtype);
10781078
SELECT * FROM cypher('cypher_match', $$ MATCH p=(x:r)-[]->(x:R) RETURN p, x $$) AS (p agtype, x agtype);
10791079

1080+
--
1081+
-- Test age.enable_containment configuration parameter
1082+
--
1083+
-- Test queries are run before and after switching off this parameter.
1084+
-- When on, the containment operator should be used to filter properties.
1085+
-- When off, the access operator should be used.
1086+
--
1087+
1088+
SELECT create_graph('test_enable_containment');
1089+
SELECT * FROM cypher('test_enable_containment',
1090+
$$
1091+
CREATE (x:Customer {
1092+
name: 'Bob',
1093+
school: {
1094+
name: 'XYZ College',
1095+
program: {
1096+
major: 'Psyc',
1097+
degree: 'BSc'
1098+
}
1099+
},
1100+
phone: [ 123456789, 987654321, 456987123 ],
1101+
addr: [
1102+
{city: 'Vancouver', street: 30},
1103+
{city: 'Toronto', street: 40}
1104+
]
1105+
})
1106+
RETURN x
1107+
$$) as (a agtype);
1108+
1109+
-- With enable_containment on
1110+
SET age.enable_containment = on;
1111+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Toronto'}]}) RETURN x $$) as (a agtype);
1112+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Toronto'}, {city: 'Vancouver'}]}) RETURN x $$) as (a agtype);
1113+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Alberta'}]}) RETURN x $$) as (a agtype);
1114+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {school:{program:{major:'Psyc'}}}) RETURN x $$) as (a agtype);
1115+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {name:'Bob',school:{program:{degree:'BSc'}}}) RETURN x $$) as (a agtype);
1116+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {school:{program:{major:'Cs'}}}) RETURN x $$) as (a agtype);
1117+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {name:'Bob',school:{program:{degree:'PHd'}}}) RETURN x $$) as (a agtype);
1118+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {phone:[987654321]}) RETURN x $$) as (a agtype);
1119+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {phone:[654765876]}) RETURN x $$) as (a agtype);
1120+
SELECT * FROM cypher('test_enable_containment', $$ EXPLAIN (COSTS OFF) MATCH (x:Customer {school:{name:'XYZ',program:{degree:'BSc'}},phone:[987654321],parents:{}}) RETURN x $$) as (a agtype);
1121+
1122+
-- Previous set of queries, with enable_containment off
1123+
SET age.enable_containment = off;
1124+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Toronto'}]}) RETURN x $$) as (a agtype);
1125+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Toronto'}, {city: 'Vancouver'}]}) RETURN x $$) as (a agtype);
1126+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {addr:[{city:'Alberta'}]}) RETURN x $$) as (a agtype);
1127+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {school:{program:{major:'Psyc'}}}) RETURN x $$) as (a agtype);
1128+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {name:'Bob',school:{program:{degree:'BSc'}}}) RETURN x $$) as (a agtype);
1129+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {school:{program:{major:'Cs'}}}) RETURN x $$) as (a agtype);
1130+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {name:'Bob',school:{program:{degree:'PHd'}}}) RETURN x $$) as (a agtype);
1131+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {phone:[987654321]}) RETURN x $$) as (a agtype);
1132+
SELECT count(*) FROM cypher('test_enable_containment', $$ MATCH (x:Customer {phone:[654765876]}) RETURN x $$) as (a agtype);
1133+
SELECT * FROM cypher('test_enable_containment', $$ EXPLAIN (COSTS OFF) MATCH (x:Customer {school:{name:'XYZ',program:{degree:'BSc'}},phone:[987654321],parents:{}}) RETURN x $$) as (a agtype);
1134+
10801135
--
10811136
-- Clean up
10821137
--
10831138
SELECT drop_graph('cypher_match', true);
10841139
SELECT drop_graph('test_retrieve_var', true);
1140+
SELECT drop_graph('test_enable_containment', true);
10851141

10861142
--
10871143
-- End

Diff for: src/backend/age.c

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "nodes/ag_nodes.h"
2626
#include "optimizer/cypher_paths.h"
2727
#include "parser/cypher_analyze.h"
28+
#include "utils/ag_guc.h"
2829

2930
PG_MODULE_MAGIC;
3031

@@ -37,6 +38,7 @@ void _PG_init(void)
3738
object_access_hook_init();
3839
process_utility_hook_init();
3940
post_parse_analyze_init();
41+
define_config_params();
4042
}
4143

4244
void _PG_fini(void);

0 commit comments

Comments
 (0)