Fix GiST picksplit crash and SP-GiST operator argument order

GiST: entryvec->vector[] uses 1-based indexing (FirstOffsetNumber),
not 0-based. Reading vector[0] hit uninitialized memory, causing
SIGSEGV on large catalogs (14k+ satellites). Fixed in gist_tle_union
and gist_tle_picksplit.

SP-GiST: PostgreSQL requires the indexed column as the LEFT argument
of the operator to form a ScanKey (skey.h:23-26). Flipped &? from
(observer_window, tle) to (tle, observer_window) so inner_consistent
receives scankeys for tree-level pruning.

Removed L0 altitude pruning from inner_consistent — SMA bins don't
carry eccentricity, so HEO satellites (e.g. CLUSTER II, e=0.88,
SMA ~70000 km, perigee ~2000 km) were falsely pruned. L0 now only
narrows SMA range for L1 footprint computation.

All 15 regression tests pass. Consistency check on 14,376 satellites
confirms 0 false negatives, 0 false positives.
This commit is contained in:
Ryan Malloy 2026-02-17 21:30:28 -07:00
parent 2a7240e739
commit e1c22cb873
6 changed files with 110 additions and 96 deletions

View File

@ -22,25 +22,27 @@ COMMENT ON TYPE observer_window IS
-- Visibility cone operator function -- Visibility cone operator function
-- ============================================================ -- ============================================================
CREATE FUNCTION tle_visibility_possible(observer_window, tle) RETURNS boolean CREATE FUNCTION tle_visibility_possible(tle, observer_window) RETURNS boolean
AS 'MODULE_PATHNAME' LANGUAGE C STABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C STABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION tle_visibility_possible(observer_window, tle) IS COMMENT ON FUNCTION tle_visibility_possible(tle, observer_window) IS
'Could this satellite be visible from the observer during the time window? Combines altitude, inclination, and RAAN checks. Conservative superset — survivors need SGP4 propagation for ground truth.'; 'Could this satellite be visible from the observer during the time window? Combines altitude, inclination, and RAAN checks. Conservative superset — survivors need SGP4 propagation for ground truth.';
-- ============================================================ -- ============================================================
-- &? operator (visibility cone check) -- &? operator (visibility cone check)
-- ============================================================ -- ============================================================
-- The indexed column (tle) MUST be the left argument so PostgreSQL
-- can form a ScanKey and pass it to inner_consistent for pruning.
CREATE OPERATOR &? ( CREATE OPERATOR &? (
LEFTARG = observer_window, LEFTARG = tle,
RIGHTARG = tle, RIGHTARG = observer_window,
FUNCTION = tle_visibility_possible, FUNCTION = tle_visibility_possible,
RESTRICT = contsel, RESTRICT = contsel,
JOIN = contjoinsel JOIN = contjoinsel
); );
COMMENT ON OPERATOR &? (observer_window, tle) IS COMMENT ON OPERATOR &? (tle, observer_window) IS
'Visibility cone check: could this satellite be visible from the observer during the time window? Index-accelerated via SP-GiST orbital trie.'; 'Visibility cone check: could this satellite be visible from the observer during the time window? Index-accelerated via SP-GiST orbital trie.';
-- ============================================================ -- ============================================================
@ -68,7 +70,7 @@ CREATE FUNCTION spgist_tle_leaf_consistent(internal, internal) RETURNS void
CREATE OPERATOR CLASS tle_spgist_ops CREATE OPERATOR CLASS tle_spgist_ops
FOR TYPE tle USING spgist AS FOR TYPE tle USING spgist AS
OPERATOR 1 &? (observer_window, tle), OPERATOR 1 &? (tle, observer_window),
FUNCTION 1 spgist_tle_config(internal, internal), FUNCTION 1 spgist_tle_config(internal, internal),
FUNCTION 2 spgist_tle_choose(internal, internal), FUNCTION 2 spgist_tle_choose(internal, internal),
FUNCTION 3 spgist_tle_picksplit(internal, internal), FUNCTION 3 spgist_tle_picksplit(internal, internal),

View File

@ -907,25 +907,27 @@ COMMENT ON TYPE observer_window IS
-- Visibility cone operator function -- Visibility cone operator function
-- ============================================================ -- ============================================================
CREATE FUNCTION tle_visibility_possible(observer_window, tle) RETURNS boolean CREATE FUNCTION tle_visibility_possible(tle, observer_window) RETURNS boolean
AS 'MODULE_PATHNAME' LANGUAGE C STABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C STABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION tle_visibility_possible(observer_window, tle) IS COMMENT ON FUNCTION tle_visibility_possible(tle, observer_window) IS
'Could this satellite be visible from the observer during the time window? Combines altitude, inclination, and RAAN checks. Conservative superset — survivors need SGP4 propagation for ground truth.'; 'Could this satellite be visible from the observer during the time window? Combines altitude, inclination, and RAAN checks. Conservative superset — survivors need SGP4 propagation for ground truth.';
-- ============================================================ -- ============================================================
-- &? operator (visibility cone check) -- &? operator (visibility cone check)
-- ============================================================ -- ============================================================
-- The indexed column (tle) MUST be the left argument so PostgreSQL
-- can form a ScanKey and pass it to inner_consistent for pruning.
CREATE OPERATOR &? ( CREATE OPERATOR &? (
LEFTARG = observer_window, LEFTARG = tle,
RIGHTARG = tle, RIGHTARG = observer_window,
FUNCTION = tle_visibility_possible, FUNCTION = tle_visibility_possible,
RESTRICT = contsel, RESTRICT = contsel,
JOIN = contjoinsel JOIN = contjoinsel
); );
COMMENT ON OPERATOR &? (observer_window, tle) IS COMMENT ON OPERATOR &? (tle, observer_window) IS
'Visibility cone check: could this satellite be visible from the observer during the time window? Index-accelerated via SP-GiST orbital trie.'; 'Visibility cone check: could this satellite be visible from the observer during the time window? Index-accelerated via SP-GiST orbital trie.';
-- ============================================================ -- ============================================================
@ -953,7 +955,7 @@ CREATE FUNCTION spgist_tle_leaf_consistent(internal, internal) RETURNS void
CREATE OPERATOR CLASS tle_spgist_ops CREATE OPERATOR CLASS tle_spgist_ops
FOR TYPE tle USING spgist AS FOR TYPE tle USING spgist AS
OPERATOR 1 &? (observer_window, tle), OPERATOR 1 &? (tle, observer_window),
FUNCTION 1 spgist_tle_config(internal, internal), FUNCTION 1 spgist_tle_config(internal, internal),
FUNCTION 2 spgist_tle_choose(internal, internal), FUNCTION 2 spgist_tle_choose(internal, internal),
FUNCTION 3 spgist_tle_picksplit(internal, internal), FUNCTION 3 spgist_tle_picksplit(internal, internal),

View File

@ -327,6 +327,10 @@ gist_tle_consistent(PG_FUNCTION_ARGS)
* gist_tle_union -- compute 2-D bounding box for a set of entries * gist_tle_union -- compute 2-D bounding box for a set of entries
* *
* The union is [min(alt_low), max(alt_high)] x [min(inc_low), max(inc_high)]. * The union is [min(alt_low), max(alt_high)] x [min(inc_low), max(inc_high)].
*
* GiST convention: entryvec->vector[] is 1-based (FirstOffsetNumber),
* vector[0] is unused. entryvec->n includes the unused slot, so
* valid indices are 1 .. entryvec->n - 1.
*/ */
Datum Datum
gist_tle_union(PG_FUNCTION_ARGS) gist_tle_union(PG_FUNCTION_ARGS)
@ -338,10 +342,10 @@ gist_tle_union(PG_FUNCTION_ARGS)
tle_orbital_key *cur; tle_orbital_key *cur;
result = (tle_orbital_key *) palloc(sizeof(tle_orbital_key)); result = (tle_orbital_key *) palloc(sizeof(tle_orbital_key));
cur = (tle_orbital_key *) DatumGetPointer(entryvec->vector[0].key); cur = (tle_orbital_key *) DatumGetPointer(entryvec->vector[FirstOffsetNumber].key);
*result = *cur; *result = *cur;
for (i = 1; i < entryvec->n; i++) for (i = FirstOffsetNumber + 1; i < entryvec->n; i++)
{ {
cur = (tle_orbital_key *) DatumGetPointer(entryvec->vector[i].key); cur = (tle_orbital_key *) DatumGetPointer(entryvec->vector[i].key);
key_merge(result, cur); key_merge(result, cur);
@ -413,35 +417,43 @@ picksplit_cmp(const void *a, const void *b)
* Standard R-tree approach: compute spread in both dimensions, split * Standard R-tree approach: compute spread in both dimensions, split
* along whichever dimension has the greater spread. This prevents * along whichever dimension has the greater spread. This prevents
* the tree from becoming biased toward one dimension. * the tree from becoming biased toward one dimension.
*
* GiST convention: entryvec->vector[] is 1-based (FirstOffsetNumber),
* vector[0] is unused/uninitialized. entryvec->n includes the unused
* slot, so the actual entry count is (entryvec->n - 1) and valid
* indices are FirstOffsetNumber .. entryvec->n - 1. The OffsetNumbers
* placed into spl_left[] and spl_right[] must also be 1-based.
*/ */
Datum Datum
gist_tle_picksplit(PG_FUNCTION_ARGS) gist_tle_picksplit(PG_FUNCTION_ARGS)
{ {
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *splitvec = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); GIST_SPLITVEC *splitvec = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
int nentries = entryvec->n; OffsetNumber maxoff = entryvec->n - 1;
int nentries = maxoff - FirstOffsetNumber + 1;
picksplit_item *items; picksplit_item *items;
tle_orbital_key *left_union; tle_orbital_key *left_union;
tle_orbital_key *right_union; tle_orbital_key *right_union;
tle_orbital_key *cur; tle_orbital_key *cur;
int split_at; int split_at;
int i; int i;
OffsetNumber off;
double alt_min, alt_max, inc_min, inc_max; double alt_min, alt_max, inc_min, inc_max;
double alt_spread, inc_spread; double alt_spread, inc_spread;
bool split_on_alt; bool split_on_alt;
/* First pass: compute spread in both dimensions */ /* First pass: compute spread in both dimensions */
cur = (tle_orbital_key *) DatumGetPointer(entryvec->vector[0].key); cur = (tle_orbital_key *) DatumGetPointer(entryvec->vector[FirstOffsetNumber].key);
alt_min = (cur->alt_low + cur->alt_high) / 2.0; alt_min = (cur->alt_low + cur->alt_high) / 2.0;
alt_max = alt_min; alt_max = alt_min;
inc_min = (cur->inc_low + cur->inc_high) / 2.0; inc_min = (cur->inc_low + cur->inc_high) / 2.0;
inc_max = inc_min; inc_max = inc_min;
for (i = 1; i < nentries; i++) for (off = FirstOffsetNumber + 1; off <= maxoff; off++)
{ {
double alt_mid, inc_mid; double alt_mid, inc_mid;
cur = (tle_orbital_key *) DatumGetPointer(entryvec->vector[i].key); cur = (tle_orbital_key *) DatumGetPointer(entryvec->vector[off].key);
alt_mid = (cur->alt_low + cur->alt_high) / 2.0; alt_mid = (cur->alt_low + cur->alt_high) / 2.0;
inc_mid = (cur->inc_low + cur->inc_high) / 2.0; inc_mid = (cur->inc_low + cur->inc_high) / 2.0;
@ -462,10 +474,10 @@ gist_tle_picksplit(PG_FUNCTION_ARGS)
/* Second pass: compute sort values in the chosen dimension */ /* Second pass: compute sort values in the chosen dimension */
items = (picksplit_item *) palloc(sizeof(picksplit_item) * nentries); items = (picksplit_item *) palloc(sizeof(picksplit_item) * nentries);
for (i = 0; i < nentries; i++) for (i = 0, off = FirstOffsetNumber; off <= maxoff; i++, off++)
{ {
cur = (tle_orbital_key *) DatumGetPointer(entryvec->vector[i].key); cur = (tle_orbital_key *) DatumGetPointer(entryvec->vector[off].key);
items[i].index = i; items[i].index = off; /* store 1-based OffsetNumber directly */
if (split_on_alt) if (split_on_alt)
items[i].sortval = (cur->alt_low + cur->alt_high) / 2.0; items[i].sortval = (cur->alt_low + cur->alt_high) / 2.0;
else else
@ -476,7 +488,7 @@ gist_tle_picksplit(PG_FUNCTION_ARGS)
split_at = nentries / 2; split_at = nentries / 2;
/* Allocate offset arrays (GiST uses OffsetNumber, 1-based) */ /* Allocate offset arrays */
splitvec->spl_left = (OffsetNumber *) palloc(sizeof(OffsetNumber) * nentries); splitvec->spl_left = (OffsetNumber *) palloc(sizeof(OffsetNumber) * nentries);
splitvec->spl_right = (OffsetNumber *) palloc(sizeof(OffsetNumber) * nentries); splitvec->spl_right = (OffsetNumber *) palloc(sizeof(OffsetNumber) * nentries);
splitvec->spl_nleft = 0; splitvec->spl_nleft = 0;
@ -497,21 +509,19 @@ gist_tle_picksplit(PG_FUNCTION_ARGS)
for (i = 0; i < nentries; i++) for (i = 0; i < nentries; i++)
{ {
int idx = items[i].index; OffsetNumber idx = items[i].index; /* already 1-based */
cur = (tle_orbital_key *) DatumGetPointer( cur = (tle_orbital_key *) DatumGetPointer(
entryvec->vector[idx].key); entryvec->vector[idx].key);
if (i < split_at) if (i < split_at)
{ {
splitvec->spl_left[splitvec->spl_nleft++] = splitvec->spl_left[splitvec->spl_nleft++] = idx;
(OffsetNumber)(idx + 1); /* 1-based */
key_merge(left_union, cur); key_merge(left_union, cur);
} }
else else
{ {
splitvec->spl_right[splitvec->spl_nright++] = splitvec->spl_right[splitvec->spl_nright++] = idx;
(OffsetNumber)(idx + 1);
key_merge(right_union, cur); key_merge(right_union, cur);
} }
} }

View File

@ -584,22 +584,18 @@ spgist_tle_inner_consistent(PG_FUNCTION_ARGS)
if (have_query && level == 0) if (have_query && level == 0)
{ {
/* /*
* L0: SMA pruning. * L0: SMA range narrowing only no altitude pruning.
* bin_low is the SMA of the lowest object in this bin at
* index build time. Later inserts with lower SMA route to
* bin 0, so for the first bin we use AE (physical minimum
* SMA) to avoid false negatives.
* *
* Conservative: assume e=0 (circular, worst case for * We cannot prune SMA bins by altitude because eccentricity
* "too high" pruning -- circular has highest perigee for * is not available at the inner node level. A satellite
* a given SMA). * at SMA 70,000 km with e=0.88 has perigee ~2,000 km
* well within typical max_alt. Without knowing e, any SMA
* bin could contain satellites with perigee near Earth's
* surface.
*
* L0 still helps by narrowing the SMA range passed to L1
* for computing a tighter ground footprint.
*/ */
double effective_low = (i == 0) ? WGS72_AE : bin_low;
double perigee_alt = effective_low - WGS72_AE;
double max_alt = max_visible_altitude_km(win.min_el_deg);
if (perigee_alt > max_alt)
dominated = true;
} }
else if (have_query && level == 1) else if (have_query && level == 1)
{ {
@ -789,7 +785,7 @@ spgist_tle_leaf_consistent(PG_FUNCTION_ARGS)
/* /*
* tle_visibility_possible(observer_window, tle) -> bool * tle_visibility_possible(tle, observer_window) -> bool
* *
* Standalone operator: can the satellite possibly be visible from * Standalone operator: can the satellite possibly be visible from
* this observer during this time window? Combines altitude check, * this observer during this time window? Combines altitude check,
@ -797,12 +793,16 @@ spgist_tle_leaf_consistent(PG_FUNCTION_ARGS)
* *
* This is the same logic as leaf_consistent, callable directly * This is the same logic as leaf_consistent, callable directly
* as a SQL operator for sequential scans or WHERE clauses. * as a SQL operator for sequential scans or WHERE clauses.
*
* The indexed column (tle) MUST be the left argument so that
* PostgreSQL can form a ScanKey and pass it to inner_consistent
* for tree-level pruning. See skey.h:23-26.
*/ */
Datum Datum
tle_visibility_possible(PG_FUNCTION_ARGS) tle_visibility_possible(PG_FUNCTION_ARGS)
{ {
HeapTupleHeader composite = PG_GETARG_HEAPTUPLEHEADER(0); pg_tle *tle = (pg_tle *) PG_GETARG_POINTER(0);
pg_tle *tle = (pg_tle *) PG_GETARG_POINTER(1); HeapTupleHeader composite = PG_GETARG_HEAPTUPLEHEADER(1);
ObserverWindow win; ObserverWindow win;
extract_observer_window(composite, &win); extract_observer_window(composite, &win);

View File

@ -42,12 +42,12 @@ INSERT INTO test_spgist (name, tle) VALUES ('GEO-SAT',
-- specific 2-hour window (correct physics, see Test 5 for 24h). -- specific 2-hour window (correct physics, see Test 5 for 24h).
-- ============================================================ -- ============================================================
SELECT name, SELECT name,
ROW( tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-01 02:00:00+00'::timestamptz, '2024-01-01 02:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle AS visible )::observer_window AS visible
FROM test_spgist FROM test_spgist
WHERE name = 'ISS'; WHERE name = 'ISS';
name | visible name | visible
@ -60,12 +60,12 @@ WHERE name = 'ISS';
-- 5 deg inc + ~12 deg footprint = 17 deg < 43.7 deg latitude -- 5 deg inc + ~12 deg footprint = 17 deg < 43.7 deg latitude
-- ============================================================ -- ============================================================
SELECT name, SELECT name,
ROW( tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-01 02:00:00+00'::timestamptz, '2024-01-01 02:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle AS visible )::observer_window AS visible
FROM test_spgist FROM test_spgist
WHERE name = 'Equatorial-LEO'; WHERE name = 'Equatorial-LEO';
name | visible name | visible
@ -82,12 +82,12 @@ CREATE INDEX test_spgist_idx ON test_spgist USING spgist (tle tle_spgist_ops);
SET enable_seqscan = off; SET enable_seqscan = off;
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('0.0N 0.0E 0m'), observer('0.0N 0.0E 0m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-01 02:00:00+00'::timestamptz, '2024-01-01 02:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
name name
--------- ---------
@ -103,12 +103,12 @@ SET enable_indexscan = off;
SET enable_bitmapscan = off; SET enable_bitmapscan = off;
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('0.0N 0.0E 0m'), observer('0.0N 0.0E 0m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-01 02:00:00+00'::timestamptz, '2024-01-01 02:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
name name
--------- ---------
@ -125,12 +125,12 @@ RESET enable_bitmapscan;
-- ============================================================ -- ============================================================
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
name name
--------- ---------
@ -144,12 +144,12 @@ ORDER BY name;
-- ============================================================ -- ============================================================
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
45.0 45.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
name name
--------- ---------
@ -174,12 +174,12 @@ WHERE a.name = 'ISS' AND b.name = 'Hubble';
SET enable_seqscan = off; SET enable_seqscan = off;
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
name name
--------- ---------
@ -194,12 +194,12 @@ RESET enable_seqscan;
INSERT INTO test_spgist (name, tle) VALUES ('NULL-SAT', NULL); INSERT INTO test_spgist (name, tle) VALUES ('NULL-SAT', NULL);
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
name name
--------- ---------
@ -214,12 +214,12 @@ INSERT INTO test_spgist (name, tle) VALUES ('DECAYED',
'1 99904U 24999D 24001.50000000 .00000000 00000+0 00000+0 0 9993 '1 99904U 24999D 24001.50000000 .00000000 00000+0 00000+0 0 9993
2 99904 0.0000 0.0000 0000000 0.0000 0.0000 0.00000000 00001'); 2 99904 0.0000 0.0000 0000000 0.0000 0.0000 0.00000000 00001');
SELECT name, SELECT name,
ROW( tle &? ROW(
observer('0.0N 0.0E 0m'), observer('0.0N 0.0E 0m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle AS visible )::observer_window AS visible
FROM test_spgist FROM test_spgist
WHERE name = 'DECAYED'; WHERE name = 'DECAYED';
name | visible name | visible
@ -234,12 +234,12 @@ WHERE name = 'DECAYED';
-- ============================================================ -- ============================================================
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('90.0N 0.0E 0m'), observer('90.0N 0.0E 0m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
name name
--------- ---------
@ -251,12 +251,12 @@ ORDER BY name;
-- overhead at the instant. RAAN window = footprint only. -- overhead at the instant. RAAN window = footprint only.
-- ============================================================ -- ============================================================
SELECT name, SELECT name,
ROW( tle &? ROW(
observer('0.0N 0.0E 0m'), observer('0.0N 0.0E 0m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle AS visible )::observer_window AS visible
FROM test_spgist FROM test_spgist
WHERE name = 'ISS'; WHERE name = 'ISS';
name | visible name | visible
@ -271,12 +271,12 @@ WHERE name = 'ISS';
SET enable_seqscan = off; SET enable_seqscan = off;
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
name name
--------- ---------
@ -289,12 +289,12 @@ SET enable_indexscan = off;
SET enable_bitmapscan = off; SET enable_bitmapscan = off;
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
name name
--------- ---------

View File

@ -51,12 +51,12 @@ INSERT INTO test_spgist (name, tle) VALUES ('GEO-SAT',
-- specific 2-hour window (correct physics, see Test 5 for 24h). -- specific 2-hour window (correct physics, see Test 5 for 24h).
-- ============================================================ -- ============================================================
SELECT name, SELECT name,
ROW( tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-01 02:00:00+00'::timestamptz, '2024-01-01 02:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle AS visible )::observer_window AS visible
FROM test_spgist FROM test_spgist
WHERE name = 'ISS'; WHERE name = 'ISS';
@ -66,12 +66,12 @@ WHERE name = 'ISS';
-- 5 deg inc + ~12 deg footprint = 17 deg < 43.7 deg latitude -- 5 deg inc + ~12 deg footprint = 17 deg < 43.7 deg latitude
-- ============================================================ -- ============================================================
SELECT name, SELECT name,
ROW( tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-01 02:00:00+00'::timestamptz, '2024-01-01 02:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle AS visible )::observer_window AS visible
FROM test_spgist FROM test_spgist
WHERE name = 'Equatorial-LEO'; WHERE name = 'Equatorial-LEO';
@ -87,12 +87,12 @@ SET enable_seqscan = off;
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('0.0N 0.0E 0m'), observer('0.0N 0.0E 0m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-01 02:00:00+00'::timestamptz, '2024-01-01 02:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
RESET enable_seqscan; RESET enable_seqscan;
@ -107,12 +107,12 @@ SET enable_bitmapscan = off;
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('0.0N 0.0E 0m'), observer('0.0N 0.0E 0m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-01 02:00:00+00'::timestamptz, '2024-01-01 02:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
RESET enable_indexscan; RESET enable_indexscan;
@ -127,12 +127,12 @@ RESET enable_bitmapscan;
-- ============================================================ -- ============================================================
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
@ -142,12 +142,12 @@ ORDER BY name;
-- ============================================================ -- ============================================================
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
45.0 45.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
@ -165,12 +165,12 @@ WHERE a.name = 'ISS' AND b.name = 'Hubble';
SET enable_seqscan = off; SET enable_seqscan = off;
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
RESET enable_seqscan; RESET enable_seqscan;
@ -182,12 +182,12 @@ INSERT INTO test_spgist (name, tle) VALUES ('NULL-SAT', NULL);
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
@ -199,12 +199,12 @@ INSERT INTO test_spgist (name, tle) VALUES ('DECAYED',
2 99904 0.0000 0.0000 0000000 0.0000 0.0000 0.00000000 00001'); 2 99904 0.0000 0.0000 0000000 0.0000 0.0000 0.00000000 00001');
SELECT name, SELECT name,
ROW( tle &? ROW(
observer('0.0N 0.0E 0m'), observer('0.0N 0.0E 0m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle AS visible )::observer_window AS visible
FROM test_spgist FROM test_spgist
WHERE name = 'DECAYED'; WHERE name = 'DECAYED';
@ -216,12 +216,12 @@ WHERE name = 'DECAYED';
-- ============================================================ -- ============================================================
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('90.0N 0.0E 0m'), observer('90.0N 0.0E 0m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
@ -230,12 +230,12 @@ ORDER BY name;
-- overhead at the instant. RAAN window = footprint only. -- overhead at the instant. RAAN window = footprint only.
-- ============================================================ -- ============================================================
SELECT name, SELECT name,
ROW( tle &? ROW(
observer('0.0N 0.0E 0m'), observer('0.0N 0.0E 0m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle AS visible )::observer_window AS visible
FROM test_spgist FROM test_spgist
WHERE name = 'ISS'; WHERE name = 'ISS';
@ -247,12 +247,12 @@ WHERE name = 'ISS';
SET enable_seqscan = off; SET enable_seqscan = off;
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
RESET enable_seqscan; RESET enable_seqscan;
@ -260,12 +260,12 @@ SET enable_indexscan = off;
SET enable_bitmapscan = off; SET enable_bitmapscan = off;
SELECT name SELECT name
FROM test_spgist FROM test_spgist
WHERE ROW( WHERE tle &? ROW(
observer('43.6977N 116.3535W 760m'), observer('43.6977N 116.3535W 760m'),
'2024-01-01 00:00:00+00'::timestamptz, '2024-01-01 00:00:00+00'::timestamptz,
'2024-01-02 00:00:00+00'::timestamptz, '2024-01-02 00:00:00+00'::timestamptz,
10.0 10.0
)::observer_window &? tle )::observer_window
ORDER BY name; ORDER BY name;
RESET enable_indexscan; RESET enable_indexscan;
RESET enable_bitmapscan; RESET enable_bitmapscan;