rpr_explain.sql
application/octet-stream
Filename: rpr_explain.sql
Type: application/octet-stream
Part: 1
Message:
Re: Row pattern recognition
--
-- Test: EXPLAIN ANALYZE output for Row Pattern Recognition NFA statistics
--
-- This file tests the NFA statistics shown in EXPLAIN ANALYZE output:
-- - NFA States: peak, total, merged
-- - NFA Contexts: peak, total, absorbed, skipped
-- - NFA: matched (len min/max/avg), mismatched (len min/max/avg)
--
-- Setup: Create test tables
CREATE TEMP TABLE nfa_test (
id serial,
v int,
cat char(1)
);
-- Insert test data: 100 rows with predictable pattern
INSERT INTO nfa_test (v, cat)
SELECT i,
CASE
WHEN i % 5 = 1 THEN 'A'
WHEN i % 5 = 2 THEN 'B'
WHEN i % 5 = 3 THEN 'C'
WHEN i % 5 = 4 THEN 'D'
ELSE 'E'
END
FROM generate_series(1, 100) i;
-- Additional test table with more complex patterns
CREATE TEMP TABLE nfa_complex (
id serial,
price int,
trend char(1) -- U=up, D=down, S=stable
);
INSERT INTO nfa_complex (price, trend)
VALUES
(100, 'S'), (105, 'U'), (110, 'U'), (108, 'D'), (112, 'U'),
(115, 'U'), (113, 'D'), (111, 'D'), (109, 'D'), (110, 'U'),
(120, 'U'), (125, 'U'), (130, 'U'), (128, 'D'), (126, 'D'),
(124, 'D'), (122, 'D'), (120, 'D'), (118, 'D'), (119, 'U'),
(121, 'U'), (123, 'U'), (125, 'U'), (127, 'U'), (129, 'U'),
(131, 'U'), (133, 'U'), (130, 'D'), (127, 'D'), (124, 'D');
--
-- Section 1: Basic NFA Statistics Tests
--
-- Test 1.1: Simple pattern - should show basic statistics
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM nfa_test
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B)
DEFINE A AS cat = 'A', B AS cat = 'B'
);
-- Test 1.2: Pattern with no matches - 0 matched
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM nfa_test
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (X Y Z)
DEFINE X AS cat = 'X', Y AS cat = 'Y', Z AS cat = 'Z'
);
-- Test 1.3: Pattern matching every row - high match count
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM nfa_test
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (R)
DEFINE R AS TRUE
);
--
-- Section 2: State Statistics Tests (peak, total, merged)
--
-- Test 2.1: Simple quantifier pattern - A+ with short matches (no merging)
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+)
DEFINE A AS v % 2 = 1
);
-- Test 2.2: Alternation pattern - multiple state branches
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM nfa_test
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A | B | C) (D | E))
DEFINE
A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
D AS cat = 'D', E AS cat = 'E'
);
-- Test 2.3: Complex pattern with high state count
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 100) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B* C+)
DEFINE
A AS v % 3 = 1,
B AS v % 3 = 2,
C AS v % 3 = 0
);
-- Test 2.4: Grouped pattern with quantifier - state merging
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 60) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A B)+)
DEFINE A AS v % 2 = 1, B AS v % 2 = 0
);
-- Test 2.5: State explosion pattern - many alternations
-- Pattern (A|B)(A|B)(A|B)(A|B) can create many parallel states
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 100) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B))
DEFINE A AS v % 2 = 1, B AS v % 2 = 0
);
-- Test 2.6: High state merging - alternation with plus quantifier
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 100) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A | B | C)+ D)
DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3, D AS v % 4 = 0
);
-- Test 2.7: Nested quantifiers causing state growth
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 1000) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (((A | B)+)+)
DEFINE A AS v % 3 = 1, B AS v % 3 = 2
);
--
-- Section 3: Context Statistics Tests (peak, total, absorbed, skipped)
--
-- Test 3.1: Context absorption with unbounded quantifier at start
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
-- Test 3.2: No absorption - bounded quantifier
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A{2,4} B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
-- Test 3.3: Contexts skipped by SKIP PAST LAST ROW
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 100) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B C)
DEFINE A AS v % 10 = 1, B AS v % 10 = 2, C AS v % 10 = 3
);
-- Test 3.4: High context absorption - unbounded group
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 100) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A B)+ C)
DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
);
--
-- Section 4: Match Length Statistics Tests
--
-- Test 4.1: Fixed length matches - all same length
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM nfa_test
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B C D E)
DEFINE
A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
D AS cat = 'D', E AS cat = 'E'
);
-- Test 4.2: Variable length matches - min/max/avg differ
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 100) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
);
-- Test 4.3: Very long matches
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 200) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v <= 195, B AS v > 195
);
-- Test 4.4: Mix of short and long matches
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 100) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE
A AS (v % 20 <> 0) AND (v % 20 <= 10 OR v % 20 > 15),
B AS v % 20 = 0
);
--
-- Section 5: Mismatch Length Statistics Tests
--
-- Test 5.1: Pattern that causes mismatches with length > 1
-- Mismatch happens when partial match fails after processing multiple rows
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM (
SELECT v,
CASE WHEN v % 10 IN (1,2,3) THEN 'A'
WHEN v % 10 IN (4,5) THEN 'B'
WHEN v % 10 = 6 THEN 'C'
ELSE 'X' END AS cat
FROM generate_series(1, 100) AS s(v)
) t
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B+ C)
DEFINE A AS cat = 'A', B AS cat = 'B', C AS cat = 'C'
);
-- Test 5.2: Long partial matches that fail
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM (
SELECT i AS v,
CASE
WHEN i <= 20 THEN 'A'
WHEN i <= 25 THEN 'B'
WHEN i = 26 THEN 'X' -- breaks the pattern
WHEN i <= 50 THEN 'A'
WHEN i <= 55 THEN 'B'
WHEN i = 56 THEN 'C' -- completes pattern
ELSE 'Y'
END AS cat
FROM generate_series(1, 60) i
) t
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B+ C)
DEFINE A AS cat = 'A', B AS cat = 'B', C AS cat = 'C'
);
--
-- Section 6: JSON Format Tests
--
-- Test 6.1: JSON format output with all statistics
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT JSON)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B+)
DEFINE A AS v % 3 = 1, B AS v % 3 = 2
);
-- Test 6.2: JSON format with match length statistics
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT JSON)
SELECT count(*) OVER w
FROM generate_series(1, 100) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
);
--
-- Section 7: XML Format Tests
--
-- Test 7.1: XML format output
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT XML)
SELECT count(*) OVER w
FROM generate_series(1, 30) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B)
DEFINE A AS v % 2 = 1, B AS v % 2 = 0
);
--
-- Section 8: Multiple Partitions Tests
--
-- Test 8.1: Statistics across multiple partitions
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM (
SELECT p, v
FROM generate_series(1, 3) p,
generate_series(1, 30) v
) t
WINDOW w AS (
PARTITION BY p
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
-- Test 8.2: Different pattern behavior per partition
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM (
SELECT
CASE WHEN v <= 25 THEN 1 ELSE 2 END AS p,
v % 10 AS val
FROM generate_series(1, 50) v
) t
WINDOW w AS (
PARTITION BY p
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS val < 5, B AS val >= 5
);
--
-- Section 9: Edge Cases
--
-- Test 9.1: Empty result set
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 0) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B)
DEFINE A AS v = 1, B AS v = 2
);
-- Test 9.2: Single row
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 1) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A)
DEFINE A AS TRUE
);
-- Test 9.3: Pattern longer than data
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 5) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B C D E F G H I J)
DEFINE
A AS v = 1, B AS v = 2, C AS v = 3, D AS v = 4, E AS v = 5,
F AS v = 6, G AS v = 7, H AS v = 8, I AS v = 9, J AS v = 10
);
-- Test 9.4: All rows match as single match
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+)
DEFINE A AS TRUE
);
--
-- Section 10: Complex Pattern Tests
--
-- Test 10.1: Nested groups
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 60) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (((A B) C)+)
DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
);
-- Test 10.2: Multiple alternations
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM nfa_test
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A | B) (C | D | E))
DEFINE
A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
D AS cat = 'D', E AS cat = 'E'
);
-- Test 10.3: Optional elements
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B? C)
DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3
);
-- Test 10.4: Bounded quantifiers
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 100) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A{2,5} B)
DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
);
-- Test 10.5: Star quantifier
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B* C)
DEFINE A AS v % 10 = 1, B AS v % 10 IN (2,3,4,5,6,7,8), C AS v % 10 = 9
);
--
-- Section 11: Real-world Pattern Examples
--
-- Test 11.1: Stock price pattern - V-shape (down then up)
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM nfa_complex
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (D+ U+)
DEFINE D AS trend = 'D', U AS trend = 'U'
);
-- Test 11.2: Stock price pattern - peak (up, stable, down)
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM nfa_complex
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (U+ S* D+)
DEFINE U AS trend = 'U', S AS trend = 'S', D AS trend = 'D'
);
-- Test 11.3: Consecutive increasing values (using PREV)
-- FIXME: The original pattern was:
-- DEFINE A AS v > PREV(v) OR PREV(v) IS NULL
-- This causes "ERROR: unrecognized node type: 15" (T_FuncExpr) because
-- NullTest(FuncExpr(PREV)) is not properly handled somewhere in the planner.
-- The expression v > PREV(v) works fine, but PREV(v) IS NULL fails.
-- Using COALESCE(PREV(v), 0) as a workaround until the bug is fixed.
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A{3,})
DEFINE A AS v > COALESCE(PREV(v), 0)
);
--
-- Section 12: Performance-oriented Tests
--
-- Test 12.1: Large dataset with simple pattern
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 1000) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B)
DEFINE A AS v % 2 = 1, B AS v % 2 = 0
);
-- Test 12.2: Large dataset with absorption
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 1000) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v % 100 <> 0, B AS v % 100 = 0
);
-- Test 12.3: High state merge ratio
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 500) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A | B)+ C)
DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
);
--
-- Section 13: INITIAL vs no INITIAL comparison
--
-- Test 13.1: With INITIAL keyword
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
INITIAL
PATTERN (A+ B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
-- Test 13.2: Without INITIAL keyword (same behavior currently)
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
--
-- Section 14: Quantifier Variations
--
-- Test 14.1: Plus quantifier
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 40) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+)
DEFINE A AS v % 4 <> 0
);
-- Test 14.2: Star quantifier (zero or more)
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 40) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A* B)
DEFINE A AS v % 4 IN (1, 2), B AS v % 4 = 3
);
-- Test 14.3: Question mark (zero or one)
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 40) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A? B C)
DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3
);
-- Test 14.4: Exact count {n}
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A{3} B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
-- Test 14.5: Range {n,m}
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A{2,4} B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
-- Test 14.6: At least {n,}
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A{3,} B)
DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
);
--
-- Section 15: Regression Tests for Statistics Accuracy
--
-- Test 15.1: Verify state count accuracy
-- Pattern A+ B with 20 rows should show predictable state behavior
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 20) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
-- Test 15.2: Verify context count with known absorption
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 30) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B C)
DEFINE A AS v % 10 IN (1,2,3,4,5,6,7), B AS v % 10 = 8, C AS v % 10 = 9
);
-- Test 15.3: Verify match length with fixed-length pattern
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 30) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B C)
DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
);
--
-- Section 16: Alternation Pattern Tests
--
-- Test 16.1: Simple alternation
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM nfa_test
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A | B) C)
DEFINE A AS cat = 'A', B AS cat = 'B', C AS cat = 'C'
);
-- Test 16.2: Multiple items in alternation
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM nfa_test
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A | B | C | D) E)
DEFINE
A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
D AS cat = 'D', E AS cat = 'E'
);
-- Test 16.3: Alternation with quantifiers
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A | B)+ C)
DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
);
--
-- Section 17: Group Pattern Tests
--
-- Test 17.1: Simple group
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 40) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A B)+)
DEFINE A AS v % 2 = 1, B AS v % 2 = 0
);
-- Test 17.2: Group with bounded quantifier
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 40) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN ((A B){2,4})
DEFINE A AS v % 2 = 1, B AS v % 2 = 0
);
-- Test 17.3: Nested groups
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 60) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (((A B){2})+)
DEFINE A AS v % 2 = 1, B AS v % 2 = 0
);
--
-- Section 18: Window Function Combinations
--
-- Test 18.1: count(*) with pattern
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 30) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
-- Test 18.2: first_value with pattern
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT first_value(v) OVER w
FROM generate_series(1, 30) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
-- Test 18.3: last_value with pattern
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT last_value(v) OVER w
FROM generate_series(1, 30) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
-- Test 18.4: Multiple window functions
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT
count(*) OVER w,
first_value(v) OVER w,
last_value(v) OVER w
FROM generate_series(1, 30) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
);
--
-- Section 19: DEFINE Expression Variations
--
-- Test 19.1: Complex boolean expressions
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 50) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE
A AS (v % 5 <> 0) AND (v % 3 <> 0),
B AS (v % 5 = 0) OR (v % 3 = 0)
);
-- Test 19.2: Using PREV function
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 30) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (S U+ D+)
DEFINE
S AS TRUE,
U AS v > PREV(v),
D AS v < PREV(v)
);
-- Test 19.3: Using NULL comparisons
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM (
SELECT CASE WHEN v % 5 = 0 THEN NULL ELSE v END AS v
FROM generate_series(1, 30) v
) t
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B)
DEFINE A AS v IS NOT NULL, B AS v IS NULL
);
--
-- Section 20: Large Scale Statistics Verification
--
-- Test 20.1: 500 rows - verify statistics scale correctly
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 500) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A+ B C)
DEFINE A AS v % 10 < 7, B AS v % 10 = 7, C AS v % 10 = 8
);
-- Test 20.2: High match count scenario
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 500) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B)
DEFINE A AS v % 2 = 1, B AS v % 2 = 0
);
-- Test 20.3: High skip count scenario
EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
SELECT count(*) OVER w
FROM generate_series(1, 500) AS s(v)
WINDOW w AS (
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
AFTER MATCH SKIP PAST LAST ROW
PATTERN (A B C D E)
DEFINE
A AS v % 100 = 1,
B AS v % 100 = 2,
C AS v % 100 = 3,
D AS v % 100 = 4,
E AS v % 100 = 5
);
-- Cleanup
DROP TABLE nfa_test;
DROP TABLE nfa_complex;