[enhancement](plsql) Support show procedure and show create procedure (#31297) (#31763)

This commit is contained in:
Vallish Pai
2024-03-08 07:30:45 +05:30
committed by yiguolei
parent 1b783aaa7f
commit 4bfecac08a
14 changed files with 290 additions and 15 deletions

View File

@ -38,6 +38,8 @@ statement
| CALL name=multipartIdentifier LEFT_PAREN (expression (COMMA expression)*)? RIGHT_PAREN #callProcedure
| (ALTER | CREATE (OR REPLACE)? | REPLACE) (PROCEDURE | PROC) name=multipartIdentifier LEFT_PAREN .*? RIGHT_PAREN .*? #createProcedure
| DROP (PROCEDURE | PROC) (IF EXISTS)? name=multipartIdentifier #dropProcedure
| SHOW PROCEDURE STATUS (LIKE pattern=valueExpression | whereClause)? #showProcedureStatus
| SHOW CREATE PROCEDURE name=multipartIdentifier #showCreateProcedure
;
statementBase

View File

@ -99,6 +99,8 @@ stmt :
| create_procedure_stmt
| declare_stmt
| drop_procedure_stmt
| show_procedure_stmt
| show_create_procedure_stmt
| exec_stmt
| exit_stmt
| fetch_stmt
@ -333,6 +335,13 @@ create_procedure_stmt :
drop_procedure_stmt:
DROP (PROCEDURE | PROC) (IF EXISTS)? name=multipartIdentifier
;
show_procedure_stmt:
SHOW PROCEDURE STATUS (LIKE pattern=valueExpression | whereClause)?
;
show_create_procedure_stmt:
SHOW CREATE PROCEDURE name=multipartIdentifier
;
create_routine_params :
LEFT_PAREN RIGHT_PAREN

View File

@ -161,6 +161,8 @@ import org.apache.doris.nereids.DorisParser.SelectColumnClauseContext;
import org.apache.doris.nereids.DorisParser.SelectHintContext;
import org.apache.doris.nereids.DorisParser.SetOperationContext;
import org.apache.doris.nereids.DorisParser.ShowConstraintContext;
import org.apache.doris.nereids.DorisParser.ShowCreateProcedureContext;
import org.apache.doris.nereids.DorisParser.ShowProcedureStatusContext;
import org.apache.doris.nereids.DorisParser.SimpleColumnDefContext;
import org.apache.doris.nereids.DorisParser.SimpleColumnDefsContext;
import org.apache.doris.nereids.DorisParser.SingleStatementContext;
@ -370,6 +372,8 @@ import org.apache.doris.nereids.trees.plans.commands.PauseMTMVCommand;
import org.apache.doris.nereids.trees.plans.commands.RefreshMTMVCommand;
import org.apache.doris.nereids.trees.plans.commands.ResumeMTMVCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowCreateProcedureCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowProcedureStatusCommand;
import org.apache.doris.nereids.trees.plans.commands.UpdateCommand;
import org.apache.doris.nereids.trees.plans.commands.info.AlterMTMVInfo;
import org.apache.doris.nereids.trees.plans.commands.info.AlterMTMVPropertyInfo;
@ -3372,4 +3376,16 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
FuncNameInfo procedureName = new FuncNameInfo(nameParts);
return ParserUtils.withOrigin(ctx, () -> new DropProcedureCommand(procedureName, getOriginSql(ctx)));
}
@Override
public LogicalPlan visitShowProcedureStatus(ShowProcedureStatusContext ctx) {
return ParserUtils.withOrigin(ctx, () -> new ShowProcedureStatusCommand());
}
@Override
public LogicalPlan visitShowCreateProcedure(ShowCreateProcedureContext ctx) {
List<String> nameParts = visitMultipartIdentifier(ctx.name);
FuncNameInfo procedureName = new FuncNameInfo(nameParts);
return ParserUtils.withOrigin(ctx, () -> new ShowCreateProcedureCommand(procedureName));
}
}

View File

@ -143,5 +143,7 @@ public enum PlanType {
CANCEL_MTMV_TASK_COMMAND,
CALL_COMMAND,
CREATE_PROCEDURE_COMMAND,
DROP_PROCEDURE_COMMAND
DROP_PROCEDURE_COMMAND,
SHOW_PROCEDURE_COMMAND,
SHOW_CREATE_PROCEDURE_COMMAND
}

View File

@ -0,0 +1,78 @@
// 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.catalog.Column;
import org.apache.doris.catalog.ScalarType;
import org.apache.doris.nereids.trees.plans.PlanType;
import org.apache.doris.nereids.trees.plans.commands.info.FuncNameInfo;
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.ShowResultSet;
import org.apache.doris.qe.ShowResultSetMetaData;
import org.apache.doris.qe.StmtExecutor;
import com.google.common.collect.ImmutableList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
/**
* show create procedure command
*/
public class ShowCreateProcedureCommand extends Command implements NoForward {
public static final Logger LOG = LogManager.getLogger(ShowCreateProcedureCommand.class);
public static final ImmutableList<String> TITLE_NAMES = new ImmutableList.Builder<String>().add("Procedure")
.add("Create Procedure").build();
private final FuncNameInfo procedureName;
/**
* constructor
*/
public ShowCreateProcedureCommand(FuncNameInfo procedureName) {
super(PlanType.SHOW_PROCEDURE_COMMAND);
this.procedureName = procedureName;
}
public ShowResultSetMetaData getMetaData() {
ShowResultSetMetaData.Builder builder = ShowResultSetMetaData.builder();
for (String title : TITLE_NAMES) {
builder.addColumn(new Column(title, ScalarType.createStringType()));
}
return builder.build();
}
@Override
public void run(ConnectContext ctx, StmtExecutor executor) throws Exception {
List<List<String>> results = new ArrayList<>();
ctx.getPlSqlOperation().getExec().functions.showCreateProcedure(this.procedureName, results);
if (!results.isEmpty()) {
ShowResultSet commonResultSet = new ShowResultSet(getMetaData(), results);
executor.sendResultSet(commonResultSet);
}
}
@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitShowCreateProcedureCommand(this, context);
}
}

View File

@ -0,0 +1,76 @@
// 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.catalog.Column;
import org.apache.doris.catalog.ScalarType;
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.ShowResultSet;
import org.apache.doris.qe.ShowResultSetMetaData;
import org.apache.doris.qe.StmtExecutor;
import com.google.common.collect.ImmutableList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
/**
* show procedure status command
*/
public class ShowProcedureStatusCommand extends Command implements NoForward {
public static final Logger LOG = LogManager.getLogger(ShowProcedureStatusCommand.class);
public static final ImmutableList<String> TITLE_NAMES = new ImmutableList.Builder<String>().add("ProcedureName")
.add("CatalogId").add("DbId").add("PackageName")
.add("OwnerName").add("CreateTime").add("ModifyTime")
.build();
/**
* constructor
*/
public ShowProcedureStatusCommand() {
super(PlanType.SHOW_PROCEDURE_COMMAND);
}
public ShowResultSetMetaData getMetaData() {
ShowResultSetMetaData.Builder builder = ShowResultSetMetaData.builder();
for (String title : TITLE_NAMES) {
builder.addColumn(new Column(title, ScalarType.createStringType()));
}
return builder.build();
}
@Override
public void run(ConnectContext ctx, StmtExecutor executor) throws Exception {
List<List<String>> results = new ArrayList<>();
ctx.getPlSqlOperation().getExec().functions.showProcedure(results);
if (!results.isEmpty()) {
ShowResultSet commonResultSet = new ShowResultSet(getMetaData(), results);
executor.sendResultSet(commonResultSet);
}
}
@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitShowProcedureStatusCommand(this, context);
}
}

View File

@ -41,6 +41,8 @@ import org.apache.doris.nereids.trees.plans.commands.PauseMTMVCommand;
import org.apache.doris.nereids.trees.plans.commands.RefreshMTMVCommand;
import org.apache.doris.nereids.trees.plans.commands.ResumeMTMVCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowCreateProcedureCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowProcedureStatusCommand;
import org.apache.doris.nereids.trees.plans.commands.UpdateCommand;
/** CommandVisitor. */
@ -146,4 +148,12 @@ public interface CommandVisitor<R, C> {
default R visitDropProcedureCommand(DropProcedureCommand dropProcedureCommand, C context) {
return visitCommand(dropProcedureCommand, context);
}
default R visitShowProcedureStatusCommand(ShowProcedureStatusCommand showProcedureStatusCommand, C context) {
return visitCommand(showProcedureStatusCommand, context);
}
default R visitShowCreateProcedureCommand(ShowCreateProcedureCommand showCreateProcedureCommand, C context) {
return visitCommand(showCreateProcedureCommand, context);
}
}

View File

@ -32,6 +32,7 @@ import org.apache.doris.plsql.Exec;
import org.apache.doris.plsql.Scope;
import org.apache.doris.plsql.Var;
import org.apache.doris.plsql.metastore.PlsqlMetaClient;
import org.apache.doris.plsql.metastore.PlsqlProcedureKey;
import org.apache.doris.plsql.metastore.PlsqlStoredProcedure;
import org.apache.doris.qe.ConnectContext;
@ -39,8 +40,11 @@ import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -85,6 +89,35 @@ public class DorisFunctionRegistry implements FunctionRegistry {
return (ConnectContext.get().getDatabase() + "." + name).toUpperCase();
}
@Override
public void showProcedure(List<List<String>> columns) {
Map<PlsqlProcedureKey, PlsqlStoredProcedure> allProc = client.getAllPlsqlStoredProcedures();
for (Map.Entry<PlsqlProcedureKey, PlsqlStoredProcedure> entry : allProc.entrySet()) {
List<String> row = new ArrayList<>();
PlsqlStoredProcedure proc = entry.getValue();
row.add(proc.getName());
row.add(Long.toString(proc.getCatalogId()));
row.add(Long.toString(proc.getDbId()));
row.add(proc.getPackageName());
row.add(proc.getOwnerName());
row.add(proc.getCreateTime());
row.add(proc.getModifyTime());
row.add(proc.getSource());
columns.add(row);
}
}
@Override
public void showCreateProcedure(FuncNameInfo procedureName, List<List<String>> columns) {
List<String> row = new ArrayList<>();
PlsqlStoredProcedure proc = client.getPlsqlStoredProcedure(procedureName.getName(),
procedureName.getCtlId(), procedureName.getDbId());
if (proc != null) {
row.add(proc.getName());
row.add(proc.getSource());
columns.add(row);
}
}
@Override
public boolean exec(FuncNameInfo procedureName, Expr_func_paramsContext ctx) {
@ -196,10 +229,21 @@ public class DorisFunctionRegistry implements FunctionRegistry {
@Override
public void save(FuncNameInfo procedureName, String source, boolean isForce) {
try {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String createTime = f.format(Calendar.getInstance().getTime());
String modifyTime = createTime;
if (isForce) {
// need to get create time and use that.
PlsqlStoredProcedure oldProc = client.getPlsqlStoredProcedure(procedureName.getName(),
procedureName.getCtlId(), procedureName.getDbId());
if (oldProc != null) {
createTime = oldProc.getCreateTime();
}
}
// TODO support packageName
client.addPlsqlStoredProcedure(procedureName.getName(), procedureName.getCtlId(), procedureName.getDbId(),
"",
ConnectContext.get().getQualifiedUser(), source, isForce);
ConnectContext.get().getQualifiedUser(), source, createTime, modifyTime, isForce);
} catch (Exception e) {
throw new RuntimeException("failed to save procedure", e);
}

View File

@ -25,6 +25,8 @@ import org.apache.doris.nereids.PLParser.Create_procedure_stmtContext;
import org.apache.doris.nereids.PLParser.Expr_func_paramsContext;
import org.apache.doris.nereids.trees.plans.commands.info.FuncNameInfo;
import java.util.List;
public interface FunctionRegistry {
boolean exec(FuncNameInfo procedureName, Expr_func_paramsContext ctx);
@ -39,4 +41,8 @@ public interface FunctionRegistry {
void remove(FuncNameInfo procedureName);
void removeCached(String name);
void showProcedure(List<List<String>> columns);
void showCreateProcedure(FuncNameInfo procedureName, List<List<String>> columns);
}

View File

@ -37,6 +37,7 @@ import org.antlr.v4.runtime.ParserRuleContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -71,6 +72,14 @@ public class InMemoryFunctionRegistry implements FunctionRegistry {
procMap.remove(procedureName.toString());
}
public void showProcedure(List<List<String>> columns) {
}
public void showCreateProcedure(FuncNameInfo procedureName, List<List<String>> columns) {
}
@Override
public boolean exec(FuncNameInfo procedureName, Expr_func_paramsContext ctx) {
if (builtinFunctions.exec(procedureName.toString(), ctx)) {
@ -134,7 +143,7 @@ public class InMemoryFunctionRegistry implements FunctionRegistry {
visit(procCtx.procedure_block());
exec.callStackPop();
exec.leaveScope();
for (Map.Entry<String, Var> i : out.entrySet()) { // Set OUT parameters, related to prepare statement.
for (Map.Entry<String, Var> i : out.entrySet()) { // Set OUT parameters, related to prepare statement.
exec.setVariable(i.getKey(), i.getValue());
}
return true;
@ -143,9 +152,8 @@ public class InMemoryFunctionRegistry implements FunctionRegistry {
/**
* Set parameters for user-defined function call
*/
public static void setCallParameters(String procName, Expr_func_paramsContext actual,
ArrayList<Var> actualValues, Create_routine_paramsContext formal, HashMap<String, Var> out,
Exec exec) {
public static void setCallParameters(String procName, Expr_func_paramsContext actual, ArrayList<Var> actualValues,
Create_routine_paramsContext formal, HashMap<String, Var> out, Exec exec) {
if (actual == null || actual.func_param() == null || actualValues == null) {
return;
}
@ -169,8 +177,8 @@ public class InMemoryFunctionRegistry implements FunctionRegistry {
}
Var var = setCallParameter(name, type, len, scale, actualValues.get(i), exec);
exec.trace(actual, "SET PARAM " + name + " = " + var.toString());
if (out != null && a.expr_atom() != null && a.expr_atom().qident() != null && (p.OUT() != null
|| p.INOUT() != null)) {
if (out != null && a.expr_atom() != null && a.expr_atom().qident() != null
&& (p.OUT() != null || p.INOUT() != null)) {
String actualName = a.expr_atom().qident().getText();
if (actualName != null) {
out.put(actualName, var);

View File

@ -48,6 +48,10 @@ public class PlsqlManager implements Writable {
return nameToStoredProcedures.get(plsqlProcedureKey);
}
public Map<PlsqlProcedureKey, PlsqlStoredProcedure> getAllPlsqlStoredProcedures() {
return nameToStoredProcedures;
}
public void addPlsqlStoredProcedure(PlsqlStoredProcedure procedure, boolean isForce) {
PlsqlProcedureKey plsqlProcedureKey = new PlsqlProcedureKey(procedure.getName(), procedure.getCatalogId(),
procedure.getDbId());

View File

@ -35,6 +35,7 @@ import org.apache.doris.thrift.TStatusCode;
import org.apache.thrift.TException;
import java.util.Map;
import java.util.Objects;
public class PlsqlMetaClient {
@ -42,16 +43,18 @@ public class PlsqlMetaClient {
}
public void addPlsqlStoredProcedure(String name, long catalogId, long dbId, String packageName,
String ownerName, String source,
String ownerName, String source, String createTime, String modifyTime,
boolean isForce) {
checkPriv();
if (Env.getCurrentEnv().isMaster()) {
Env.getCurrentEnv().getPlsqlManager()
.addPlsqlStoredProcedure(
new PlsqlStoredProcedure(name, catalogId, dbId, packageName, ownerName, source),
new PlsqlStoredProcedure(name, catalogId, dbId, packageName, ownerName, source,
createTime, modifyTime),
isForce);
} else {
addPlsqlStoredProcedureThrift(name, catalogId, dbId, packageName, ownerName, source, isForce);
addPlsqlStoredProcedureThrift(name, catalogId, dbId, packageName, ownerName, source,
createTime, modifyTime, isForce);
}
}
@ -70,6 +73,11 @@ public class PlsqlMetaClient {
.getPlsqlStoredProcedure(new PlsqlProcedureKey(name, catalogId, dbId));
}
public Map<PlsqlProcedureKey, PlsqlStoredProcedure> getAllPlsqlStoredProcedures() {
return Env.getCurrentEnv().getPlsqlManager()
.getAllPlsqlStoredProcedures();
}
public void addPlsqlPackage(String name, long catalogId, long dbId, String ownerName, String header,
String body) {
checkPriv();
@ -97,10 +105,12 @@ public class PlsqlMetaClient {
protected void addPlsqlStoredProcedureThrift(String name, long catalogId, long dbId, String packageName,
String ownerName,
String source, boolean isForce) {
String source, String createTime, String modifyTime, boolean isForce) {
TPlsqlStoredProcedure tPlsqlStoredProcedure = new TPlsqlStoredProcedure().setName(name)
.setCatalogId(catalogId).setDbId(dbId)
.setPackageName(packageName).setOwnerName(ownerName).setSource(source);
.setPackageName(packageName).setOwnerName(ownerName).setSource(source)
.setCreateTime(createTime).setModifyTime(modifyTime);
TAddPlsqlStoredProcedureRequest tAddPlsqlStoredProcedureRequest = new TAddPlsqlStoredProcedureRequest()
.setPlsqlStoredProcedure(tPlsqlStoredProcedure);
tAddPlsqlStoredProcedureRequest.setIsForce(isForce);

View File

@ -51,14 +51,22 @@ public class PlsqlStoredProcedure implements Writable {
@SerializedName(value = "source")
private String source;
@SerializedName(value = "createTime")
private String createTime;
@SerializedName(value = "modifyTime")
private String modifyTime;
public TPlsqlStoredProcedure toThrift() {
return new TPlsqlStoredProcedure().setName(name).setCatalogId(catalogId).setDbId(dbId)
.setPackageName(packageName).setOwnerName(ownerName).setSource(source);
.setPackageName(packageName).setOwnerName(ownerName).setSource(source)
.setCreateTime(createTime).setModifyTime(modifyTime);
}
public static PlsqlStoredProcedure fromThrift(TPlsqlStoredProcedure procedure) {
return new PlsqlStoredProcedure(procedure.getName(), procedure.getCatalogId(), procedure.getDbId(),
procedure.getPackageName(), procedure.getOwnerName(), procedure.source);
procedure.getPackageName(), procedure.getOwnerName(), procedure.source,
procedure.getCreateTime(), procedure.getModifyTime());
}
@Override