-- Test equatorial GiST index: KNN ordering, RA wrapping, cone search CREATE EXTENSION IF NOT EXISTS pg_orrery; NOTICE: extension "pg_orrery" already exists, skipping -- ============================================================ -- Test table: known sky positions -- ============================================================ CREATE TABLE sky_test ( id serial, name text, eq equatorial ); -- Planets and Sun at a fixed epoch INSERT INTO sky_test (name, eq) VALUES ('Jupiter', planet_equatorial_apparent(5, '2024-06-15 12:00:00+00')), ('Saturn', planet_equatorial_apparent(6, '2024-06-15 12:00:00+00')), ('Mars', planet_equatorial_apparent(4, '2024-06-15 12:00:00+00')), ('Venus', planet_equatorial_apparent(2, '2024-06-15 12:00:00+00')), ('Mercury', planet_equatorial_apparent(1, '2024-06-15 12:00:00+00')), ('Sun', sun_equatorial('2024-06-15 12:00:00+00')), ('Moon', moon_equatorial('2024-06-15 12:00:00+00')); -- Bright stars at well-known positions INSERT INTO sky_test (name, eq) VALUES ('Polaris', star_equatorial(2.530, 89.264, '2024-06-15 12:00:00+00')), ('Sirius', star_equatorial(6.752, -16.716, '2024-06-15 12:00:00+00')), ('Vega', star_equatorial(18.616, 38.784, '2024-06-15 12:00:00+00')), ('Canopus', star_equatorial(6.399, -52.696, '2024-06-15 12:00:00+00')), ('Arcturus', star_equatorial(14.261, 19.182, '2024-06-15 12:00:00+00')); -- RA-wrapping test: objects near 0h and 23.9h INSERT INTO sky_test (name, eq) VALUES ('NearZeroH', '(0.10000000,15.00000000,0.000)'::equatorial), ('Near24H', '(23.90000000,15.00000000,0.000)'::equatorial); -- ============================================================ -- Test 1: Create GiST index -- ============================================================ CREATE INDEX idx_sky_gist ON sky_test USING gist (eq); -- ============================================================ -- Test 2: KNN correctness -- seqscan vs index scan -- Query: 5 nearest to Jupiter -- ============================================================ -- First get seqscan ordering SET enable_indexscan = off; SET enable_bitmapscan = off; SELECT 'knn_seq' AS test, name, round((eq <-> planet_equatorial_apparent(5, '2024-06-15 12:00:00+00'))::numeric, 4) AS dist FROM sky_test WHERE name != 'Jupiter' ORDER BY eq <-> planet_equatorial_apparent(5, '2024-06-15 12:00:00+00') LIMIT 5; test | name | dist ---------+---------+--------- knn_seq | Sun | 20.1241 knn_seq | Mercury | 21.1875 knn_seq | Venus | 23.0885 knn_seq | Mars | 30.0971 knn_seq | Sirius | 53.0538 (5 rows) RESET enable_indexscan; RESET enable_bitmapscan; -- Now force index scan SET enable_seqscan = off; SELECT 'knn_idx' AS test, name, round((eq <-> planet_equatorial_apparent(5, '2024-06-15 12:00:00+00'))::numeric, 4) AS dist FROM sky_test WHERE name != 'Jupiter' ORDER BY eq <-> planet_equatorial_apparent(5, '2024-06-15 12:00:00+00') LIMIT 5; test | name | dist ---------+---------+--------- knn_idx | Sun | 20.1241 knn_idx | Mercury | 21.1875 knn_idx | Venus | 23.0885 knn_idx | Mars | 30.0971 knn_idx | Sirius | 53.0538 (5 rows) RESET enable_seqscan; -- ============================================================ -- Test 3: KNN near Polaris (high declination) -- ============================================================ SET enable_seqscan = off; SELECT 'knn_polaris' AS test, name, round((eq <-> star_equatorial(2.530, 89.264, '2024-06-15 12:00:00+00'))::numeric, 2) AS dist FROM sky_test ORDER BY eq <-> star_equatorial(2.530, 89.264, '2024-06-15 12:00:00+00') LIMIT 3; test | name | dist -------------+---------+------- knn_polaris | Polaris | 0.00 knn_polaris | Vega | 51.57 knn_polaris | Mercury | 65.08 (3 rows) RESET enable_seqscan; -- ============================================================ -- Test 4: RA wrapping -- NearZeroH and Near24H should be neighbors -- (They are only 0.2h * 15 deg/h * cos(15) ~ 2.9 deg apart) -- ============================================================ SET enable_seqscan = off; SELECT 'ra_wrap' AS test, name, round((eq <-> '(0.10000000,15.00000000,0.000)'::equatorial)::numeric, 2) AS dist FROM sky_test ORDER BY eq <-> '(0.10000000,15.00000000,0.000)'::equatorial LIMIT 3; test | name | dist ---------+-----------+------- ra_wrap | NearZeroH | 0.00 ra_wrap | Near24H | 2.90 ra_wrap | Saturn | 23.50 (3 rows) RESET enable_seqscan; -- ============================================================ -- Test 5: Cone search -- everything within 15 degrees of Vega -- ============================================================ SET enable_seqscan = off; SELECT 'cone_vega' AS test, name, round((eq <-> star_equatorial(18.616, 38.784, '2024-06-15 12:00:00+00'))::numeric, 2) AS dist FROM sky_test WHERE eq_within_cone(eq, star_equatorial(18.616, 38.784, '2024-06-15 12:00:00+00'), 15.0) ORDER BY eq <-> star_equatorial(18.616, 38.784, '2024-06-15 12:00:00+00'); test | name | dist -----------+------+------ cone_vega | Vega | 0.00 (1 row) RESET enable_seqscan; -- ============================================================ -- Test 6: EXPLAIN shows Index Scan -- ============================================================ SET enable_seqscan = off; EXPLAIN (COSTS OFF) SELECT name FROM sky_test ORDER BY eq <-> '(12.00000000,0.00000000,0.000)'::equatorial LIMIT 3; QUERY PLAN ------------------------------------------------------------------------- Limit -> Index Scan using idx_sky_gist on sky_test Order By: (eq <-> '(12.00000000,0.00000000,0.000)'::equatorial) (3 rows) RESET enable_seqscan; -- ============================================================ -- Test 7: Empty table doesn't crash -- ============================================================ CREATE TABLE sky_empty (eq equatorial); CREATE INDEX idx_sky_empty ON sky_empty USING gist (eq); SELECT 'empty_knn' AS test, count(*) AS n FROM ( SELECT eq FROM sky_empty ORDER BY eq <-> '(12.00000000,0.00000000,0.000)'::equatorial ) sub; test | n -----------+--- empty_knn | 0 (1 row) DROP TABLE sky_empty; -- ============================================================ -- Test 8: Single row -- ============================================================ CREATE TABLE sky_single (eq equatorial); INSERT INTO sky_single VALUES ('(6.00000000,30.00000000,1000.000)'::equatorial); CREATE INDEX idx_sky_single ON sky_single USING gist (eq); SET enable_seqscan = off; SELECT 'single_knn' AS test, round((eq <-> '(12.00000000,0.00000000,0.000)'::equatorial)::numeric, 2) AS dist FROM sky_single ORDER BY eq <-> '(12.00000000,0.00000000,0.000)'::equatorial LIMIT 1; test | dist ------------+------- single_knn | 90.00 (1 row) RESET enable_seqscan; DROP TABLE sky_single; -- ============================================================ -- Test 9: Larger batch -- verify no crashes on tree rebalancing -- ============================================================ CREATE TABLE sky_batch (eq equatorial); INSERT INTO sky_batch SELECT planet_equatorial_apparent( (i % 7) + 1 + (CASE WHEN (i % 7) + 1 >= 3 THEN 1 ELSE 0 END), '2024-01-01 00:00:00+00'::timestamptz + (i || ' hours')::interval ) FROM generate_series(1, 100) AS i; CREATE INDEX idx_sky_batch ON sky_batch USING gist (eq); SET enable_seqscan = off; SELECT 'batch_knn' AS test, count(*) AS n FROM ( SELECT eq FROM sky_batch ORDER BY eq <-> '(12.00000000,0.00000000,0.000)'::equatorial LIMIT 10 ) sub; test | n -----------+---- batch_knn | 10 (1 row) RESET enable_seqscan; DROP TABLE sky_batch; -- Cleanup DROP TABLE sky_test;