-- test/sql/newline_option.sql -- -- The (?n) option (REG_NEWLINE for the rest of the enclosing group). -- TRE's parser applied it to "." only: ^ and $ were decided from the -- regcomp() flags, which an inline (?n) never reaches, and [^...] still -- matched a newline. So E'a\nfoo' %~~ tre_pattern('(?n)^foo', 0) was -- false, where core's ~ says true (patches/tre-newline-assert.patch). -- -- Every case runs through tre_amatch (the seq-scan UDF), the %~~ -- operator, a bitmap and a plain index scan, and for k = 0 also core ~ -- as the reference. The parallel, backtracking (back reference) and -- approximate (k > 0) matchers each decide assertions separately. CREATE EXTENSION IF NOT EXISTS pg_tre; psql:test/sql/newline_option.sql:14: NOTICE: extension "pg_tre" already exists, skipping CREATE EXTENSION \set VERBOSITY terse SET client_min_messages = warning; SET CREATE TABLE nl_cases (id int PRIMARY KEY, s text, p text, k int, want bool); CREATE TABLE INSERT INTO nl_cases VALUES -- from the report ( 1, E'a\nfoo', '(?n)^foo', 0, true), ( 2, E'a\nfoo', '(?n)a$', 0, true), ( 3, E'a\nfoo', '(?n)a.f', 0, false), ( 4, E'a\nfoo', '(?n)^a', 0, true), -- without (?n): ^ and $ anchor the string, . and [^x] match \n ( 5, E'a\nfoo', '^foo', 0, false), ( 6, E'a\nfoo', 'a$', 0, false), ( 7, E'a\nfoo', 'a.f', 0, true), ( 8, E'a\nfoo', 'a[^x]f', 0, true), -- [^...] excludes \n under (?n) ( 9, E'a\nfoo', '(?n)a[^x]f', 0, false), (10, E'a\nfoo', '(?n)a[^\n]f', 0, false), -- $ before a trailing newline, ^$ on the empty last line (11, E'foo\n', '(?n)foo$', 0, true), (12, E'foo\n', '(?n)^$', 0, true), (13, E'foo\n', '^$', 0, false), -- turned off again, and scoped to a group (14, E'a\nfoo', '(?n-n)^foo', 0, false), (15, E'a\nfoo', '(?n:^foo)', 0, true), (16, E'a\nfoo', 'x|(?n:^foo)', 0, true), (17, E'a\nfoo', '(?n:x)|^foo', 0, false), -- approximate matcher (18, E'a\nfoX', '(?n)^foo', 1, true), (19, E'a\nfoX', '(?n)^foo$', 1, true), (20, E'a\nfoX', '^foo$', 1, false), -- backtracking matcher (a back reference selects it) (21, E'a\nfoofoo', '(?n)^(foo)\1', 0, true), (22, E'a\nfoofoo', '^(foo)\1', 0, false), -- multibyte text (23, E'é\nñandú', '(?n)^ñandú$', 0, true), (24, E'é\nñandú', '^ñandú$', 0, false), -- combined with (?i) (25, E'a\nFOO', '(?in)^foo$', 0, true), (26, E'a\nFOO', '(?i)^foo$', 0, false); INSERT 0 26 -- seq-scan UDF and operator; core ~ where it has the same syntax SELECT id, p, k, tre_amatch(s, p, k) AS udf, s %~~ tre_pattern(p, k) AS op, CASE WHEN k = 0 AND p !~ '\(\?n(-n|:)' THEN s ~ p END AS core, (tre_amatch(s, p, k) = want AND (s %~~ tre_pattern(p, k)) = want) AS ok FROM nl_cases ORDER BY id; id | p | k | udf | op | core | ok ----+--------------+---+-----+----+------+---- 1 | (?n)^foo | 0 | t | t | t | t 2 | (?n)a$ | 0 | t | t | t | t 3 | (?n)a.f | 0 | f | f | f | t 4 | (?n)^a | 0 | t | t | t | t 5 | ^foo | 0 | f | f | f | t 6 | a$ | 0 | f | f | f | t 7 | a.f | 0 | t | t | t | t 8 | a[^x]f | 0 | t | t | t | t 9 | (?n)a[^x]f | 0 | f | f | f | t 10 | (?n)a[^\n]f | 0 | f | f | f | t 11 | (?n)foo$ | 0 | t | t | t | t 12 | (?n)^$ | 0 | t | t | t | t 13 | ^$ | 0 | f | f | f | t 14 | (?n-n)^foo | 0 | f | f | | t 15 | (?n:^foo) | 0 | t | t | | t 16 | x|(?n:^foo) | 0 | t | t | | t 17 | (?n:x)|^foo | 0 | f | f | | t 18 | (?n)^foo | 1 | t | t | | t 19 | (?n)^foo$ | 1 | t | t | | t 20 | ^foo$ | 1 | f | f | | t 21 | (?n)^(foo)\1 | 0 | t | t | t | t 22 | ^(foo)\1 | 0 | f | f | f | t 23 | (?n)^ñandú$ | 0 | t | t | t | t 24 | ^ñandú$ | 0 | f | f | f | t 25 | (?in)^foo$ | 0 | t | t | t | t 26 | (?i)^foo$ | 0 | f | f | f | t (26 rows) -- through the index, bitmap and plain scans CREATE INDEX nl_cases_tre ON nl_cases USING tre (s); CREATE INDEX CREATE FUNCTION nl_ids(bitmap bool) RETURNS int[] LANGUAGE plpgsql AS $$ DECLARE r int[]; BEGIN PERFORM set_config('enable_seqscan', 'off', true); PERFORM set_config('enable_bitmapscan', CASE WHEN bitmap THEN 'on' ELSE 'off' END, true); PERFORM set_config('enable_indexscan', CASE WHEN bitmap THEN 'off' ELSE 'on' END, true); SELECT array_agg(c.id ORDER BY c.id) INTO r FROM nl_cases c WHERE EXISTS (SELECT 1 FROM nl_cases t WHERE t.id = c.id AND t.s %~~ tre_pattern(c.p, c.k)); RETURN r; END $$; CREATE FUNCTION SELECT nl_ids(true) = (SELECT array_agg(id ORDER BY id) FROM nl_cases WHERE want) AS bitmap_ok, nl_ids(false) = (SELECT array_agg(id ORDER BY id) FROM nl_cases WHERE want) AS indexscan_ok; bitmap_ok | indexscan_ok -----------+-------------- t | t (1 row) -- the index agrees with a sequential scan for a line-anchored pattern SELECT op, seq_rows, bitmap_rows, indexscan_rows, ok FROM pg_tre_index_check('nl_cases_tre') WHERE NOT ok; op | seq_rows | bitmap_rows | indexscan_rows | ok ----+----------+-------------+----------------+---- (0 rows) DROP TABLE nl_cases; DROP TABLE DROP FUNCTION nl_ids(bool); DROP FUNCTION