This is an automated email from the ASF dual-hosted git repository.
mtaha pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/age.git
The following commit(s) were added to refs/heads/master by this push:
new 7beb6533 Fix and improve index.sql regression test coverage (#2300)
7beb6533 is described below
commit 7beb653303529b05391667de4204ffb4da318eeb
Author: John Gemignani <[email protected]>
AuthorDate: Fri Jan 9 12:55:36 2026 -0800
Fix and improve index.sql regression test coverage (#2300)
NOTE: This PR was created with AI tools and a human.
- Remove unused copy command (leftover from deleted agload_test_graph test)
- Replace broken Section 4 that referenced non-existent graph with
comprehensive WHERE clause tests covering string, int, bool, and float
properties with AND/OR/NOT operators
- Add EXPLAIN tests to verify index usage:
- Section 3: Validate GIN indices (load_city_gin_idx,
load_country_gin_idx)
show Bitmap Index Scan for property matching
- Section 4: Validate all expression indices (city_country_code_idx,
city_id_idx, city_west_coast_idx, country_life_exp_idx) show Index Scan
for WHERE clause filtering
All indices now have EXPLAIN verification confirming they are used as
expected.
modified: regress/expected/index.out
modified: regress/sql/index.sql
---
regress/expected/index.out | 290 ++++++++++++++++++++++++++++++++++++++++++---
regress/sql/index.sql | 174 +++++++++++++++++++++++++--
2 files changed, 436 insertions(+), 28 deletions(-)
diff --git a/regress/expected/index.out b/regress/expected/index.out
index 3ed7b1c3..9faead66 100644
--- a/regress/expected/index.out
+++ b/regress/expected/index.out
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-\! cp -r regress/age_load/data regress/instance/data/age_load
LOAD 'age';
SET search_path TO ag_catalog;
SET enable_mergejoin = ON;
@@ -385,6 +384,19 @@ CREATE INDEX load_city_gin_idx
ON cypher_index."City" USING gin (properties);
CREATE INDEX load_country_gin_idx
ON cypher_index."Country" USING gin (properties);
+-- Verify GIN index is used for City property match
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (c:City {city_id: 1})
+ RETURN c
+$$) as (plan agtype);
+ QUERY PLAN
+--------------------------------------------------------------
+ Bitmap Heap Scan on "City" c
+ Recheck Cond: (properties @> '{"city_id": 1}'::agtype)
+ -> Bitmap Index Scan on load_city_gin_idx
+ Index Cond: (properties @> '{"city_id": 1}'::agtype)
+(4 rows)
+
SELECT * FROM cypher('cypher_index', $$
MATCH (c:City {city_id: 1})
RETURN c
@@ -418,6 +430,19 @@ $$) as (n agtype);
{"id": 1970324836974597, "label": "City", "properties": {"name": "Vancouver",
"city_id": 5, "west_coast": true, "country_code": "CA"}}::vertex
(4 rows)
+-- Verify GIN index is used for Country property match
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (c:Country {life_expectancy: 82.05})
+ RETURN c
+$$) as (plan agtype);
+ QUERY PLAN
+--------------------------------------------------------------------------
+ Bitmap Heap Scan on "Country" c
+ Recheck Cond: (properties @> '{"life_expectancy": 82.05}'::agtype)
+ -> Bitmap Index Scan on load_country_gin_idx
+ Index Cond: (properties @> '{"life_expectancy": 82.05}'::agtype)
+(4 rows)
+
SELECT * FROM cypher('cypher_index', $$
MATCH (c:Country {life_expectancy: 82.05})
RETURN c
@@ -441,26 +466,259 @@ DROP INDEX cypher_index.load_country_gin_idx;
--
-- Section 4: Index use with WHERE clause
--
-SELECT COUNT(*) FROM cypher('cypher_index', $$
+-- Create expression index on country_code property
+CREATE INDEX city_country_code_idx ON cypher_index."City"
+(ag_catalog.agtype_access_operator(properties, '"country_code"'::agtype));
+-- Verify index is used with EXPLAIN (should show Index Scan on
city_country_code_idx)
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (a:City)
+ WHERE a.country_code = 'US'
+ RETURN a
+$$) as (plan agtype);
+ QUERY PLAN
+---------------------------------------------------------------------------------------------------------------
+ Index Scan using city_country_code_idx on "City" a
+ Index Cond: (agtype_access_operator(VARIADIC ARRAY[properties,
'"country_code"'::agtype]) = '"US"'::agtype)
+(2 rows)
+
+-- Test WHERE with indexed string property
+SELECT * FROM cypher('cypher_index', $$
MATCH (a:City)
- WHERE a.country_code = 'RS'
+ WHERE a.country_code = 'US'
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+ name
+-----------------
+ "New York"
+ "San Fransisco"
+ "Los Angeles"
+ "Seattle"
+(4 rows)
+
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.country_code = 'CA'
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+ name
+-------------
+ "Vancouver"
+ "Toronto"
+ "Montreal"
+(3 rows)
+
+-- Test WHERE with no matching results
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.country_code = 'XX'
+ RETURN a.name
+$$) as (name agtype);
+ name
+------
+(0 rows)
+
+-- Create expression index on city_id property
+CREATE INDEX city_id_idx ON cypher_index."City"
+(ag_catalog.agtype_access_operator(properties, '"city_id"'::agtype));
+-- Verify index is used with EXPLAIN for integer property
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (a:City)
+ WHERE a.city_id = 1
RETURN a
-$$) as (n agtype);
- count
--------
- 0
+$$) as (plan agtype);
+ QUERY PLAN
+-------------------------------------------------------------------------------------------------------
+ Index Scan using city_id_idx on "City" a
+ Index Cond: (agtype_access_operator(VARIADIC ARRAY[properties,
'"city_id"'::agtype]) = '1'::agtype)
+(2 rows)
+
+-- Test WHERE with indexed integer property
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.city_id = 1
+ RETURN a.name
+$$) as (name agtype);
+ name
+------------
+ "New York"
(1 row)
-CREATE INDEX CONCURRENTLY cntry_ode_idx ON cypher_index."City"
-(ag_catalog.agtype_access_operator(properties, '"country_code"'::agtype));
-SELECT COUNT(*) FROM cypher('agload_test_graph', $$
+SELECT * FROM cypher('cypher_index', $$
MATCH (a:City)
- WHERE a.country_code = 'RS'
+ WHERE a.city_id = 5
+ RETURN a.name
+$$) as (name agtype);
+ name
+-------------
+ "Vancouver"
+(1 row)
+
+-- Test WHERE with comparison operators on indexed property
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.city_id < 3
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+ name
+-----------------
+ "New York"
+ "San Fransisco"
+(2 rows)
+
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.city_id >= 8
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+ name
+-------------
+ "Monterrey"
+ "Tijuana"
+(2 rows)
+
+-- Create expression index on west_coast boolean property
+CREATE INDEX city_west_coast_idx ON cypher_index."City"
+(ag_catalog.agtype_access_operator(properties, '"west_coast"'::agtype));
+-- Verify index is used with EXPLAIN for boolean property
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (a:City)
+ WHERE a.west_coast = true
RETURN a
-$$) as (n agtype);
-ERROR: graph "agload_test_graph" does not exist
-LINE 1: SELECT COUNT(*) FROM cypher('agload_test_graph', $$
- ^
+$$) as (plan agtype);
+ QUERY PLAN
+-------------------------------------------------------------------------------------------------------------
+ Index Scan using city_west_coast_idx on "City" a
+ Index Cond: (agtype_access_operator(VARIADIC ARRAY[properties,
'"west_coast"'::agtype]) = 'true'::agtype)
+(2 rows)
+
+-- Test WHERE with indexed boolean property
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.west_coast = true
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+ name
+-----------------
+ "San Fransisco"
+ "Los Angeles"
+ "Seattle"
+ "Vancouver"
+(4 rows)
+
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.west_coast = false
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+ name
+---------------
+ "New York"
+ "Toronto"
+ "Montreal"
+ "Mexico City"
+ "Monterrey"
+ "Tijuana"
+(6 rows)
+
+-- Test WHERE with multiple conditions (AND)
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.country_code = 'US' AND a.west_coast = true
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+ name
+-----------------
+ "San Fransisco"
+ "Los Angeles"
+ "Seattle"
+(3 rows)
+
+-- Test WHERE with OR conditions
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.city_id = 1 OR a.city_id = 5
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+ name
+-------------
+ "New York"
+ "Vancouver"
+(2 rows)
+
+-- Test WHERE with NOT
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE NOT a.west_coast = true AND a.country_code = 'US'
+ RETURN a.name
+$$) as (name agtype);
+ name
+------------
+ "New York"
+(1 row)
+
+-- Create expression index on life_expectancy for Country
+CREATE INDEX country_life_exp_idx ON cypher_index."Country"
+(ag_catalog.agtype_access_operator(properties, '"life_expectancy"'::agtype));
+-- Verify index is used with EXPLAIN for float property
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (c:Country)
+ WHERE c.life_expectancy > 80.0
+ RETURN c
+$$) as (plan agtype);
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------
+ Index Scan using country_life_exp_idx on "Country" c
+ Index Cond: (agtype_access_operator(VARIADIC ARRAY[properties,
'"life_expectancy"'::agtype]) > '80.0'::agtype)
+(2 rows)
+
+-- Test WHERE with float property
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (c:Country)
+ WHERE c.life_expectancy > 80.0
+ RETURN c.name
+$$) as (name agtype);
+ name
+----------
+ "Canada"
+(1 row)
+
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (c:Country)
+ WHERE c.life_expectancy < 76.0
+ RETURN c.name
+$$) as (name agtype);
+ name
+----------
+ "Mexico"
+(1 row)
+
+-- Test WHERE in combination with pattern matching
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (country:Country)<-[:has_city]-(city:City)
+ WHERE country.country_code = 'CA'
+ RETURN city.name
+ ORDER BY city.city_id
+$$) as (name agtype);
+ name
+-------------
+ "Vancouver"
+ "Toronto"
+ "Montreal"
+(3 rows)
+
+-- Clean up indices
+DROP INDEX cypher_index.city_country_code_idx;
+DROP INDEX cypher_index.city_id_idx;
+DROP INDEX cypher_index.city_west_coast_idx;
+DROP INDEX cypher_index.country_life_exp_idx;
--
-- General Cleanup
--
@@ -478,5 +736,3 @@ NOTICE: graph "cypher_index" has been dropped
(1 row)
-SELECT drop_graph('agload_test_graph', true);
-ERROR: graph "agload_test_graph" does not exist
diff --git a/regress/sql/index.sql b/regress/sql/index.sql
index d9a4331a..96e7dd81 100644
--- a/regress/sql/index.sql
+++ b/regress/sql/index.sql
@@ -17,8 +17,6 @@
* under the License.
*/
-\! cp -r regress/age_load/data regress/instance/data/age_load
-
LOAD 'age';
SET search_path TO ag_catalog;
@@ -219,6 +217,11 @@ ON cypher_index."City" USING gin (properties);
CREATE INDEX load_country_gin_idx
ON cypher_index."Country" USING gin (properties);
+-- Verify GIN index is used for City property match
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (c:City {city_id: 1})
+ RETURN c
+$$) as (plan agtype);
SELECT * FROM cypher('cypher_index', $$
MATCH (c:City {city_id: 1})
@@ -235,6 +238,12 @@ SELECT * FROM cypher('cypher_index', $$
RETURN c
$$) as (n agtype);
+-- Verify GIN index is used for Country property match
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (c:Country {life_expectancy: 82.05})
+ RETURN c
+$$) as (plan agtype);
+
SELECT * FROM cypher('cypher_index', $$
MATCH (c:Country {life_expectancy: 82.05})
RETURN c
@@ -250,23 +259,166 @@ DROP INDEX cypher_index.load_country_gin_idx;
--
-- Section 4: Index use with WHERE clause
--
-SELECT COUNT(*) FROM cypher('cypher_index', $$
+-- Create expression index on country_code property
+CREATE INDEX city_country_code_idx ON cypher_index."City"
+(ag_catalog.agtype_access_operator(properties, '"country_code"'::agtype));
+
+-- Verify index is used with EXPLAIN (should show Index Scan on
city_country_code_idx)
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (a:City)
+ WHERE a.country_code = 'US'
+ RETURN a
+$$) as (plan agtype);
+
+-- Test WHERE with indexed string property
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.country_code = 'US'
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+
+SELECT * FROM cypher('cypher_index', $$
MATCH (a:City)
- WHERE a.country_code = 'RS'
+ WHERE a.country_code = 'CA'
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+
+-- Test WHERE with no matching results
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.country_code = 'XX'
+ RETURN a.name
+$$) as (name agtype);
+
+-- Create expression index on city_id property
+CREATE INDEX city_id_idx ON cypher_index."City"
+(ag_catalog.agtype_access_operator(properties, '"city_id"'::agtype));
+
+-- Verify index is used with EXPLAIN for integer property
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (a:City)
+ WHERE a.city_id = 1
RETURN a
-$$) as (n agtype);
+$$) as (plan agtype);
-CREATE INDEX CONCURRENTLY cntry_ode_idx ON cypher_index."City"
-(ag_catalog.agtype_access_operator(properties, '"country_code"'::agtype));
+-- Test WHERE with indexed integer property
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.city_id = 1
+ RETURN a.name
+$$) as (name agtype);
+
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.city_id = 5
+ RETURN a.name
+$$) as (name agtype);
+
+-- Test WHERE with comparison operators on indexed property
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.city_id < 3
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
-SELECT COUNT(*) FROM cypher('agload_test_graph', $$
+SELECT * FROM cypher('cypher_index', $$
MATCH (a:City)
- WHERE a.country_code = 'RS'
+ WHERE a.city_id >= 8
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+
+-- Create expression index on west_coast boolean property
+CREATE INDEX city_west_coast_idx ON cypher_index."City"
+(ag_catalog.agtype_access_operator(properties, '"west_coast"'::agtype));
+
+-- Verify index is used with EXPLAIN for boolean property
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (a:City)
+ WHERE a.west_coast = true
RETURN a
-$$) as (n agtype);
+$$) as (plan agtype);
+
+-- Test WHERE with indexed boolean property
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.west_coast = true
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.west_coast = false
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+
+-- Test WHERE with multiple conditions (AND)
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.country_code = 'US' AND a.west_coast = true
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+
+-- Test WHERE with OR conditions
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE a.city_id = 1 OR a.city_id = 5
+ RETURN a.name
+ ORDER BY a.city_id
+$$) as (name agtype);
+
+-- Test WHERE with NOT
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (a:City)
+ WHERE NOT a.west_coast = true AND a.country_code = 'US'
+ RETURN a.name
+$$) as (name agtype);
+
+-- Create expression index on life_expectancy for Country
+CREATE INDEX country_life_exp_idx ON cypher_index."Country"
+(ag_catalog.agtype_access_operator(properties, '"life_expectancy"'::agtype));
+
+-- Verify index is used with EXPLAIN for float property
+SELECT * FROM cypher('cypher_index', $$
+ EXPLAIN (costs off) MATCH (c:Country)
+ WHERE c.life_expectancy > 80.0
+ RETURN c
+$$) as (plan agtype);
+
+-- Test WHERE with float property
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (c:Country)
+ WHERE c.life_expectancy > 80.0
+ RETURN c.name
+$$) as (name agtype);
+
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (c:Country)
+ WHERE c.life_expectancy < 76.0
+ RETURN c.name
+$$) as (name agtype);
+
+-- Test WHERE in combination with pattern matching
+SELECT * FROM cypher('cypher_index', $$
+ MATCH (country:Country)<-[:has_city]-(city:City)
+ WHERE country.country_code = 'CA'
+ RETURN city.name
+ ORDER BY city.city_id
+$$) as (name agtype);
+
+-- Clean up indices
+DROP INDEX cypher_index.city_country_code_idx;
+DROP INDEX cypher_index.city_id_idx;
+DROP INDEX cypher_index.city_west_coast_idx;
+DROP INDEX cypher_index.country_life_exp_idx;
--
-- General Cleanup
--
SELECT drop_graph('cypher_index', true);
-SELECT drop_graph('agload_test_graph', true);