diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 index 2dc34aae0470ab..80da53a51bde33 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 @@ -53,12 +53,12 @@ statementBase | supportedJobStatement #supportedJobStatementAlias | constraintStatement #constraintStatementAlias | supportedDropStatement #supportedDropStatementAlias + | supportedSetStatement #supportedSetStatementAlias | unsupportedStatement #unsupported ; unsupportedStatement - : unsupportedSetStatement - | unsupoortedUnsetStatement + : unsupoortedUnsetStatement | unsupportedUseStatement | unsupportedDmlStatement | unsupportedKillStatement @@ -798,7 +798,7 @@ functionArgument | dataType ; -unsupportedSetStatement +supportedSetStatement : SET (optionWithType | optionWithoutType) (COMMA (optionWithType | optionWithoutType))* #setOptions | SET identifier AS DEFAULT STORAGE VAULT #setDefaultStorageVault @@ -811,7 +811,7 @@ unsupportedSetStatement ; optionWithType - : (GLOBAL | LOCAL | SESSION) identifier EQ (expression | DEFAULT) + : (GLOBAL | LOCAL | SESSION) identifier EQ (expression | DEFAULT) #setVariableWithType ; optionWithoutType @@ -820,7 +820,7 @@ optionWithoutType | NAMES (charsetName=identifierOrText | DEFAULT) (COLLATE collateName=identifierOrText | DEFAULT)? #setCollate | PASSWORD (FOR userIdentify)? EQ (STRING_LITERAL - | (PASSWORD LEFT_PAREN STRING_LITERAL RIGHT_PAREN)) #setPassword + | (isPlain=PASSWORD LEFT_PAREN STRING_LITERAL RIGHT_PAREN)) #setPassword | LDAP_ADMIN_PASSWORD EQ (STRING_LITERAL | (PASSWORD LEFT_PAREN STRING_LITERAL RIGHT_PAREN)) #setLdapAdminPassword | variable #setVariableWithoutType diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/StorageVaultMgr.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/StorageVaultMgr.java index 71aa71842abeea..762acc7bed28d0 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/StorageVaultMgr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/StorageVaultMgr.java @@ -176,29 +176,33 @@ public void alterStorageVault(StorageVaultType type, Map propert @VisibleForTesting public void setDefaultStorageVault(SetDefaultStorageVaultStmt stmt) throws DdlException { + setDefaultStorageVault(stmt.getStorageVaultName()); + } + + public void setDefaultStorageVault(String vaultName) throws DdlException { Cloud.AlterObjStoreInfoRequest.Builder builder = Cloud.AlterObjStoreInfoRequest.newBuilder(); Cloud.StorageVaultPB.Builder vaultBuilder = Cloud.StorageVaultPB.newBuilder(); - vaultBuilder.setName(stmt.getStorageVaultName()); + vaultBuilder.setName(vaultName); builder.setVault(vaultBuilder.build()); builder.setOp(Operation.SET_DEFAULT_VAULT); String vaultId; - LOG.info("try to set vault {} as default vault", stmt.getStorageVaultName()); + LOG.info("try to set vault {} as default vault", vaultName); try { Cloud.AlterObjStoreInfoResponse resp = MetaServiceProxy.getInstance().alterStorageVault(builder.build()); if (resp.getStatus().getCode() != Cloud.MetaServiceCode.OK) { LOG.warn("failed to set default storage vault response: {}, vault name {}", - resp, stmt.getStorageVaultName()); + resp, vaultName); throw new DdlException(resp.getStatus().getMsg()); } vaultId = resp.getStorageVaultId(); } catch (RpcException e) { LOG.warn("failed to set default storage vault due to RpcException: {}, vault name {}", - e, stmt.getStorageVaultName()); + e, vaultName); throw new DdlException(e.getMessage()); } - LOG.info("succeed to set {} as default vault, vault id {}", stmt.getStorageVaultName(), vaultId); - setDefaultStorageVault(Pair.of(stmt.getStorageVaultName(), vaultId)); + LOG.info("succeed to set {} as default vault, vault id {}", vaultName, vaultId); + setDefaultStorageVault(Pair.of(vaultName, vaultId)); } public void unsetDefaultStorageVault() throws DdlException { diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java index 6642b28424362b..12db90761cbd03 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java +++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java @@ -943,6 +943,11 @@ public void setPassword(SetPassVar stmt) throws DdlException { false /* set by resolver */, false); } + public void setPassword(UserIdentity userIdentity, byte[] password) throws DdlException { + setPasswordInternal(userIdentity, password, null, true /* err on non exist */, + false /* set by resolver */, false); + } + public void replaySetPassword(PrivInfo info) { try { setPasswordInternal(info.getUserIdent(), info.getPasswd(), null, true /* err on non exist */, @@ -986,6 +991,12 @@ public void setLdapPassword(SetLdapPassVar stmt) { LOG.info("finished to set ldap password."); } + public void setLdapPassword(String ldapPassword) { + ldapInfo = new LdapInfo(ldapPassword); + Env.getCurrentEnv().getEditLog().logSetLdapPassword(ldapInfo); + LOG.info("finished to set ldap password."); + } + public void replaySetLdapPassword(LdapInfo info) { ldapInfo = info; if (LOG.isDebugEnabled()) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index 068cf124aa0eff..a15966ed55f3be 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -20,6 +20,8 @@ import org.apache.doris.analysis.ArithmeticExpr.Operator; import org.apache.doris.analysis.BrokerDesc; import org.apache.doris.analysis.ColumnNullableType; +import org.apache.doris.analysis.PassVar; +import org.apache.doris.analysis.SetType; import org.apache.doris.analysis.StorageBackend; import org.apache.doris.analysis.TableName; import org.apache.doris.analysis.TableScanParams; @@ -173,7 +175,19 @@ import org.apache.doris.nereids.DorisParser.SelectClauseContext; import org.apache.doris.nereids.DorisParser.SelectColumnClauseContext; import org.apache.doris.nereids.DorisParser.SelectHintContext; +import org.apache.doris.nereids.DorisParser.SetCharsetContext; +import org.apache.doris.nereids.DorisParser.SetCollateContext; +import org.apache.doris.nereids.DorisParser.SetDefaultStorageVaultContext; +import org.apache.doris.nereids.DorisParser.SetLdapAdminPasswordContext; +import org.apache.doris.nereids.DorisParser.SetNamesContext; import org.apache.doris.nereids.DorisParser.SetOperationContext; +import org.apache.doris.nereids.DorisParser.SetOptionsContext; +import org.apache.doris.nereids.DorisParser.SetPasswordContext; +import org.apache.doris.nereids.DorisParser.SetSystemVariableContext; +import org.apache.doris.nereids.DorisParser.SetTransactionContext; +import org.apache.doris.nereids.DorisParser.SetUserPropertiesContext; +import org.apache.doris.nereids.DorisParser.SetUserVariableContext; +import org.apache.doris.nereids.DorisParser.SetVariableWithTypeContext; import org.apache.doris.nereids.DorisParser.ShowConfigContext; import org.apache.doris.nereids.DorisParser.ShowConstraintContext; import org.apache.doris.nereids.DorisParser.ShowCreateMTMVContext; @@ -398,6 +412,10 @@ import org.apache.doris.nereids.trees.plans.commands.RefreshMTMVCommand; import org.apache.doris.nereids.trees.plans.commands.ReplayCommand; import org.apache.doris.nereids.trees.plans.commands.ResumeMTMVCommand; +import org.apache.doris.nereids.trees.plans.commands.SetDefaultStorageVaultCommand; +import org.apache.doris.nereids.trees.plans.commands.SetOptionsCommand; +import org.apache.doris.nereids.trees.plans.commands.SetTransactionCommand; +import org.apache.doris.nereids.trees.plans.commands.SetUserPropertiesCommand; import org.apache.doris.nereids.trees.plans.commands.ShowConfigCommand; import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCreateMTMVCommand; @@ -438,6 +456,14 @@ import org.apache.doris.nereids.trees.plans.commands.info.RefreshMTMVInfo; import org.apache.doris.nereids.trees.plans.commands.info.ResumeMTMVInfo; import org.apache.doris.nereids.trees.plans.commands.info.RollupDefinition; +import org.apache.doris.nereids.trees.plans.commands.info.SetCharsetAndCollateVarOp; +import org.apache.doris.nereids.trees.plans.commands.info.SetLdapPassVarOp; +import org.apache.doris.nereids.trees.plans.commands.info.SetNamesVarOp; +import org.apache.doris.nereids.trees.plans.commands.info.SetPassVarOp; +import org.apache.doris.nereids.trees.plans.commands.info.SetSessionVarOp; +import org.apache.doris.nereids.trees.plans.commands.info.SetUserDefinedVarOp; +import org.apache.doris.nereids.trees.plans.commands.info.SetUserPropertyVarOp; +import org.apache.doris.nereids.trees.plans.commands.info.SetVarOp; import org.apache.doris.nereids.trees.plans.commands.info.ShowCreateMTMVInfo; import org.apache.doris.nereids.trees.plans.commands.info.SimpleColumnDefinition; import org.apache.doris.nereids.trees.plans.commands.info.StepPartition; @@ -3833,4 +3859,111 @@ public LogicalPlan visitShowConfig(ShowConfigContext ctx) { } return command; } + + @Override + public SetOptionsCommand visitSetOptions(SetOptionsContext ctx) { + List setVarOpList = new ArrayList<>(1); + for (Object child : ctx.children) { + if (child instanceof RuleNode) { + setVarOpList.add(typedVisit((RuleNode) child)); + } + } + return new SetOptionsCommand(setVarOpList); + } + + @Override + public SetVarOp visitSetSystemVariable(SetSystemVariableContext ctx) { + SetType type = SetType.DEFAULT; + if (ctx.GLOBAL() != null) { + type = SetType.GLOBAL; + } else if (ctx.LOCAL() != null || ctx.SESSION() != null) { + type = SetType.SESSION; + } + String name = stripQuotes(ctx.identifier().getText()); + Expression expression = ctx.expression() != null ? typedVisit(ctx.expression()) : null; + return new SetSessionVarOp(type, name, expression); + } + + @Override + public SetVarOp visitSetVariableWithType(SetVariableWithTypeContext ctx) { + SetType type = SetType.DEFAULT; + if (ctx.GLOBAL() != null) { + type = SetType.GLOBAL; + } else if (ctx.LOCAL() != null || ctx.SESSION() != null) { + type = SetType.SESSION; + } + String name = stripQuotes(ctx.identifier().getText()); + Expression expression = ctx.expression() != null ? typedVisit(ctx.expression()) : null; + return new SetSessionVarOp(type, name, expression); + } + + @Override + public SetVarOp visitSetPassword(SetPasswordContext ctx) { + String user; + String host; + boolean isDomain; + String passwordText; + UserIdentity userIdentity = null; + if (ctx.userIdentify() != null) { + user = stripQuotes(ctx.userIdentify().user.getText()); + host = ctx.userIdentify().host != null ? stripQuotes(ctx.userIdentify().host.getText()) : "%"; + isDomain = ctx.userIdentify().ATSIGN() != null; + userIdentity = new UserIdentity(user, host, isDomain); + } + passwordText = stripQuotes(ctx.STRING_LITERAL().getText()); + return new SetPassVarOp(userIdentity, new PassVar(passwordText, ctx.isPlain != null)); + } + + @Override + public SetVarOp visitSetNames(SetNamesContext ctx) { + return new SetNamesVarOp(); + } + + @Override + public SetVarOp visitSetCharset(SetCharsetContext ctx) { + String charset = ctx.charsetName != null ? stripQuotes(ctx.charsetName.getText()) : null; + return new SetCharsetAndCollateVarOp(charset); + } + + @Override + public SetVarOp visitSetCollate(SetCollateContext ctx) { + String charset = ctx.charsetName != null ? stripQuotes(ctx.charsetName.getText()) : null; + String collate = ctx.collateName != null ? stripQuotes(ctx.collateName.getText()) : null; + return new SetCharsetAndCollateVarOp(charset, collate); + } + + @Override + public SetVarOp visitSetLdapAdminPassword(SetLdapAdminPasswordContext ctx) { + String passwordText = stripQuotes(ctx.STRING_LITERAL().getText()); + boolean isPlain = ctx.PASSWORD() != null; + return new SetLdapPassVarOp(new PassVar(passwordText, isPlain)); + } + + @Override + public SetVarOp visitSetUserVariable(SetUserVariableContext ctx) { + String name = stripQuotes(ctx.identifier().getText()); + Expression expression = typedVisit(ctx.expression()); + return new SetUserDefinedVarOp(name, expression); + } + + @Override + public SetTransactionCommand visitSetTransaction(SetTransactionContext ctx) { + return new SetTransactionCommand(); + } + + @Override + public SetUserPropertiesCommand visitSetUserProperties(SetUserPropertiesContext ctx) { + String user = ctx.user != null ? stripQuotes(ctx.user.getText()) : null; + Map userPropertiesMap = visitPropertyItemList(ctx.propertyItemList()); + List setUserPropertyVarOpList = new ArrayList<>(userPropertiesMap.size()); + for (Map.Entry entry : userPropertiesMap.entrySet()) { + setUserPropertyVarOpList.add(new SetUserPropertyVarOp(user, entry.getKey(), entry.getValue())); + } + return new SetUserPropertiesCommand(user, setUserPropertyVarOpList); + } + + @Override + public SetDefaultStorageVaultCommand visitSetDefaultStorageVault(SetDefaultStorageVaultContext ctx) { + return new SetDefaultStorageVaultCommand(stripQuotes(ctx.identifier().getText())); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java index 73ea61fcface45..988ca381304f8e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java @@ -165,6 +165,10 @@ public enum PlanType { DROP_CATALOG_RECYCLE_BIN_COMMAND, UNSUPPORTED_COMMAND, CREATE_TABLE_LIKE_COMMAND, + SET_OPTIONS_COMMAND, + SET_TRANSACTION_COMMAND, + SET_USER_PROPERTIES_COMMAND, + SET_DEFAULT_STORAGE_VAULT_COMMAND, PREPARED_COMMAND, EXECUTE_COMMAND, diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/Forward.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/Forward.java index 1f8f0a33eb5773..c175ccc820e28b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/Forward.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/Forward.java @@ -18,10 +18,13 @@ package org.apache.doris.nereids.trees.plans.commands; import org.apache.doris.analysis.RedirectStatus; +import org.apache.doris.qe.ConnectContext; /** * forward to master. */ public interface Forward { RedirectStatus toRedirectStatus(); + + default void afterForwardToMaster(ConnectContext ctx) throws Exception {} } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/NeedAuditEncryption.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/NeedAuditEncryption.java new file mode 100644 index 00000000000000..df1c22ffe531fd --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/NeedAuditEncryption.java @@ -0,0 +1,29 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands; + +/** + * NeedAuditEncryption + */ +public interface NeedAuditEncryption { + + boolean needAuditEncryption(); + + String toSql(); + +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetDefaultStorageVaultCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetDefaultStorageVaultCommand.java new file mode 100644 index 00000000000000..8251f923f34462 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetDefaultStorageVaultCommand.java @@ -0,0 +1,73 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands; + +import org.apache.doris.analysis.StmtType; +import org.apache.doris.catalog.Env; +import org.apache.doris.cloud.catalog.CloudEnv; +import org.apache.doris.common.Config; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.FeConstants; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.StmtExecutor; + +/** + * SetDefaultStorageVaultCommand + */ +public class SetDefaultStorageVaultCommand extends Command implements ForwardWithSync { + private final String vaultName; + + public SetDefaultStorageVaultCommand(String vaultName) { + super(PlanType.SET_DEFAULT_STORAGE_VAULT_COMMAND); + this.vaultName = vaultName; + } + + @Override + public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { + if (Config.isNotCloudMode()) { + throw new AnalysisException("Storage Vault is only supported for cloud mode"); + } + if (!FeConstants.runningUnitTest) { + // In legacy cloud mode, some s3 back-ended storage does need to use storage vault. + if (!((CloudEnv) Env.getCurrentEnv()).getEnableStorageVault()) { + throw new AnalysisException("Your cloud instance doesn't support storage vault"); + } + } + + // check auth + if (!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), PrivPredicate.ADMIN)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "ADMIN"); + } + ctx.getEnv().getStorageVaultMgr().setDefaultStorageVault(vaultName); + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitSetDefaultStorageVault(this, context); + } + + @Override + public StmtType stmtType() { + return StmtType.SET; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetOptionsCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetOptionsCommand.java new file mode 100644 index 00000000000000..d3e280d541d210 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetOptionsCommand.java @@ -0,0 +1,105 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands; + +import org.apache.doris.analysis.RedirectStatus; +import org.apache.doris.analysis.SetType; +import org.apache.doris.analysis.StmtType; +import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.commands.info.SetLdapPassVarOp; +import org.apache.doris.nereids.trees.plans.commands.info.SetPassVarOp; +import org.apache.doris.nereids.trees.plans.commands.info.SetVarOp; +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.StmtExecutor; + +import java.util.List; + +/** + * SetOptionsCommand + */ +public class SetOptionsCommand extends Command implements Forward, NeedAuditEncryption { + private final List setVarOpList; + + public SetOptionsCommand(List setVarOpList) { + super(PlanType.SET_OPTIONS_COMMAND); + this.setVarOpList = setVarOpList; + } + + @Override + public RedirectStatus toRedirectStatus() { + for (SetVarOp varOp : setVarOpList) { + if (varOp.getType() == SetType.GLOBAL || varOp instanceof SetPassVarOp + || varOp instanceof SetLdapPassVarOp) { + return RedirectStatus.FORWARD_WITH_SYNC; + } + } + return RedirectStatus.NO_FORWARD; + } + + @Override + public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { + for (SetVarOp varOp : setVarOpList) { + varOp.validate(ctx); + varOp.run(ctx); + } + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitSetOptionsCommand(this, context); + } + + @Override + public StmtType stmtType() { + return StmtType.SET; + } + + @Override + public boolean needAuditEncryption() { + for (SetVarOp setVarOp : setVarOpList) { + if (setVarOp.needAuditEncryption()) { + return true; + } + } + return false; + } + + @Override + public String toSql() { + StringBuilder sb = new StringBuilder(); + sb.append("SET "); + int idx = 0; + for (SetVarOp variableInfo : setVarOpList) { + if (idx != 0) { + sb.append(", "); + } + sb.append(variableInfo.toSql()); + idx++; + } + return sb.toString(); + } + + @Override + public void afterForwardToMaster(ConnectContext ctx) throws Exception { + for (SetVarOp varOp : setVarOpList) { + varOp.validate(ctx); + varOp.afterForwardToMaster(ctx); + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetTransactionCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetTransactionCommand.java new file mode 100644 index 00000000000000..cd1e13fab79461 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetTransactionCommand.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands; + +import org.apache.doris.analysis.StmtType; +import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.StmtExecutor; + +/** + * SetTransactionCommand + */ +public class SetTransactionCommand extends Command implements ForwardWithSync { + public SetTransactionCommand() { + super(PlanType.SET_TRANSACTION_COMMAND); + } + + @Override + public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { + // do nothing + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitSetTransactionCommand(this, context); + } + + @Override + public StmtType stmtType() { + return StmtType.SET; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetUserPropertiesCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetUserPropertiesCommand.java new file mode 100644 index 00000000000000..f3921decf69fe6 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/SetUserPropertiesCommand.java @@ -0,0 +1,63 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands; + +import org.apache.doris.analysis.StmtType; +import org.apache.doris.common.Pair; +import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.commands.info.SetUserPropertyVarOp; +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.StmtExecutor; + +import java.util.ArrayList; +import java.util.List; + +/** + * SetUserPropertiesCommand + */ +public class SetUserPropertiesCommand extends Command implements ForwardWithSync { + private final String user; + private final List setUserPropertyVarOpList; + + public SetUserPropertiesCommand(String user, List setUserPropertyVarOpList) { + super(PlanType.SET_OPTIONS_COMMAND); + this.user = user != null ? user : ConnectContext.get().getQualifiedUser(); + this.setUserPropertyVarOpList = setUserPropertyVarOpList; + } + + @Override + public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { + List> properties = new ArrayList<>(setUserPropertyVarOpList.size()); + for (SetUserPropertyVarOp op : setUserPropertyVarOpList) { + op.validate(ctx); + properties.add(Pair.of(op.getPropertyKey(), op.getPropertyValue())); + } + ctx.getEnv().getAuth().updateUserPropertyInternal(user, properties, false); + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitSetUserPropertiesCommand(this, context); + } + + @Override + public StmtType stmtType() { + return StmtType.SET; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetCharsetAndCollateVarOp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetCharsetAndCollateVarOp.java new file mode 100644 index 00000000000000..bdbf9d05232f77 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetCharsetAndCollateVarOp.java @@ -0,0 +1,73 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands.info; + +import org.apache.doris.analysis.SetType; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.UserException; +import org.apache.doris.qe.ConnectContext; + +import com.google.common.base.Strings; + +/** + * SetCharsetAndCollateVarOp + */ +public class SetCharsetAndCollateVarOp extends SetVarOp { + private static final String DEFAULT_NAMES = "utf8"; + private String charset; + private String collate; + + public SetCharsetAndCollateVarOp(String charsetName) { + this(charsetName, null); + } + + public SetCharsetAndCollateVarOp(String charsetName, String collate) { + super(SetType.DEFAULT); + this.charset = charsetName; + this.collate = collate; + } + + @Override + public void run(ConnectContext ctx) throws Exception { + // do nothing + } + + @Override + public void validate(ConnectContext ctx) throws UserException { + if (Strings.isNullOrEmpty(charset)) { + charset = DEFAULT_NAMES; + } else { + charset = charset.toLowerCase(); + } + // utf8-superset transform to utf8 + if (charset.startsWith(DEFAULT_NAMES)) { + charset = DEFAULT_NAMES; + } + + if (!charset.equalsIgnoreCase(DEFAULT_NAMES)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_UNKNOWN_CHARACTER_SET, charset); + } + } + + @Override + public String toSql() { + return "NAMES '" + charset + "' COLLATE " + + (Strings.isNullOrEmpty(collate) ? "DEFAULT" : "'" + collate.toLowerCase() + "'"); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetLdapPassVarOp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetLdapPassVarOp.java new file mode 100644 index 00000000000000..df3b42b97a5f7b --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetLdapPassVarOp.java @@ -0,0 +1,73 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands.info; + +import org.apache.doris.analysis.PassVar; +import org.apache.doris.analysis.SetType; +import org.apache.doris.catalog.Env; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.UserException; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.qe.ConnectContext; + +/** + * SetLdapPassVarOp + */ +public class SetLdapPassVarOp extends SetVarOp { + private final PassVar passVar; + + public SetLdapPassVarOp(PassVar passVar) { + super(SetType.DEFAULT); + this.passVar = passVar; + } + + public String getLdapPassword() { + return passVar.getText(); + } + + @Override + public void validate(ConnectContext ctx) throws UserException { + if (!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ctx, PrivPredicate.ADMIN)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, + PrivPredicate.ADMIN.getPrivs().toString()); + } + if (!passVar.isPlain()) { + throw new AnalysisException("Only support set ldap password with plain text"); + } + passVar.analyze(); + } + + @Override + public void run(ConnectContext ctx) throws Exception { + ctx.getEnv().getAuth().setLdapPassword(passVar.getText()); + } + + @Override + public String toSql() { + StringBuilder sb = new StringBuilder("SET LDAP_ADMIN_PASSWORD"); + sb.append(" = '*XXX'"); + return sb.toString(); + } + + @Override + public boolean needAuditEncryption() { + return true; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetNamesVarOp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetNamesVarOp.java new file mode 100644 index 00000000000000..e247fe5ce27464 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetNamesVarOp.java @@ -0,0 +1,35 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands.info; + +import org.apache.doris.analysis.SetType; +import org.apache.doris.qe.ConnectContext; + +/** + * SetNamesVarOp + */ +public class SetNamesVarOp extends SetVarOp { + public SetNamesVarOp() { + super(SetType.DEFAULT); + } + + @Override + public void run(ConnectContext ctx) throws Exception { + // do nothing + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetPassVarOp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetPassVarOp.java new file mode 100644 index 00000000000000..718c9acd4b096c --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetPassVarOp.java @@ -0,0 +1,101 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands.info; + +import org.apache.doris.analysis.PassVar; +import org.apache.doris.analysis.SetType; +import org.apache.doris.analysis.UserIdentity; +import org.apache.doris.catalog.Env; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.UserException; +import org.apache.doris.mysql.privilege.Auth; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.qe.ConnectContext; + +/** + * SetPassVarOp + */ +public class SetPassVarOp extends SetVarOp { + private UserIdentity userIdent; + private PassVar passVar; + + // The password in parameter is a hashed password. + public SetPassVarOp(UserIdentity userIdent, PassVar passVar) { + super(SetType.DEFAULT); + this.userIdent = userIdent; + this.passVar = passVar; + } + + @Override + public void validate(ConnectContext ctx) throws UserException { + boolean isSelf = false; + if (userIdent == null) { + // set userIdent as what current_user() returns + userIdent = ctx.getCurrentUserIdentity(); + isSelf = true; + } else { + userIdent.analyze(); + if (userIdent.equals(ctx.getCurrentUserIdentity())) { + isSelf = true; + } + } + + // Check password + if (passVar != null) { + passVar.analyze(); + } + + // check privs. + // 1. this is user itself + if (isSelf) { + return; + } + + // 2. No user can set password for root expect for root user itself + if (userIdent.getQualifiedUser().equals(Auth.ROOT_USER)) { + throw new AnalysisException("Can not set password for root user, except root itself"); + } + + // 3. user has grant privs + if (!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ctx, PrivPredicate.GRANT)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT"); + } + } + + @Override + public void run(ConnectContext ctx) throws Exception { + ctx.getEnv().getAuth().setPassword(userIdent, passVar.getScrambled()); + } + + @Override + public String toSql() { + StringBuilder sb = new StringBuilder("SET PASSWORD"); + if (userIdent != null) { + sb.append(" FOR ").append(userIdent); + } + sb.append(" = '*XXX'"); + return sb.toString(); + } + + @Override + public boolean needAuditEncryption() { + return true; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetSessionVarOp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetSessionVarOp.java new file mode 100644 index 00000000000000..5ecb175a67f0f0 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetSessionVarOp.java @@ -0,0 +1,151 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands.info; + +import org.apache.doris.analysis.Expr; +import org.apache.doris.analysis.SetType; +import org.apache.doris.analysis.SetVar; +import org.apache.doris.catalog.Env; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.UserException; +import org.apache.doris.common.util.ParseUtil; +import org.apache.doris.common.util.TimeUtils; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.nereids.CascadesContext; +import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.glue.translator.ExpressionTranslator; +import org.apache.doris.nereids.glue.translator.PlanTranslatorContext; +import org.apache.doris.nereids.properties.PhysicalProperties; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; +import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation; +import org.apache.doris.nereids.util.ExpressionUtils; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.GlobalVariable; +import org.apache.doris.qe.SessionVariable; +import org.apache.doris.qe.VariableMgr; +import org.apache.doris.system.HeartbeatFlags; + +import java.util.ArrayList; + +/** + * SetSessionVarOp + */ +public class SetSessionVarOp extends SetVarOp { + private String name; + private final Expression expression; + private Literal value; + private final boolean isDefault; + + /** constructor*/ + public SetSessionVarOp(SetType type, String name, Expression expression) { + super(type); + this.name = name; + this.expression = expression; + this.isDefault = expression == null; + } + + @Override + public void validate(ConnectContext ctx) throws UserException { + if (isDefault) { + value = new StringLiteral("default"); + return; + } + value = ExpressionUtils.analyzeAndFoldToLiteral(ctx, expression); + + if (getType() == SetType.GLOBAL) { + if (!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), PrivPredicate.ADMIN)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "ADMIN"); + } + } + + if (name.equalsIgnoreCase(GlobalVariable.DEFAULT_ROWSET_TYPE)) { + if (value != null && !HeartbeatFlags.isValidRowsetType(value.getStringValue())) { + throw new AnalysisException("Invalid rowset type, now we support {alpha, beta}."); + } + } + + if (name.equalsIgnoreCase(SessionVariable.PREFER_JOIN_METHOD)) { + String val = value.getStringValue(); + if (!val.equalsIgnoreCase("broadcast") && !val.equalsIgnoreCase("shuffle")) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_VALUE_FOR_VAR, + SessionVariable.PREFER_JOIN_METHOD, val); + } + } + + // Check variable time_zone value is valid + if (name.equalsIgnoreCase(SessionVariable.TIME_ZONE)) { + this.value = new StringLiteral(TimeUtils.checkTimeZoneValidAndStandardize(value.getStringValue())); + } + + if (name.equalsIgnoreCase(SessionVariable.EXEC_MEM_LIMIT) + || name.equalsIgnoreCase(SessionVariable.SCAN_QUEUE_MEM_LIMIT)) { + this.value = new StringLiteral(Long.toString(ParseUtil.analyzeDataVolume(value.getStringValue()))); + } + + if (name.equalsIgnoreCase(SessionVariable.FILE_SPLIT_SIZE)) { + try { + this.value = new StringLiteral( + Long.toString(ParseUtil.analyzeDataVolume(value.getStringValue()))); + } catch (Throwable t) { + // The way of handling file_split_size should be same as exec_mem_limit or scan_queue_mem_limit. + // But ParseUtil.analyzeDataVolume() does not accept 0 as a valid value. + // So for compatibility, we set origin value to file_split_size + // when the value is 0 or other invalid value. + this.value = new StringLiteral(value.getStringValue()); + } + } + + if (name.equalsIgnoreCase("is_report_success")) { + name = SessionVariable.ENABLE_PROFILE; + } + } + + public void run(ConnectContext ctx) throws Exception { + VariableMgr.setVar(ctx.getSessionVariable(), translateToLegacyVar(ctx)); + } + + @Override + public String toSql() { + StringBuilder sb = new StringBuilder(); + sb.append(getType().toSql()); + sb.append(" ").append(name).append(" = ").append(value.toSql()); + return sb.toString(); + } + + public void afterForwardToMaster(ConnectContext ctx) throws Exception { + setType(SetType.SESSION); + VariableMgr.setVarForNonMasterFE(ctx.getSessionVariable(), translateToLegacyVar(ctx)); + } + + // TODO delete this method after removing dependence of SetVar in VariableMgr + private SetVar translateToLegacyVar(ConnectContext ctx) { + if (isDefault) { + return new SetVar(getType(), name, null); + } else { + LogicalEmptyRelation plan = new LogicalEmptyRelation( + ConnectContext.get().getStatementContext().getNextRelationId(), new ArrayList<>()); + CascadesContext cascadesContext = CascadesContext.initContext(ctx.getStatementContext(), plan, + PhysicalProperties.ANY); + Expr expr = ExpressionTranslator.translate(value, new PlanTranslatorContext(cascadesContext)); + return new SetVar(getType(), name, expr); + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetUserDefinedVarOp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetUserDefinedVarOp.java new file mode 100644 index 00000000000000..4cb32d6ec18666 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetUserDefinedVarOp.java @@ -0,0 +1,50 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands.info; + +import org.apache.doris.analysis.SetType; +import org.apache.doris.common.UserException; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.util.ExpressionUtils; +import org.apache.doris.qe.ConnectContext; + +/** + * SetUserDefinedVarOp + */ +public class SetUserDefinedVarOp extends SetVarOp { + private final String name; + private final Expression expression; + private Literal value; + + public SetUserDefinedVarOp(String name, Expression expression) { + super(SetType.USER); + this.name = name; + this.expression = expression; + } + + @Override + public void validate(ConnectContext ctx) throws UserException { + value = ExpressionUtils.analyzeAndFoldToLiteral(ctx, expression); + } + + @Override + public void run(ConnectContext ctx) throws Exception { + ctx.setUserVar(name, value.toLegacyLiteral()); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetUserPropertyVarOp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetUserPropertyVarOp.java new file mode 100644 index 00000000000000..12db9218a1676e --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetUserPropertyVarOp.java @@ -0,0 +1,125 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands.info; + +import org.apache.doris.catalog.Env; +import org.apache.doris.cloud.system.CloudSystemInfoService; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Config; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.UserException; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.mysql.privilege.UserProperty; +import org.apache.doris.qe.ConnectContext; + +import com.google.common.base.Strings; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * SetUserPropertyVarOp + */ +public class SetUserPropertyVarOp { + private final String user; + private final String key; + private final String value; + + public SetUserPropertyVarOp(String user, String key, String value) { + this.key = key; + this.value = value; + this.user = user != null ? user : ConnectContext.get().getQualifiedUser(); + } + + public String getPropertyKey() { + return key; + } + + public String getPropertyValue() { + return value; + } + + /**validate*/ + public void validate(ConnectContext ctx) throws UserException { + if (Strings.isNullOrEmpty(key)) { + throw new AnalysisException("User property key is null"); + } + + if (value == null) { + throw new AnalysisException("User property value is null"); + } + + for (Pattern advPattern : UserProperty.ADVANCED_PROPERTIES) { + Matcher matcher = advPattern.matcher(key); + if (matcher.find()) { + if (!Env.getCurrentEnv().getAccessManager() + .checkGlobalPriv(ctx, PrivPredicate.ADMIN)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "ADMIN"); + } + return; + } + } + boolean isSelf = user.equals(ctx.getQualifiedUser()); + for (Pattern commPattern : UserProperty.COMMON_PROPERTIES) { + Matcher matcher = commPattern.matcher(key); + if (matcher.find()) { + if (!isSelf && !Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ctx, + PrivPredicate.ADMIN)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, + "GRANT"); + } + if (Config.isCloudMode()) { + // check value, clusterName is valid. + if (key.equals(UserProperty.DEFAULT_CLOUD_CLUSTER) + && !Strings.isNullOrEmpty(value) + && !((CloudSystemInfoService) Env.getCurrentSystemInfo()) + .getCloudClusterNames().contains(value)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_CLOUD_CLUSTER_ERROR, value); + } + + if (key.equals(UserProperty.DEFAULT_COMPUTE_GROUP) + && !Strings.isNullOrEmpty(value) + && !((CloudSystemInfoService) Env.getCurrentSystemInfo()) + .getCloudClusterNames().contains(value)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_CLOUD_CLUSTER_ERROR, value); + } + } + return; + } + } + + throw new AnalysisException("Unknown property key: " + key); + } + + /**toSql*/ + public String toSql() { + StringBuilder sb = new StringBuilder(); + sb.append("'"); + sb.append(key); + sb.append("' = "); + if (value != null) { + sb.append("'"); + sb.append(value); + sb.append("'"); + } else { + sb.append("NULL"); + } + return sb.toString(); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetVarOp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetVarOp.java new file mode 100644 index 00000000000000..4fff13ca786422 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/SetVarOp.java @@ -0,0 +1,57 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands.info; + +import org.apache.doris.analysis.SetType; +import org.apache.doris.common.UserException; +import org.apache.doris.qe.ConnectContext; + +/** + * SetVarOp + */ +public abstract class SetVarOp { + private SetType type; + + /** constructor*/ + public SetVarOp(SetType type) { + this.type = type; + } + + /**validate*/ + public void validate(ConnectContext ctx) throws UserException {} + + public void run(ConnectContext ctx) throws Exception {} + + public SetType getType() { + return type; + } + + public void setType(SetType type) { + this.type = type; + } + + public String toSql() { + return ""; + } + + public boolean needAuditEncryption() { + return false; + } + + public void afterForwardToMaster(ConnectContext ctx) throws Exception {} +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java index f35e6f8a6400b3..03e2853ffa0d12 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java @@ -43,6 +43,10 @@ import org.apache.doris.nereids.trees.plans.commands.RefreshMTMVCommand; import org.apache.doris.nereids.trees.plans.commands.ReplayCommand; import org.apache.doris.nereids.trees.plans.commands.ResumeMTMVCommand; +import org.apache.doris.nereids.trees.plans.commands.SetDefaultStorageVaultCommand; +import org.apache.doris.nereids.trees.plans.commands.SetOptionsCommand; +import org.apache.doris.nereids.trees.plans.commands.SetTransactionCommand; +import org.apache.doris.nereids.trees.plans.commands.SetUserPropertiesCommand; import org.apache.doris.nereids.trees.plans.commands.ShowConfigCommand; import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCreateMTMVCommand; @@ -201,4 +205,20 @@ default R visitCreateTableLikeCommand(CreateTableLikeCommand createTableLikeComm default R visitShowConfigCommand(ShowConfigCommand showConfigCommand, C context) { return visitCommand(showConfigCommand, context); } + + default R visitSetOptionsCommand(SetOptionsCommand setOptionsCommand, C context) { + return visitCommand(setOptionsCommand, context); + } + + default R visitSetTransactionCommand(SetTransactionCommand setTransactionCommand, C context) { + return visitCommand(setTransactionCommand, context); + } + + default R visitSetUserPropertiesCommand(SetUserPropertiesCommand setUserPropertiesCommand, C context) { + return visitCommand(setUserPropertiesCommand, context); + } + + default R visitSetDefaultStorageVault(SetDefaultStorageVaultCommand setDefaultStorageVaultCommand, C context) { + return visitCommand(setDefaultStorageVaultCommand, context); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java index 67734f66aa17c1..bf4d6e084795f1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java @@ -21,10 +21,18 @@ import org.apache.doris.common.MaterializedViewException; import org.apache.doris.common.NereidsException; import org.apache.doris.common.Pair; +import org.apache.doris.common.UserException; import org.apache.doris.nereids.CascadesContext; +import org.apache.doris.nereids.analyzer.Scope; +import org.apache.doris.nereids.analyzer.UnboundSlot; +import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.properties.PhysicalProperties; +import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer; +import org.apache.doris.nereids.rules.expression.ExpressionRewrite; import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext; +import org.apache.doris.nereids.rules.expression.ExpressionRuleExecutor; import org.apache.doris.nereids.rules.expression.rules.FoldConstantRule; +import org.apache.doris.nereids.rules.expression.rules.ReplaceVariableByLiteral; import org.apache.doris.nereids.trees.TreeNode; import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.And; @@ -50,9 +58,11 @@ import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; +import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter; import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor; import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation; import org.apache.doris.nereids.trees.plans.logical.LogicalUnion; import org.apache.doris.nereids.trees.plans.visitor.ExpressionLineageReplacer; import org.apache.doris.nereids.types.BooleanType; @@ -962,4 +972,48 @@ public static boolean unionConstExprsSatisfyConjuncts(LogicalUnion union, Set()); + LogicalEmptyRelation plan = new LogicalEmptyRelation( + ConnectContext.get().getStatementContext().getNextRelationId(), + new ArrayList<>()); + CascadesContext cascadesContext = CascadesContext.initContext(ctx.getStatementContext(), plan, + PhysicalProperties.ANY); + ExpressionAnalyzer analyzer = new ExpressionAnalyzer(null, scope, cascadesContext, false, false); + Expression boundExpr = UnboundSlotRewriter.INSTANCE.rewrite(expression, null); + Expression analyzedExpr; + try { + analyzedExpr = analyzer.analyze(boundExpr, new ExpressionRewriteContext(cascadesContext)); + } catch (AnalysisException e) { + throw new UserException(expression + " must be constant value"); + } + ExpressionRewriteContext context = new ExpressionRewriteContext(cascadesContext); + ExpressionRuleExecutor executor = new ExpressionRuleExecutor(ImmutableList.of( + ExpressionRewrite.bottomUp(ReplaceVariableByLiteral.INSTANCE) + )); + Expression rewrittenExpression = executor.rewrite(analyzedExpr, context); + Expression foldExpression = FoldConstantRule.evaluate(rewrittenExpression, context); + if (foldExpression instanceof Literal) { + return (Literal) foldExpression; + } else { + throw new UserException(expression + " must be constant value"); + } + } + + private static class UnboundSlotRewriter extends DefaultExpressionRewriter { + public static final UnboundSlotRewriter INSTANCE = new UnboundSlotRewriter(); + + public Expression rewrite(Expression e, Void ctx) { + return e.accept(this, ctx); + } + + @Override + public Expression visitUnboundSlot(UnboundSlot unboundSlot, Void ctx) { + // set exec_mem_limit=21G, '21G' will be parsed as unbound slot + // we need to rewrite it to String Literal '21G' + return new StringLiteral(unboundSlot.getName()); + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java b/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java index 0bbc37cd47b132..2bd3cf9c335245 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java @@ -36,6 +36,7 @@ import org.apache.doris.nereids.analyzer.UnboundTableSink; import org.apache.doris.nereids.glue.LogicalPlanAdapter; import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.commands.NeedAuditEncryption; import org.apache.doris.nereids.trees.plans.commands.insert.InsertIntoTableCommand; import org.apache.doris.nereids.trees.plans.logical.LogicalInlineTable; import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; @@ -259,10 +260,22 @@ private static void logAuditLogImpl(ConnectContext ctx, String origStmt, Stateme auditEventBuilder.setFeIp(FrontendOptions.getLocalHostAddress()); // We put origin query stmt at the end of audit log, for parsing the log more convenient. - if (!ctx.getState().isQuery() && (parsedStmt != null && parsedStmt.needAuditEncryption())) { - auditEventBuilder.setStmt(parsedStmt.toSql()); + if (parsedStmt instanceof LogicalPlanAdapter) { + if (!ctx.getState().isQuery() && (parsedStmt != null + && (((LogicalPlanAdapter) parsedStmt).getLogicalPlan() instanceof NeedAuditEncryption) + && ((NeedAuditEncryption) ((LogicalPlanAdapter) parsedStmt).getLogicalPlan()) + .needAuditEncryption())) { + auditEventBuilder + .setStmt(((NeedAuditEncryption) ((LogicalPlanAdapter) parsedStmt).getLogicalPlan()).toSql()); + } else { + auditEventBuilder.setStmt(origStmt); + } } else { - auditEventBuilder.setStmt(origStmt); + if (!ctx.getState().isQuery() && (parsedStmt != null && parsedStmt.needAuditEncryption())) { + auditEventBuilder.setStmt(parsedStmt.toSql()); + } else { + auditEventBuilder.setStmt(origStmt); + } } auditEventBuilder.setStmtType(getStmtType(parsedStmt)); diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java index 2493b8e6203476..e7f2b4b662bd6d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java @@ -554,6 +554,10 @@ public void setUserVar(SetVar setVar) { userVars.put(setVar.getVariable().toLowerCase(), setVar.getResult()); } + public void setUserVar(String name, LiteralExpr value) { + userVars.put(name.toLowerCase(), value); + } + public @Nullable Literal getLiteralForUserVar(String varName) { varName = varName.toLowerCase(); if (userVars.containsKey(varName)) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java index f4ae8f588737c1..1726e85fb73d99 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java @@ -1200,7 +1200,13 @@ private void forwardToMaster() throws Exception { LOG.debug("need to transfer to Master. stmt: {}", context.getStmtId()); } masterOpExecutor.execute(); - if (parsedStmt instanceof SetStmt) { + if (parsedStmt instanceof LogicalPlanAdapter) { + // for nereids command + if (((LogicalPlanAdapter) parsedStmt).getLogicalPlan() instanceof Forward) { + Forward forward = (Forward) ((LogicalPlanAdapter) parsedStmt).getLogicalPlan(); + forward.afterForwardToMaster(context); + } + } else if (parsedStmt instanceof SetStmt) { SetStmt setStmt = (SetStmt) parsedStmt; setStmt.modifySetVarsForExecute(); for (SetVar var : setStmt.getSetVars()) { diff --git a/regression-test/suites/query_p0/set/test_set_command.groovy b/regression-test/suites/query_p0/set/test_set_command.groovy new file mode 100644 index 00000000000000..8b29304fbe240f --- /dev/null +++ b/regression-test/suites/query_p0/set/test_set_command.groovy @@ -0,0 +1,66 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +suite("test_set_command") { + // setSystemVariable + def default_value = sql """show variables where variable_name = 'insert_timeout';""" + sql """set insert_timeout=97531;""" + def modified_value = sql """show variables where variable_name = 'insert_timeout';""" + assertTrue(modified_value.toString().contains('97531')) + sql """set insert_timeout=DEFAULT;""" + def restored_value = sql """show variables where variable_name = 'insert_timeout';""" + assertEquals(default_value, restored_value) + + default_value = sql """show variables where variable_name = 'insert_timeout';""" + sql """set @@insert_timeout=97531;""" + modified_value = sql """show variables where variable_name = 'insert_timeout';""" + assertTrue(modified_value.toString().contains('97531')) + sql """set @@insert_timeout=DEFAULT;""" + restored_value = sql """show variables where variable_name = 'insert_timeout';""" + assertEquals(default_value, restored_value) + + default_value = sql """show variables where variable_name = 'insert_timeout';""" + sql """set @@session.insert_timeout=97531;""" + modified_value = sql """show variables where variable_name = 'insert_timeout';""" + assertTrue(modified_value.toString().contains('97531')) + sql """set @@session.insert_timeout=DEFAULT;""" + restored_value = sql """show variables where variable_name = 'insert_timeout';""" + assertEquals(default_value, restored_value) + + // setVariableWithType + default_value = sql """show variables where variable_name = 'insert_timeout';""" + sql """set session insert_timeout=97531;""" + modified_value = sql """show variables where variable_name = 'insert_timeout';""" + assertTrue(modified_value.toString().contains('97531')) + sql """set session insert_timeout=DEFAULT;""" + restored_value = sql """show variables where variable_name = 'insert_timeout';""" + assertEquals(default_value, restored_value) + + // setNames do nothing + sql """set names = utf8;""" + + // setCollate do nothing + sql """set names default collate utf_8_ci;""" + + // setTransaction do nothing + sql """set transaction read only;""" + sql """set transaction read write;""" + + // setCharset do nothing + sql """set charset utf8;""" + sql """set charset default;""" +} \ No newline at end of file