This is an automated email from the ASF dual-hosted git repository.

MuhammadTahaNaveed 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 d1b7cc4e Fix agtype B-tree comparison strategy numbers (#2577)
d1b7cc4e is described below

commit d1b7cc4e1b8d708720740c5ed38ce3826f2ff2ea
Author: Eeshwar Gurushankar <[email protected]>
AuthorDate: Sat Oct 10 23:26:32 2026 -0700

    Fix agtype B-tree comparison strategy numbers (#2577)
    
    Register >= as strategy 4 and > as strategy 5, matching PostgreSQL.
    The reversed mapping causes indexed >= queries to omit boundary rows.
    
    Repair existing operator-family entries during upgrade without dropping
    dependent indexes. Update the existing pg_opclass row to invalidate
    cached ordering operators in sessions that used agtype before upgrade.
    
    Cover indexed boundaries, duplicate values, forward/backward scans,
    and existing indexes and ordering caches across upgrade. Correct the
    existing >= expectation that omitted its boundary row.
    
    Verified on PostgreSQL 18.4: build with COPT=-Werror and all 43 core
    regression tests passed.
---
 age--1.8.0--y.y.y.sql            |  35 ++++++++++++++
 regress/expected/age_upgrade.out |  38 +++++++++++++++
 regress/expected/index.out       | 101 +++++++++++++++++++++++++++++++++++++--
 regress/sql/age_upgrade.sql      |  15 ++++++
 regress/sql/index.sql            |  36 ++++++++++++++
 sql/agtype_comparison.sql        |   4 +-
 6 files changed, 224 insertions(+), 5 deletions(-)

diff --git a/age--1.8.0--y.y.y.sql b/age--1.8.0--y.y.y.sql
index 097d066e..974604dc 100644
--- a/age--1.8.0--y.y.y.sql
+++ b/age--1.8.0--y.y.y.sql
@@ -65,3 +65,38 @@ CREATE FUNCTION ag_catalog.load_edges_from_file(graph_name 
name,
     RETURNS void
     LANGUAGE c
     AS 'MODULE_PATHNAME';
+
+DO $$
+DECLARE
+    btree_family oid;
+BEGIN
+    SELECT opcfamily INTO STRICT btree_family
+    FROM pg_catalog.pg_opclass
+    WHERE opcnamespace = 'ag_catalog'::regnamespace
+      AND opcname = 'agtype_ops_btree'
+      AND opcmethod = (SELECT oid FROM pg_catalog.pg_am WHERE amname = 
'btree');
+
+    UPDATE pg_catalog.pg_amop
+    SET amopstrategy = amopstrategy + 10
+    WHERE amopfamily = btree_family
+      AND amoplefttype = 'ag_catalog.agtype'::regtype
+      AND amoprighttype = 'ag_catalog.agtype'::regtype
+      AND amopstrategy IN (4, 5);
+
+    UPDATE pg_catalog.pg_amop
+    SET amopstrategy = CASE amopopr
+        WHEN 'ag_catalog.>=(ag_catalog.agtype,ag_catalog.agtype)'::regoperator 
THEN 4
+        WHEN 'ag_catalog.>(ag_catalog.agtype,ag_catalog.agtype)'::regoperator 
THEN 5
+    END
+    WHERE amopfamily = btree_family
+      AND amoplefttype = 'ag_catalog.agtype'::regtype
+      AND amoprighttype = 'ag_catalog.agtype'::regtype
+      AND amopstrategy IN (14, 15);
+
+    UPDATE pg_catalog.pg_opclass
+    SET opcfamily = btree_family
+    WHERE opcnamespace = 'ag_catalog'::regnamespace
+      AND opcname = 'agtype_ops_btree'
+      AND opcfamily = btree_family;
+END
+$$;
diff --git a/regress/expected/age_upgrade.out b/regress/expected/age_upgrade.out
index 446e0eb7..9c80fbdd 100644
--- a/regress/expected/age_upgrade.out
+++ b/regress/expected/age_upgrade.out
@@ -225,6 +225,19 @@ BEGIN
 END;
 $$;
 RESET check_function_bodies;
+BEGIN;
+CREATE TEMP TABLE agtype_upgrade_test (value agtype);
+INSERT INTO agtype_upgrade_test VALUES ('1'), ('2'), ('2'), ('3');
+CREATE INDEX agtype_upgrade_test_idx ON agtype_upgrade_test USING btree 
(value);
+SELECT value FROM agtype_upgrade_test ORDER BY value DESC;
+ value 
+-------
+ 3
+ 2
+ 2
+ 1
+(4 rows)
+
 -- Step 11: Upgrade to the current (default) version via the stamped template.
 DO $$
 DECLARE curr_ver text;
@@ -246,6 +259,31 @@ FROM pg_available_extensions WHERE name = 'age';
  t
 (1 row)
 
+SET LOCAL enable_seqscan = off;
+SET LOCAL enable_bitmapscan = off;
+SELECT count(*) AS greater FROM agtype_upgrade_test WHERE value > '2'::agtype;
+ greater 
+---------
+       1
+(1 row)
+
+SELECT count(*) AS greater_equal FROM agtype_upgrade_test WHERE value >= 
'2'::agtype;
+ greater_equal 
+---------------
+             3
+(1 row)
+
+SELECT value FROM agtype_upgrade_test ORDER BY value DESC;
+ value 
+-------
+ 3
+ 2
+ 2
+ 1
+(4 rows)
+
+DROP TABLE agtype_upgrade_test;
+COMMIT;
 -- =====================================================================
 -- UPGRADED INSTALL SNAPSHOTS (Steps 13-18b)
 -- Capture the catalog state after upgrade from initial to current.
diff --git a/regress/expected/index.out b/regress/expected/index.out
index ec62bf57..ed7363dd 100644
--- a/regress/expected/index.out
+++ b/regress/expected/index.out
@@ -574,11 +574,12 @@ SELECT * FROM cypher('cypher_index', $$
     RETURN a.name
     ORDER BY a.city_id
 $$) as (name agtype);
-    name     
--------------
+     name      
+---------------
+ "Mexico City"
  "Monterrey"
  "Tijuana"
-(2 rows)
+(3 rows)
 
 -- Create expression index on west_coast boolean property
 CREATE INDEX city_west_coast_idx ON cypher_index."City"
@@ -754,6 +755,100 @@ DROP INDEX cypher_index.city_id_idx;
 DROP INDEX cypher_index.city_west_coast_idx;
 DROP INDEX cypher_index.country_life_exp_idx;
 --
+-- Section 5: Agtype B-tree range boundaries
+--
+BEGIN;
+SET LOCAL enable_seqscan = off;
+SET LOCAL enable_bitmapscan = off;
+SET LOCAL enable_indexonlyscan = off;
+CREATE TEMP TABLE agtype_btree_test (value agtype);
+INSERT INTO agtype_btree_test VALUES ('1'), ('2'), ('2'), ('3');
+CREATE INDEX agtype_btree_test_idx ON agtype_btree_test USING btree (value);
+ANALYZE agtype_btree_test;
+EXPLAIN (COSTS OFF)
+SELECT value FROM agtype_btree_test WHERE value > '2'::agtype ORDER BY value;
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Index Scan using agtype_btree_test_idx on agtype_btree_test
+   Index Cond: (value > '2'::agtype)
+(2 rows)
+
+SELECT value FROM agtype_btree_test WHERE value > '2'::agtype ORDER BY value;
+ value 
+-------
+ 3
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT value FROM agtype_btree_test WHERE value >= '2'::agtype ORDER BY value;
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Index Scan using agtype_btree_test_idx on agtype_btree_test
+   Index Cond: (value >= '2'::agtype)
+(2 rows)
+
+SELECT value FROM agtype_btree_test WHERE value >= '2'::agtype ORDER BY value;
+ value 
+-------
+ 2
+ 2
+ 3
+(3 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT value FROM agtype_btree_test WHERE value > '2'::agtype ORDER BY value 
DESC;
+                              QUERY PLAN                              
+----------------------------------------------------------------------
+ Index Scan Backward using agtype_btree_test_idx on agtype_btree_test
+   Index Cond: (value > '2'::agtype)
+(2 rows)
+
+SELECT value FROM agtype_btree_test WHERE value > '2'::agtype ORDER BY value 
DESC;
+ value 
+-------
+ 3
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT value FROM agtype_btree_test WHERE value >= '2'::agtype ORDER BY value 
DESC;
+                              QUERY PLAN                              
+----------------------------------------------------------------------
+ Index Scan Backward using agtype_btree_test_idx on agtype_btree_test
+   Index Cond: (value >= '2'::agtype)
+(2 rows)
+
+SELECT value FROM agtype_btree_test WHERE value >= '2'::agtype ORDER BY value 
DESC;
+ value 
+-------
+ 3
+ 2
+ 2
+(3 rows)
+
+SELECT value FROM agtype_btree_test WHERE value < '2'::agtype ORDER BY value;
+ value 
+-------
+ 1
+(1 row)
+
+SELECT value FROM agtype_btree_test WHERE value <= '2'::agtype ORDER BY value;
+ value 
+-------
+ 1
+ 2
+ 2
+(3 rows)
+
+SELECT value FROM agtype_btree_test WHERE value = '2'::agtype ORDER BY value;
+ value 
+-------
+ 2
+ 2
+(2 rows)
+
+DROP TABLE agtype_btree_test;
+COMMIT;
+--
 -- General Cleanup
 --
 SELECT drop_graph('cypher_index', true);
diff --git a/regress/sql/age_upgrade.sql b/regress/sql/age_upgrade.sql
index 98ce3e21..5febc0b3 100644
--- a/regress/sql/age_upgrade.sql
+++ b/regress/sql/age_upgrade.sql
@@ -236,6 +236,13 @@ END;
 $$;
 RESET check_function_bodies;
 
+BEGIN;
+CREATE TEMP TABLE agtype_upgrade_test (value agtype);
+INSERT INTO agtype_upgrade_test VALUES ('1'), ('2'), ('2'), ('3');
+CREATE INDEX agtype_upgrade_test_idx ON agtype_upgrade_test USING btree 
(value);
+
+SELECT value FROM agtype_upgrade_test ORDER BY value DESC;
+
 -- Step 11: Upgrade to the current (default) version via the stamped template.
 DO $$
 DECLARE curr_ver text;
@@ -254,6 +261,14 @@ $$;
 SELECT installed_version = default_version AS upgraded_to_current
 FROM pg_available_extensions WHERE name = 'age';
 
+SET LOCAL enable_seqscan = off;
+SET LOCAL enable_bitmapscan = off;
+SELECT count(*) AS greater FROM agtype_upgrade_test WHERE value > '2'::agtype;
+SELECT count(*) AS greater_equal FROM agtype_upgrade_test WHERE value >= 
'2'::agtype;
+SELECT value FROM agtype_upgrade_test ORDER BY value DESC;
+DROP TABLE agtype_upgrade_test;
+COMMIT;
+
 -- =====================================================================
 -- UPGRADED INSTALL SNAPSHOTS (Steps 13-18b)
 -- Capture the catalog state after upgrade from initial to current.
diff --git a/regress/sql/index.sql b/regress/sql/index.sql
index d4a4b24a..1dd475d3 100644
--- a/regress/sql/index.sql
+++ b/regress/sql/index.sql
@@ -432,6 +432,42 @@ DROP INDEX cypher_index.city_id_idx;
 DROP INDEX cypher_index.city_west_coast_idx;
 DROP INDEX cypher_index.country_life_exp_idx;
 
+--
+-- Section 5: Agtype B-tree range boundaries
+--
+BEGIN;
+SET LOCAL enable_seqscan = off;
+SET LOCAL enable_bitmapscan = off;
+SET LOCAL enable_indexonlyscan = off;
+
+CREATE TEMP TABLE agtype_btree_test (value agtype);
+INSERT INTO agtype_btree_test VALUES ('1'), ('2'), ('2'), ('3');
+CREATE INDEX agtype_btree_test_idx ON agtype_btree_test USING btree (value);
+ANALYZE agtype_btree_test;
+
+EXPLAIN (COSTS OFF)
+SELECT value FROM agtype_btree_test WHERE value > '2'::agtype ORDER BY value;
+SELECT value FROM agtype_btree_test WHERE value > '2'::agtype ORDER BY value;
+
+EXPLAIN (COSTS OFF)
+SELECT value FROM agtype_btree_test WHERE value >= '2'::agtype ORDER BY value;
+SELECT value FROM agtype_btree_test WHERE value >= '2'::agtype ORDER BY value;
+
+EXPLAIN (COSTS OFF)
+SELECT value FROM agtype_btree_test WHERE value > '2'::agtype ORDER BY value 
DESC;
+SELECT value FROM agtype_btree_test WHERE value > '2'::agtype ORDER BY value 
DESC;
+
+EXPLAIN (COSTS OFF)
+SELECT value FROM agtype_btree_test WHERE value >= '2'::agtype ORDER BY value 
DESC;
+SELECT value FROM agtype_btree_test WHERE value >= '2'::agtype ORDER BY value 
DESC;
+
+SELECT value FROM agtype_btree_test WHERE value < '2'::agtype ORDER BY value;
+SELECT value FROM agtype_btree_test WHERE value <= '2'::agtype ORDER BY value;
+SELECT value FROM agtype_btree_test WHERE value = '2'::agtype ORDER BY value;
+
+DROP TABLE agtype_btree_test;
+COMMIT;
+
 --
 -- General Cleanup
 --
diff --git a/sql/agtype_comparison.sql b/sql/agtype_comparison.sql
index 5b937e84..55200366 100644
--- a/sql/agtype_comparison.sql
+++ b/sql/agtype_comparison.sql
@@ -1440,8 +1440,8 @@ CREATE OPERATOR CLASS agtype_ops_btree
   OPERATOR 1 <,
   OPERATOR 2 <=,
   OPERATOR 3 =,
-  OPERATOR 4 >,
-  OPERATOR 5 >=,
+  OPERATOR 4 >=,
+  OPERATOR 5 >,
   FUNCTION 1 ag_catalog.agtype_btree_cmp(agtype, agtype);
 
 CREATE FUNCTION ag_catalog.agtype_hash_cmp(agtype)

Reply via email to