[feature](hint)(mysql-compatibility) Support general hints in select statement (#7664)

Support general hints.

Sql example:

```sql
SELECT /*+ one_hint(1000000) another_hint(k = "v")*/ 1;
```

hints syntax is:

```
/*+ [ HINT_NAME( [ key [ =value ]? ]* ) ]+ */
```

- support multi hints, sep with space
- hint name could be any string in identifier format
- hint could have zero or more parameters, sep with comma
- hint parameter must have one key
- hint parameter could have zero or one value
- hint parameter‘s key and value connected by equal sign
This commit is contained in:
vinson0526
2022-01-09 16:59:08 +08:00
committed by GitHub
parent 7254bcc8ca
commit ff4284f3fa
4 changed files with 100 additions and 12 deletions

View File

@ -18,10 +18,12 @@
package org.apache.doris.analysis;
import java.math.BigDecimal;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.doris.analysis.AlterDatabaseQuotaStmt.QuotaType;
@ -261,7 +263,7 @@ terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE, KW_ALIAS, KW_ALL, KW_A
KW_RANDOM, KW_RANGE, KW_READ, KW_RECOVER, KW_REGEXP, KW_RELEASE, KW_RENAME,
KW_REPAIR, KW_REPEATABLE, KW_REPOSITORY, KW_REPOSITORIES, KW_REPLACE, KW_REPLACE_IF_NOT_NULL, KW_REPLICA, KW_RESOURCE, KW_RESOURCES, KW_RESTORE, KW_RETURNS, KW_RESUME, KW_REVOKE,
KW_RIGHT, KW_ROLE, KW_ROLES, KW_ROLLBACK, KW_ROLLUP, KW_ROUTINE, KW_ROW, KW_ROWS,
KW_S3, KW_SCHEMA, KW_SCHEMAS, KW_SECOND, KW_SELECT, KW_SEMI, KW_SERIALIZABLE, KW_SESSION, KW_SET, KW_SETS, KW_SET_VAR, KW_SHOW, KW_SIGNED,
KW_S3, KW_SCHEMA, KW_SCHEMAS, KW_SECOND, KW_SELECT, KW_SEMI, KW_SERIALIZABLE, KW_SESSION, KW_SET, KW_SETS, KW_SHOW, KW_SIGNED,
KW_SKEW,
KW_SMALLINT, KW_SNAPSHOT, KW_SONAME, KW_SPLIT, KW_START, KW_STATUS, KW_STATS, KW_STOP, KW_STORAGE, KW_STREAM, KW_STRING, KW_STRUCT,
KW_SUM, KW_SUPERUSER, KW_SYNC, KW_SYSTEM,
@ -398,7 +400,12 @@ nonterminal InlineViewRef inline_view_ref;
nonterminal JoinOperator join_operator;
nonterminal ArrayList<String> opt_plan_hints;
nonterminal ArrayList<String> opt_sort_hints;
nonterminal HashMap<String, String> select_hints_list, opt_select_hints;
nonterminal Map<String, Map<String, String>> opt_select_hints;
nonterminal Map<String, Map<String, String>> query_hints;
nonterminal Map.Entry<String, Map<String, String>> query_hint;
nonterminal Map<String, String> query_hint_parameters;
nonterminal Map.Entry<String, String> query_hint_parameter;
nonterminal String query_hint_parameter_key;
nonterminal Expr sign_chain_expr;
nonterminal Qualifier opt_set_qualifier;
nonterminal Operation set_op;
@ -3851,16 +3858,65 @@ select_clause ::=
:}
;
select_hints_list ::=
select_hints_list:map COMMA variable_name:k equal literal_or_ident:v
query_hint_parameter_key ::=
literal:k
{:
map.put(k, v);
RESULT = k.getStringValue();
:}
| variable_name:k
{:
RESULT = k;
:}
;
query_hint_parameter ::=
query_hint_parameter_key:k
{:
RESULT = new AbstractMap.SimpleEntry<String, String>(k, null);
:}
| query_hint_parameter_key:k equal literal_or_ident:v
{:
RESULT = new AbstractMap.SimpleEntry<String, String>(k, v);
:}
;
query_hint_parameters ::=
query_hint_parameters:map COMMA query_hint_parameter:kv
{:
map.put(kv.getKey(), kv.getValue());
RESULT = map;
:}
| variable_name:k equal literal_or_ident:v
| query_hint_parameter:kv
{:
HashMap<String, String> map = new HashMap<String, String>();
map.put(k, v);
Map<String, String> map = new HashMap<>();
map.put(kv.getKey(), kv.getValue());
RESULT = map;
:}
|
{:
RESULT = new HashMap<String, String>();
:}
;
query_hint ::=
ident:k LPAREN query_hint_parameters:v RPAREN
{:
RESULT = new AbstractMap.SimpleEntry<String, Map<String, String>>(k.toLowerCase(Locale.ROOT), v);
:}
;
query_hints ::=
query_hints:map query_hint:kv
{:
map.computeIfAbsent(kv.getKey(), k -> new HashMap<>());
map.get(kv.getKey()).putAll(kv.getValue());
RESULT = map;
:}
| query_hint:kv
{:
Map<String, Map<String, String>> map = new HashMap<>();
map.put(kv.getKey(), kv.getValue());
RESULT = map;
:}
;
@ -3877,7 +3933,7 @@ literal_or_ident ::=
;
opt_select_hints ::=
COMMENTED_PLAN_HINT_START KW_SET_VAR LPAREN select_hints_list:map RPAREN COMMENTED_PLAN_HINT_END
COMMENTED_PLAN_HINT_START query_hints:map COMMENTED_PLAN_HINT_END
{:
RESULT = map;
:}

View File

@ -30,6 +30,8 @@ import java.util.Map;
* Select list items plus distinct clause.
*/
public class SelectList {
private static final String SET_VAR_KEY = "set_var";
private boolean isDistinct;
private Map<String, String> optHints;
@ -79,8 +81,10 @@ public class SelectList {
return optHints;
}
public void setOptHints(Map<String, String> optHints) {
this.optHints = optHints;
public void setOptHints(Map<String, Map<String, String>> optHints) {
if (optHints != null) {
this.optHints = optHints.get(SET_VAR_KEY);
}
}
public void reset() {

View File

@ -349,7 +349,6 @@ import org.apache.doris.qe.SqlModeHelper;
keywordMap.put("serializable", new Integer(SqlParserSymbols.KW_SERIALIZABLE));
keywordMap.put("session", new Integer(SqlParserSymbols.KW_SESSION));
keywordMap.put("set", new Integer(SqlParserSymbols.KW_SET));
keywordMap.put("set_var", new Integer(SqlParserSymbols.KW_SET_VAR));
keywordMap.put("sets", new Integer(SqlParserSymbols.KW_SETS));
keywordMap.put("show", new Integer(SqlParserSymbols.KW_SHOW));
keywordMap.put("signed", new Integer(SqlParserSymbols.KW_SIGNED));

View File

@ -523,6 +523,35 @@ public class SelectStmtTest {
Assert.assertFalse(dorisAssert.query(sql8).explainQuery().contains("`table2`.`__DORIS_DELETE_SIGN__` = 0"));
}
@Test
public void testSelectHints() throws Exception {
ConnectContext ctx = UtFrameUtils.createDefaultCtx();
// hint with integer literal parameter
String sql = "select /*+ common_hint(1) */ 1";
UtFrameUtils.parseAndAnalyzeStmt(sql, ctx);
// hint with float literal parameter
sql = "select /*+ common_hint(1.1) */ 1";
UtFrameUtils.parseAndAnalyzeStmt(sql, ctx);
// hint with string literal parameter
sql = "select /*+ common_hint(\"string\") */ 1";
UtFrameUtils.parseAndAnalyzeStmt(sql, ctx);
// hint with key value parameter
sql = "select /*+ common_hint(k = \"v\") */ 1";
UtFrameUtils.parseAndAnalyzeStmt(sql, ctx);
// hint with multi-parameters
sql = "select /*+ common_hint(1, 1.1, \"string\") */ 1";
UtFrameUtils.parseAndAnalyzeStmt(sql, ctx);
// multi-hints
sql = "select /*+ common_hint(1) another_hint(2) */ 1";
UtFrameUtils.parseAndAnalyzeStmt(sql, ctx);
}
@Test
public void testSelectHintSetVar() throws Exception {
String sql = "SELECT sleep(3);";