feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed 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 physicaltable
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/database"
|
||||
model "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/database"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/table"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/convertor"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/rdb"
|
||||
entity3 "github.com/coze-dev/coze-studio/backend/infra/contract/rdb/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
||||
)
|
||||
|
||||
func CreatePhysicalTable(ctx context.Context, db rdb.RDB, columns []*entity3.Column) (*rdb.CreateTableResponse, error) {
|
||||
table := &entity3.Table{
|
||||
Columns: columns,
|
||||
}
|
||||
// get indexes
|
||||
indexes := make([]*entity3.Index, 0)
|
||||
indexes = append(indexes, &entity3.Index{
|
||||
Name: "PRIMARY",
|
||||
Type: entity3.PrimaryKey,
|
||||
Columns: []string{database.DefaultIDColName},
|
||||
})
|
||||
indexes = append(indexes, &entity3.Index{
|
||||
Name: "idx_uid",
|
||||
Type: entity3.NormalKey,
|
||||
Columns: []string{database.DefaultUidColName, database.DefaultCidColName},
|
||||
})
|
||||
table.Indexes = indexes
|
||||
|
||||
physicalTableRes, err := db.CreateTable(ctx, &rdb.CreateTableRequest{Table: table})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return physicalTableRes, nil
|
||||
}
|
||||
|
||||
func CreateFieldInfo(fieldItems []*model.FieldItem) ([]*model.FieldItem, []*entity3.Column) {
|
||||
columns := make([]*entity3.Column, len(fieldItems))
|
||||
|
||||
fieldID := int64(1)
|
||||
for i, field := range fieldItems {
|
||||
field.AlterID = fieldID
|
||||
field.PhysicalName = GetFieldPhysicsName(fieldID)
|
||||
|
||||
columns[i] = &entity3.Column{
|
||||
Name: GetFieldPhysicsName(fieldID),
|
||||
DataType: convertor.SwitchToDataType(field.Type),
|
||||
NotNull: field.MustRequired,
|
||||
Comment: &field.Desc,
|
||||
}
|
||||
|
||||
if field.Type == table.FieldItemType_Text && !field.MustRequired {
|
||||
columns[i].DefaultValue = ptr.Of("")
|
||||
}
|
||||
|
||||
fieldID++ // field is incremented begin from 1
|
||||
}
|
||||
|
||||
columns = append(columns, GetDefaultColumns()...)
|
||||
|
||||
return fieldItems, columns
|
||||
}
|
||||
|
||||
func GetDefaultColumns() []*entity3.Column {
|
||||
return getDefaultColumns()
|
||||
}
|
||||
|
||||
func getDefaultColumns() []*entity3.Column {
|
||||
return []*entity3.Column{
|
||||
{
|
||||
Name: database.DefaultIDColName,
|
||||
DataType: entity3.TypeBigInt,
|
||||
NotNull: true,
|
||||
AutoIncrement: true,
|
||||
},
|
||||
{
|
||||
Name: database.DefaultUidColName,
|
||||
DataType: entity3.TypeVarchar,
|
||||
NotNull: true,
|
||||
},
|
||||
{
|
||||
Name: database.DefaultCidColName,
|
||||
DataType: entity3.TypeVarchar,
|
||||
NotNull: true,
|
||||
},
|
||||
{
|
||||
Name: database.DefaultCreateTimeColName,
|
||||
DataType: entity3.TypeTimestamp,
|
||||
NotNull: true,
|
||||
DefaultValue: ptr.Of("CURRENT_TIMESTAMP"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func GetTablePhysicsName(tableID int64) string {
|
||||
return fmt.Sprintf("table_%d", tableID)
|
||||
}
|
||||
|
||||
func GetFieldPhysicsName(fieldID int64) string {
|
||||
return fmt.Sprintf("f_%d", fieldID)
|
||||
}
|
||||
|
||||
// UpdateFieldInfo handles field information updates.
|
||||
// 1. If alterID exists, use alterID to update existing fields.
|
||||
// 2. If alterID does not exist, add new fields.
|
||||
// 3. Delete fields that have alterIDs not present in the new list.
|
||||
func UpdateFieldInfo(newFieldItems []*database.FieldItem, existingFieldItems []*database.FieldItem) ([]*database.FieldItem, []*entity3.Column, []string, error) {
|
||||
existingFieldMap := make(map[int64]*database.FieldItem)
|
||||
maxAlterID := int64(-1)
|
||||
for _, field := range existingFieldItems {
|
||||
if field.AlterID > 0 {
|
||||
existingFieldMap[field.AlterID] = field
|
||||
maxAlterID = max(maxAlterID, field.AlterID)
|
||||
}
|
||||
}
|
||||
|
||||
newFieldIDs := make(map[int64]bool)
|
||||
|
||||
updatedColumns := make([]*entity3.Column, 0, len(newFieldItems))
|
||||
updatedFieldItems := make([]*database.FieldItem, 0, len(newFieldItems))
|
||||
|
||||
for _, field := range newFieldItems {
|
||||
if field.AlterID > 0 {
|
||||
// update field
|
||||
newFieldIDs[field.AlterID] = true
|
||||
field.PhysicalName = GetFieldPhysicsName(field.AlterID)
|
||||
updatedFieldItems = append(updatedFieldItems, field)
|
||||
|
||||
updatedColumns = append(updatedColumns, &entity3.Column{
|
||||
Name: GetFieldPhysicsName(field.AlterID),
|
||||
DataType: convertor.SwitchToDataType(field.Type),
|
||||
NotNull: field.MustRequired,
|
||||
Comment: &field.Desc,
|
||||
})
|
||||
} else {
|
||||
fieldID := maxAlterID + 1 // auto increment begin from existing maxAlterID
|
||||
maxAlterID++
|
||||
field.AlterID = fieldID
|
||||
field.PhysicalName = GetFieldPhysicsName(fieldID)
|
||||
updatedFieldItems = append(updatedFieldItems, field)
|
||||
|
||||
updatedColumns = append(updatedColumns, &entity3.Column{
|
||||
Name: GetFieldPhysicsName(fieldID),
|
||||
DataType: convertor.SwitchToDataType(field.Type),
|
||||
NotNull: field.MustRequired,
|
||||
Comment: &field.Desc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
droppedColumns := make([]string, 0, len(existingFieldMap))
|
||||
// get dropped columns
|
||||
for alterID := range existingFieldMap {
|
||||
if !newFieldIDs[alterID] {
|
||||
droppedColumns = append(droppedColumns, GetFieldPhysicsName(alterID))
|
||||
}
|
||||
}
|
||||
|
||||
return updatedFieldItems, updatedColumns, droppedColumns, nil
|
||||
}
|
||||
|
||||
// UpdatePhysicalTableWithDrops 更新物理表结构,包括显式指定要删除的列
|
||||
func UpdatePhysicalTableWithDrops(ctx context.Context, db rdb.RDB, existingTable *entity3.Table, newColumns []*entity3.Column, droppedColumns []string, tableName string) error {
|
||||
// 创建列名到列的映射
|
||||
existingColumnMap := make(map[string]*entity3.Column)
|
||||
for _, col := range existingTable.Columns {
|
||||
existingColumnMap[col.Name] = col
|
||||
}
|
||||
|
||||
// 收集要添加和修改的列
|
||||
var columnsToAdd, columnsToModify []*entity3.Column
|
||||
|
||||
// 查找要添加和修改的列
|
||||
for _, newCol := range newColumns {
|
||||
if _, exists := existingColumnMap[newCol.Name]; exists {
|
||||
columnsToModify = append(columnsToModify, newCol)
|
||||
} else {
|
||||
columnsToAdd = append(columnsToAdd, newCol)
|
||||
}
|
||||
}
|
||||
|
||||
// 应用变更到物理表
|
||||
if len(columnsToAdd) > 0 || len(columnsToModify) > 0 || len(droppedColumns) > 0 {
|
||||
// build AlterTableRequest
|
||||
alterReq := &rdb.AlterTableRequest{
|
||||
TableName: tableName,
|
||||
Operations: getOperation(columnsToAdd, columnsToModify, droppedColumns),
|
||||
}
|
||||
|
||||
// 执行表结构变更
|
||||
_, err := db.AlterTable(ctx, alterReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getOperation 将列的添加、修改和删除操作转换为 AlterTableOperation 数组
|
||||
func getOperation(columnsToAdd, columnsToModify []*entity3.Column, droppedColumns []string) []*rdb.AlterTableOperation {
|
||||
operations := make([]*rdb.AlterTableOperation, 0)
|
||||
|
||||
// 处理添加列操作
|
||||
for _, column := range columnsToAdd {
|
||||
operations = append(operations, &rdb.AlterTableOperation{
|
||||
Action: entity3.AddColumn,
|
||||
Column: column,
|
||||
})
|
||||
}
|
||||
|
||||
// 处理修改列操作
|
||||
for _, column := range columnsToModify {
|
||||
operations = append(operations, &rdb.AlterTableOperation{
|
||||
Action: entity3.ModifyColumn,
|
||||
Column: column,
|
||||
})
|
||||
}
|
||||
|
||||
// 处理删除列操作
|
||||
for _, columnName := range droppedColumns {
|
||||
operations = append(operations, &rdb.AlterTableOperation{
|
||||
Action: entity3.DropColumn,
|
||||
Column: &entity3.Column{Name: columnName},
|
||||
})
|
||||
}
|
||||
|
||||
return operations
|
||||
}
|
||||
|
||||
func GetTemplateTypeMap() map[table.FieldItemType]string {
|
||||
return map[table.FieldItemType]string{
|
||||
table.FieldItemType_Boolean: "false",
|
||||
table.FieldItemType_Number: "0",
|
||||
table.FieldItemType_Date: "0001-01-01 00:00:00",
|
||||
table.FieldItemType_Text: "",
|
||||
table.FieldItemType_Float: "0",
|
||||
}
|
||||
}
|
||||
|
||||
func GetCreateTimeField() *database.FieldItem {
|
||||
return &database.FieldItem{
|
||||
Name: database.DefaultCreateTimeColName,
|
||||
Desc: "create time",
|
||||
Type: table.FieldItemType_Date,
|
||||
MustRequired: false,
|
||||
IsSystemField: true,
|
||||
AlterID: 103,
|
||||
PhysicalName: database.DefaultCreateTimeColName,
|
||||
}
|
||||
}
|
||||
|
||||
func GetUidField() *database.FieldItem {
|
||||
return &database.FieldItem{
|
||||
Name: database.DefaultUidColName,
|
||||
Desc: "user id",
|
||||
Type: table.FieldItemType_Text,
|
||||
MustRequired: false,
|
||||
IsSystemField: true,
|
||||
AlterID: 101,
|
||||
PhysicalName: database.DefaultUidColName,
|
||||
}
|
||||
}
|
||||
|
||||
func GetConnectIDField() *database.FieldItem {
|
||||
return &database.FieldItem{
|
||||
Name: database.DefaultCidColName,
|
||||
Desc: "connector id",
|
||||
Type: table.FieldItemType_Text,
|
||||
MustRequired: false,
|
||||
IsSystemField: true,
|
||||
AlterID: 104,
|
||||
PhysicalName: database.DefaultCidColName,
|
||||
}
|
||||
}
|
||||
|
||||
func GetIDField() *database.FieldItem {
|
||||
return &database.FieldItem{
|
||||
Name: database.DefaultIDColName,
|
||||
Desc: "primary_key",
|
||||
Type: table.FieldItemType_Number,
|
||||
MustRequired: false,
|
||||
IsSystemField: true,
|
||||
AlterID: 102,
|
||||
PhysicalName: database.DefaultIDColName,
|
||||
}
|
||||
}
|
||||
|
||||
func GetDisplayCreateTimeField() *database.FieldItem {
|
||||
return &database.FieldItem{
|
||||
Name: database.DefaultCreateTimeDisplayColName,
|
||||
Desc: "create time",
|
||||
Type: table.FieldItemType_Date,
|
||||
MustRequired: false,
|
||||
IsSystemField: true,
|
||||
AlterID: 103,
|
||||
PhysicalName: database.DefaultCreateTimeDisplayColName,
|
||||
}
|
||||
}
|
||||
|
||||
func GetDisplayUidField() *database.FieldItem {
|
||||
return &database.FieldItem{
|
||||
Name: database.DefaultUidDisplayColName,
|
||||
Desc: "user id",
|
||||
Type: table.FieldItemType_Text,
|
||||
MustRequired: false,
|
||||
IsSystemField: true,
|
||||
AlterID: 101,
|
||||
PhysicalName: database.DefaultUidDisplayColName,
|
||||
}
|
||||
}
|
||||
|
||||
func GetDisplayIDField() *database.FieldItem {
|
||||
return &database.FieldItem{
|
||||
Name: database.DefaultIDDisplayColName,
|
||||
Desc: "primary_key",
|
||||
Type: table.FieldItemType_Number,
|
||||
MustRequired: false,
|
||||
IsSystemField: true,
|
||||
AlterID: 102,
|
||||
PhysicalName: database.DefaultIDDisplayColName,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user