pg_orrery/test/sql/gist_equatorial.sql
Ryan Malloy 84ce1f1b8d Add v0.12.0: equatorial GiST operator class + DE moon equatorial functions
Feature A: GiST index for equatorial type with KNN ordering (<-> strategy 15).
24-byte float-precision spherical bounding box, RA-wrapping aware merge/split,
Vincenty lower-bound distance for correct KNN pruning. Apollo-hardened with
epsilon-widened bounds, circular-aware picksplit, compile-time size assertions.

Feature B: 4 new DE moon equatorial functions (galilean_equatorial_de,
saturn_moon_equatorial_de, uranus_moon_equatorial_de, mars_moon_equatorial_de).
Same-provider rule enforced, transparent VSOP87 fallback.

120 -> 132 SQL objects. 22 regression suites passing.
2026-02-24 13:15:34 -07:00

162 lines
6.3 KiB
SQL

-- Test equatorial GiST index: KNN ordering, RA wrapping, cone search
CREATE EXTENSION IF NOT EXISTS pg_orrery;
-- ============================================================
-- 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;
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;
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;
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;
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');
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;
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;
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;
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;
RESET enable_seqscan;
DROP TABLE sky_batch;
-- Cleanup
DROP TABLE sky_test;