-- hostile and oversized queries: limits are errors, never crashes or unbounded work CREATE EXTENSION pg_sift; CREATE TABLE q (id int, body text); INSERT INTO q SELECT i, 'tok' || i % 700 || ' common word' || i % 50 FROM generate_series(1, 3000) i; CREATE INDEX q_idx ON q USING sift (body); ANALYZE q; CREATE FUNCTION hits(query text) RETURNS text LANGUAGE plpgsql AS $$ DECLARE n bigint; BEGIN EXECUTE 'SELECT count(*) FROM q WHERE body @@> $1' INTO n USING query; RETURN n::text; END $$; CREATE FUNCTION hits_seq(query text) RETURNS text LANGUAGE plpgsql SET enable_indexscan = off SET enable_bitmapscan = off SET sift.enable_scan = off SET sift.enable_count = off AS $$ DECLARE n bigint; BEGIN EXECUTE 'SELECT count(*) FROM q WHERE body @@> $1' INTO n USING query; RETURN n::text; END $$; CREATE FUNCTION same(query text) RETURNS text LANGUAGE sql RETURN hits(query) || CASE WHEN hits(query) = hits_seq(query) THEN '' ELSE ' MISMATCH ' || hits_seq(query) END; -- many terms SELECT same((SELECT string_agg('tok' || g, ' OR ') FROM generate_series(1, 200) g)); same ------ 1000 (1 row) SELECT same((SELECT string_agg('tok' || g, ' OR ') FROM generate_series(1, 256) g)); same ------ 1224 (1 row) SELECT hits((SELECT string_agg('tok' || g, ' OR ') FROM generate_series(1, 257) g)); ERROR: sift query: more than 256 terms at position 2453 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits((SELECT string_agg('tok' || g, ' OR ') FROM generate_series(1, 200000) g)); ERROR: sift query is too long DETAIL: The query has 2488891 bytes, the maximum is 262144. CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE -- long queries SELECT hits(repeat('x', 262144)); hits ------ 0 (1 row) SELECT hits(repeat('x', 262145)); ERROR: sift query is too long DETAIL: The query has 262145 bytes, the maximum is 262144. CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits(repeat('x', 10000000)); ERROR: sift query is too long DETAIL: The query has 10000000 bytes, the maximum is 262144. CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits(repeat('word1 ', 40000)); ERROR: sift query: more than 256 terms at position 1537 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE -- nesting SELECT same(repeat('(', 30) || 'tok5' || repeat(')', 30)); same ------ 5 (1 row) SELECT hits(repeat('(', 100000) || 'tok5' || repeat(')', 100000)); ERROR: sift query: nesting deeper than 32 at position 33 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits(repeat('NOT ', 100000) || 'tok5'); ERROR: sift query is too long DETAIL: The query has 400004 bytes, the maximum is 262144. CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits(repeat('(', 100)); ERROR: sift query: nesting deeper than 32 at position 33 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits(repeat(')', 100)); ERROR: sift query: unexpected ")" at position 1 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE -- phrases SELECT same('"' || (SELECT string_agg('tok' || g, ' ') FROM generate_series(1, 100) g) || '"'); same ------ 0 (1 row) SELECT hits('"' || (SELECT string_agg('tok' || g, ' ') FROM generate_series(1, 1000) g) || '"'); ERROR: sift query: phrase longer than 128 terms at position 1 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE -- expansions SELECT same('tok12*'), same('tok1*'), same('*k12'), same('/tok1[0-9]/'), same('tok12~1'); same | same | same | same | same ------+------+------+------+------ 55 | 555 | 5 | 50 | 215 (1 row) SELECT hits('tok*'); hits ------ 3000 (1 row) SELECT hits('t*'); hits ------ 3000 (1 row) SELECT hits('tok?*'); hits ------ 3000 (1 row) SELECT hits('t?*'); hits ------ 3000 (1 row) SELECT hits('*'); ERROR: sift query: wildcard pattern needs a literal character at position 1 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits(repeat('a', 300) || '*'); hits ------ 0 (1 row) SELECT hits('t' || repeat('o', 500) || '~2'); hits ------ 0 (1 row) SELECT hits('tok1~9'); ERROR: sift query: fuzzy distance must be 1 or 2 at position 5 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits('/(((((((((((a*)*)*)*)*)*)*)*)*)*)*)*b/'); hits ------ 0 (1 row) SELECT hits('/' || repeat('a?', 5000) || '/'); ERROR: sift query: regular expression longer than 1000 bytes at position 1 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits('/' || repeat('(', 3000) || 'a' || repeat(')', 3000) || '/'); ERROR: sift query: regular expression longer than 1000 bytes at position 1 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE -- boosts, odd operators, odd characters, empty queries SELECT hits('tok5^0'); hits ------ 5 (1 row) SELECT hits('tok5^99999999999999999999'); ERROR: sift query: boost must be between 0 and 10000 at position 6 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits('^'); ERROR: sift query: unexpected "^" at position 1 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits('AND'); ERROR: sift query: unexpected AND at position 1 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits('tok5 AND'); ERROR: sift query: expected a term after AND, found end of query at position 9 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits('OR OR'); ERROR: sift query: unexpected OR at position 1 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits('tok5 e' || chr(769) || ' ' || chr(128512)); hits ------ 0 (1 row) SELECT hits(chr(1) || 'tok5'); hits ------ 5 (1 row) SELECT hits('tok5' || E'\n\tOR\ttok6'); hits ------ 10 (1 row) SELECT hits(''); ERROR: sift query: empty query at position 1 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits(' '); ERROR: sift query: empty query at position 4 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits('-'); hits ------ 0 (1 row) SELECT hits('"'); ERROR: sift query: unterminated phrase at position 1 CONTEXT: SQL statement "SELECT count(*) FROM q WHERE body @@> $1" PL/pgSQL function hits(text) line 4 at EXECUTE SELECT hits('"" tok5'); hits ------ 5 (1 row) DROP TABLE q; DROP FUNCTION same(text), hits(text), hits_seq(text); DROP EXTENSION pg_sift;