Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pkg/common/moerr/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ const (
ErrWrongUsage uint16 = 20321
ErrUpdateTableUsed uint16 = 20322
ErrWindowInvalidUse uint16 = 20323
ErrViewSelectTmpTable uint16 = 20324

// Group 4: unexpected state and io errors
ErrInvalidState uint16 = 20400
Expand Down Expand Up @@ -427,6 +428,7 @@ var errorMsgRefer = map[uint16]moErrorMsgItem{
ErrWrongUsage: {ER_WRONG_USAGE, []string{MySQLDefaultSqlState}, "Incorrect usage of %s and %s"},
ErrUpdateTableUsed: {ER_UPDATE_TABLE_USED, []string{MySQLDefaultSqlState}, "You can't specify target table '%-.192s' for update in FROM clause"},
ErrWindowInvalidUse: {ER_WINDOW_INVALID_WINDOW_FUNC_USE, []string{"HY000"}, "You cannot use the window function '%s' in this context"},
ErrViewSelectTmpTable: {ER_VIEW_SELECT_TMPTABLE, []string{MySQLDefaultSqlState}, "View's SELECT refers to a temporary table '%-.192s'"},

// Group 4: unexpected state or file io error
ErrInvalidState: {ER_UNKNOWN_ERROR, []string{MySQLDefaultSqlState}, "invalid state %s"},
Expand Down Expand Up @@ -1498,6 +1500,10 @@ func NewViewWrongList(ctx context.Context) *Error {
return newError(ctx, ErrViewWrongList)
}

func NewViewSelectTmpTable(ctx context.Context, table string) *Error {
return newError(ctx, ErrViewSelectTmpTable, table)
}

func NewOperandColumns(ctx context.Context, columns int) *Error {
return newError(ctx, ErrOperandColumns, columns)
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/common/moerr/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ func TestWindowInvalidUseMySQLError(t *testing.T) {
require.Equal(t, "You cannot use the window function 'row_number' in this context", err.Error())
}

func TestViewSelectTmpTableMySQLError(t *testing.T) {
err := NewViewSelectTmpTable(context.Background(), "temp_for_view")
require.Equal(t, ErrViewSelectTmpTable, err.ErrorCode())
require.Equal(t, ER_VIEW_SELECT_TMPTABLE, err.MySQLCode())
require.Equal(t, MySQLDefaultSqlState, err.SqlState())
require.Equal(t, "View's SELECT refers to a temporary table 'temp_for_view'", err.Error())
}

func TestLockWaitTimeoutMySQLError(t *testing.T) {
err := NewLockWaitTimeout(context.Background())
require.Equal(t, ErrLockWaitTimeout, err.ErrorCode())
Expand Down
16 changes: 16 additions & 0 deletions pkg/sql/plan/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ import (
)

func bindAndOptimizeSelectQuery(stmtType plan.Query_StatementType, ctx CompilerContext, stmt *tree.Select, isPrepareStmt bool, skipStats bool) (*Plan, error) {
return bindAndOptimizeSelectQueryWithValidator(stmtType, ctx, stmt, isPrepareStmt, skipStats, nil)
}

func bindAndOptimizeSelectQueryWithValidator(
stmtType plan.Query_StatementType,
ctx CompilerContext,
stmt *tree.Select,
isPrepareStmt bool,
skipStats bool,
validate func(*Query) error,
) (*Plan, error) {
start := time.Now()
defer func() {
v2.TxnStatementBuildSelectHistogram.Observe(time.Since(start).Seconds())
Expand All @@ -48,6 +59,11 @@ func bindAndOptimizeSelectQuery(stmtType plan.Query_StatementType, ctx CompilerC
ctx.SetViews(bindCtx.views)

builder.qry.Steps = append(builder.qry.Steps, rootId)
if validate != nil {
if err = validate(builder.qry); err != nil {
return nil, err
}
}
query, err := builder.createQuery()
if err != nil {
return nil, err
Expand Down
21 changes: 19 additions & 2 deletions pkg/sql/plan/build_ddl.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,35 @@ func createTableSQLForCatalog(ctx CompilerContext, stmt *tree.CreateTable) strin

func genViewTableDef(ctx CompilerContext, stmt *tree.Select, colNames tree.IdentifierList) (*plan.TableDef, error) {
var tableDef plan.TableDef
validate := func(query *Query) error {
for _, node := range query.Nodes {
if node == nil || node.NodeType != plan.Node_TABLE_SCAN || node.TableDef == nil {
continue
}
if !node.TableDef.IsTemporary && node.TableDef.TableType != catalog.SystemTemporaryTable {
continue
}

tableName := node.TableDef.OriginalName
if tableName == "" {
tableName = node.TableDef.Name
}
return moerr.NewViewSelectTmpTable(ctx.GetContext(), tableName)
}
return nil
}

// check view statement
var stmtPlan *Plan
var err error
switch s := stmt.Select.(type) {
case *tree.ParenSelect:
stmtPlan, err = bindAndOptimizeSelectQuery(plan.Query_SELECT, ctx, s.Select, false, true)
stmtPlan, err = bindAndOptimizeSelectQueryWithValidator(plan.Query_SELECT, ctx, s.Select, false, true, validate)
if err != nil {
return nil, err
}
default:
stmtPlan, err = bindAndOptimizeSelectQuery(plan.Query_SELECT, ctx, stmt, false, true)
stmtPlan, err = bindAndOptimizeSelectQueryWithValidator(plan.Query_SELECT, ctx, stmt, false, true, validate)
if err != nil {
return nil, err
}
Expand Down
27 changes: 27 additions & 0 deletions pkg/sql/plan/build_ddl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,33 @@ func TestBuildCreateViewExplicitColumnList(t *testing.T) {
})
}

func TestBuildCreateViewRejectsTemporaryTable(t *testing.T) {
tests := []string{
"create view v as select * from nation",
"create view v as select 1 from nation where false",
"create view v as select * from (select * from nation) n",
"create view v as select (select n_name from nation limit 1)",
"create view v as (select * from nation)",
}

for _, sql := range tests {
t.Run(sql, func(t *testing.T) {
ctx := NewMockCompilerContext(false)
ctx.tables["nation"].IsTemporary = true

stmt, err := parsers.ParseOne(t.Context(), dialect.MYSQL, sql, 1)
require.NoError(t, err)
defer stmt.Free()

_, err = BuildPlan(ctx, stmt, false)
require.Error(t, err)
require.True(t, moerr.IsMoErrCode(err, moerr.ErrViewSelectTmpTable))
require.Equal(t, uint16(moerr.ER_VIEW_SELECT_TMPTABLE), err.(*moerr.Error).MySQLCode())
require.Equal(t, "View's SELECT refers to a temporary table 'nation'", err.Error())
})
}
}

func TestBuildTemporaryTableMarksCatalogRelkind(t *testing.T) {
const rootSQL = "create temporary table temp_marked (id int, unique key uk_id (id))"
ctx := &rootSQLCompilerContext{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,25 +132,19 @@ drop database test_temp_db;
select * from test_temp_db.temp_in_db;
Unknown database test_temp_db
drop database if exists temp_view;
[unknown result because it is related to issue#23700]
create database temp_view;
[unknown result because it is related to issue#23700]
use temp_view;
[unknown result because it is related to issue#23700]
create temporary table temp_for_view (
id int,
name varchar(50)
);
[unknown result because it is related to issue#23700]
insert into temp_for_view values (1, 'alice');
[unknown result because it is related to issue#23700]
create view view_on_temp as
select * from temp_for_view;
[unknown result because it is related to issue#23700]
View's SELECT refers to a temporary table 'temp_for_view'
drop view if exists view_on_temp;
[unknown result because it is related to issue#23700]
drop table temp_for_view;
[unknown result because it is related to issue#23700]
drop database temp_view;
delimiter //
create procedure test_temp_in_proc()
begin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,6 @@ select * from test_temp_db.temp_in_db;

-- 测试用例 9.1: 基于临时表创建视图
-- 预期结果: 不支持
-- @bvt:issue#23700
drop database if exists temp_view;
create database temp_view;
use temp_view;
Expand All @@ -260,7 +259,7 @@ select * from temp_for_view;
-- 清理
drop view if exists view_on_temp;
drop table temp_for_view;
-- @bvt:issue
drop database temp_view;

-- ============================================================================
-- 测试分类 11: 存储过程和函数限制
Expand Down
Loading