Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
921586f
fix(replace): apply parent-side foreign key actions during REPLACE
ck89119 Jun 23, 2026
33db07d
Merge branch 'main' into issue-24951-main
mergify[bot] Jun 23, 2026
a4af0e3
fix(replace): address review on parent-side FK handling
ck89119 Jun 23, 2026
c43253d
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 11, 2026
34f6a99
fix(plan): fail closed for unsafe replace FK actions
ck89119 Jul 11, 2026
4b87ef0
fix(plan): restrict replace for set default FKs
ck89119 Jul 12, 2026
3bf7617
Merge branch 'main' into issue-24951-main
XuPeng-SH Jul 12, 2026
c910bce
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 13, 2026
33aec1f
Merge branch 'main' into issue-24951-main
ck89119 Jul 13, 2026
feeb9a0
fix(plan): resolve replace FK actions from conflicts
ck89119 Jul 14, 2026
410e787
fix(plan): preserve unique prefix semantics in replace
ck89119 Jul 14, 2026
8da66fd
Merge branch 'main' into issue-24951-main
XuPeng-SH Jul 14, 2026
396b96a
fix(plan): materialize omitted replace conflict defaults
ck89119 Jul 15, 2026
054bb35
fix(plan): format temporal replace conflict defaults
ck89119 Jul 15, 2026
3e49c0d
fix(replace): serialize parent foreign key actions
ck89119 Jul 15, 2026
ce3795b
fix(plan): preserve lock mode in deep copy
ck89119 Jul 15, 2026
aad5a51
fix(plan): preserve lock target positions in deep copy
ck89119 Jul 15, 2026
17a826b
fix(plan): lock foreign key parent keys before validation
ck89119 Jul 17, 2026
9f87499
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 17, 2026
ea06887
fix(plan): support legacy foreign key parent locks
ck89119 Jul 17, 2026
50004c4
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 17, 2026
c2abe7b
fix(plan): preserve replace checks and shared table locks
ck89119 Jul 17, 2026
a624aab
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 18, 2026
8890557
fix(plan): align replace locks with referenced unique keys
ck89119 Jul 18, 2026
f758cbd
fix(plan): order foreign key prerequisite locks
ck89119 Jul 18, 2026
82d0410
fix(replace): address foreign key review feedback
ck89119 Jul 19, 2026
e0c8afb
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 19, 2026
f1b0efc
fix: address latest replace foreign key reviews
ck89119 Jul 20, 2026
d508df7
fix: support dynamic replace foreign key actions
ck89119 Jul 20, 2026
ddbb417
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 20, 2026
38c39f6
fix self-referencing replace cascade
ck89119 Jul 20, 2026
ae752dc
avoid duplicate replace cascade deletes
ck89119 Jul 21, 2026
b6ab524
fix: canonicalize replace unique index lock order
ck89119 Jul 21, 2026
75e3501
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 21, 2026
720bb5d
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 21, 2026
5e5bd66
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 21, 2026
74f3acd
fix: retain old unique index locks for null replace keys
ck89119 Jul 21, 2026
892790c
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 21, 2026
3cbd211
fix: close replace foreign key action gaps
ck89119 Jul 21, 2026
d6c91bc
fix: close replace cascade locking gaps
ck89119 Jul 21, 2026
0a599fa
fix: preserve combined set null row layout
ck89119 Jul 21, 2026
8e72cd6
fix: skip null lock keys
ck89119 Jul 22, 2026
a60e659
fix: globally order merged lock targets
ck89119 Jul 22, 2026
821d6a3
fix: bound merged lock target handling
ck89119 Jul 22, 2026
0d201bc
fix replace foreign key lock correctness
ck89119 Jul 22, 2026
056767c
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 23, 2026
fd44904
Merge remote-tracking branch 'mo/main' into issue-24951-main
ck89119 Jul 23, 2026
7423004
fix(test): restore compile test fmt import
ck89119 Jul 23, 2026
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
19 changes: 16 additions & 3 deletions pkg/frontend/computation_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,11 +597,16 @@ func initExecuteStmtParam(execCtx *ExecCtx, ses *Session, cwft *TxnComputationWr
}
}

// FK-sensitive plans also depend on the current foreign_key_checks session
// value, which does not invalidate prepared statements. Rebuild them for
// every EXECUTE so both enabled->disabled and disabled->enabled transitions
// observe the current setting.
fkSensitive := shouldRebuildPreparePlan(false, preparePlan.Plan)
modeMismatch := prepareStmt.NativeMode != currentNativeMode

// Rebuild the plan when catalog schema, session temporary-table name
// resolution, or the session's compatibility mode changed.
if change || modeMismatch {
// resolution, FK-check state, or compatibility mode changed.
if change || fkSensitive || modeMismatch {
originPrepareStmt := &tree.PrepareStmt{
Name: tree.Identifier(prepareStmt.Name),
Stmt: prepareStmt.PrepareStmt,
Expand Down Expand Up @@ -643,7 +648,7 @@ func initExecuteStmtParam(execCtx *ExecCtx, ses *Session, cwft *TxnComputationWr
// query); recompiling would fail with ErrCantCompileForPrepare on every
// execution, so leave it to the regular compile path (isPrepare=false).
// See: https://github.com/matrixorigin/matrixone/issues/25614
if (change || modeMismatch) && prepareStmt.compile != nil {
if (change || fkSensitive || modeMismatch) && prepareStmt.compile != nil {
prepareStmt.compile.FreeOperator()
prepareStmt.compile.SetIsPrepare(false)
prepareStmt.compile.Release()
Expand Down Expand Up @@ -789,6 +794,14 @@ func shouldCachePrepareCompile(p *plan.Plan) bool {
return !query.GetHasForeignKeyAction()
}

func shouldRebuildPreparePlan(schemaChanged bool, p *plan.Plan) bool {
if schemaChanged || p == nil {
return schemaChanged
}
query := p.GetQuery()
return query != nil && query.GetHasForeignKeyAction()
}

func createCompile(
execCtx *ExecCtx,
ses FeSession,
Expand Down
3 changes: 3 additions & 0 deletions pkg/frontend/mysql_cmd_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4569,6 +4569,9 @@ func checkNodeCanCache(p *plan2.Plan) bool {
return true
}
if q, ok := p.Plan.(*plan2.Plan_Query); ok {
if q.Query.GetHasForeignKeyAction() {
return false
}
for _, node := range q.Query.Nodes {
if node.NotCacheable {
return false
Expand Down
9 changes: 9 additions & 0 deletions pkg/frontend/prepared_fk_cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ func TestShouldCachePrepareCompileForeignKeyActions(t *testing.T) {

require.False(t, shouldCachePrepareCompile(makePlan(plan.Query_UPDATE, true)))
require.False(t, shouldCachePrepareCompile(makePlan(plan.Query_DELETE, true)))
require.False(t, shouldCachePrepareCompile(makePlan(plan.Query_INSERT, true)))

require.True(t, checkNodeCanCache(makePlan(plan.Query_INSERT, false)))
require.False(t, checkNodeCanCache(makePlan(plan.Query_INSERT, true)))

require.False(t, shouldRebuildPreparePlan(false, nil))
require.False(t, shouldRebuildPreparePlan(false, makePlan(plan.Query_INSERT, false)))
require.True(t, shouldRebuildPreparePlan(false, makePlan(plan.Query_INSERT, true)))
require.True(t, shouldRebuildPreparePlan(true, makePlan(plan.Query_INSERT, false)))
}

func TestShouldCachePrepareCompileRejectsIcebergScan(t *testing.T) {
Expand Down
111 changes: 63 additions & 48 deletions pkg/sql/colexec/lockop/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,77 +29,92 @@ import (
var (
minUUID = [16]byte{}
maxUUID = [16]byte{
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8,
math.MaxInt8}
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8,
math.MaxUint8}
)

// GetFetchRowsFunc get FetchLockRowsFunc based on primary key type
func GetFetchRowsFunc(t types.Type) FetchLockRowsFunc {
var fetcher FetchLockRowsFunc
switch t.Oid {
case types.T_bool:
return fetchBoolRows
fetcher = fetchBoolRows
case types.T_bit:
return fetchUint64Rows
fetcher = fetchUint64Rows
case types.T_int8:
return fetchInt8Rows
fetcher = fetchInt8Rows
case types.T_int16:
return fetchInt16Rows
fetcher = fetchInt16Rows
case types.T_int32:
return fetchInt32Rows
fetcher = fetchInt32Rows
case types.T_int64:
return fetchInt64Rows
fetcher = fetchInt64Rows
case types.T_uint8:
return fetchUint8Rows
fetcher = fetchUint8Rows
case types.T_uint16:
return fetchUint16Rows
fetcher = fetchUint16Rows
case types.T_uint32:
return fetchUint32Rows
fetcher = fetchUint32Rows
case types.T_uint64:
return fetchUint64Rows
fetcher = fetchUint64Rows
case types.T_float32:
return fetchFloat32Rows
fetcher = fetchFloat32Rows
case types.T_float64:
return fetchFloat64Rows
fetcher = fetchFloat64Rows
case types.T_date:
return fetchDateRows
fetcher = fetchDateRows
case types.T_year:
return fetchYearRows
fetcher = fetchYearRows
case types.T_time:
return fetchTimeRows
fetcher = fetchTimeRows
case types.T_datetime:
return fetchDateTimeRows
fetcher = fetchDateTimeRows
case types.T_timestamp:
return fetchTimestampRows
fetcher = fetchTimestampRows
case types.T_decimal64:
return fetchDecimal64Rows
fetcher = fetchDecimal64Rows
case types.T_decimal128:
return fetchDecimal128Rows
fetcher = fetchDecimal128Rows
case types.T_decimal256:
return fetchDecimal256Rows
fetcher = fetchDecimal256Rows
case types.T_uuid:
return fetchUUIDRows
fetcher = fetchUUIDRows
case types.T_char, types.T_varchar, types.T_binary, types.T_varbinary:
return fetchVarlenaRows
fetcher = fetchVarlenaRows
// T_json, T_blob, T_array_float32 etc. cannot be PK.
case types.T_enum:
return fetchEnumRows
fetcher = fetchEnumRows
default:
panic(fmt.Sprintf("not support for %s", t.String()))
}
return func(
vec *vector.Vector,
packer *types.Packer,
tp types.Type,
max int,
lockTable bool,
filter RowsFilter,
filterCols []int32,
) (bool, [][]byte, lock.Granularity) {
if !lockTable && vec.IsConstNull() {
return false, nil, lock.Granularity_Row
}
return fetcher(vec, packer, tp, max, lockTable, filter, filterCols)
}
}

func fetchBoolRows(
Expand Down Expand Up @@ -349,7 +364,7 @@ func fetchFloat32Rows(
return parker.Bytes()
}
if lockTable {
min := fn(math.SmallestNonzeroFloat32)
min := fn(-math.MaxFloat32)
max := fn(math.MaxFloat32)
return true, [][]byte{min, max},
lock.Granularity_Range
Expand All @@ -376,7 +391,7 @@ func fetchFloat64Rows(
return parker.Bytes()
}
if lockTable {
min := fn(math.SmallestNonzeroFloat64)
min := fn(-math.MaxFloat64)
max := fn(math.MaxFloat64)
return true, [][]byte{min, max},
lock.Granularity_Range
Expand Down Expand Up @@ -705,7 +720,7 @@ func fetchVarlenaRows(
}

if lockTable {
min := fn([]byte{0})
min := fn([]byte{})
max := fn(nil)
return true, [][]byte{min, max},
lock.Granularity_Range
Expand All @@ -714,7 +729,7 @@ func fetchVarlenaRows(
n := vec.Length()
data, area := vector.MustVarlenaRawData(vec)
if n == 1 {
if filter != nil &&
if vec.GetNulls().Contains(0) || filter != nil &&
!filter(0, filterCols) {
return false, nil, lock.Granularity_Row
}
Expand All @@ -727,7 +742,7 @@ func fetchVarlenaRows(
initialized := false
applied := 0
for i := 0; i < n; i++ {
if filter != nil &&
if vec.GetNulls().Contains(uint64(i)) || filter != nil &&
!filter(i, filterCols) {
continue
}
Expand Down Expand Up @@ -757,7 +772,7 @@ func fetchVarlenaRows(
}
rows := make([][]byte, 0, n)
for idx := range data {
if filter != nil &&
if vec.GetNulls().Contains(uint64(idx)) || filter != nil &&
!filter(idx, filterCols) {
continue
}
Expand Down Expand Up @@ -799,7 +814,7 @@ func fetchFixedRowsWithCompare[T any](
n := vec.Length()
values := vector.MustFixedColWithTypeCheck[T](vec)
if n == 1 {
if filter != nil && !filter(0, filterCols) {
if vec.GetNulls().Contains(0) || filter != nil && !filter(0, filterCols) {
return false, nil, lock.Granularity_Row
}
return true, [][]byte{fn(values[0])}, lock.Granularity_Row
Expand All @@ -809,7 +824,7 @@ func fetchFixedRowsWithCompare[T any](
initialized := false
applied := 0
for row, v := range values {
if filter != nil &&
if vec.GetNulls().Contains(uint64(row)) || filter != nil &&
!filter(row, filterCols) {
continue
}
Expand Down Expand Up @@ -838,7 +853,7 @@ func fetchFixedRowsWithCompare[T any](
}
rows := make([][]byte, 0, n)
for row, v := range values {
if filter != nil &&
if vec.GetNulls().Contains(uint64(row)) || filter != nil &&
!filter(row, filterCols) {
continue
}
Expand Down
Loading
Loading