From 8edf291cf35b850607aa6aeedbfb2952d58295f9 Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Tue, 12 Mar 2024 10:54:36 -0400 Subject: [PATCH 1/3] sql/parser: move index hint tests to new file This is purely a mechanical movement of parser tests with index hints into a new file. Release note: None --- pkg/sql/parser/testdata/hints | 278 ++++++++++++++++++++++++ pkg/sql/parser/testdata/select_clauses | 280 ------------------------- 2 files changed, 278 insertions(+), 280 deletions(-) create mode 100644 pkg/sql/parser/testdata/hints diff --git a/pkg/sql/parser/testdata/hints b/pkg/sql/parser/testdata/hints new file mode 100644 index 000000000000..bcb969e57305 --- /dev/null +++ b/pkg/sql/parser/testdata/hints @@ -0,0 +1,278 @@ +parse +SELECT 'a' FROM t@bar +---- +SELECT 'a' FROM t@bar +SELECT ('a') FROM t@bar -- fully parenthesized +SELECT '_' FROM t@bar -- literals removed +SELECT 'a' FROM _@_ -- identifiers removed + +parse +SELECT 'a' FROM t@primary +---- +SELECT 'a' FROM t@primary +SELECT ('a') FROM t@primary -- fully parenthesized +SELECT '_' FROM t@primary -- literals removed +SELECT 'a' FROM _@_ -- identifiers removed + +parse +SELECT 'a' FROM t@like +---- +SELECT 'a' FROM t@like +SELECT ('a') FROM t@like -- fully parenthesized +SELECT '_' FROM t@like -- literals removed +SELECT 'a' FROM _@_ -- identifiers removed + +parse +SELECT 'a' FROM t@{NO_INDEX_JOIN} +---- +SELECT 'a' FROM t@{NO_INDEX_JOIN} +SELECT ('a') FROM t@{NO_INDEX_JOIN} -- fully parenthesized +SELECT '_' FROM t@{NO_INDEX_JOIN} -- literals removed +SELECT 'a' FROM _@{NO_INDEX_JOIN} -- identifiers removed + +parse +SELECT 'a' FROM t@{NO_ZIGZAG_JOIN} +---- +SELECT 'a' FROM t@{NO_ZIGZAG_JOIN} +SELECT ('a') FROM t@{NO_ZIGZAG_JOIN} -- fully parenthesized +SELECT '_' FROM t@{NO_ZIGZAG_JOIN} -- literals removed +SELECT 'a' FROM _@{NO_ZIGZAG_JOIN} -- identifiers removed + +parse +SELECT 'a' FROM t@{NO_FULL_SCAN} +---- +SELECT 'a' FROM t@{NO_FULL_SCAN} +SELECT ('a') FROM t@{NO_FULL_SCAN} -- fully parenthesized +SELECT '_' FROM t@{NO_FULL_SCAN} -- literals removed +SELECT 'a' FROM _@{NO_FULL_SCAN} -- identifiers removed + +parse +SELECT 'a' FROM t@{IGNORE_FOREIGN_KEYS} +---- +SELECT 'a' FROM t@{IGNORE_FOREIGN_KEYS} +SELECT ('a') FROM t@{IGNORE_FOREIGN_KEYS} -- fully parenthesized +SELECT '_' FROM t@{IGNORE_FOREIGN_KEYS} -- literals removed +SELECT 'a' FROM _@{IGNORE_FOREIGN_KEYS} -- identifiers removed + +parse +SELECT 'a' FROM t@{FORCE_INDEX=idx,ASC} +---- +SELECT 'a' FROM t@{FORCE_INDEX=idx,ASC} +SELECT ('a') FROM t@{FORCE_INDEX=idx,ASC} -- fully parenthesized +SELECT '_' FROM t@{FORCE_INDEX=idx,ASC} -- literals removed +SELECT 'a' FROM _@{FORCE_INDEX=_,ASC} -- identifiers removed + +parse +SELECT 'a' FROM t@{FORCE_INDEX=idx,DESC,IGNORE_FOREIGN_KEYS,NO_ZIGZAG_JOIN,NO_FULL_SCAN} +---- +SELECT 'a' FROM t@{FORCE_INDEX=idx,DESC,NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS} -- normalized! +SELECT ('a') FROM t@{FORCE_INDEX=idx,DESC,NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS} -- fully parenthesized +SELECT '_' FROM t@{FORCE_INDEX=idx,DESC,NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS} -- literals removed +SELECT 'a' FROM _@{FORCE_INDEX=_,DESC,NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS} -- identifiers removed + +error +SELECT a FROM foo@{FORCE_INDEX} +---- +at or near "}": syntax error +DETAIL: source SQL: +SELECT a FROM foo@{FORCE_INDEX} + ^ +HINT: try \h + +error +SELECT a FROM foo@{FORCE_INDEX=} +---- +at or near "}": syntax error +DETAIL: source SQL: +SELECT a FROM foo@{FORCE_INDEX=} + ^ +HINT: try \h + +error +SELECT a FROM foo@{FORCE_INDEX=bar,FORCE_INDEX=baz} +---- +at or near "baz": syntax error: FORCE_INDEX specified multiple times +DETAIL: source SQL: +SELECT a FROM foo@{FORCE_INDEX=bar,FORCE_INDEX=baz} + ^ + +error +SELECT a FROM foo@{FORCE_INDEX=bar,NO_INDEX_JOIN} +---- +at or near "}": syntax error: FORCE_INDEX cannot be specified in conjunction with NO_INDEX_JOIN +DETAIL: source SQL: +SELECT a FROM foo@{FORCE_INDEX=bar,NO_INDEX_JOIN} + ^ + +error +SELECT a FROM foo@{NO_INDEX_JOIN,NO_INDEX_JOIN} +---- +at or near "no_index_join": syntax error: NO_INDEX_JOIN specified multiple times +DETAIL: source SQL: +SELECT a FROM foo@{NO_INDEX_JOIN,NO_INDEX_JOIN} + ^ + +error +SELECT a FROM foo@{IGNORE_FOREIGN_KEYS,IGNORE_FOREIGN_KEYS} +---- +at or near "ignore_foreign_keys": syntax error: IGNORE_FOREIGN_KEYS specified multiple times +DETAIL: source SQL: +SELECT a FROM foo@{IGNORE_FOREIGN_KEYS,IGNORE_FOREIGN_KEYS} + ^ + +parse +SELECT 'a' FROM t@{FORCE_ZIGZAG} +---- +SELECT 'a' FROM t@{FORCE_ZIGZAG} +SELECT ('a') FROM t@{FORCE_ZIGZAG} -- fully parenthesized +SELECT '_' FROM t@{FORCE_ZIGZAG} -- literals removed +SELECT 'a' FROM _@{FORCE_ZIGZAG} -- identifiers removed + +parse +SELECT 'a' FROM t@{FORCE_ZIGZAG=idx} +---- +SELECT 'a' FROM t@{FORCE_ZIGZAG=idx} +SELECT ('a') FROM t@{FORCE_ZIGZAG=idx} -- fully parenthesized +SELECT '_' FROM t@{FORCE_ZIGZAG=idx} -- literals removed +SELECT 'a' FROM _@{FORCE_ZIGZAG=_} -- identifiers removed + +parse +SELECT 'a' FROM t@{FORCE_ZIGZAG=idxa,FORCE_ZIGZAG=idxb} +---- +SELECT 'a' FROM t@{FORCE_ZIGZAG=idxa,FORCE_ZIGZAG=idxb} +SELECT ('a') FROM t@{FORCE_ZIGZAG=idxa,FORCE_ZIGZAG=idxb} -- fully parenthesized +SELECT '_' FROM t@{FORCE_ZIGZAG=idxa,FORCE_ZIGZAG=idxb} -- literals removed +SELECT 'a' FROM _@{FORCE_ZIGZAG=_,FORCE_ZIGZAG=_} -- identifiers removed + +error +SELECT 'a' FROM t@{FORCE_ZIGZAG=1} +---- +at or near "1": syntax error +DETAIL: source SQL: +SELECT 'a' FROM t@{FORCE_ZIGZAG=1} + ^ +HINT: try \h + +parse +SELECT 'a' FROM t@{FORCE_ZIGZAG=[1]} +---- +SELECT 'a' FROM t@{FORCE_ZIGZAG=[1]} +SELECT ('a') FROM t@{FORCE_ZIGZAG=[1]} -- fully parenthesized +SELECT '_' FROM t@{FORCE_ZIGZAG=[1]} -- literals removed +SELECT 'a' FROM _@{FORCE_ZIGZAG=[1]} -- identifiers removed + +parse +SELECT 'a' FROM t@{FORCE_ZIGZAG=[1],FORCE_ZIGZAG=[2]} +---- +SELECT 'a' FROM t@{FORCE_ZIGZAG=[1],FORCE_ZIGZAG=[2]} +SELECT ('a') FROM t@{FORCE_ZIGZAG=[1],FORCE_ZIGZAG=[2]} -- fully parenthesized +SELECT '_' FROM t@{FORCE_ZIGZAG=[1],FORCE_ZIGZAG=[2]} -- literals removed +SELECT 'a' FROM _@{FORCE_ZIGZAG=[1],FORCE_ZIGZAG=[2]} -- identifiers removed + +error +SELECT a FROM foo@{FORCE_ZIGZAG,NO_INDEX_JOIN} +---- +at or near "}": syntax error: FORCE_ZIGZAG cannot be specified in conjunction with NO_INDEX_JOIN +DETAIL: source SQL: +SELECT a FROM foo@{FORCE_ZIGZAG,NO_INDEX_JOIN} + ^ + +error +SELECT a FROM foo@{FORCE_ZIGZAG,FORCE_INDEX=a} +---- +at or near "}": syntax error: FORCE_ZIGZAG cannot be specified in conjunction with FORCE_INDEX +DETAIL: source SQL: +SELECT a FROM foo@{FORCE_ZIGZAG,FORCE_INDEX=a} + ^ + +error +SELECT a FROM foo@{FORCE_ZIGZAG,FORCE_ZIGZAG} +---- +at or near "}": syntax error: FORCE_ZIGZAG specified multiple times +DETAIL: source SQL: +SELECT a FROM foo@{FORCE_ZIGZAG,FORCE_ZIGZAG} + ^ + +error +SELECT 'a' FROM t@{FORCE_ZIGZAG="",IGNORE_FOREIGN_KEYS} +---- +at or near "}": syntax error: FORCE_ZIGZAG index name cannot be empty string +DETAIL: source SQL: +SELECT 'a' FROM t@{FORCE_ZIGZAG="",IGNORE_FOREIGN_KEYS} + ^ + +error +SELECT 'a' FROM t@{FORCE_ZIGZAG=[],IGNORE_FOREIGN_KEYS} +---- +at or near "]": syntax error +DETAIL: source SQL: +SELECT 'a' FROM t@{FORCE_ZIGZAG=[],IGNORE_FOREIGN_KEYS} + ^ +HINT: try \h + +parse +SELECT 'a' FROM t@{FORCE_ZIGZAG=a,IGNORE_FOREIGN_KEYS} +---- +SELECT 'a' FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a} -- normalized! +SELECT ('a') FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a} -- fully parenthesized +SELECT '_' FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a} -- literals removed +SELECT 'a' FROM _@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=_} -- identifiers removed + +parse +SELECT 'a' FROM t@{FORCE_ZIGZAG=a,FORCE_ZIGZAG=b,IGNORE_FOREIGN_KEYS} +---- +SELECT 'a' FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a,FORCE_ZIGZAG=b} -- normalized! +SELECT ('a') FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a,FORCE_ZIGZAG=b} -- fully parenthesized +SELECT '_' FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a,FORCE_ZIGZAG=b} -- literals removed +SELECT 'a' FROM _@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=_,FORCE_ZIGZAG=_} -- identifiers removed + +error +SELECT * FROM a@{FORCE_ZIGZAG=} WHERE a = 3 AND b = 7 +---- +at or near "}": syntax error +DETAIL: source SQL: +SELECT * FROM a@{FORCE_ZIGZAG=} WHERE a = 3 AND b = 7 + ^ +HINT: try \h + +error +SELECT * FROM a@{FORCE_ZIGZAG=foo,bar} WHERE a = 3 AND b = 7 +---- +at or near "bar": syntax error +DETAIL: source SQL: +SELECT * FROM a@{FORCE_ZIGZAG=foo,bar} WHERE a = 3 AND b = 7 + ^ +HINT: try \h + +error +SELECT a FROM foo@{ASC} +---- +at or near "}": syntax error: ASC/DESC must be specified in conjunction with an index +DETAIL: source SQL: +SELECT a FROM foo@{ASC} + ^ + +error +SELECT a FROM foo@{DESC} +---- +at or near "}": syntax error: ASC/DESC must be specified in conjunction with an index +DETAIL: source SQL: +SELECT a FROM foo@{DESC} + ^ + +parse +SELECT 'a' FROM t@{FORCE_INDEX=bar} +---- +SELECT 'a' FROM t@bar -- normalized! +SELECT ('a') FROM t@bar -- fully parenthesized +SELECT '_' FROM t@bar -- literals removed +SELECT 'a' FROM _@_ -- identifiers removed + +parse +SELECT 'a' FROM t@{ASC,FORCE_INDEX=idx} +---- +SELECT 'a' FROM t@{FORCE_INDEX=idx,ASC} -- normalized! +SELECT ('a') FROM t@{FORCE_INDEX=idx,ASC} -- fully parenthesized +SELECT '_' FROM t@{FORCE_INDEX=idx,ASC} -- literals removed +SELECT 'a' FROM _@{FORCE_INDEX=_,ASC} -- identifiers removed + diff --git a/pkg/sql/parser/testdata/select_clauses b/pkg/sql/parser/testdata/select_clauses index 883a91196d7d..3c8a6820d830 100644 --- a/pkg/sql/parser/testdata/select_clauses +++ b/pkg/sql/parser/testdata/select_clauses @@ -286,269 +286,6 @@ SELECT ('a') FROM t -- fully parenthesized SELECT '_' FROM t -- literals removed SELECT 'a' FROM _ -- identifiers removed -parse -SELECT 'a' FROM t@bar ----- -SELECT 'a' FROM t@bar -SELECT ('a') FROM t@bar -- fully parenthesized -SELECT '_' FROM t@bar -- literals removed -SELECT 'a' FROM _@_ -- identifiers removed - -parse -SELECT 'a' FROM t@primary ----- -SELECT 'a' FROM t@primary -SELECT ('a') FROM t@primary -- fully parenthesized -SELECT '_' FROM t@primary -- literals removed -SELECT 'a' FROM _@_ -- identifiers removed - -parse -SELECT 'a' FROM t@like ----- -SELECT 'a' FROM t@like -SELECT ('a') FROM t@like -- fully parenthesized -SELECT '_' FROM t@like -- literals removed -SELECT 'a' FROM _@_ -- identifiers removed - -parse -SELECT 'a' FROM t@{NO_INDEX_JOIN} ----- -SELECT 'a' FROM t@{NO_INDEX_JOIN} -SELECT ('a') FROM t@{NO_INDEX_JOIN} -- fully parenthesized -SELECT '_' FROM t@{NO_INDEX_JOIN} -- literals removed -SELECT 'a' FROM _@{NO_INDEX_JOIN} -- identifiers removed - -parse -SELECT 'a' FROM t@{NO_ZIGZAG_JOIN} ----- -SELECT 'a' FROM t@{NO_ZIGZAG_JOIN} -SELECT ('a') FROM t@{NO_ZIGZAG_JOIN} -- fully parenthesized -SELECT '_' FROM t@{NO_ZIGZAG_JOIN} -- literals removed -SELECT 'a' FROM _@{NO_ZIGZAG_JOIN} -- identifiers removed - -parse -SELECT 'a' FROM t@{NO_FULL_SCAN} ----- -SELECT 'a' FROM t@{NO_FULL_SCAN} -SELECT ('a') FROM t@{NO_FULL_SCAN} -- fully parenthesized -SELECT '_' FROM t@{NO_FULL_SCAN} -- literals removed -SELECT 'a' FROM _@{NO_FULL_SCAN} -- identifiers removed - -parse -SELECT 'a' FROM t@{IGNORE_FOREIGN_KEYS} ----- -SELECT 'a' FROM t@{IGNORE_FOREIGN_KEYS} -SELECT ('a') FROM t@{IGNORE_FOREIGN_KEYS} -- fully parenthesized -SELECT '_' FROM t@{IGNORE_FOREIGN_KEYS} -- literals removed -SELECT 'a' FROM _@{IGNORE_FOREIGN_KEYS} -- identifiers removed - -parse -SELECT 'a' FROM t@{FORCE_INDEX=idx,ASC} ----- -SELECT 'a' FROM t@{FORCE_INDEX=idx,ASC} -SELECT ('a') FROM t@{FORCE_INDEX=idx,ASC} -- fully parenthesized -SELECT '_' FROM t@{FORCE_INDEX=idx,ASC} -- literals removed -SELECT 'a' FROM _@{FORCE_INDEX=_,ASC} -- identifiers removed - -parse -SELECT 'a' FROM t@{FORCE_INDEX=idx,DESC,IGNORE_FOREIGN_KEYS,NO_ZIGZAG_JOIN,NO_FULL_SCAN} ----- -SELECT 'a' FROM t@{FORCE_INDEX=idx,DESC,NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS} -- normalized! -SELECT ('a') FROM t@{FORCE_INDEX=idx,DESC,NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS} -- fully parenthesized -SELECT '_' FROM t@{FORCE_INDEX=idx,DESC,NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS} -- literals removed -SELECT 'a' FROM _@{FORCE_INDEX=_,DESC,NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS} -- identifiers removed - -error -SELECT a FROM foo@{FORCE_INDEX} ----- -at or near "}": syntax error -DETAIL: source SQL: -SELECT a FROM foo@{FORCE_INDEX} - ^ -HINT: try \h - -error -SELECT a FROM foo@{FORCE_INDEX=} ----- -at or near "}": syntax error -DETAIL: source SQL: -SELECT a FROM foo@{FORCE_INDEX=} - ^ -HINT: try \h - -error -SELECT a FROM foo@{FORCE_INDEX=bar,FORCE_INDEX=baz} ----- -at or near "baz": syntax error: FORCE_INDEX specified multiple times -DETAIL: source SQL: -SELECT a FROM foo@{FORCE_INDEX=bar,FORCE_INDEX=baz} - ^ - -error -SELECT a FROM foo@{FORCE_INDEX=bar,NO_INDEX_JOIN} ----- -at or near "}": syntax error: FORCE_INDEX cannot be specified in conjunction with NO_INDEX_JOIN -DETAIL: source SQL: -SELECT a FROM foo@{FORCE_INDEX=bar,NO_INDEX_JOIN} - ^ - -error -SELECT a FROM foo@{NO_INDEX_JOIN,NO_INDEX_JOIN} ----- -at or near "no_index_join": syntax error: NO_INDEX_JOIN specified multiple times -DETAIL: source SQL: -SELECT a FROM foo@{NO_INDEX_JOIN,NO_INDEX_JOIN} - ^ - -error -SELECT a FROM foo@{IGNORE_FOREIGN_KEYS,IGNORE_FOREIGN_KEYS} ----- -at or near "ignore_foreign_keys": syntax error: IGNORE_FOREIGN_KEYS specified multiple times -DETAIL: source SQL: -SELECT a FROM foo@{IGNORE_FOREIGN_KEYS,IGNORE_FOREIGN_KEYS} - ^ - -parse -SELECT 'a' FROM t@{FORCE_ZIGZAG} ----- -SELECT 'a' FROM t@{FORCE_ZIGZAG} -SELECT ('a') FROM t@{FORCE_ZIGZAG} -- fully parenthesized -SELECT '_' FROM t@{FORCE_ZIGZAG} -- literals removed -SELECT 'a' FROM _@{FORCE_ZIGZAG} -- identifiers removed - -parse -SELECT 'a' FROM t@{FORCE_ZIGZAG=idx} ----- -SELECT 'a' FROM t@{FORCE_ZIGZAG=idx} -SELECT ('a') FROM t@{FORCE_ZIGZAG=idx} -- fully parenthesized -SELECT '_' FROM t@{FORCE_ZIGZAG=idx} -- literals removed -SELECT 'a' FROM _@{FORCE_ZIGZAG=_} -- identifiers removed - -parse -SELECT 'a' FROM t@{FORCE_ZIGZAG=idxa,FORCE_ZIGZAG=idxb} ----- -SELECT 'a' FROM t@{FORCE_ZIGZAG=idxa,FORCE_ZIGZAG=idxb} -SELECT ('a') FROM t@{FORCE_ZIGZAG=idxa,FORCE_ZIGZAG=idxb} -- fully parenthesized -SELECT '_' FROM t@{FORCE_ZIGZAG=idxa,FORCE_ZIGZAG=idxb} -- literals removed -SELECT 'a' FROM _@{FORCE_ZIGZAG=_,FORCE_ZIGZAG=_} -- identifiers removed - -error -SELECT 'a' FROM t@{FORCE_ZIGZAG=1} ----- -at or near "1": syntax error -DETAIL: source SQL: -SELECT 'a' FROM t@{FORCE_ZIGZAG=1} - ^ -HINT: try \h - -parse -SELECT 'a' FROM t@{FORCE_ZIGZAG=[1]} ----- -SELECT 'a' FROM t@{FORCE_ZIGZAG=[1]} -SELECT ('a') FROM t@{FORCE_ZIGZAG=[1]} -- fully parenthesized -SELECT '_' FROM t@{FORCE_ZIGZAG=[1]} -- literals removed -SELECT 'a' FROM _@{FORCE_ZIGZAG=[1]} -- identifiers removed - -parse -SELECT 'a' FROM t@{FORCE_ZIGZAG=[1],FORCE_ZIGZAG=[2]} ----- -SELECT 'a' FROM t@{FORCE_ZIGZAG=[1],FORCE_ZIGZAG=[2]} -SELECT ('a') FROM t@{FORCE_ZIGZAG=[1],FORCE_ZIGZAG=[2]} -- fully parenthesized -SELECT '_' FROM t@{FORCE_ZIGZAG=[1],FORCE_ZIGZAG=[2]} -- literals removed -SELECT 'a' FROM _@{FORCE_ZIGZAG=[1],FORCE_ZIGZAG=[2]} -- identifiers removed - -error -SELECT a FROM foo@{FORCE_ZIGZAG,NO_INDEX_JOIN} ----- -at or near "}": syntax error: FORCE_ZIGZAG cannot be specified in conjunction with NO_INDEX_JOIN -DETAIL: source SQL: -SELECT a FROM foo@{FORCE_ZIGZAG,NO_INDEX_JOIN} - ^ - -error -SELECT a FROM foo@{FORCE_ZIGZAG,FORCE_INDEX=a} ----- -at or near "}": syntax error: FORCE_ZIGZAG cannot be specified in conjunction with FORCE_INDEX -DETAIL: source SQL: -SELECT a FROM foo@{FORCE_ZIGZAG,FORCE_INDEX=a} - ^ - -error -SELECT a FROM foo@{FORCE_ZIGZAG,FORCE_ZIGZAG} ----- -at or near "}": syntax error: FORCE_ZIGZAG specified multiple times -DETAIL: source SQL: -SELECT a FROM foo@{FORCE_ZIGZAG,FORCE_ZIGZAG} - ^ - -error -SELECT 'a' FROM t@{FORCE_ZIGZAG="",IGNORE_FOREIGN_KEYS} ----- -at or near "}": syntax error: FORCE_ZIGZAG index name cannot be empty string -DETAIL: source SQL: -SELECT 'a' FROM t@{FORCE_ZIGZAG="",IGNORE_FOREIGN_KEYS} - ^ - -error -SELECT 'a' FROM t@{FORCE_ZIGZAG=[],IGNORE_FOREIGN_KEYS} ----- -at or near "]": syntax error -DETAIL: source SQL: -SELECT 'a' FROM t@{FORCE_ZIGZAG=[],IGNORE_FOREIGN_KEYS} - ^ -HINT: try \h - -parse -SELECT 'a' FROM t@{FORCE_ZIGZAG=a,IGNORE_FOREIGN_KEYS} ----- -SELECT 'a' FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a} -- normalized! -SELECT ('a') FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a} -- fully parenthesized -SELECT '_' FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a} -- literals removed -SELECT 'a' FROM _@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=_} -- identifiers removed - -parse -SELECT 'a' FROM t@{FORCE_ZIGZAG=a,FORCE_ZIGZAG=b,IGNORE_FOREIGN_KEYS} ----- -SELECT 'a' FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a,FORCE_ZIGZAG=b} -- normalized! -SELECT ('a') FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a,FORCE_ZIGZAG=b} -- fully parenthesized -SELECT '_' FROM t@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=a,FORCE_ZIGZAG=b} -- literals removed -SELECT 'a' FROM _@{IGNORE_FOREIGN_KEYS,FORCE_ZIGZAG=_,FORCE_ZIGZAG=_} -- identifiers removed - -error -SELECT * FROM a@{FORCE_ZIGZAG=} WHERE a = 3 AND b = 7 ----- -at or near "}": syntax error -DETAIL: source SQL: -SELECT * FROM a@{FORCE_ZIGZAG=} WHERE a = 3 AND b = 7 - ^ -HINT: try \h - -error -SELECT * FROM a@{FORCE_ZIGZAG=foo,bar} WHERE a = 3 AND b = 7 ----- -at or near "bar": syntax error -DETAIL: source SQL: -SELECT * FROM a@{FORCE_ZIGZAG=foo,bar} WHERE a = 3 AND b = 7 - ^ -HINT: try \h - - -error -SELECT a FROM foo@{ASC} ----- -at or near "}": syntax error: ASC/DESC must be specified in conjunction with an index -DETAIL: source SQL: -SELECT a FROM foo@{ASC} - ^ - -error -SELECT a FROM foo@{DESC} ----- -at or near "}": syntax error: ASC/DESC must be specified in conjunction with an index -DETAIL: source SQL: -SELECT a FROM foo@{DESC} - ^ - parse SELECT * FROM t AS "of" AS OF SYSTEM TIME '2016-01-01' ---- @@ -2886,23 +2623,6 @@ SELECT (*) FROM ((VALUES ((1), (2)))) AS foo (a, b) -- fully parenthesized SELECT * FROM (VALUES (_, _)) AS foo (a, b) -- literals removed SELECT * FROM (VALUES (1, 2)) AS _ (_, _) -- identifiers removed -parse -SELECT 'a' FROM t@{FORCE_INDEX=bar} ----- -SELECT 'a' FROM t@bar -- normalized! -SELECT ('a') FROM t@bar -- fully parenthesized -SELECT '_' FROM t@bar -- literals removed -SELECT 'a' FROM _@_ -- identifiers removed - -parse -SELECT 'a' FROM t@{ASC,FORCE_INDEX=idx} ----- -SELECT 'a' FROM t@{FORCE_INDEX=idx,ASC} -- normalized! -SELECT ('a') FROM t@{FORCE_INDEX=idx,ASC} -- fully parenthesized -SELECT '_' FROM t@{FORCE_INDEX=idx,ASC} -- literals removed -SELECT 'a' FROM _@{FORCE_INDEX=_,ASC} -- identifiers removed - - parse SELECT 1 FOR UPDATE ---- From 3c29134a4b57614aace8f8bee3b51d66125c2a8a Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Tue, 12 Mar 2024 15:41:04 -0400 Subject: [PATCH 2/3] sql/parser: support FORCE_INVERTED_INDEX hint This commit adds parsing support for the `FORCE_INVERTED_INDEX` hint. The hint currently has no effect. Release note: None --- docs/generated/sql/bnf/stmt_block.bnf | 2 ++ pkg/sql/parser/sql.y | 27 +++++++-------- pkg/sql/parser/testdata/hints | 47 +++++++++++++++++++++++++++ pkg/sql/sem/tree/select.go | 25 ++++++++++++-- 4 files changed, 86 insertions(+), 15 deletions(-) diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index fb80c2707e29..090e0b60248b 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -1119,6 +1119,7 @@ unreserved_keyword ::= | 'FORCE_NULL' | 'FORCE_QUOTE' | 'FORCE_INDEX' + | 'FORCE_INVERTED_INDEX' | 'FORCE_ZIGZAG' | 'FORWARD' | 'FREEZE' @@ -3624,6 +3625,7 @@ bare_label_keywords ::= | 'FORCE_NULL' | 'FORCE_QUOTE' | 'FORCE_INDEX' + | 'FORCE_INVERTED_INDEX' | 'FORCE_ZIGZAG' | 'FOREIGN' | 'FORMAT' diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index bfa35d542035..a09426c4e77b 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -917,7 +917,7 @@ func (u *sqlSymUnion) beginTransaction() *tree.BeginTransaction { %token FAILURE FALSE FAMILY FETCH FETCHVAL FETCHTEXT FETCHVAL_PATH FETCHTEXT_PATH %token FILES FILTER -%token FIRST FLOAT FLOAT4 FLOAT8 FLOORDIV FOLLOWING FOR FORCE FORCE_INDEX +%token FIRST FLOAT FLOAT4 FLOAT8 FLOORDIV FOLLOWING FOR FORCE FORCE_INDEX FORCE_INVERTED_INDEX %token FORCE_NOT_NULL FORCE_NULL FORCE_QUOTE FORCE_ZIGZAG %token FOREIGN FORMAT FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS @@ -12900,34 +12900,33 @@ index_flags_param: { $$.val = &tree.IndexFlags{NoIndexJoin: true} } -| - NO_ZIGZAG_JOIN +| NO_ZIGZAG_JOIN { $$.val = &tree.IndexFlags{NoZigzagJoin: true} } -| - NO_FULL_SCAN +| NO_FULL_SCAN { $$.val = &tree.IndexFlags{NoFullScan: true} } -| - IGNORE_FOREIGN_KEYS +| IGNORE_FOREIGN_KEYS { /* SKIP DOC */ $$.val = &tree.IndexFlags{IgnoreForeignKeys: true} } -| - FORCE_ZIGZAG +| FORCE_INVERTED_INDEX + { + /* SKIP DOC */ + $$.val = &tree.IndexFlags{ForceInvertedIndex: true} + } +| FORCE_ZIGZAG { $$.val = &tree.IndexFlags{ForceZigzag: true} } -| - FORCE_ZIGZAG '=' index_name +| FORCE_ZIGZAG '=' index_name { $$.val = &tree.IndexFlags{ZigzagIndexes: []tree.UnrestrictedName{tree.UnrestrictedName($3)}} } -| - FORCE_ZIGZAG '=' '[' iconst64 ']' +| FORCE_ZIGZAG '=' '[' iconst64 ']' { /* SKIP DOC */ $$.val = &tree.IndexFlags{ZigzagIndexIDs: []tree.IndexID{tree.IndexID($4.int64())}} @@ -16540,6 +16539,7 @@ unreserved_keyword: | FORCE_NULL | FORCE_QUOTE | FORCE_INDEX +| FORCE_INVERTED_INDEX | FORCE_ZIGZAG | FORWARD | FREEZE @@ -17044,6 +17044,7 @@ bare_label_keywords: | FORCE_NULL | FORCE_QUOTE | FORCE_INDEX +| FORCE_INVERTED_INDEX | FORCE_ZIGZAG | FOREIGN | FORMAT diff --git a/pkg/sql/parser/testdata/hints b/pkg/sql/parser/testdata/hints index bcb969e57305..82f3c57a4a99 100644 --- a/pkg/sql/parser/testdata/hints +++ b/pkg/sql/parser/testdata/hints @@ -70,6 +70,14 @@ SELECT ('a') FROM t@{FORCE_INDEX=idx,DESC,NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOR SELECT '_' FROM t@{FORCE_INDEX=idx,DESC,NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS} -- literals removed SELECT 'a' FROM _@{FORCE_INDEX=_,DESC,NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS} -- identifiers removed +parse +SELECT 'a' FROM t@{IGNORE_FOREIGN_KEYS,NO_ZIGZAG_JOIN,NO_FULL_SCAN,FORCE_INVERTED_INDEX} +---- +SELECT 'a' FROM t@{NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS,FORCE_INVERTED_INDEX} -- normalized! +SELECT ('a') FROM t@{NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS,FORCE_INVERTED_INDEX} -- fully parenthesized +SELECT '_' FROM t@{NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS,FORCE_INVERTED_INDEX} -- literals removed +SELECT 'a' FROM _@{NO_ZIGZAG_JOIN,NO_FULL_SCAN,IGNORE_FOREIGN_KEYS,FORCE_INVERTED_INDEX} -- identifiers removed + error SELECT a FROM foo@{FORCE_INDEX} ---- @@ -276,3 +284,42 @@ SELECT ('a') FROM t@{FORCE_INDEX=idx,ASC} -- fully parenthesized SELECT '_' FROM t@{FORCE_INDEX=idx,ASC} -- literals removed SELECT 'a' FROM _@{FORCE_INDEX=_,ASC} -- identifiers removed +parse +SELECT 'a' FROM t@{FORCE_INVERTED_INDEX} +---- +SELECT 'a' FROM t@{FORCE_INVERTED_INDEX} +SELECT ('a') FROM t@{FORCE_INVERTED_INDEX} -- fully parenthesized +SELECT '_' FROM t@{FORCE_INVERTED_INDEX} -- literals removed +SELECT 'a' FROM _@{FORCE_INVERTED_INDEX} -- identifiers removed + +error +SELECT a FROM foo@{FORCE_INVERTED_INDEX,FORCE_INVERTED_INDEX} +---- +at or near "force_inverted_index": syntax error: FORCE_INVERTED_INDEX specified multiple times +DETAIL: source SQL: +SELECT a FROM foo@{FORCE_INVERTED_INDEX,FORCE_INVERTED_INDEX} + ^ + +error +SELECT a FROM foo@{FORCE_INVERTED_INDEX,FORCE_INDEX=a} +---- +at or near "}": syntax error: FORCE_INVERTED_INDEX cannot be specified in conjunction with FORCE_INDEX +DETAIL: source SQL: +SELECT a FROM foo@{FORCE_INVERTED_INDEX,FORCE_INDEX=a} + ^ + +error +SELECT 'a' FROM t@{FORCE_INVERTED_INDEX,ASC} +---- +at or near "}": syntax error: ASC/DESC must be specified in conjunction with an index +DETAIL: source SQL: +SELECT 'a' FROM t@{FORCE_INVERTED_INDEX,ASC} + ^ + +error +SELECT 'a' FROM t@{FORCE_INVERTED_INDEX,DESC} +---- +at or near "}": syntax error: ASC/DESC must be specified in conjunction with an index +DETAIL: source SQL: +SELECT 'a' FROM t@{FORCE_INVERTED_INDEX,DESC} + ^ diff --git a/pkg/sql/sem/tree/select.go b/pkg/sql/sem/tree/select.go index a40426a4d07b..cc00f66e97e2 100644 --- a/pkg/sql/sem/tree/select.go +++ b/pkg/sql/sem/tree/select.go @@ -317,6 +317,7 @@ type FamilyID = catid.FamilyID // - NO_ZIGZAG_JOIN // - NO_FULL_SCAN // - IGNORE_FOREIGN_KEYS +// - FORCE_INVERTED_INDEX // - FORCE_ZIGZAG // - FORCE_ZIGZAG=* // - FAMILY=[family_id] @@ -341,6 +342,9 @@ type IndexFlags struct { // IgnoreUniqueWithoutIndexKeys disables optimizations based on unique without // index constraints. IgnoreUniqueWithoutIndexKeys bool + // ForceInvertedIndex indicates that we should only plan scans over inverted + // indexes. + ForceInvertedIndex bool // Zigzag hinting fields are distinct: // ForceZigzag means we saw a TABLE@{FORCE_ZIGZAG} // ZigzagIndexes means we saw TABLE@{FORCE_ZIGZAG=name} @@ -379,6 +383,9 @@ func (ih *IndexFlags) CombineWith(other *IndexFlags) error { if ih.IgnoreUniqueWithoutIndexKeys && other.IgnoreUniqueWithoutIndexKeys { return errors.New("IGNORE_UNIQUE_WITHOUT_INDEX_KEYS specified multiple times") } + if ih.ForceInvertedIndex && other.ForceInvertedIndex { + return errors.New("FORCE_INVERTED_INDEX specified multiple times") + } result := *ih result.NoIndexJoin = ih.NoIndexJoin || other.NoIndexJoin result.NoZigzagJoin = ih.NoZigzagJoin || other.NoZigzagJoin @@ -386,6 +393,7 @@ func (ih *IndexFlags) CombineWith(other *IndexFlags) error { result.IgnoreForeignKeys = ih.IgnoreForeignKeys || other.IgnoreForeignKeys result.IgnoreUniqueWithoutIndexKeys = ih.IgnoreUniqueWithoutIndexKeys || other.IgnoreUniqueWithoutIndexKeys + result.ForceInvertedIndex = ih.ForceInvertedIndex || other.ForceInvertedIndex if other.Direction != 0 { if ih.Direction != 0 { @@ -441,6 +449,9 @@ func (ih *IndexFlags) Check() error { if ih.Direction != 0 && !ih.ForceIndex() { return errors.New("ASC/DESC must be specified in conjunction with an index") } + if ih.ForceInvertedIndex && ih.ForceIndex() { + return errors.New("FORCE_INVERTED_INDEX cannot be specified in conjunction with FORCE_INDEX") + } if ih.zigzagForced() && ih.NoIndexJoin { return errors.New("FORCE_ZIGZAG cannot be specified in conjunction with NO_INDEX_JOIN") } @@ -479,8 +490,7 @@ func TestingEnableFamilyIndexHint() func() { // Format implements the NodeFormatter interface. func (ih *IndexFlags) Format(ctx *FmtCtx) { ctx.WriteByte('@') - if !ih.NoIndexJoin && !ih.NoZigzagJoin && !ih.NoFullScan && !ih.IgnoreForeignKeys && - !ih.IgnoreUniqueWithoutIndexKeys && ih.Direction == 0 && !ih.zigzagForced() && ih.FamilyID == nil { + if ih.indexOnlyHint() { if ih.Index != "" { ctx.FormatNode(&ih.Index) } else { @@ -530,6 +540,11 @@ func (ih *IndexFlags) Format(ctx *FmtCtx) { ctx.WriteString("IGNORE_UNIQUE_WITHOUT_INDEX_KEYS") } + if ih.ForceInvertedIndex { + sep() + ctx.WriteString("FORCE_INVERTED_INDEX") + } + if ih.ForceZigzag || len(ih.ZigzagIndexes) > 0 || len(ih.ZigzagIndexIDs) > 0 { sep() if ih.ForceZigzag { @@ -561,6 +576,12 @@ func (ih *IndexFlags) Format(ctx *FmtCtx) { } } +func (ih *IndexFlags) indexOnlyHint() bool { + return !ih.NoIndexJoin && !ih.NoZigzagJoin && !ih.NoFullScan && !ih.IgnoreForeignKeys && + !ih.IgnoreUniqueWithoutIndexKeys && ih.Direction == 0 && !ih.ForceInvertedIndex && + !ih.zigzagForced() && ih.FamilyID == nil +} + func (ih *IndexFlags) zigzagForced() bool { return ih.ForceZigzag || len(ih.ZigzagIndexes) > 0 || len(ih.ZigzagIndexIDs) > 0 } From a09bdf083eaba3a65aafcb66f5f34cdd3735bb1c Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Tue, 12 Mar 2024 16:31:41 -0400 Subject: [PATCH 3/3] sql: support FORCE_INVERTED_INDEX hint Release note (sql change): The `FORCE_INVERTED_INDEX` hint is now supported. This makes the optimizer prefer a query plan scan over any inverted index of the hinted table. The query will result in an error if no such query plan can be generated. --- pkg/sql/opt/exec/execbuilder/relational.go | 4 + .../exec/execbuilder/testdata/inverted_index | 3 + pkg/sql/opt/memo/expr.go | 16 +++- pkg/sql/opt/memo/interner.go | 1 + pkg/sql/opt/memo/interner_test.go | 6 +- pkg/sql/opt/optbuilder/select.go | 1 + pkg/sql/opt/xform/coster.go | 4 + pkg/sql/opt/xform/testdata/coster/scan | 73 +++++++++++++++++++ 8 files changed, 103 insertions(+), 5 deletions(-) diff --git a/pkg/sql/opt/exec/execbuilder/relational.go b/pkg/sql/opt/exec/execbuilder/relational.go index 5dfe425a039d..da112f3f51da 100644 --- a/pkg/sql/opt/exec/execbuilder/relational.go +++ b/pkg/sql/opt/exec/execbuilder/relational.go @@ -756,6 +756,10 @@ func (b *Builder) buildScan(scan *memo.ScanExpr) (execPlan, error) { } } + if scan.Flags.ForceInvertedIndex && !scan.IsInvertedScan() { + return execPlan{}, fmt.Errorf("could not produce a query plan conforming to the FORCE_INVERTED_INDEX hint") + } + idx := tab.Index(scan.Index) if idx.IsInverted() && len(scan.InvertedConstraint) == 0 { return execPlan{}, diff --git a/pkg/sql/opt/exec/execbuilder/testdata/inverted_index b/pkg/sql/opt/exec/execbuilder/testdata/inverted_index index 651475117d0b..aa415c6b6b43 100644 --- a/pkg/sql/opt/exec/execbuilder/testdata/inverted_index +++ b/pkg/sql/opt/exec/execbuilder/testdata/inverted_index @@ -224,6 +224,9 @@ Put /Table/108/1/0/0 -> /TUPLE/ Del /Table/108/2/1/0/0 InitPut /Table/108/2/15/0/0 -> /BYTES/ +statement error pgcode XXUUU could not produce a query plan conforming to the FORCE_INVERTED_INDEX hint +SELECT * from d@{FORCE_INVERTED_INDEX} + query T EXPLAIN (VERBOSE) SELECT * from d where b @>'{"a": "b"}' ---- diff --git a/pkg/sql/opt/memo/expr.go b/pkg/sql/opt/memo/expr.go index 9e4690f8204c..6eae583abe5b 100644 --- a/pkg/sql/opt/memo/expr.go +++ b/pkg/sql/opt/memo/expr.go @@ -424,10 +424,12 @@ type ScanFlags struct { // ForceIndex forces the use of a specific index (specified in Index). // ForceIndex and NoIndexJoin cannot both be set at the same time. - ForceIndex bool - ForceZigzag bool - Direction tree.Direction - Index int + ForceIndex bool + // ForceInvertedIndex forces the use of an inverted index. + ForceInvertedIndex bool + ForceZigzag bool + Direction tree.Direction + Index int // When the optimizer is performing unique constraint or foreign key // constraint check, we will temporarily disable the not visible index feature @@ -753,6 +755,12 @@ func (s *ScanPrivate) IsFullIndexScan(md *opt.Metadata) bool { s.HardLimit == 0 } +// IsInvertedScan returns true if the index being scanned is an inverted +// index. +func (s *ScanPrivate) IsInvertedScan() bool { + return s.InvertedConstraint != nil +} + // IsVirtualTable returns true if the table being scanned is a virtual table. func (s *ScanPrivate) IsVirtualTable(md *opt.Metadata) bool { tab := md.Table(s.Table) diff --git a/pkg/sql/opt/memo/interner.go b/pkg/sql/opt/memo/interner.go index 2e0f86c470d1..aed27e943153 100644 --- a/pkg/sql/opt/memo/interner.go +++ b/pkg/sql/opt/memo/interner.go @@ -516,6 +516,7 @@ func (h *hasher) HashScanFlags(val ScanFlags) { h.HashBool(val.NoZigzagJoin) h.HashBool(val.NoFullScan) h.HashBool(val.ForceIndex) + h.HashBool(val.ForceInvertedIndex) h.HashBool(val.ForceZigzag) h.HashInt(int(val.Direction)) h.HashUint64(uint64(val.Index)) diff --git a/pkg/sql/opt/memo/interner_test.go b/pkg/sql/opt/memo/interner_test.go index db6d78b3ef6e..673204d81ff2 100644 --- a/pkg/sql/opt/memo/interner_test.go +++ b/pkg/sql/opt/memo/interner_test.go @@ -378,7 +378,7 @@ func TestInterner(t *testing.T) { {hashFn: in.hasher.HashScanFlags, eqFn: in.hasher.IsScanFlagsEqual, variations: []testVariation{ // Use unnamed fields so that compilation fails if a new field is // added to ScanFlags. - {val1: ScanFlags{false, false, false, false, false, 0, 0, false, intsets.Fast{}}, val2: ScanFlags{}, equal: true}, + {val1: ScanFlags{false, false, false, false, false, false, 0, 0, false, intsets.Fast{}}, val2: ScanFlags{}, equal: true}, {val1: ScanFlags{}, val2: ScanFlags{}, equal: true}, {val1: ScanFlags{NoIndexJoin: false}, val2: ScanFlags{NoIndexJoin: true}, equal: false}, {val1: ScanFlags{NoIndexJoin: true}, val2: ScanFlags{NoIndexJoin: true}, equal: true}, @@ -391,6 +391,10 @@ func TestInterner(t *testing.T) { {val1: ScanFlags{NoIndexJoin: true, Index: 1}, val2: ScanFlags{NoIndexJoin: true, Index: 1}, equal: true}, {val1: ScanFlags{NoIndexJoin: true, Index: 1}, val2: ScanFlags{NoIndexJoin: true, Index: 2}, equal: false}, {val1: ScanFlags{NoIndexJoin: true, Index: 1}, val2: ScanFlags{NoIndexJoin: false, Index: 1}, equal: false}, + {val1: ScanFlags{NoFullScan: true}, val2: ScanFlags{NoFullScan: false}, equal: false}, + {val1: ScanFlags{NoFullScan: true}, val2: ScanFlags{NoFullScan: true}, equal: true}, + {val1: ScanFlags{ForceInvertedIndex: true}, val2: ScanFlags{ForceInvertedIndex: false}, equal: false}, + {val1: ScanFlags{ForceInvertedIndex: true}, val2: ScanFlags{ForceInvertedIndex: true}, equal: true}, { val1: ScanFlags{NoIndexJoin: true, ForceIndex: true, Direction: tree.Ascending, Index: 1}, val2: ScanFlags{NoIndexJoin: false, ForceIndex: true, Direction: tree.Ascending, Index: 1}, diff --git a/pkg/sql/opt/optbuilder/select.go b/pkg/sql/opt/optbuilder/select.go index c5d2c044f591..6b956ad1b19f 100644 --- a/pkg/sql/opt/optbuilder/select.go +++ b/pkg/sql/opt/optbuilder/select.go @@ -624,6 +624,7 @@ func (b *Builder) buildScan( private.Flags.NoIndexJoin = indexFlags.NoIndexJoin private.Flags.NoZigzagJoin = indexFlags.NoZigzagJoin private.Flags.NoFullScan = indexFlags.NoFullScan + private.Flags.ForceInvertedIndex = indexFlags.ForceInvertedIndex if indexFlags.Index != "" || indexFlags.IndexID != 0 { idx := -1 for i := 0; i < tab.IndexCount(); i++ { diff --git a/pkg/sql/opt/xform/coster.go b/pkg/sql/opt/xform/coster.go index 910c1d09a03b..aa4e88152ffe 100644 --- a/pkg/sql/opt/xform/coster.go +++ b/pkg/sql/opt/xform/coster.go @@ -759,6 +759,10 @@ func (c *coster) computeScanCost(scan *memo.ScanExpr, required *physical.Require } } + if scan.Flags.ForceInvertedIndex && !scan.IsInvertedScan() { + return hugeCost + } + stats := scan.Relational().Statistics() rowCount := stats.RowCount if isUnfiltered && c.evalCtx != nil && c.evalCtx.SessionData().DisallowFullTableScans { diff --git a/pkg/sql/opt/xform/testdata/coster/scan b/pkg/sql/opt/xform/testdata/coster/scan index 2ece2a7f94c0..136af1b41dab 100644 --- a/pkg/sql/opt/xform/testdata/coster/scan +++ b/pkg/sql/opt/xform/testdata/coster/scan @@ -641,6 +641,79 @@ select └── filters └── c:4 > 0 [outer=(4), constraints=(/4: [/1 - ]; tight)] +# Test that the FORCE_INVERTED_INDEX hint creates a huge cost for full table +# scans. +opt +SELECT * FROM t@{FORCE_INVERTED_INDEX} +---- +scan t + ├── columns: k:1!null a:2 b:3 c:4 + ├── partial index predicates + │ └── b_partial: filters + │ └── c:4 > 0 [outer=(4), constraints=(/4: [/1 - ]; tight)] + ├── flags: + ├── stats: [rows=100000] + ├── cost: 1e+100 + ├── key: (1) + └── fd: (1)-->(2-4) + +# Test that the FORCE_INVERTED_INDEX hint creates a huge cost for scans on +# non-inverted indexes. +opt +SELECT * FROM t@{FORCE_INVERTED_INDEX} WHERE b = 0 +---- +select + ├── columns: k:1!null a:2 b:3!null c:4 + ├── stats: [rows=2e-05, distinct(3)=2e-05, null(3)=0] + │ histogram(3)= 0 0 + │ <--- 0 + ├── cost: 1e+100 + ├── key: (1) + ├── fd: ()-->(3), (1)-->(2,4) + ├── scan t + │ ├── columns: k:1!null a:2 b:3 c:4 + │ ├── partial index predicates + │ │ └── b_partial: filters + │ │ └── c:4 > 0 [outer=(4), constraints=(/4: [/1 - ]; tight)] + │ ├── flags: + │ ├── stats: [rows=100000, distinct(1)=3, null(1)=0, distinct(3)=100, null(3)=0] + │ │ histogram(3)= 0 0 1e+05 0 + │ │ <--- 0 ------- 10 + │ ├── cost: 1e+100 + │ ├── key: (1) + │ └── fd: (1)-->(2-4) + └── filters + └── b:3 = 0 [outer=(3), constraints=(/3: [/0 - /0]; tight), fd=()-->(3)] + +exec-ddl +CREATE TABLE inv ( + k INT PRIMARY KEY, + j JSON, + INVERTED INDEX (j) +) +---- + +# Test that FORCE_INVERTED_INDEX does not create a huge cost for scans on +# inverted indexes. +opt +SELECT * FROM inv@{FORCE_INVERTED_INDEX} WHERE j->'a' = '1' +---- +index-join inv + ├── columns: k:1!null j:2 + ├── immutable + ├── stats: [rows=111.1111] + ├── cost: 803.595556 + ├── key: (1) + ├── fd: (1)-->(2) + └── scan inv@inv_j_idx + ├── columns: k:1!null + ├── inverted constraint: /5/1 + │ └── spans: ["7a\x00\x01*\x02\x00", "7a\x00\x01*\x02\x00"] + ├── flags: + ├── stats: [rows=111.1111, distinct(5)=100, null(5)=0] + ├── cost: 132.464444 + └── key: (1) + exec-ddl CREATE TABLE b (x INT, y INT, z INT, s STRING, INDEX xs (x, s), INDEX xyz (x, y, z)) ----