feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
90
backend/domain/memory/database/entity/database.go
Normal file
90
backend/domain/memory/database/entity/database.go
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 entity
|
||||
|
||||
import (
|
||||
"github.com/xuri/excelize/v2"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/common"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/database"
|
||||
)
|
||||
|
||||
type Database = database.Database
|
||||
|
||||
// DatabaseFilter 数据库过滤条件
|
||||
type DatabaseFilter struct {
|
||||
CreatorID *int64
|
||||
SpaceID *int64
|
||||
TableName *string
|
||||
AppID *int64
|
||||
}
|
||||
|
||||
// Pagination pagination
|
||||
type Pagination struct {
|
||||
Total int64
|
||||
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
||||
type TableSheet struct {
|
||||
SheetID int64
|
||||
HeaderLineIdx int64
|
||||
StartLineIdx int64
|
||||
}
|
||||
|
||||
type TableReaderMeta struct {
|
||||
TosMaxLine int64
|
||||
SheetId int64
|
||||
HeaderLineIdx int64
|
||||
StartLineIdx int64
|
||||
ReaderMethod database.TableReadDataMethod
|
||||
ReadLineCnt int64
|
||||
Schema []*common.DocTableColumn
|
||||
}
|
||||
|
||||
type TableReaderSheetData struct {
|
||||
Columns []*common.DocTableColumn
|
||||
SampleData [][]string
|
||||
}
|
||||
|
||||
type ExcelExtraInfo struct {
|
||||
Sheets []*common.DocTableSheet
|
||||
ExtensionName string // 扩展名
|
||||
FileSize int64 // 文件大小
|
||||
SourceFileID int64
|
||||
TosURI string
|
||||
}
|
||||
|
||||
type LocalTableMeta struct {
|
||||
ExcelFile *excelize.File // xlsx格式文件
|
||||
RawLines [][]string // csv|xls 的全部内容
|
||||
SheetsNameList []string
|
||||
SheetsRowCount []int
|
||||
ExtensionName string // 扩展名
|
||||
FileSize int64 // 文件大小
|
||||
}
|
||||
|
||||
type ColumnInfo struct {
|
||||
ColumnType common.ColumnType
|
||||
ContainsEmptyValue bool
|
||||
}
|
||||
|
||||
type SelectFieldList struct {
|
||||
FieldID []string
|
||||
IsDistinct bool
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 convertor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/table"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/rdb/entity"
|
||||
)
|
||||
|
||||
func ConvertResultSetToString(resultSet *entity.ResultSet, physicalToFieldName map[string]string, physicalToFieldType map[string]table.FieldItemType) []map[string]string {
|
||||
records := make([]map[string]string, 0, len(resultSet.Rows))
|
||||
|
||||
for _, row := range resultSet.Rows {
|
||||
record := make(map[string]string)
|
||||
|
||||
for physicalName, value := range row {
|
||||
if logicalName, exists := physicalToFieldName[physicalName]; exists {
|
||||
if value == nil {
|
||||
record[logicalName] = ""
|
||||
} else {
|
||||
fieldType, hasType := physicalToFieldType[physicalName]
|
||||
if hasType {
|
||||
convertedValue := ConvertDBValueToString(value, fieldType)
|
||||
record[logicalName] = convertedValue
|
||||
} else {
|
||||
record[logicalName] = fmt.Sprintf("%v", value)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if value == nil {
|
||||
record[physicalName] = ""
|
||||
} else {
|
||||
record[physicalName] = ConvertSystemFieldToString(physicalName, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
records = append(records, record)
|
||||
}
|
||||
|
||||
return records
|
||||
}
|
||||
|
||||
func ConvertResultSet(resultSet *entity.ResultSet, physicalToFieldName map[string]string, physicalToFieldType map[string]table.FieldItemType) []map[string]any {
|
||||
records := make([]map[string]any, 0, len(resultSet.Rows))
|
||||
|
||||
for _, row := range resultSet.Rows {
|
||||
record := make(map[string]any)
|
||||
|
||||
for physicalName, value := range row {
|
||||
if logicalName, exists := physicalToFieldName[physicalName]; exists {
|
||||
record[logicalName] = value
|
||||
} else {
|
||||
record[physicalName] = value
|
||||
}
|
||||
}
|
||||
records = append(records, record)
|
||||
}
|
||||
|
||||
return records
|
||||
}
|
||||
218
backend/domain/memory/database/internal/convertor/types.go
Normal file
218
backend/domain/memory/database/internal/convertor/types.go
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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 convertor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"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/infra/contract/rdb/entity"
|
||||
)
|
||||
|
||||
const (
|
||||
TimeFormat = "2006-01-02 15:04:05"
|
||||
)
|
||||
|
||||
func SwitchToDataType(itemType table.FieldItemType) entity.DataType {
|
||||
switch itemType {
|
||||
case table.FieldItemType_Text:
|
||||
return entity.TypeVarchar
|
||||
case table.FieldItemType_Number:
|
||||
return entity.TypeBigInt
|
||||
case table.FieldItemType_Date:
|
||||
return entity.TypeTimestamp
|
||||
case table.FieldItemType_Float:
|
||||
return entity.TypeDouble
|
||||
case table.FieldItemType_Boolean:
|
||||
return entity.TypeBoolean
|
||||
default:
|
||||
// 默认使用 VARCHAR
|
||||
return entity.TypeVarchar
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertValueByType converts a string value to the specified type.
|
||||
func ConvertValueByType(value string, fieldType table.FieldItemType) (interface{}, error) {
|
||||
if value == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch fieldType {
|
||||
case table.FieldItemType_Number:
|
||||
intVal, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("cannot convert %s to number", value)
|
||||
}
|
||||
|
||||
return intVal, nil
|
||||
|
||||
case table.FieldItemType_Float:
|
||||
if floatVal, err := strconv.ParseFloat(value, 64); err == nil {
|
||||
return floatVal, nil
|
||||
}
|
||||
|
||||
return 0.0, fmt.Errorf("cannot convert %s to float", value)
|
||||
|
||||
case table.FieldItemType_Boolean:
|
||||
if boolVal, err := strconv.ParseBool(value); err == nil {
|
||||
return boolVal, nil
|
||||
}
|
||||
|
||||
// if err, try 0/1
|
||||
if value == "0" {
|
||||
return false, nil
|
||||
}
|
||||
if value == "1" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("cannot convert %s to boolean", value)
|
||||
|
||||
case table.FieldItemType_Date:
|
||||
t, err := time.Parse(TimeFormat, value) // database use this format
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot convert %s to date", value)
|
||||
}
|
||||
|
||||
return t, nil
|
||||
|
||||
case table.FieldItemType_Text:
|
||||
return value, nil
|
||||
|
||||
default:
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertDBValueToString converts a database value to a string.
|
||||
func ConvertDBValueToString(value interface{}, fieldType table.FieldItemType) string {
|
||||
switch fieldType {
|
||||
case table.FieldItemType_Text:
|
||||
if byteArray, ok := value.([]uint8); ok {
|
||||
return string(byteArray)
|
||||
}
|
||||
|
||||
case table.FieldItemType_Number:
|
||||
switch v := value.(type) {
|
||||
case int64:
|
||||
return strconv.FormatInt(v, 10)
|
||||
case []uint8:
|
||||
return string(v)
|
||||
}
|
||||
|
||||
case table.FieldItemType_Float:
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
return strconv.FormatFloat(v, 'f', -1, 64)
|
||||
case []uint8:
|
||||
return string(v)
|
||||
}
|
||||
|
||||
case table.FieldItemType_Boolean:
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
return strconv.FormatBool(v)
|
||||
case int64:
|
||||
return strconv.FormatBool(v != 0)
|
||||
case []uint8:
|
||||
boolStr := string(v)
|
||||
if boolStr == "1" || boolStr == "true" {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
case table.FieldItemType_Date:
|
||||
switch v := value.(type) {
|
||||
case time.Time:
|
||||
return v.Format(TimeFormat)
|
||||
case []uint8:
|
||||
return string(v)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", value)
|
||||
}
|
||||
|
||||
// ConvertSystemFieldToString converts a system field value to a string.
|
||||
func ConvertSystemFieldToString(fieldName string, value interface{}) string {
|
||||
switch fieldName {
|
||||
case database.DefaultIDColName:
|
||||
if intVal, ok := value.(int64); ok {
|
||||
return strconv.FormatInt(intVal, 10)
|
||||
}
|
||||
case database.DefaultUidColName, database.DefaultCidColName:
|
||||
if byteArray, ok := value.([]uint8); ok {
|
||||
return string(byteArray)
|
||||
}
|
||||
case database.DefaultCreateTimeColName:
|
||||
switch v := value.(type) {
|
||||
case time.Time:
|
||||
return v.Format(TimeFormat)
|
||||
case []uint8:
|
||||
// 尝试解析字符串表示的时间
|
||||
return string(v)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", value)
|
||||
}
|
||||
|
||||
func ConvertLogicOperator(logic database.Logic) entity.LogicalOperator {
|
||||
switch logic {
|
||||
case database.Logic_And:
|
||||
return entity.AND
|
||||
case database.Logic_Or:
|
||||
return entity.OR
|
||||
default:
|
||||
return entity.AND // 默认使用AND
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertOperator(op database.Operation) entity.Operator {
|
||||
switch op {
|
||||
case database.Operation_EQUAL:
|
||||
return entity.OperatorEqual
|
||||
case database.Operation_NOT_EQUAL:
|
||||
return entity.OperatorNotEqual
|
||||
case database.Operation_GREATER_THAN:
|
||||
return entity.OperatorGreater
|
||||
case database.Operation_GREATER_EQUAL:
|
||||
return entity.OperatorGreaterEqual
|
||||
case database.Operation_LESS_THAN:
|
||||
return entity.OperatorLess
|
||||
case database.Operation_LESS_EQUAL:
|
||||
return entity.OperatorLessEqual
|
||||
case database.Operation_IN:
|
||||
return entity.OperatorIn
|
||||
case database.Operation_NOT_IN:
|
||||
return entity.OperatorNotIn
|
||||
case database.Operation_LIKE:
|
||||
return entity.OperatorLike
|
||||
case database.Operation_NOT_LIKE:
|
||||
return entity.OperatorNotLike
|
||||
case database.Operation_IS_NULL:
|
||||
return entity.OperatorIsNull
|
||||
case database.Operation_IS_NOT_NULL:
|
||||
return entity.OperatorIsNotNull
|
||||
default:
|
||||
return entity.OperatorEqual
|
||||
}
|
||||
}
|
||||
138
backend/domain/memory/database/internal/dal/agent_to_database.go
Normal file
138
backend/domain/memory/database/internal/dal/agent_to_database.go
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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 dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"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/dal/model"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal/query"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
|
||||
)
|
||||
|
||||
var (
|
||||
agentToDatabaseOnce sync.Once
|
||||
singletonAgentToDb *AgentToDatabaseImpl
|
||||
)
|
||||
|
||||
type AgentToDatabaseImpl struct {
|
||||
IDGen idgen.IDGenerator
|
||||
query *query.Query
|
||||
}
|
||||
|
||||
func NewAgentToDatabaseDAO(db *gorm.DB, idGen idgen.IDGenerator) *AgentToDatabaseImpl {
|
||||
agentToDatabaseOnce.Do(func() {
|
||||
singletonAgentToDb = &AgentToDatabaseImpl{
|
||||
IDGen: idGen,
|
||||
query: query.Use(db),
|
||||
}
|
||||
})
|
||||
|
||||
return singletonAgentToDb
|
||||
}
|
||||
|
||||
func (d *AgentToDatabaseImpl) BatchCreate(ctx context.Context, relations []*database.AgentToDatabase) ([]int64, error) {
|
||||
if len(relations) == 0 {
|
||||
return []int64{}, nil
|
||||
}
|
||||
|
||||
ids, err := d.IDGen.GenMultiIDs(ctx, len(relations))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("generate IDs failed: %v", err)
|
||||
}
|
||||
|
||||
agentToDbs := make([]*model.AgentToDatabase, len(relations))
|
||||
for i, relation := range relations {
|
||||
agentToDbs[i] = &model.AgentToDatabase{
|
||||
ID: ids[i],
|
||||
AgentID: relation.AgentID,
|
||||
DatabaseID: relation.DatabaseID,
|
||||
IsDraft: relation.TableType == table.TableType_DraftTable,
|
||||
PromptDisable: relation.PromptDisabled,
|
||||
}
|
||||
}
|
||||
|
||||
res := d.query.AgentToDatabase
|
||||
err = res.WithContext(ctx).CreateInBatches(agentToDbs, 10)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("batch create agent to database relations failed: %v", err)
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (d *AgentToDatabaseImpl) BatchDelete(ctx context.Context, basicRelations []*database.AgentToDatabaseBasic) error {
|
||||
if len(basicRelations) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := d.query.AgentToDatabase
|
||||
|
||||
for _, relation := range basicRelations {
|
||||
q := res.WithContext(ctx).
|
||||
Where(res.AgentID.Eq(relation.AgentID)).
|
||||
Where(res.DatabaseID.Eq(relation.DatabaseID))
|
||||
|
||||
_, err := q.Delete()
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete relation failed for agent=%d, database=%d: %v",
|
||||
relation.AgentID, relation.DatabaseID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *AgentToDatabaseImpl) ListByAgentID(ctx context.Context, agentID int64, tableType table.TableType) ([]*database.AgentToDatabase, error) {
|
||||
res := d.query.AgentToDatabase
|
||||
|
||||
q := res.WithContext(ctx).Where(res.AgentID.Eq(agentID))
|
||||
|
||||
if tableType == table.TableType_DraftTable {
|
||||
q = q.Where(res.IsDraft.Is(true))
|
||||
} else {
|
||||
q = q.Where(res.IsDraft.Is(false))
|
||||
}
|
||||
|
||||
records, err := q.Find()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list agent to database relations failed: %v", err)
|
||||
}
|
||||
|
||||
relations := make([]*database.AgentToDatabase, 0, len(records))
|
||||
for _, info := range records {
|
||||
tType := table.TableType_OnlineTable
|
||||
if info.IsDraft {
|
||||
tType = table.TableType_DraftTable
|
||||
}
|
||||
relation := &database.AgentToDatabase{
|
||||
AgentID: info.AgentID,
|
||||
DatabaseID: info.DatabaseID,
|
||||
TableType: tType,
|
||||
PromptDisabled: info.PromptDisable,
|
||||
}
|
||||
relations = append(relations, relation)
|
||||
}
|
||||
|
||||
return relations, nil
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* 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 dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"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/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal/model"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal/query"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
||||
"github.com/coze-dev/coze-studio/backend/types/errno"
|
||||
)
|
||||
|
||||
var (
|
||||
draftOnce sync.Once
|
||||
singletonDraft *DraftImpl
|
||||
)
|
||||
|
||||
type DraftImpl struct {
|
||||
IDGen idgen.IDGenerator
|
||||
query *query.Query
|
||||
}
|
||||
|
||||
func NewDraftDatabaseDAO(db *gorm.DB, idGen idgen.IDGenerator) *DraftImpl {
|
||||
draftOnce.Do(func() {
|
||||
singletonDraft = &DraftImpl{
|
||||
IDGen: idGen,
|
||||
query: query.Use(db),
|
||||
}
|
||||
})
|
||||
|
||||
return singletonDraft
|
||||
}
|
||||
|
||||
func (d *DraftImpl) CreateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database, draftID, onlineID int64, physicalTableName string) (*entity.Database, error) {
|
||||
now := time.Now().UnixMilli()
|
||||
|
||||
draftInfo := &model.DraftDatabaseInfo{
|
||||
ID: draftID,
|
||||
AppID: database.AppID,
|
||||
SpaceID: database.SpaceID,
|
||||
RelatedOnlineID: onlineID,
|
||||
IsVisible: 1, // 默认可见
|
||||
PromptDisabled: func() int32 {
|
||||
if database.PromptDisabled {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}(),
|
||||
TableName_: database.TableName,
|
||||
TableDesc: database.TableDesc,
|
||||
TableField: database.FieldList,
|
||||
CreatorID: database.CreatorID,
|
||||
IconURI: database.IconURI,
|
||||
PhysicalTableName: physicalTableName,
|
||||
RwMode: int64(database.RwMode),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
table := tx.DraftDatabaseInfo
|
||||
|
||||
err := table.WithContext(ctx).Create(draftInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
database.CreatedAtMs = now
|
||||
database.UpdatedAtMs = now
|
||||
|
||||
return database, nil
|
||||
}
|
||||
|
||||
// Get 获取草稿数据库信息
|
||||
func (d *DraftImpl) Get(ctx context.Context, id int64) (*entity.Database, error) {
|
||||
res := d.query.DraftDatabaseInfo
|
||||
|
||||
info, err := res.WithContext(ctx).Where(res.ID.Eq(id)).First()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errorx.New(errno.ErrMemoryDatabaseNotFoundCode)
|
||||
}
|
||||
return nil, fmt.Errorf("query draft database failed: %v", err)
|
||||
}
|
||||
|
||||
// 构建返回的数据库对象
|
||||
db := &entity.Database{
|
||||
ID: info.ID,
|
||||
SpaceID: info.SpaceID,
|
||||
CreatorID: info.CreatorID,
|
||||
IconURI: info.IconURI,
|
||||
|
||||
AppID: info.AppID,
|
||||
IsVisible: info.IsVisible == 1,
|
||||
PromptDisabled: info.PromptDisabled == 1,
|
||||
TableName: info.TableName_,
|
||||
TableDesc: info.TableDesc,
|
||||
FieldList: info.TableField,
|
||||
Status: table.BotTableStatus_Online,
|
||||
ActualTableName: info.PhysicalTableName,
|
||||
RwMode: table.BotTableRWMode(info.RwMode),
|
||||
OnlineID: &info.RelatedOnlineID,
|
||||
DraftID: &info.ID,
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func (d *DraftImpl) MGet(ctx context.Context, ids []int64) ([]*entity.Database, error) {
|
||||
if len(ids) == 0 {
|
||||
return []*entity.Database{}, nil
|
||||
}
|
||||
|
||||
res := d.query.DraftDatabaseInfo
|
||||
|
||||
records, err := res.WithContext(ctx).
|
||||
Where(res.ID.In(ids...)).
|
||||
Find()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("batch query draft database failed: %v", err)
|
||||
}
|
||||
|
||||
databases := make([]*entity.Database, 0, len(records))
|
||||
for _, info := range records {
|
||||
|
||||
db := &entity.Database{
|
||||
ID: info.ID,
|
||||
SpaceID: info.SpaceID,
|
||||
CreatorID: info.CreatorID,
|
||||
IconURI: info.IconURI,
|
||||
|
||||
AppID: info.AppID,
|
||||
IsVisible: info.IsVisible == 1,
|
||||
PromptDisabled: info.PromptDisabled == 1,
|
||||
TableName: info.TableName_,
|
||||
TableDesc: info.TableDesc,
|
||||
FieldList: info.TableField,
|
||||
Status: table.BotTableStatus_Online,
|
||||
ActualTableName: info.PhysicalTableName,
|
||||
RwMode: table.BotTableRWMode(info.RwMode),
|
||||
OnlineID: &info.RelatedOnlineID,
|
||||
DraftID: &info.ID,
|
||||
|
||||
CreatedAtMs: info.CreatedAt,
|
||||
UpdatedAtMs: info.UpdatedAt,
|
||||
}
|
||||
|
||||
databases = append(databases, db)
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// UpdateWithTX 使用事务更新草稿数据库信息
|
||||
func (d *DraftImpl) UpdateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database) (*entity.Database, error) {
|
||||
fieldJson, err := json.Marshal(database.FieldList)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal field list failed: %v", err)
|
||||
}
|
||||
|
||||
fieldJsonStr := string(fieldJson)
|
||||
now := time.Now().UnixMilli()
|
||||
|
||||
updates := map[string]interface{}{
|
||||
"app_id": database.AppID,
|
||||
"table_name": database.TableName,
|
||||
"table_desc": database.TableDesc,
|
||||
"table_field": fieldJsonStr,
|
||||
"icon_uri": database.IconURI,
|
||||
"prompt_disabled": func() int32 {
|
||||
if database.PromptDisabled {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}(),
|
||||
"rw_mode": int64(database.RwMode),
|
||||
"updated_at": now,
|
||||
}
|
||||
|
||||
// 执行更新
|
||||
res := tx.DraftDatabaseInfo
|
||||
_, err = res.WithContext(ctx).Where(res.ID.Eq(database.ID)).Updates(updates)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update draft database failed: %v", err)
|
||||
}
|
||||
|
||||
database.UpdatedAtMs = now
|
||||
return database, nil
|
||||
}
|
||||
|
||||
func (d *DraftImpl) DeleteWithTX(ctx context.Context, tx *query.QueryTx, id int64) error {
|
||||
res := tx.DraftDatabaseInfo
|
||||
_, err := res.WithContext(ctx).Where(res.ID.Eq(id)).Delete(&model.DraftDatabaseInfo{})
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errorx.New(errno.ErrMemoryDatabaseNotFoundCode)
|
||||
}
|
||||
return fmt.Errorf("delete draft database failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// List 列出符合条件的数据库信息
|
||||
func (d *DraftImpl) List(ctx context.Context, filter *entity.DatabaseFilter, page *entity.Pagination, orderBy []*database.OrderBy) ([]*entity.Database, int64, error) {
|
||||
res := d.query.DraftDatabaseInfo
|
||||
|
||||
q := res.WithContext(ctx)
|
||||
|
||||
// 添加过滤条件
|
||||
if filter != nil {
|
||||
if filter.CreatorID != nil {
|
||||
q = q.Where(res.CreatorID.Eq(*filter.CreatorID))
|
||||
}
|
||||
|
||||
if filter.SpaceID != nil {
|
||||
q = q.Where(res.SpaceID.Eq(*filter.SpaceID))
|
||||
}
|
||||
|
||||
if filter.AppID != nil {
|
||||
q = q.Where(res.AppID.Eq(*filter.AppID))
|
||||
}
|
||||
|
||||
if filter.TableName != nil {
|
||||
q = q.Where(res.TableName_.Like("%" + *filter.TableName + "%"))
|
||||
}
|
||||
|
||||
q = q.Where(res.IsVisible.Eq(1))
|
||||
}
|
||||
|
||||
count, err := q.Count()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("count online database failed: %v", err)
|
||||
}
|
||||
|
||||
limit := int64(50)
|
||||
if page != nil && page.Limit > 0 {
|
||||
limit = int64(page.Limit)
|
||||
}
|
||||
|
||||
offset := 0
|
||||
if page != nil && page.Offset > 0 {
|
||||
offset = page.Offset
|
||||
}
|
||||
|
||||
if len(orderBy) > 0 {
|
||||
for _, order := range orderBy {
|
||||
switch order.Field {
|
||||
case "created_at":
|
||||
if order.Direction == table.SortDirection_Desc {
|
||||
q = q.Order(res.CreatedAt.Desc())
|
||||
} else {
|
||||
q = q.Order(res.CreatedAt)
|
||||
}
|
||||
case "updated_at":
|
||||
if order.Direction == table.SortDirection_Desc {
|
||||
q = q.Order(res.UpdatedAt.Desc())
|
||||
} else {
|
||||
q = q.Order(res.UpdatedAt)
|
||||
}
|
||||
default:
|
||||
q = q.Order(res.CreatedAt.Desc())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
q = q.Order(res.CreatedAt.Desc())
|
||||
}
|
||||
|
||||
records, err := q.Limit(int(limit)).Offset(offset).Find()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("list online database failed: %v", err)
|
||||
}
|
||||
|
||||
databases := make([]*entity.Database, 0, len(records))
|
||||
for _, info := range records {
|
||||
db := &entity.Database{
|
||||
ID: info.ID,
|
||||
SpaceID: info.SpaceID,
|
||||
CreatorID: info.CreatorID,
|
||||
IconURI: info.IconURI,
|
||||
|
||||
AppID: info.AppID,
|
||||
IsVisible: info.IsVisible == 1,
|
||||
PromptDisabled: info.PromptDisabled == 1,
|
||||
TableName: info.TableName_,
|
||||
TableDesc: info.TableDesc,
|
||||
FieldList: info.TableField,
|
||||
Status: table.BotTableStatus_Online,
|
||||
ActualTableName: info.PhysicalTableName,
|
||||
RwMode: table.BotTableRWMode(info.RwMode),
|
||||
TableType: ptr.Of(table.TableType_DraftTable),
|
||||
OnlineID: &info.RelatedOnlineID,
|
||||
DraftID: &info.ID,
|
||||
}
|
||||
|
||||
databases = append(databases, db)
|
||||
}
|
||||
|
||||
return databases, count, nil
|
||||
}
|
||||
|
||||
func (d *DraftImpl) BatchDeleteWithTX(ctx context.Context, tx *query.QueryTx, ids []int64) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := tx.DraftDatabaseInfo
|
||||
_, err := res.WithContext(ctx).Where(res.ID.In(ids...)).Delete()
|
||||
if err != nil {
|
||||
return fmt.Errorf("batch delete draft database failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
const TableNameAgentToDatabase = "agent_to_database"
|
||||
|
||||
// AgentToDatabase agent_to_database info
|
||||
type AgentToDatabase struct {
|
||||
ID int64 `gorm:"column:id;primaryKey;comment:ID" json:"id"` // ID
|
||||
AgentID int64 `gorm:"column:agent_id;not null;comment:Agent ID" json:"agent_id"` // Agent ID
|
||||
DatabaseID int64 `gorm:"column:database_id;not null;comment:ID of database_info" json:"database_id"` // ID of database_info
|
||||
IsDraft bool `gorm:"column:is_draft;not null;comment:Is draft" json:"is_draft"` // Is draft
|
||||
PromptDisable bool `gorm:"column:prompt_disable;not null;comment:Support prompt calls: 1 not supported, 0 supported" json:"prompt_disable"` // Support prompt calls: 1 not supported, 0 supported
|
||||
}
|
||||
|
||||
// TableName AgentToDatabase's table name
|
||||
func (*AgentToDatabase) TableName() string {
|
||||
return TableNameAgentToDatabase
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/database"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const TableNameDraftDatabaseInfo = "draft_database_info"
|
||||
|
||||
// DraftDatabaseInfo draft database info
|
||||
type DraftDatabaseInfo struct {
|
||||
ID int64 `gorm:"column:id;primaryKey;comment:ID" json:"id"` // ID
|
||||
AppID int64 `gorm:"column:app_id;comment:App ID" json:"app_id"` // App ID
|
||||
SpaceID int64 `gorm:"column:space_id;not null;comment:Space ID" json:"space_id"` // Space ID
|
||||
RelatedOnlineID int64 `gorm:"column:related_online_id;not null;comment:The primary key ID of online_database_info table" json:"related_online_id"` // The primary key ID of online_database_info table
|
||||
IsVisible int32 `gorm:"column:is_visible;not null;default:1;comment:Visibility: 0 invisible, 1 visible" json:"is_visible"` // Visibility: 0 invisible, 1 visible
|
||||
PromptDisabled int32 `gorm:"column:prompt_disabled;not null;comment:Support prompt calls: 1 not supported, 0 supported" json:"prompt_disabled"` // Support prompt calls: 1 not supported, 0 supported
|
||||
TableName_ string `gorm:"column:table_name;not null;comment:Table name" json:"table_name"` // Table name
|
||||
TableDesc string `gorm:"column:table_desc;comment:Table description" json:"table_desc"` // Table description
|
||||
TableField []*database.FieldItem `gorm:"column:table_field;comment:Table field info;serializer:json" json:"table_field"` // Table field info
|
||||
CreatorID int64 `gorm:"column:creator_id;not null;comment:Creator ID" json:"creator_id"` // Creator ID
|
||||
IconURI string `gorm:"column:icon_uri;not null;comment:Icon Uri" json:"icon_uri"` // Icon Uri
|
||||
PhysicalTableName string `gorm:"column:physical_table_name;comment:The name of the real physical table" json:"physical_table_name"` // The name of the real physical table
|
||||
RwMode int64 `gorm:"column:rw_mode;not null;default:1;comment:Read and write permission modes: 1. Limited read and write mode 2. Read-only mode 3. Full read and write mode" json:"rw_mode"` // Read and write permission modes: 1. Limited read and write mode 2. Read-only mode 3. Full read and write mode
|
||||
CreatedAt int64 `gorm:"column:created_at;not null;autoCreateTime:milli;comment:Create Time in Milliseconds" json:"created_at"` // Create Time in Milliseconds
|
||||
UpdatedAt int64 `gorm:"column:updated_at;not null;autoUpdateTime:milli;comment:Update Time in Milliseconds" json:"updated_at"` // Update Time in Milliseconds
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:Delete Time" json:"deleted_at"` // Delete Time
|
||||
}
|
||||
|
||||
// TableName DraftDatabaseInfo's table name
|
||||
func (*DraftDatabaseInfo) TableName() string {
|
||||
return TableNameDraftDatabaseInfo
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/database"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const TableNameOnlineDatabaseInfo = "online_database_info"
|
||||
|
||||
// OnlineDatabaseInfo online database info
|
||||
type OnlineDatabaseInfo struct {
|
||||
ID int64 `gorm:"column:id;primaryKey;comment:ID" json:"id"` // ID
|
||||
AppID int64 `gorm:"column:app_id;comment:App ID" json:"app_id"` // App ID
|
||||
SpaceID int64 `gorm:"column:space_id;not null;comment:Space ID" json:"space_id"` // Space ID
|
||||
RelatedDraftID int64 `gorm:"column:related_draft_id;not null;comment:The primary key ID of draft_database_info table" json:"related_draft_id"` // The primary key ID of draft_database_info table
|
||||
IsVisible int32 `gorm:"column:is_visible;not null;default:1;comment:Visibility: 0 invisible, 1 visible" json:"is_visible"` // Visibility: 0 invisible, 1 visible
|
||||
PromptDisabled int32 `gorm:"column:prompt_disabled;not null;comment:Support prompt calls: 1 not supported, 0 supported" json:"prompt_disabled"` // Support prompt calls: 1 not supported, 0 supported
|
||||
TableName_ string `gorm:"column:table_name;not null;comment:Table name" json:"table_name"` // Table name
|
||||
TableDesc string `gorm:"column:table_desc;comment:Table description" json:"table_desc"` // Table description
|
||||
TableField []*database.FieldItem `gorm:"column:table_field;comment:Table field info;serializer:json" json:"table_field"` // Table field info
|
||||
CreatorID int64 `gorm:"column:creator_id;not null;comment:Creator ID" json:"creator_id"` // Creator ID
|
||||
IconURI string `gorm:"column:icon_uri;not null;comment:Icon Uri" json:"icon_uri"` // Icon Uri
|
||||
PhysicalTableName string `gorm:"column:physical_table_name;comment:The name of the real physical table" json:"physical_table_name"` // The name of the real physical table
|
||||
RwMode int64 `gorm:"column:rw_mode;not null;default:1;comment:Read and write permission modes: 1. Limited read and write mode 2. Read-only mode 3. Full read and write mode" json:"rw_mode"` // Read and write permission modes: 1. Limited read and write mode 2. Read-only mode 3. Full read and write mode
|
||||
CreatedAt int64 `gorm:"column:created_at;not null;autoCreateTime:milli;comment:Create Time in Milliseconds" json:"created_at"` // Create Time in Milliseconds
|
||||
UpdatedAt int64 `gorm:"column:updated_at;not null;autoUpdateTime:milli;comment:Update Time in Milliseconds" json:"updated_at"` // Update Time in Milliseconds
|
||||
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:Delete Time" json:"deleted_at"` // Delete Time
|
||||
}
|
||||
|
||||
// TableName OnlineDatabaseInfo's table name
|
||||
func (*OnlineDatabaseInfo) TableName() string {
|
||||
return TableNameOnlineDatabaseInfo
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* 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 dal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"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/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal/model"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal/query"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
||||
"github.com/coze-dev/coze-studio/backend/types/errno"
|
||||
)
|
||||
|
||||
var (
|
||||
onlineOnce sync.Once
|
||||
singletonOnline *OlineImpl
|
||||
)
|
||||
|
||||
type OlineImpl struct {
|
||||
IDGen idgen.IDGenerator
|
||||
query *query.Query
|
||||
}
|
||||
|
||||
func NewOnlineDatabaseDAO(db *gorm.DB, idGen idgen.IDGenerator) *OlineImpl {
|
||||
onlineOnce.Do(func() {
|
||||
singletonOnline = &OlineImpl{
|
||||
IDGen: idGen,
|
||||
query: query.Use(db),
|
||||
}
|
||||
})
|
||||
|
||||
return singletonOnline
|
||||
}
|
||||
|
||||
func (o *OlineImpl) CreateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database, draftID, onlineID int64, physicalTableName string) (*entity.Database, error) {
|
||||
now := time.Now().UnixMilli()
|
||||
onlineInfo := &model.OnlineDatabaseInfo{
|
||||
ID: onlineID,
|
||||
AppID: database.AppID,
|
||||
SpaceID: database.SpaceID,
|
||||
RelatedDraftID: draftID,
|
||||
IsVisible: 1, // 默认可见
|
||||
PromptDisabled: func() int32 {
|
||||
if database.PromptDisabled {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}(),
|
||||
TableName_: database.TableName,
|
||||
TableDesc: database.TableDesc,
|
||||
TableField: database.FieldList,
|
||||
CreatorID: database.CreatorID,
|
||||
IconURI: database.IconURI,
|
||||
PhysicalTableName: physicalTableName,
|
||||
RwMode: int64(database.RwMode),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
table := tx.OnlineDatabaseInfo
|
||||
|
||||
err := table.WithContext(ctx).Create(onlineInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
database.CreatedAtMs = now
|
||||
database.UpdatedAtMs = now
|
||||
|
||||
return database, nil
|
||||
}
|
||||
|
||||
// Get 获取线上数据库信息
|
||||
func (o *OlineImpl) Get(ctx context.Context, id int64) (*entity.Database, error) {
|
||||
res := o.query.OnlineDatabaseInfo
|
||||
|
||||
info, err := res.WithContext(ctx).Where(res.ID.Eq(id)).First()
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errorx.New(errno.ErrMemoryDatabaseNotFoundCode)
|
||||
}
|
||||
return nil, fmt.Errorf("query online database failed: %v", err)
|
||||
}
|
||||
|
||||
db := &entity.Database{
|
||||
ID: info.ID,
|
||||
SpaceID: info.SpaceID,
|
||||
CreatorID: info.CreatorID,
|
||||
IconURI: info.IconURI,
|
||||
|
||||
AppID: info.AppID,
|
||||
DraftID: &info.RelatedDraftID,
|
||||
OnlineID: &info.ID,
|
||||
IsVisible: info.IsVisible == 1,
|
||||
PromptDisabled: info.PromptDisabled == 1,
|
||||
TableName: info.TableName_,
|
||||
TableDesc: info.TableDesc,
|
||||
FieldList: info.TableField,
|
||||
Status: table.BotTableStatus_Online,
|
||||
ActualTableName: info.PhysicalTableName,
|
||||
RwMode: table.BotTableRWMode(info.RwMode),
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// UpdateWithTX 使用事务更新线上数据库信息
|
||||
func (o *OlineImpl) UpdateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database) (*entity.Database, error) {
|
||||
fieldJson, err := json.Marshal(database.FieldList)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal field list failed: %v", err)
|
||||
}
|
||||
|
||||
fieldJsonStr := string(fieldJson)
|
||||
now := time.Now().UnixMilli()
|
||||
|
||||
// 构建更新内容
|
||||
updates := map[string]interface{}{
|
||||
"app_id": database.AppID,
|
||||
"table_name": database.TableName,
|
||||
"table_desc": database.TableDesc,
|
||||
"table_field": fieldJsonStr,
|
||||
"icon_uri": database.IconURI,
|
||||
"prompt_disabled": func() int32 {
|
||||
if database.PromptDisabled {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}(),
|
||||
"rw_mode": int64(database.RwMode),
|
||||
"updated_at": now,
|
||||
}
|
||||
|
||||
// 执行更新
|
||||
res := tx.OnlineDatabaseInfo
|
||||
_, err = res.WithContext(ctx).Where(res.ID.Eq(database.ID)).Updates(updates)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update online database failed: %v", err)
|
||||
}
|
||||
|
||||
_, err = res.WithContext(ctx).Where(res.ID.In(database.ID)).Updates(updates)
|
||||
|
||||
database.UpdatedAtMs = now
|
||||
return database, nil
|
||||
}
|
||||
|
||||
func (o *OlineImpl) DeleteWithTX(ctx context.Context, tx *query.QueryTx, id int64) error {
|
||||
res := tx.OnlineDatabaseInfo
|
||||
_, err := res.WithContext(ctx).Where(res.ID.Eq(id)).Delete(&model.OnlineDatabaseInfo{})
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errorx.New(errno.ErrMemoryDatabaseNotFoundCode)
|
||||
}
|
||||
return fmt.Errorf("delete online database failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MGet 批量获取在线数据库信息
|
||||
func (o *OlineImpl) MGet(ctx context.Context, ids []int64) ([]*entity.Database, error) {
|
||||
if len(ids) == 0 {
|
||||
return []*entity.Database{}, nil
|
||||
}
|
||||
|
||||
res := o.query.OnlineDatabaseInfo
|
||||
|
||||
// 查询未删除的、ID在给定列表中的记录
|
||||
records, err := res.WithContext(ctx).
|
||||
Where(res.ID.In(ids...)).
|
||||
Find()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("batch query online database failed: %v", err)
|
||||
}
|
||||
|
||||
// 构建返回结果
|
||||
databases := make([]*entity.Database, 0, len(records))
|
||||
for _, info := range records {
|
||||
db := &entity.Database{
|
||||
ID: info.ID,
|
||||
SpaceID: info.SpaceID,
|
||||
CreatorID: info.CreatorID,
|
||||
IconURI: info.IconURI,
|
||||
|
||||
AppID: info.AppID,
|
||||
DraftID: &info.RelatedDraftID,
|
||||
OnlineID: &info.ID,
|
||||
IsVisible: info.IsVisible == 1,
|
||||
PromptDisabled: info.PromptDisabled == 1,
|
||||
TableName: info.TableName_,
|
||||
TableDesc: info.TableDesc,
|
||||
FieldList: info.TableField,
|
||||
Status: table.BotTableStatus_Online,
|
||||
ActualTableName: info.PhysicalTableName,
|
||||
RwMode: table.BotTableRWMode(info.RwMode),
|
||||
|
||||
CreatedAtMs: info.CreatedAt,
|
||||
UpdatedAtMs: info.UpdatedAt,
|
||||
}
|
||||
|
||||
databases = append(databases, db)
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// List 列出符合条件的数据库信息
|
||||
func (o *OlineImpl) List(ctx context.Context, filter *entity.DatabaseFilter, page *entity.Pagination, orderBy []*database.OrderBy) ([]*entity.Database, int64, error) {
|
||||
res := o.query.OnlineDatabaseInfo
|
||||
|
||||
// 构建基础查询
|
||||
q := res.WithContext(ctx)
|
||||
|
||||
// 添加过滤条件
|
||||
if filter != nil {
|
||||
if filter.CreatorID != nil {
|
||||
q = q.Where(res.CreatorID.Eq(*filter.CreatorID))
|
||||
}
|
||||
|
||||
if filter.SpaceID != nil {
|
||||
q = q.Where(res.SpaceID.Eq(*filter.SpaceID))
|
||||
}
|
||||
|
||||
if filter.AppID != nil {
|
||||
q = q.Where(res.AppID.Eq(*filter.AppID))
|
||||
}
|
||||
|
||||
if filter.TableName != nil {
|
||||
q = q.Where(res.TableName_.Like("%" + *filter.TableName + "%"))
|
||||
}
|
||||
|
||||
q = q.Where(res.IsVisible.Eq(1))
|
||||
}
|
||||
|
||||
count, err := q.Count()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("count online database failed: %v", err)
|
||||
}
|
||||
|
||||
limit := int64(50) // default
|
||||
if page != nil && page.Limit > 0 {
|
||||
limit = int64(page.Limit)
|
||||
}
|
||||
|
||||
offset := 0
|
||||
if page != nil && page.Offset > 0 {
|
||||
offset = page.Offset
|
||||
}
|
||||
|
||||
// 处理排序
|
||||
if len(orderBy) > 0 {
|
||||
for _, order := range orderBy {
|
||||
switch order.Field {
|
||||
case "created_at":
|
||||
if order.Direction == table.SortDirection_Desc {
|
||||
q = q.Order(res.CreatedAt.Desc())
|
||||
} else {
|
||||
q = q.Order(res.CreatedAt)
|
||||
}
|
||||
case "updated_at":
|
||||
if order.Direction == table.SortDirection_Desc {
|
||||
q = q.Order(res.UpdatedAt.Desc())
|
||||
} else {
|
||||
q = q.Order(res.UpdatedAt)
|
||||
}
|
||||
default:
|
||||
q = q.Order(res.CreatedAt.Desc())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
q = q.Order(res.CreatedAt.Desc())
|
||||
}
|
||||
|
||||
records, err := q.Limit(int(limit)).Offset(offset).Find()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("list online database failed: %v", err)
|
||||
}
|
||||
|
||||
databases := make([]*entity.Database, 0, len(records))
|
||||
for _, info := range records {
|
||||
d := &entity.Database{
|
||||
ID: info.ID,
|
||||
SpaceID: info.SpaceID,
|
||||
CreatorID: info.CreatorID,
|
||||
IconURI: info.IconURI,
|
||||
|
||||
AppID: info.AppID,
|
||||
DraftID: &info.RelatedDraftID,
|
||||
OnlineID: &info.ID,
|
||||
IsVisible: info.IsVisible == 1,
|
||||
PromptDisabled: info.PromptDisabled == 1,
|
||||
TableName: info.TableName_,
|
||||
TableDesc: info.TableDesc,
|
||||
FieldList: info.TableField,
|
||||
Status: table.BotTableStatus_Online,
|
||||
ActualTableName: info.PhysicalTableName,
|
||||
RwMode: table.BotTableRWMode(info.RwMode),
|
||||
TableType: ptr.Of(table.TableType_OnlineTable),
|
||||
}
|
||||
|
||||
databases = append(databases, d)
|
||||
}
|
||||
|
||||
return databases, count, nil
|
||||
}
|
||||
|
||||
func (o *OlineImpl) BatchDeleteWithTX(ctx context.Context, tx *query.QueryTx, ids []int64) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := tx.OnlineDatabaseInfo
|
||||
_, err := res.WithContext(ctx).Where(res.ID.In(ids...)).Delete()
|
||||
if err != nil {
|
||||
return fmt.Errorf("batch delete online database failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal/model"
|
||||
)
|
||||
|
||||
func newAgentToDatabase(db *gorm.DB, opts ...gen.DOOption) agentToDatabase {
|
||||
_agentToDatabase := agentToDatabase{}
|
||||
|
||||
_agentToDatabase.agentToDatabaseDo.UseDB(db, opts...)
|
||||
_agentToDatabase.agentToDatabaseDo.UseModel(&model.AgentToDatabase{})
|
||||
|
||||
tableName := _agentToDatabase.agentToDatabaseDo.TableName()
|
||||
_agentToDatabase.ALL = field.NewAsterisk(tableName)
|
||||
_agentToDatabase.ID = field.NewInt64(tableName, "id")
|
||||
_agentToDatabase.AgentID = field.NewInt64(tableName, "agent_id")
|
||||
_agentToDatabase.DatabaseID = field.NewInt64(tableName, "database_id")
|
||||
_agentToDatabase.IsDraft = field.NewBool(tableName, "is_draft")
|
||||
_agentToDatabase.PromptDisable = field.NewBool(tableName, "prompt_disable")
|
||||
|
||||
_agentToDatabase.fillFieldMap()
|
||||
|
||||
return _agentToDatabase
|
||||
}
|
||||
|
||||
// agentToDatabase agent_to_database info
|
||||
type agentToDatabase struct {
|
||||
agentToDatabaseDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int64 // ID
|
||||
AgentID field.Int64 // Agent ID
|
||||
DatabaseID field.Int64 // ID of database_info
|
||||
IsDraft field.Bool // Is draft
|
||||
PromptDisable field.Bool // Support prompt calls: 1 not supported, 0 supported
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (a agentToDatabase) Table(newTableName string) *agentToDatabase {
|
||||
a.agentToDatabaseDo.UseTable(newTableName)
|
||||
return a.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (a agentToDatabase) As(alias string) *agentToDatabase {
|
||||
a.agentToDatabaseDo.DO = *(a.agentToDatabaseDo.As(alias).(*gen.DO))
|
||||
return a.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (a *agentToDatabase) updateTableName(table string) *agentToDatabase {
|
||||
a.ALL = field.NewAsterisk(table)
|
||||
a.ID = field.NewInt64(table, "id")
|
||||
a.AgentID = field.NewInt64(table, "agent_id")
|
||||
a.DatabaseID = field.NewInt64(table, "database_id")
|
||||
a.IsDraft = field.NewBool(table, "is_draft")
|
||||
a.PromptDisable = field.NewBool(table, "prompt_disable")
|
||||
|
||||
a.fillFieldMap()
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *agentToDatabase) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := a.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (a *agentToDatabase) fillFieldMap() {
|
||||
a.fieldMap = make(map[string]field.Expr, 5)
|
||||
a.fieldMap["id"] = a.ID
|
||||
a.fieldMap["agent_id"] = a.AgentID
|
||||
a.fieldMap["database_id"] = a.DatabaseID
|
||||
a.fieldMap["is_draft"] = a.IsDraft
|
||||
a.fieldMap["prompt_disable"] = a.PromptDisable
|
||||
}
|
||||
|
||||
func (a agentToDatabase) clone(db *gorm.DB) agentToDatabase {
|
||||
a.agentToDatabaseDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a agentToDatabase) replaceDB(db *gorm.DB) agentToDatabase {
|
||||
a.agentToDatabaseDo.ReplaceDB(db)
|
||||
return a
|
||||
}
|
||||
|
||||
type agentToDatabaseDo struct{ gen.DO }
|
||||
|
||||
type IAgentToDatabaseDo interface {
|
||||
gen.SubQuery
|
||||
Debug() IAgentToDatabaseDo
|
||||
WithContext(ctx context.Context) IAgentToDatabaseDo
|
||||
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
|
||||
ReplaceDB(db *gorm.DB)
|
||||
ReadDB() IAgentToDatabaseDo
|
||||
WriteDB() IAgentToDatabaseDo
|
||||
As(alias string) gen.Dao
|
||||
Session(config *gorm.Session) IAgentToDatabaseDo
|
||||
Columns(cols ...field.Expr) gen.Columns
|
||||
Clauses(conds ...clause.Expression) IAgentToDatabaseDo
|
||||
Not(conds ...gen.Condition) IAgentToDatabaseDo
|
||||
Or(conds ...gen.Condition) IAgentToDatabaseDo
|
||||
Select(conds ...field.Expr) IAgentToDatabaseDo
|
||||
Where(conds ...gen.Condition) IAgentToDatabaseDo
|
||||
Order(conds ...field.Expr) IAgentToDatabaseDo
|
||||
Distinct(cols ...field.Expr) IAgentToDatabaseDo
|
||||
Omit(cols ...field.Expr) IAgentToDatabaseDo
|
||||
Join(table schema.Tabler, on ...field.Expr) IAgentToDatabaseDo
|
||||
LeftJoin(table schema.Tabler, on ...field.Expr) IAgentToDatabaseDo
|
||||
RightJoin(table schema.Tabler, on ...field.Expr) IAgentToDatabaseDo
|
||||
Group(cols ...field.Expr) IAgentToDatabaseDo
|
||||
Having(conds ...gen.Condition) IAgentToDatabaseDo
|
||||
Limit(limit int) IAgentToDatabaseDo
|
||||
Offset(offset int) IAgentToDatabaseDo
|
||||
Count() (count int64, err error)
|
||||
Scopes(funcs ...func(gen.Dao) gen.Dao) IAgentToDatabaseDo
|
||||
Unscoped() IAgentToDatabaseDo
|
||||
Create(values ...*model.AgentToDatabase) error
|
||||
CreateInBatches(values []*model.AgentToDatabase, batchSize int) error
|
||||
Save(values ...*model.AgentToDatabase) error
|
||||
First() (*model.AgentToDatabase, error)
|
||||
Take() (*model.AgentToDatabase, error)
|
||||
Last() (*model.AgentToDatabase, error)
|
||||
Find() ([]*model.AgentToDatabase, error)
|
||||
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.AgentToDatabase, err error)
|
||||
FindInBatches(result *[]*model.AgentToDatabase, batchSize int, fc func(tx gen.Dao, batch int) error) error
|
||||
Pluck(column field.Expr, dest interface{}) error
|
||||
Delete(...*model.AgentToDatabase) (info gen.ResultInfo, err error)
|
||||
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
|
||||
Updates(value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
|
||||
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateFrom(q gen.SubQuery) gen.Dao
|
||||
Attrs(attrs ...field.AssignExpr) IAgentToDatabaseDo
|
||||
Assign(attrs ...field.AssignExpr) IAgentToDatabaseDo
|
||||
Joins(fields ...field.RelationField) IAgentToDatabaseDo
|
||||
Preload(fields ...field.RelationField) IAgentToDatabaseDo
|
||||
FirstOrInit() (*model.AgentToDatabase, error)
|
||||
FirstOrCreate() (*model.AgentToDatabase, error)
|
||||
FindByPage(offset int, limit int) (result []*model.AgentToDatabase, count int64, err error)
|
||||
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
|
||||
Scan(result interface{}) (err error)
|
||||
Returning(value interface{}, columns ...string) IAgentToDatabaseDo
|
||||
UnderlyingDB() *gorm.DB
|
||||
schema.Tabler
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Debug() IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Debug())
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) WithContext(ctx context.Context) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) ReadDB() IAgentToDatabaseDo {
|
||||
return a.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) WriteDB() IAgentToDatabaseDo {
|
||||
return a.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Session(config *gorm.Session) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Session(config))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Clauses(conds ...clause.Expression) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Returning(value interface{}, columns ...string) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Not(conds ...gen.Condition) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Or(conds ...gen.Condition) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Select(conds ...field.Expr) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Where(conds ...gen.Condition) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Order(conds ...field.Expr) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Distinct(cols ...field.Expr) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Omit(cols ...field.Expr) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Join(table schema.Tabler, on ...field.Expr) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) LeftJoin(table schema.Tabler, on ...field.Expr) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) RightJoin(table schema.Tabler, on ...field.Expr) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Group(cols ...field.Expr) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Having(conds ...gen.Condition) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Limit(limit int) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Offset(offset int) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Unscoped() IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Create(values ...*model.AgentToDatabase) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return a.DO.Create(values)
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) CreateInBatches(values []*model.AgentToDatabase, batchSize int) error {
|
||||
return a.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (a agentToDatabaseDo) Save(values ...*model.AgentToDatabase) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return a.DO.Save(values)
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) First() (*model.AgentToDatabase, error) {
|
||||
if result, err := a.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.AgentToDatabase), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Take() (*model.AgentToDatabase, error) {
|
||||
if result, err := a.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.AgentToDatabase), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Last() (*model.AgentToDatabase, error) {
|
||||
if result, err := a.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.AgentToDatabase), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Find() ([]*model.AgentToDatabase, error) {
|
||||
result, err := a.DO.Find()
|
||||
return result.([]*model.AgentToDatabase), err
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.AgentToDatabase, err error) {
|
||||
buf := make([]*model.AgentToDatabase, 0, batchSize)
|
||||
err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) FindInBatches(result *[]*model.AgentToDatabase, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return a.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Attrs(attrs ...field.AssignExpr) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Assign(attrs ...field.AssignExpr) IAgentToDatabaseDo {
|
||||
return a.withDO(a.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Joins(fields ...field.RelationField) IAgentToDatabaseDo {
|
||||
for _, _f := range fields {
|
||||
a = *a.withDO(a.DO.Joins(_f))
|
||||
}
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Preload(fields ...field.RelationField) IAgentToDatabaseDo {
|
||||
for _, _f := range fields {
|
||||
a = *a.withDO(a.DO.Preload(_f))
|
||||
}
|
||||
return &a
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) FirstOrInit() (*model.AgentToDatabase, error) {
|
||||
if result, err := a.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.AgentToDatabase), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) FirstOrCreate() (*model.AgentToDatabase, error) {
|
||||
if result, err := a.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.AgentToDatabase), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) FindByPage(offset int, limit int) (result []*model.AgentToDatabase, count int64, err error) {
|
||||
result, err = a.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = a.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = a.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = a.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Scan(result interface{}) (err error) {
|
||||
return a.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (a agentToDatabaseDo) Delete(models ...*model.AgentToDatabase) (result gen.ResultInfo, err error) {
|
||||
return a.DO.Delete(models)
|
||||
}
|
||||
|
||||
func (a *agentToDatabaseDo) withDO(do gen.Dao) *agentToDatabaseDo {
|
||||
a.DO = *do.(*gen.DO)
|
||||
return a
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal/model"
|
||||
)
|
||||
|
||||
func newDraftDatabaseInfo(db *gorm.DB, opts ...gen.DOOption) draftDatabaseInfo {
|
||||
_draftDatabaseInfo := draftDatabaseInfo{}
|
||||
|
||||
_draftDatabaseInfo.draftDatabaseInfoDo.UseDB(db, opts...)
|
||||
_draftDatabaseInfo.draftDatabaseInfoDo.UseModel(&model.DraftDatabaseInfo{})
|
||||
|
||||
tableName := _draftDatabaseInfo.draftDatabaseInfoDo.TableName()
|
||||
_draftDatabaseInfo.ALL = field.NewAsterisk(tableName)
|
||||
_draftDatabaseInfo.ID = field.NewInt64(tableName, "id")
|
||||
_draftDatabaseInfo.AppID = field.NewInt64(tableName, "app_id")
|
||||
_draftDatabaseInfo.SpaceID = field.NewInt64(tableName, "space_id")
|
||||
_draftDatabaseInfo.RelatedOnlineID = field.NewInt64(tableName, "related_online_id")
|
||||
_draftDatabaseInfo.IsVisible = field.NewInt32(tableName, "is_visible")
|
||||
_draftDatabaseInfo.PromptDisabled = field.NewInt32(tableName, "prompt_disabled")
|
||||
_draftDatabaseInfo.TableName_ = field.NewString(tableName, "table_name")
|
||||
_draftDatabaseInfo.TableDesc = field.NewString(tableName, "table_desc")
|
||||
_draftDatabaseInfo.TableField = field.NewField(tableName, "table_field")
|
||||
_draftDatabaseInfo.CreatorID = field.NewInt64(tableName, "creator_id")
|
||||
_draftDatabaseInfo.IconURI = field.NewString(tableName, "icon_uri")
|
||||
_draftDatabaseInfo.PhysicalTableName = field.NewString(tableName, "physical_table_name")
|
||||
_draftDatabaseInfo.RwMode = field.NewInt64(tableName, "rw_mode")
|
||||
_draftDatabaseInfo.CreatedAt = field.NewInt64(tableName, "created_at")
|
||||
_draftDatabaseInfo.UpdatedAt = field.NewInt64(tableName, "updated_at")
|
||||
_draftDatabaseInfo.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||
|
||||
_draftDatabaseInfo.fillFieldMap()
|
||||
|
||||
return _draftDatabaseInfo
|
||||
}
|
||||
|
||||
// draftDatabaseInfo draft database info
|
||||
type draftDatabaseInfo struct {
|
||||
draftDatabaseInfoDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int64 // ID
|
||||
AppID field.Int64 // App ID
|
||||
SpaceID field.Int64 // Space ID
|
||||
RelatedOnlineID field.Int64 // The primary key ID of online_database_info table
|
||||
IsVisible field.Int32 // Visibility: 0 invisible, 1 visible
|
||||
PromptDisabled field.Int32 // Support prompt calls: 1 not supported, 0 supported
|
||||
TableName_ field.String // Table name
|
||||
TableDesc field.String // Table description
|
||||
TableField field.Field // Table field info
|
||||
CreatorID field.Int64 // Creator ID
|
||||
IconURI field.String // Icon Uri
|
||||
PhysicalTableName field.String // The name of the real physical table
|
||||
RwMode field.Int64 // Read and write permission modes: 1. Limited read and write mode 2. Read-only mode 3. Full read and write mode
|
||||
CreatedAt field.Int64 // Create Time in Milliseconds
|
||||
UpdatedAt field.Int64 // Update Time in Milliseconds
|
||||
DeletedAt field.Field // Delete Time
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfo) Table(newTableName string) *draftDatabaseInfo {
|
||||
d.draftDatabaseInfoDo.UseTable(newTableName)
|
||||
return d.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfo) As(alias string) *draftDatabaseInfo {
|
||||
d.draftDatabaseInfoDo.DO = *(d.draftDatabaseInfoDo.As(alias).(*gen.DO))
|
||||
return d.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (d *draftDatabaseInfo) updateTableName(table string) *draftDatabaseInfo {
|
||||
d.ALL = field.NewAsterisk(table)
|
||||
d.ID = field.NewInt64(table, "id")
|
||||
d.AppID = field.NewInt64(table, "app_id")
|
||||
d.SpaceID = field.NewInt64(table, "space_id")
|
||||
d.RelatedOnlineID = field.NewInt64(table, "related_online_id")
|
||||
d.IsVisible = field.NewInt32(table, "is_visible")
|
||||
d.PromptDisabled = field.NewInt32(table, "prompt_disabled")
|
||||
d.TableName_ = field.NewString(table, "table_name")
|
||||
d.TableDesc = field.NewString(table, "table_desc")
|
||||
d.TableField = field.NewField(table, "table_field")
|
||||
d.CreatorID = field.NewInt64(table, "creator_id")
|
||||
d.IconURI = field.NewString(table, "icon_uri")
|
||||
d.PhysicalTableName = field.NewString(table, "physical_table_name")
|
||||
d.RwMode = field.NewInt64(table, "rw_mode")
|
||||
d.CreatedAt = field.NewInt64(table, "created_at")
|
||||
d.UpdatedAt = field.NewInt64(table, "updated_at")
|
||||
d.DeletedAt = field.NewField(table, "deleted_at")
|
||||
|
||||
d.fillFieldMap()
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *draftDatabaseInfo) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := d.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (d *draftDatabaseInfo) fillFieldMap() {
|
||||
d.fieldMap = make(map[string]field.Expr, 16)
|
||||
d.fieldMap["id"] = d.ID
|
||||
d.fieldMap["app_id"] = d.AppID
|
||||
d.fieldMap["space_id"] = d.SpaceID
|
||||
d.fieldMap["related_online_id"] = d.RelatedOnlineID
|
||||
d.fieldMap["is_visible"] = d.IsVisible
|
||||
d.fieldMap["prompt_disabled"] = d.PromptDisabled
|
||||
d.fieldMap["table_name"] = d.TableName_
|
||||
d.fieldMap["table_desc"] = d.TableDesc
|
||||
d.fieldMap["table_field"] = d.TableField
|
||||
d.fieldMap["creator_id"] = d.CreatorID
|
||||
d.fieldMap["icon_uri"] = d.IconURI
|
||||
d.fieldMap["physical_table_name"] = d.PhysicalTableName
|
||||
d.fieldMap["rw_mode"] = d.RwMode
|
||||
d.fieldMap["created_at"] = d.CreatedAt
|
||||
d.fieldMap["updated_at"] = d.UpdatedAt
|
||||
d.fieldMap["deleted_at"] = d.DeletedAt
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfo) clone(db *gorm.DB) draftDatabaseInfo {
|
||||
d.draftDatabaseInfoDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
return d
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfo) replaceDB(db *gorm.DB) draftDatabaseInfo {
|
||||
d.draftDatabaseInfoDo.ReplaceDB(db)
|
||||
return d
|
||||
}
|
||||
|
||||
type draftDatabaseInfoDo struct{ gen.DO }
|
||||
|
||||
type IDraftDatabaseInfoDo interface {
|
||||
gen.SubQuery
|
||||
Debug() IDraftDatabaseInfoDo
|
||||
WithContext(ctx context.Context) IDraftDatabaseInfoDo
|
||||
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
|
||||
ReplaceDB(db *gorm.DB)
|
||||
ReadDB() IDraftDatabaseInfoDo
|
||||
WriteDB() IDraftDatabaseInfoDo
|
||||
As(alias string) gen.Dao
|
||||
Session(config *gorm.Session) IDraftDatabaseInfoDo
|
||||
Columns(cols ...field.Expr) gen.Columns
|
||||
Clauses(conds ...clause.Expression) IDraftDatabaseInfoDo
|
||||
Not(conds ...gen.Condition) IDraftDatabaseInfoDo
|
||||
Or(conds ...gen.Condition) IDraftDatabaseInfoDo
|
||||
Select(conds ...field.Expr) IDraftDatabaseInfoDo
|
||||
Where(conds ...gen.Condition) IDraftDatabaseInfoDo
|
||||
Order(conds ...field.Expr) IDraftDatabaseInfoDo
|
||||
Distinct(cols ...field.Expr) IDraftDatabaseInfoDo
|
||||
Omit(cols ...field.Expr) IDraftDatabaseInfoDo
|
||||
Join(table schema.Tabler, on ...field.Expr) IDraftDatabaseInfoDo
|
||||
LeftJoin(table schema.Tabler, on ...field.Expr) IDraftDatabaseInfoDo
|
||||
RightJoin(table schema.Tabler, on ...field.Expr) IDraftDatabaseInfoDo
|
||||
Group(cols ...field.Expr) IDraftDatabaseInfoDo
|
||||
Having(conds ...gen.Condition) IDraftDatabaseInfoDo
|
||||
Limit(limit int) IDraftDatabaseInfoDo
|
||||
Offset(offset int) IDraftDatabaseInfoDo
|
||||
Count() (count int64, err error)
|
||||
Scopes(funcs ...func(gen.Dao) gen.Dao) IDraftDatabaseInfoDo
|
||||
Unscoped() IDraftDatabaseInfoDo
|
||||
Create(values ...*model.DraftDatabaseInfo) error
|
||||
CreateInBatches(values []*model.DraftDatabaseInfo, batchSize int) error
|
||||
Save(values ...*model.DraftDatabaseInfo) error
|
||||
First() (*model.DraftDatabaseInfo, error)
|
||||
Take() (*model.DraftDatabaseInfo, error)
|
||||
Last() (*model.DraftDatabaseInfo, error)
|
||||
Find() ([]*model.DraftDatabaseInfo, error)
|
||||
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.DraftDatabaseInfo, err error)
|
||||
FindInBatches(result *[]*model.DraftDatabaseInfo, batchSize int, fc func(tx gen.Dao, batch int) error) error
|
||||
Pluck(column field.Expr, dest interface{}) error
|
||||
Delete(...*model.DraftDatabaseInfo) (info gen.ResultInfo, err error)
|
||||
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
|
||||
Updates(value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
|
||||
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateFrom(q gen.SubQuery) gen.Dao
|
||||
Attrs(attrs ...field.AssignExpr) IDraftDatabaseInfoDo
|
||||
Assign(attrs ...field.AssignExpr) IDraftDatabaseInfoDo
|
||||
Joins(fields ...field.RelationField) IDraftDatabaseInfoDo
|
||||
Preload(fields ...field.RelationField) IDraftDatabaseInfoDo
|
||||
FirstOrInit() (*model.DraftDatabaseInfo, error)
|
||||
FirstOrCreate() (*model.DraftDatabaseInfo, error)
|
||||
FindByPage(offset int, limit int) (result []*model.DraftDatabaseInfo, count int64, err error)
|
||||
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
|
||||
Scan(result interface{}) (err error)
|
||||
Returning(value interface{}, columns ...string) IDraftDatabaseInfoDo
|
||||
UnderlyingDB() *gorm.DB
|
||||
schema.Tabler
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Debug() IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Debug())
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) WithContext(ctx context.Context) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) ReadDB() IDraftDatabaseInfoDo {
|
||||
return d.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) WriteDB() IDraftDatabaseInfoDo {
|
||||
return d.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Session(config *gorm.Session) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Session(config))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Clauses(conds ...clause.Expression) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Returning(value interface{}, columns ...string) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Not(conds ...gen.Condition) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Or(conds ...gen.Condition) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Select(conds ...field.Expr) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Where(conds ...gen.Condition) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Order(conds ...field.Expr) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Distinct(cols ...field.Expr) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Omit(cols ...field.Expr) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Join(table schema.Tabler, on ...field.Expr) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) LeftJoin(table schema.Tabler, on ...field.Expr) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) RightJoin(table schema.Tabler, on ...field.Expr) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Group(cols ...field.Expr) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Having(conds ...gen.Condition) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Limit(limit int) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Offset(offset int) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Unscoped() IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Create(values ...*model.DraftDatabaseInfo) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return d.DO.Create(values)
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) CreateInBatches(values []*model.DraftDatabaseInfo, batchSize int) error {
|
||||
return d.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (d draftDatabaseInfoDo) Save(values ...*model.DraftDatabaseInfo) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return d.DO.Save(values)
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) First() (*model.DraftDatabaseInfo, error) {
|
||||
if result, err := d.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.DraftDatabaseInfo), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Take() (*model.DraftDatabaseInfo, error) {
|
||||
if result, err := d.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.DraftDatabaseInfo), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Last() (*model.DraftDatabaseInfo, error) {
|
||||
if result, err := d.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.DraftDatabaseInfo), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Find() ([]*model.DraftDatabaseInfo, error) {
|
||||
result, err := d.DO.Find()
|
||||
return result.([]*model.DraftDatabaseInfo), err
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.DraftDatabaseInfo, err error) {
|
||||
buf := make([]*model.DraftDatabaseInfo, 0, batchSize)
|
||||
err = d.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) FindInBatches(result *[]*model.DraftDatabaseInfo, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return d.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Attrs(attrs ...field.AssignExpr) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Assign(attrs ...field.AssignExpr) IDraftDatabaseInfoDo {
|
||||
return d.withDO(d.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Joins(fields ...field.RelationField) IDraftDatabaseInfoDo {
|
||||
for _, _f := range fields {
|
||||
d = *d.withDO(d.DO.Joins(_f))
|
||||
}
|
||||
return &d
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Preload(fields ...field.RelationField) IDraftDatabaseInfoDo {
|
||||
for _, _f := range fields {
|
||||
d = *d.withDO(d.DO.Preload(_f))
|
||||
}
|
||||
return &d
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) FirstOrInit() (*model.DraftDatabaseInfo, error) {
|
||||
if result, err := d.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.DraftDatabaseInfo), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) FirstOrCreate() (*model.DraftDatabaseInfo, error) {
|
||||
if result, err := d.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.DraftDatabaseInfo), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) FindByPage(offset int, limit int) (result []*model.DraftDatabaseInfo, count int64, err error) {
|
||||
result, err = d.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = d.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = d.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = d.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Scan(result interface{}) (err error) {
|
||||
return d.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (d draftDatabaseInfoDo) Delete(models ...*model.DraftDatabaseInfo) (result gen.ResultInfo, err error) {
|
||||
return d.DO.Delete(models)
|
||||
}
|
||||
|
||||
func (d *draftDatabaseInfoDo) withDO(do gen.Dao) *draftDatabaseInfoDo {
|
||||
d.DO = *do.(*gen.DO)
|
||||
return d
|
||||
}
|
||||
119
backend/domain/memory/database/internal/dal/query/gen.go
Normal file
119
backend/domain/memory/database/internal/dal/query/gen.go
Normal file
@@ -0,0 +1,119 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"gorm.io/gen"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
)
|
||||
|
||||
var (
|
||||
Q = new(Query)
|
||||
AgentToDatabase *agentToDatabase
|
||||
DraftDatabaseInfo *draftDatabaseInfo
|
||||
OnlineDatabaseInfo *onlineDatabaseInfo
|
||||
)
|
||||
|
||||
func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
|
||||
*Q = *Use(db, opts...)
|
||||
AgentToDatabase = &Q.AgentToDatabase
|
||||
DraftDatabaseInfo = &Q.DraftDatabaseInfo
|
||||
OnlineDatabaseInfo = &Q.OnlineDatabaseInfo
|
||||
}
|
||||
|
||||
func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
AgentToDatabase: newAgentToDatabase(db, opts...),
|
||||
DraftDatabaseInfo: newDraftDatabaseInfo(db, opts...),
|
||||
OnlineDatabaseInfo: newOnlineDatabaseInfo(db, opts...),
|
||||
}
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
db *gorm.DB
|
||||
|
||||
AgentToDatabase agentToDatabase
|
||||
DraftDatabaseInfo draftDatabaseInfo
|
||||
OnlineDatabaseInfo onlineDatabaseInfo
|
||||
}
|
||||
|
||||
func (q *Query) Available() bool { return q.db != nil }
|
||||
|
||||
func (q *Query) clone(db *gorm.DB) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
AgentToDatabase: q.AgentToDatabase.clone(db),
|
||||
DraftDatabaseInfo: q.DraftDatabaseInfo.clone(db),
|
||||
OnlineDatabaseInfo: q.OnlineDatabaseInfo.clone(db),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) ReadDB() *Query {
|
||||
return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
|
||||
}
|
||||
|
||||
func (q *Query) WriteDB() *Query {
|
||||
return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
|
||||
}
|
||||
|
||||
func (q *Query) ReplaceDB(db *gorm.DB) *Query {
|
||||
return &Query{
|
||||
db: db,
|
||||
AgentToDatabase: q.AgentToDatabase.replaceDB(db),
|
||||
DraftDatabaseInfo: q.DraftDatabaseInfo.replaceDB(db),
|
||||
OnlineDatabaseInfo: q.OnlineDatabaseInfo.replaceDB(db),
|
||||
}
|
||||
}
|
||||
|
||||
type queryCtx struct {
|
||||
AgentToDatabase IAgentToDatabaseDo
|
||||
DraftDatabaseInfo IDraftDatabaseInfoDo
|
||||
OnlineDatabaseInfo IOnlineDatabaseInfoDo
|
||||
}
|
||||
|
||||
func (q *Query) WithContext(ctx context.Context) *queryCtx {
|
||||
return &queryCtx{
|
||||
AgentToDatabase: q.AgentToDatabase.WithContext(ctx),
|
||||
DraftDatabaseInfo: q.DraftDatabaseInfo.WithContext(ctx),
|
||||
OnlineDatabaseInfo: q.OnlineDatabaseInfo.WithContext(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {
|
||||
return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
|
||||
}
|
||||
|
||||
func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {
|
||||
tx := q.db.Begin(opts...)
|
||||
return &QueryTx{Query: q.clone(tx), Error: tx.Error}
|
||||
}
|
||||
|
||||
type QueryTx struct {
|
||||
*Query
|
||||
Error error
|
||||
}
|
||||
|
||||
func (q *QueryTx) Commit() error {
|
||||
return q.db.Commit().Error
|
||||
}
|
||||
|
||||
func (q *QueryTx) Rollback() error {
|
||||
return q.db.Rollback().Error
|
||||
}
|
||||
|
||||
func (q *QueryTx) SavePoint(name string) error {
|
||||
return q.db.SavePoint(name).Error
|
||||
}
|
||||
|
||||
func (q *QueryTx) RollbackTo(name string) error {
|
||||
return q.db.RollbackTo(name).Error
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/schema"
|
||||
|
||||
"gorm.io/gen"
|
||||
"gorm.io/gen/field"
|
||||
|
||||
"gorm.io/plugin/dbresolver"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal/model"
|
||||
)
|
||||
|
||||
func newOnlineDatabaseInfo(db *gorm.DB, opts ...gen.DOOption) onlineDatabaseInfo {
|
||||
_onlineDatabaseInfo := onlineDatabaseInfo{}
|
||||
|
||||
_onlineDatabaseInfo.onlineDatabaseInfoDo.UseDB(db, opts...)
|
||||
_onlineDatabaseInfo.onlineDatabaseInfoDo.UseModel(&model.OnlineDatabaseInfo{})
|
||||
|
||||
tableName := _onlineDatabaseInfo.onlineDatabaseInfoDo.TableName()
|
||||
_onlineDatabaseInfo.ALL = field.NewAsterisk(tableName)
|
||||
_onlineDatabaseInfo.ID = field.NewInt64(tableName, "id")
|
||||
_onlineDatabaseInfo.AppID = field.NewInt64(tableName, "app_id")
|
||||
_onlineDatabaseInfo.SpaceID = field.NewInt64(tableName, "space_id")
|
||||
_onlineDatabaseInfo.RelatedDraftID = field.NewInt64(tableName, "related_draft_id")
|
||||
_onlineDatabaseInfo.IsVisible = field.NewInt32(tableName, "is_visible")
|
||||
_onlineDatabaseInfo.PromptDisabled = field.NewInt32(tableName, "prompt_disabled")
|
||||
_onlineDatabaseInfo.TableName_ = field.NewString(tableName, "table_name")
|
||||
_onlineDatabaseInfo.TableDesc = field.NewString(tableName, "table_desc")
|
||||
_onlineDatabaseInfo.TableField = field.NewField(tableName, "table_field")
|
||||
_onlineDatabaseInfo.CreatorID = field.NewInt64(tableName, "creator_id")
|
||||
_onlineDatabaseInfo.IconURI = field.NewString(tableName, "icon_uri")
|
||||
_onlineDatabaseInfo.PhysicalTableName = field.NewString(tableName, "physical_table_name")
|
||||
_onlineDatabaseInfo.RwMode = field.NewInt64(tableName, "rw_mode")
|
||||
_onlineDatabaseInfo.CreatedAt = field.NewInt64(tableName, "created_at")
|
||||
_onlineDatabaseInfo.UpdatedAt = field.NewInt64(tableName, "updated_at")
|
||||
_onlineDatabaseInfo.DeletedAt = field.NewField(tableName, "deleted_at")
|
||||
|
||||
_onlineDatabaseInfo.fillFieldMap()
|
||||
|
||||
return _onlineDatabaseInfo
|
||||
}
|
||||
|
||||
// onlineDatabaseInfo online database info
|
||||
type onlineDatabaseInfo struct {
|
||||
onlineDatabaseInfoDo
|
||||
|
||||
ALL field.Asterisk
|
||||
ID field.Int64 // ID
|
||||
AppID field.Int64 // App ID
|
||||
SpaceID field.Int64 // Space ID
|
||||
RelatedDraftID field.Int64 // The primary key ID of draft_database_info table
|
||||
IsVisible field.Int32 // Visibility: 0 invisible, 1 visible
|
||||
PromptDisabled field.Int32 // Support prompt calls: 1 not supported, 0 supported
|
||||
TableName_ field.String // Table name
|
||||
TableDesc field.String // Table description
|
||||
TableField field.Field // Table field info
|
||||
CreatorID field.Int64 // Creator ID
|
||||
IconURI field.String // Icon Uri
|
||||
PhysicalTableName field.String // The name of the real physical table
|
||||
RwMode field.Int64 // Read and write permission modes: 1. Limited read and write mode 2. Read-only mode 3. Full read and write mode
|
||||
CreatedAt field.Int64 // Create Time in Milliseconds
|
||||
UpdatedAt field.Int64 // Update Time in Milliseconds
|
||||
DeletedAt field.Field // Delete Time
|
||||
|
||||
fieldMap map[string]field.Expr
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfo) Table(newTableName string) *onlineDatabaseInfo {
|
||||
o.onlineDatabaseInfoDo.UseTable(newTableName)
|
||||
return o.updateTableName(newTableName)
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfo) As(alias string) *onlineDatabaseInfo {
|
||||
o.onlineDatabaseInfoDo.DO = *(o.onlineDatabaseInfoDo.As(alias).(*gen.DO))
|
||||
return o.updateTableName(alias)
|
||||
}
|
||||
|
||||
func (o *onlineDatabaseInfo) updateTableName(table string) *onlineDatabaseInfo {
|
||||
o.ALL = field.NewAsterisk(table)
|
||||
o.ID = field.NewInt64(table, "id")
|
||||
o.AppID = field.NewInt64(table, "app_id")
|
||||
o.SpaceID = field.NewInt64(table, "space_id")
|
||||
o.RelatedDraftID = field.NewInt64(table, "related_draft_id")
|
||||
o.IsVisible = field.NewInt32(table, "is_visible")
|
||||
o.PromptDisabled = field.NewInt32(table, "prompt_disabled")
|
||||
o.TableName_ = field.NewString(table, "table_name")
|
||||
o.TableDesc = field.NewString(table, "table_desc")
|
||||
o.TableField = field.NewField(table, "table_field")
|
||||
o.CreatorID = field.NewInt64(table, "creator_id")
|
||||
o.IconURI = field.NewString(table, "icon_uri")
|
||||
o.PhysicalTableName = field.NewString(table, "physical_table_name")
|
||||
o.RwMode = field.NewInt64(table, "rw_mode")
|
||||
o.CreatedAt = field.NewInt64(table, "created_at")
|
||||
o.UpdatedAt = field.NewInt64(table, "updated_at")
|
||||
o.DeletedAt = field.NewField(table, "deleted_at")
|
||||
|
||||
o.fillFieldMap()
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *onlineDatabaseInfo) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
|
||||
_f, ok := o.fieldMap[fieldName]
|
||||
if !ok || _f == nil {
|
||||
return nil, false
|
||||
}
|
||||
_oe, ok := _f.(field.OrderExpr)
|
||||
return _oe, ok
|
||||
}
|
||||
|
||||
func (o *onlineDatabaseInfo) fillFieldMap() {
|
||||
o.fieldMap = make(map[string]field.Expr, 16)
|
||||
o.fieldMap["id"] = o.ID
|
||||
o.fieldMap["app_id"] = o.AppID
|
||||
o.fieldMap["space_id"] = o.SpaceID
|
||||
o.fieldMap["related_draft_id"] = o.RelatedDraftID
|
||||
o.fieldMap["is_visible"] = o.IsVisible
|
||||
o.fieldMap["prompt_disabled"] = o.PromptDisabled
|
||||
o.fieldMap["table_name"] = o.TableName_
|
||||
o.fieldMap["table_desc"] = o.TableDesc
|
||||
o.fieldMap["table_field"] = o.TableField
|
||||
o.fieldMap["creator_id"] = o.CreatorID
|
||||
o.fieldMap["icon_uri"] = o.IconURI
|
||||
o.fieldMap["physical_table_name"] = o.PhysicalTableName
|
||||
o.fieldMap["rw_mode"] = o.RwMode
|
||||
o.fieldMap["created_at"] = o.CreatedAt
|
||||
o.fieldMap["updated_at"] = o.UpdatedAt
|
||||
o.fieldMap["deleted_at"] = o.DeletedAt
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfo) clone(db *gorm.DB) onlineDatabaseInfo {
|
||||
o.onlineDatabaseInfoDo.ReplaceConnPool(db.Statement.ConnPool)
|
||||
return o
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfo) replaceDB(db *gorm.DB) onlineDatabaseInfo {
|
||||
o.onlineDatabaseInfoDo.ReplaceDB(db)
|
||||
return o
|
||||
}
|
||||
|
||||
type onlineDatabaseInfoDo struct{ gen.DO }
|
||||
|
||||
type IOnlineDatabaseInfoDo interface {
|
||||
gen.SubQuery
|
||||
Debug() IOnlineDatabaseInfoDo
|
||||
WithContext(ctx context.Context) IOnlineDatabaseInfoDo
|
||||
WithResult(fc func(tx gen.Dao)) gen.ResultInfo
|
||||
ReplaceDB(db *gorm.DB)
|
||||
ReadDB() IOnlineDatabaseInfoDo
|
||||
WriteDB() IOnlineDatabaseInfoDo
|
||||
As(alias string) gen.Dao
|
||||
Session(config *gorm.Session) IOnlineDatabaseInfoDo
|
||||
Columns(cols ...field.Expr) gen.Columns
|
||||
Clauses(conds ...clause.Expression) IOnlineDatabaseInfoDo
|
||||
Not(conds ...gen.Condition) IOnlineDatabaseInfoDo
|
||||
Or(conds ...gen.Condition) IOnlineDatabaseInfoDo
|
||||
Select(conds ...field.Expr) IOnlineDatabaseInfoDo
|
||||
Where(conds ...gen.Condition) IOnlineDatabaseInfoDo
|
||||
Order(conds ...field.Expr) IOnlineDatabaseInfoDo
|
||||
Distinct(cols ...field.Expr) IOnlineDatabaseInfoDo
|
||||
Omit(cols ...field.Expr) IOnlineDatabaseInfoDo
|
||||
Join(table schema.Tabler, on ...field.Expr) IOnlineDatabaseInfoDo
|
||||
LeftJoin(table schema.Tabler, on ...field.Expr) IOnlineDatabaseInfoDo
|
||||
RightJoin(table schema.Tabler, on ...field.Expr) IOnlineDatabaseInfoDo
|
||||
Group(cols ...field.Expr) IOnlineDatabaseInfoDo
|
||||
Having(conds ...gen.Condition) IOnlineDatabaseInfoDo
|
||||
Limit(limit int) IOnlineDatabaseInfoDo
|
||||
Offset(offset int) IOnlineDatabaseInfoDo
|
||||
Count() (count int64, err error)
|
||||
Scopes(funcs ...func(gen.Dao) gen.Dao) IOnlineDatabaseInfoDo
|
||||
Unscoped() IOnlineDatabaseInfoDo
|
||||
Create(values ...*model.OnlineDatabaseInfo) error
|
||||
CreateInBatches(values []*model.OnlineDatabaseInfo, batchSize int) error
|
||||
Save(values ...*model.OnlineDatabaseInfo) error
|
||||
First() (*model.OnlineDatabaseInfo, error)
|
||||
Take() (*model.OnlineDatabaseInfo, error)
|
||||
Last() (*model.OnlineDatabaseInfo, error)
|
||||
Find() ([]*model.OnlineDatabaseInfo, error)
|
||||
FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.OnlineDatabaseInfo, err error)
|
||||
FindInBatches(result *[]*model.OnlineDatabaseInfo, batchSize int, fc func(tx gen.Dao, batch int) error) error
|
||||
Pluck(column field.Expr, dest interface{}) error
|
||||
Delete(...*model.OnlineDatabaseInfo) (info gen.ResultInfo, err error)
|
||||
Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
|
||||
Updates(value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)
|
||||
UpdateColumns(value interface{}) (info gen.ResultInfo, err error)
|
||||
UpdateFrom(q gen.SubQuery) gen.Dao
|
||||
Attrs(attrs ...field.AssignExpr) IOnlineDatabaseInfoDo
|
||||
Assign(attrs ...field.AssignExpr) IOnlineDatabaseInfoDo
|
||||
Joins(fields ...field.RelationField) IOnlineDatabaseInfoDo
|
||||
Preload(fields ...field.RelationField) IOnlineDatabaseInfoDo
|
||||
FirstOrInit() (*model.OnlineDatabaseInfo, error)
|
||||
FirstOrCreate() (*model.OnlineDatabaseInfo, error)
|
||||
FindByPage(offset int, limit int) (result []*model.OnlineDatabaseInfo, count int64, err error)
|
||||
ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
|
||||
Scan(result interface{}) (err error)
|
||||
Returning(value interface{}, columns ...string) IOnlineDatabaseInfoDo
|
||||
UnderlyingDB() *gorm.DB
|
||||
schema.Tabler
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Debug() IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Debug())
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) WithContext(ctx context.Context) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) ReadDB() IOnlineDatabaseInfoDo {
|
||||
return o.Clauses(dbresolver.Read)
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) WriteDB() IOnlineDatabaseInfoDo {
|
||||
return o.Clauses(dbresolver.Write)
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Session(config *gorm.Session) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Session(config))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Clauses(conds ...clause.Expression) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Clauses(conds...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Returning(value interface{}, columns ...string) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Returning(value, columns...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Not(conds ...gen.Condition) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Not(conds...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Or(conds ...gen.Condition) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Or(conds...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Select(conds ...field.Expr) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Select(conds...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Where(conds ...gen.Condition) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Where(conds...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Order(conds ...field.Expr) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Order(conds...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Distinct(cols ...field.Expr) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Distinct(cols...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Omit(cols ...field.Expr) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Omit(cols...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Join(table schema.Tabler, on ...field.Expr) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Join(table, on...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) LeftJoin(table schema.Tabler, on ...field.Expr) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.LeftJoin(table, on...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) RightJoin(table schema.Tabler, on ...field.Expr) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.RightJoin(table, on...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Group(cols ...field.Expr) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Group(cols...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Having(conds ...gen.Condition) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Having(conds...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Limit(limit int) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Limit(limit))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Offset(offset int) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Offset(offset))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Scopes(funcs...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Unscoped() IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Unscoped())
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Create(values ...*model.OnlineDatabaseInfo) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return o.DO.Create(values)
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) CreateInBatches(values []*model.OnlineDatabaseInfo, batchSize int) error {
|
||||
return o.DO.CreateInBatches(values, batchSize)
|
||||
}
|
||||
|
||||
// Save : !!! underlying implementation is different with GORM
|
||||
// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
|
||||
func (o onlineDatabaseInfoDo) Save(values ...*model.OnlineDatabaseInfo) error {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return o.DO.Save(values)
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) First() (*model.OnlineDatabaseInfo, error) {
|
||||
if result, err := o.DO.First(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.OnlineDatabaseInfo), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Take() (*model.OnlineDatabaseInfo, error) {
|
||||
if result, err := o.DO.Take(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.OnlineDatabaseInfo), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Last() (*model.OnlineDatabaseInfo, error) {
|
||||
if result, err := o.DO.Last(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.OnlineDatabaseInfo), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Find() ([]*model.OnlineDatabaseInfo, error) {
|
||||
result, err := o.DO.Find()
|
||||
return result.([]*model.OnlineDatabaseInfo), err
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.OnlineDatabaseInfo, err error) {
|
||||
buf := make([]*model.OnlineDatabaseInfo, 0, batchSize)
|
||||
err = o.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
|
||||
defer func() { results = append(results, buf...) }()
|
||||
return fc(tx, batch)
|
||||
})
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) FindInBatches(result *[]*model.OnlineDatabaseInfo, batchSize int, fc func(tx gen.Dao, batch int) error) error {
|
||||
return o.DO.FindInBatches(result, batchSize, fc)
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Attrs(attrs ...field.AssignExpr) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Attrs(attrs...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Assign(attrs ...field.AssignExpr) IOnlineDatabaseInfoDo {
|
||||
return o.withDO(o.DO.Assign(attrs...))
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Joins(fields ...field.RelationField) IOnlineDatabaseInfoDo {
|
||||
for _, _f := range fields {
|
||||
o = *o.withDO(o.DO.Joins(_f))
|
||||
}
|
||||
return &o
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Preload(fields ...field.RelationField) IOnlineDatabaseInfoDo {
|
||||
for _, _f := range fields {
|
||||
o = *o.withDO(o.DO.Preload(_f))
|
||||
}
|
||||
return &o
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) FirstOrInit() (*model.OnlineDatabaseInfo, error) {
|
||||
if result, err := o.DO.FirstOrInit(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.OnlineDatabaseInfo), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) FirstOrCreate() (*model.OnlineDatabaseInfo, error) {
|
||||
if result, err := o.DO.FirstOrCreate(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return result.(*model.OnlineDatabaseInfo), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) FindByPage(offset int, limit int) (result []*model.OnlineDatabaseInfo, count int64, err error) {
|
||||
result, err = o.Offset(offset).Limit(limit).Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if size := len(result); 0 < limit && 0 < size && size < limit {
|
||||
count = int64(size + offset)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = o.Offset(-1).Limit(-1).Count()
|
||||
return
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
|
||||
count, err = o.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = o.Offset(offset).Limit(limit).Scan(result)
|
||||
return
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Scan(result interface{}) (err error) {
|
||||
return o.DO.Scan(result)
|
||||
}
|
||||
|
||||
func (o onlineDatabaseInfoDo) Delete(models ...*model.OnlineDatabaseInfo) (result gen.ResultInfo, err error) {
|
||||
return o.DO.Delete(models)
|
||||
}
|
||||
|
||||
func (o *onlineDatabaseInfoDo) withDO(do gen.Dao) *onlineDatabaseInfoDo {
|
||||
o.DO = *do.(*gen.DO)
|
||||
return o
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
738
backend/domain/memory/database/internal/sheet/sheet.go
Normal file
738
backend/domain/memory/database/internal/sheet/sheet.go
Normal file
@@ -0,0 +1,738 @@
|
||||
/*
|
||||
* 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 sheet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/extrame/xls"
|
||||
"github.com/xuri/excelize/v2"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/common"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/database"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/storage"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/errorx"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/logs"
|
||||
"github.com/coze-dev/coze-studio/backend/types/errno"
|
||||
)
|
||||
|
||||
type CellTypeIdentifier struct {
|
||||
Priority int64
|
||||
IdentifyCellType func(cellValue string) *common.ColumnType
|
||||
TargetColumnType common.ColumnType
|
||||
}
|
||||
|
||||
var (
|
||||
identifierChain []CellTypeIdentifier
|
||||
initIdentifierOnce sync.Once
|
||||
dateTimePattern string
|
||||
)
|
||||
|
||||
type TosTableParser struct {
|
||||
UserID int64
|
||||
DocumentSource database.DocumentSourceType
|
||||
TosURI string
|
||||
TosServ storage.Storage
|
||||
}
|
||||
|
||||
func (t *TosTableParser) GetTableDataBySheetIDx(ctx context.Context, rMeta entity.TableReaderMeta) (*entity.TableReaderSheetData, *entity.ExcelExtraInfo, error) {
|
||||
meta, err := t.getLocalSheetMeta(ctx, rMeta.TosMaxLine)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if meta == nil || meta.ExcelFile == nil {
|
||||
return
|
||||
}
|
||||
if err1 := meta.ExcelFile.Close(); err1 != nil {
|
||||
logs.CtxInfof(ctx, "[GetTableDataBySheetIdx] close excel file failed, err: %v", err1)
|
||||
}
|
||||
}()
|
||||
|
||||
if len(meta.SheetsRowCount) == 0 {
|
||||
return nil, nil, errorx.New(errno.ErrMemoryDatabaseNoSheetFound)
|
||||
}
|
||||
|
||||
if int(rMeta.SheetId) >= len(meta.SheetsRowCount) {
|
||||
return nil, nil, errorx.New(errno.ErrMemoryDatabaseSheetIndexExceed)
|
||||
}
|
||||
|
||||
// get execl range: sheetIdx + headerIdx + startLineIdx
|
||||
if rMeta.StartLineIdx > int64(meta.SheetsRowCount[rMeta.SheetId]) {
|
||||
return nil, nil, errorx.New(errno.ErrMemoryDatabaseSheetIndexExceed)
|
||||
}
|
||||
extra := &entity.ExcelExtraInfo{
|
||||
ExtensionName: meta.ExtensionName,
|
||||
FileSize: meta.FileSize,
|
||||
TosURI: t.TosURI,
|
||||
}
|
||||
|
||||
data, ocErr := t.getTableDataBySheetIdx(ctx, rMeta, meta, rMeta.SheetId)
|
||||
if ocErr != nil {
|
||||
return nil, nil, ocErr
|
||||
}
|
||||
for i := range meta.SheetsRowCount {
|
||||
extra.Sheets = append(extra.Sheets, &common.DocTableSheet{
|
||||
ID: int64(i),
|
||||
SheetName: meta.SheetsNameList[i],
|
||||
TotalRow: int64(meta.SheetsRowCount[i]),
|
||||
})
|
||||
}
|
||||
return data, extra, ocErr
|
||||
}
|
||||
|
||||
func (t *TosTableParser) getLocalSheetMeta(ctx context.Context, maxLine int64) (*entity.LocalTableMeta, error) {
|
||||
documentExtension, object, ocErr := t.GetTosTableFile(ctx)
|
||||
if ocErr != nil {
|
||||
return nil, ocErr
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(object)
|
||||
if documentExtension == "csv" { // 处理 csv 文件
|
||||
records, err := csv.NewReader(reader).ReadAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if int64(len(records)) > maxLine {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &entity.LocalTableMeta{
|
||||
RawLines: records,
|
||||
SheetsNameList: []string{"default"},
|
||||
SheetsRowCount: []int{len(records)},
|
||||
ExtensionName: documentExtension,
|
||||
FileSize: int64(len(object)),
|
||||
}, nil
|
||||
} else if documentExtension == "xls" {
|
||||
result, err := HandleTmpFile(ctx, reader, "xls", getXlsLocalSheetMetaWithTmpFileCallback, int64(len(object)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localTableMeta, ok := result.(*entity.LocalTableMeta)
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return localTableMeta, nil
|
||||
}
|
||||
|
||||
excelFile, err := excelize.OpenReader(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
meta := &entity.LocalTableMeta{
|
||||
ExcelFile: excelFile,
|
||||
ExtensionName: documentExtension,
|
||||
FileSize: int64(len(object)),
|
||||
}
|
||||
|
||||
for _, sheetName := range excelFile.GetSheetList() {
|
||||
total := 0
|
||||
rows, rowsErr := excelFile.Rows(sheetName)
|
||||
if rowsErr != nil {
|
||||
return nil, rowsErr
|
||||
}
|
||||
for rows.Next() {
|
||||
total++
|
||||
if int64(total) > maxLine {
|
||||
return nil, errorx.New(errno.ErrMemoryDatabaseSheetRowCountExceed, errorx.KVf("msg", "row count exceed limit when database importing excel, limit: %v", maxLine))
|
||||
}
|
||||
}
|
||||
meta.SheetsRowCount = append(meta.SheetsRowCount, total)
|
||||
meta.SheetsNameList = append(meta.SheetsNameList, sheetName)
|
||||
}
|
||||
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func (t *TosTableParser) GetTosTableFile(ctx context.Context) (string, []byte, error) {
|
||||
names := strings.Split(t.TosURI, "/")
|
||||
objectName := strings.Split(names[len(names)-1], ".")
|
||||
documentExtension := objectName[len(objectName)-1]
|
||||
if documentExtension != "csv" && documentExtension != "xlsx" {
|
||||
return "", nil, errorx.New(errno.ErrMemoryDatabaseUnsupportedFileType)
|
||||
}
|
||||
|
||||
object, err := t.TosServ.GetObject(ctx, t.TosURI)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("get tos file failed:%+v", err)
|
||||
}
|
||||
|
||||
if len(object) > 104857600 {
|
||||
return "", nil, errorx.New(errno.ErrMemoryDatabaseSheetSizeExceed)
|
||||
}
|
||||
|
||||
return documentExtension, object, nil
|
||||
}
|
||||
|
||||
func (t *TosTableParser) getTableDataBySheetIdx(ctx context.Context, rMeta entity.TableReaderMeta, localMeta *entity.LocalTableMeta, sheetIdx int64) (*entity.TableReaderSheetData, error) {
|
||||
if int(sheetIdx) >= len(localMeta.SheetsRowCount) {
|
||||
return nil, fmt.Errorf("start sheet index out of range")
|
||||
}
|
||||
|
||||
res := &entity.TableReaderSheetData{
|
||||
Columns: make([]*common.DocTableColumn, 0),
|
||||
}
|
||||
defer func() {
|
||||
if localMeta != nil && localMeta.ExcelFile != nil {
|
||||
if err := localMeta.ExcelFile.Close(); err != nil {
|
||||
logs.CtxErrorf(ctx, "[GetTableDataBySheetIdx] close excel file err: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
sheetRowTotal := localMeta.SheetsRowCount[sheetIdx]
|
||||
headLine, sampleData, ocErr := t.parseSchemaInfo(ctx, localMeta, rMeta, int(sheetIdx), sheetRowTotal)
|
||||
if ocErr != nil {
|
||||
return nil, ocErr
|
||||
}
|
||||
|
||||
if len(headLine) == 0 {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
for colIndex, cell := range headLine {
|
||||
res.Columns = append(res.Columns, &common.DocTableColumn{
|
||||
ColumnName: cell,
|
||||
Sequence: int64(colIndex),
|
||||
})
|
||||
}
|
||||
res.SampleData = sampleData
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (t *TosTableParser) parseSchemaInfo(ctx context.Context, meta *entity.LocalTableMeta, rMeta entity.TableReaderMeta, sheetIdx, sheetRowTotal int) (header []string, sampleData [][]string, err error) {
|
||||
if len(meta.RawLines) != 0 {
|
||||
if len(meta.RawLines) == 0 {
|
||||
return []string{}, [][]string{}, nil
|
||||
}
|
||||
if rMeta.HeaderLineIdx >= int64(len(meta.RawLines)) {
|
||||
return nil, nil, fmt.Errorf("header idx exceed")
|
||||
}
|
||||
if rMeta.StartLineIdx > int64(len(meta.RawLines)) {
|
||||
return meta.RawLines[rMeta.HeaderLineIdx], [][]string{}, nil
|
||||
}
|
||||
|
||||
switch rMeta.ReaderMethod {
|
||||
case database.TableReadDataMethodHead:
|
||||
end := int(rMeta.StartLineIdx + rMeta.ReadLineCnt)
|
||||
if end > len(meta.RawLines) {
|
||||
end = len(meta.RawLines)
|
||||
}
|
||||
return meta.RawLines[rMeta.HeaderLineIdx], meta.RawLines[rMeta.StartLineIdx:end], nil
|
||||
|
||||
case database.TableReadDataMethodOnlyHeader:
|
||||
if len(meta.RawLines[rMeta.HeaderLineIdx]) >= 50 {
|
||||
return meta.RawLines[rMeta.HeaderLineIdx][:50], [][]string{}, nil
|
||||
}
|
||||
return meta.RawLines[rMeta.HeaderLineIdx], [][]string{}, nil
|
||||
|
||||
case database.TableReadDataMethodAll:
|
||||
return meta.RawLines[rMeta.HeaderLineIdx], meta.RawLines[rMeta.StartLineIdx:], nil
|
||||
default:
|
||||
logs.CtxInfof(ctx, "[parseSchemaInfo] reader method is:%d", rMeta.ReaderMethod)
|
||||
}
|
||||
|
||||
return nil, nil, fmt.Errorf("reader method is:%d", rMeta.ReaderMethod)
|
||||
}
|
||||
|
||||
sampleData, header, err = t.GetExcelToParseTableRange(ctx, meta.ExcelFile, sheetIdx, int64(sheetRowTotal), rMeta)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return header, sampleData, nil
|
||||
}
|
||||
|
||||
func (t *TosTableParser) GetExcelToParseTableRange(ctx context.Context, file *excelize.File, sheetIdx int, rowCount int64, rMeta entity.TableReaderMeta) ([][]string, []string, error) {
|
||||
selectFunc := func(idx int64) (isContinue, isBreak bool) {
|
||||
return idx < rMeta.StartLineIdx, false
|
||||
}
|
||||
if rMeta.ReaderMethod == database.TableReadDataMethodHead {
|
||||
selectFunc = func(idx int64) (isContinue, isBreak bool) {
|
||||
return idx < rMeta.StartLineIdx, idx-rMeta.StartLineIdx > rMeta.ReadLineCnt
|
||||
}
|
||||
}
|
||||
|
||||
selectedExcelContent := make([][]string, 0)
|
||||
sheetName := file.GetSheetName(sheetIdx)
|
||||
rows, err := file.Rows(sheetName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
headLineContent := make([]string, 0)
|
||||
var (
|
||||
curRowIndex int64
|
||||
columnCount int
|
||||
)
|
||||
for rows.Next() {
|
||||
if curRowIndex < rMeta.HeaderLineIdx {
|
||||
curRowIndex++
|
||||
continue
|
||||
}
|
||||
|
||||
if curRowIndex == rMeta.HeaderLineIdx {
|
||||
content, rowErr := rows.Columns()
|
||||
if rowErr != nil {
|
||||
return nil, nil, rowErr
|
||||
}
|
||||
if len(content) > 50 {
|
||||
content = content[:50]
|
||||
}
|
||||
headLineContent = content
|
||||
curRowIndex++
|
||||
columnCount = len(headLineContent)
|
||||
if rMeta.ReaderMethod == database.TableReadDataMethodOnlyHeader {
|
||||
return [][]string{}, headLineContent, nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
isContinue, isBreak := selectFunc(curRowIndex)
|
||||
if isContinue {
|
||||
curRowIndex++
|
||||
continue
|
||||
}
|
||||
if isBreak {
|
||||
break
|
||||
}
|
||||
curRowIndex++
|
||||
|
||||
columns, err := rows.Columns()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(columns) < columnCount {
|
||||
emptyStrs := make([]string, columnCount-len(columns))
|
||||
columns = append(columns, emptyStrs...)
|
||||
}
|
||||
if len(columns) > 50 {
|
||||
columns = columns[:50]
|
||||
}
|
||||
|
||||
if len(columns) != 0 && getCharCount(columns) > 0 {
|
||||
selectedExcelContent = append(selectedExcelContent, columns)
|
||||
}
|
||||
}
|
||||
|
||||
return selectedExcelContent, headLineContent, nil
|
||||
}
|
||||
|
||||
func getCharCount(strs []string) int {
|
||||
var count int
|
||||
for _, str := range strs {
|
||||
count += len(str)
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
type TmpFileCallback func(context.Context, string, ...interface{}) (interface{}, error)
|
||||
|
||||
func getTmpFile(extension string) string {
|
||||
return fmt.Sprintf("%s/%s", "/tmp", getTmpFileName(extension))
|
||||
}
|
||||
|
||||
func getTmpFileName(extension string) string {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
name := fmt.Sprintf("%s_%d_%d.%s", "dataset_", time.Now().UnixMilli(), r.Intn(10000), extension)
|
||||
return name
|
||||
}
|
||||
|
||||
func HandleTmpFile(ctx context.Context, reader io.Reader, extension string, callback TmpFileCallback, args ...interface{}) (interface{}, error) {
|
||||
tmpFile := getTmpFile(extension)
|
||||
file, err := os.OpenFile(tmpFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reader != nil {
|
||||
_, err = io.Copy(file, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err = file.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err = os.Remove(tmpFile); err != nil {
|
||||
logs.CtxErrorf(ctx, "[HandleTmpFile]Error removing tmpFile: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
result, err := callback(ctx, tmpFile, args...)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func getXlsLocalSheetMetaWithTmpFileCallback(ctx context.Context, tmpFile string, args ...interface{}) (interface{}, error) {
|
||||
const prefix = "[getXlsLocalSheetMetaWithTmpFileCallback]"
|
||||
defer func() {
|
||||
if panicErr := recover(); panicErr != nil {
|
||||
s := string(debug.Stack())
|
||||
logs.CtxErrorf(ctx, "%s get recover, stack: %s", prefix, s)
|
||||
return
|
||||
}
|
||||
}()
|
||||
size, _ := args[0].(int64)
|
||||
file, err := os.Open(tmpFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err = file.Close(); err != nil {
|
||||
logs.CtxErrorf(ctx, "%v close excel reader failed:%+v", prefix, err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
xlFile, err := xls.OpenReader(file, "utf-8")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sheet := xlFile.GetSheet(0)
|
||||
if sheet == nil {
|
||||
return nil, errorx.New(errno.ErrMemoryDatabaseXlsSheetEmpty)
|
||||
}
|
||||
|
||||
records := make([][]string, 0)
|
||||
if sheet.MaxRow < 1 {
|
||||
return nil, errorx.New(errno.ErrMemoryDatabaseXlsSheetEmpty)
|
||||
}
|
||||
for i := 0; i <= int(sheet.MaxRow); i++ {
|
||||
row := sheet.Row(i)
|
||||
record := make([]string, row.LastCol()-row.FirstCol()+1)
|
||||
for j := row.FirstCol(); j <= row.LastCol(); j++ {
|
||||
record[j-row.FirstCol()] = row.Col(j)
|
||||
}
|
||||
records = append(records, record)
|
||||
}
|
||||
|
||||
return &entity.LocalTableMeta{
|
||||
RawLines: records,
|
||||
SheetsNameList: []string{"default"},
|
||||
SheetsRowCount: []int{len(records)},
|
||||
ExtensionName: "xls",
|
||||
FileSize: size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *TosTableParser) PredictColumnType(columns []*common.DocTableColumn, sampleData [][]string, sheetIdx, startLineIdx int64) ([]*common.DocTableColumn, error) {
|
||||
if len(sampleData) == 0 {
|
||||
for _, column := range columns {
|
||||
column.ColumnType = common.ColumnTypePtr(common.ColumnType_Text) // 兜底展示
|
||||
column.ContainsEmptyValue = ptr.Of(true)
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
columnInfos, err := ParseDocTableColumnInfo(sampleData, len(columns))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(columns) != len(columnInfos) {
|
||||
return nil, errorx.New(errno.ErrMemoryDatabaseColumnNotMatch, errorx.KVf("msg", "columnType length is not match with column count, column length:%d, predict column length:%d", len(columns), len(columnInfos)))
|
||||
}
|
||||
|
||||
for i := range columns {
|
||||
columns[i].ContainsEmptyValue = &columnInfos[i].ContainsEmptyValue
|
||||
columns[i].ColumnType = &columnInfos[i].ColumnType
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func ParseDocTableColumnInfo(selectedExcelContent [][]string, columnCount int) ([]entity.ColumnInfo, error) {
|
||||
columnList := transposeExcelContent(selectedExcelContent, columnCount)
|
||||
columInfoList := make([]entity.ColumnInfo, 0)
|
||||
for _, col := range columnList {
|
||||
columnType, containsEmptyValue := predictColumnType(col)
|
||||
columInfoList = append(columInfoList, entity.ColumnInfo{
|
||||
ColumnType: columnType,
|
||||
ContainsEmptyValue: containsEmptyValue,
|
||||
})
|
||||
}
|
||||
|
||||
return columInfoList, nil
|
||||
}
|
||||
|
||||
func transposeExcelContent(excelContent [][]string, columnCount int) [][]string {
|
||||
if len(excelContent) == 0 {
|
||||
return make([][]string, 0)
|
||||
}
|
||||
|
||||
rowCount := len(excelContent)
|
||||
transposedExcelContent := make([][]string, columnCount)
|
||||
for i := range transposedExcelContent {
|
||||
transposedExcelContent[i] = make([]string, rowCount)
|
||||
}
|
||||
|
||||
for i := range excelContent {
|
||||
for j := range excelContent[i] {
|
||||
if j >= columnCount {
|
||||
continue
|
||||
}
|
||||
transposedExcelContent[j][i] = excelContent[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
return transposedExcelContent
|
||||
}
|
||||
|
||||
func predictColumnType(columnContent []string) (common.ColumnType, bool) {
|
||||
columnType := common.ColumnType_Text
|
||||
containsEmptyValue := false
|
||||
for i, col := range columnContent {
|
||||
if col == "" {
|
||||
containsEmptyValue = true
|
||||
continue
|
||||
}
|
||||
|
||||
cellType := GetCellType(col)
|
||||
if i == 0 {
|
||||
columnType = cellType
|
||||
continue
|
||||
}
|
||||
|
||||
if GetColumnTypeCategory(columnType) != GetColumnTypeCategory(cellType) {
|
||||
return common.ColumnType_Text, containsEmptyValue
|
||||
}
|
||||
|
||||
if GetColumnTypePriority(cellType) < GetColumnTypePriority(columnType) {
|
||||
columnType = cellType
|
||||
}
|
||||
}
|
||||
return columnType, containsEmptyValue
|
||||
}
|
||||
|
||||
func GetColumnTypePriority(columnType common.ColumnType) int64 {
|
||||
for _, identifier := range identifierChain {
|
||||
if identifier.TargetColumnType == columnType {
|
||||
return identifier.Priority
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func GetColumnTypeCategory(columnType common.ColumnType) database.ColumnTypeCategory {
|
||||
if columnType == common.ColumnType_Number || columnType == common.ColumnType_Float {
|
||||
return database.ColumnTypeCategoryNumber
|
||||
}
|
||||
return database.ColumnTypeCategoryText
|
||||
}
|
||||
|
||||
func GetCellType(cellValue string) common.ColumnType {
|
||||
InitIdentifier()
|
||||
cellTypeResult := common.ColumnType_Text
|
||||
for _, identifier := range identifierChain {
|
||||
cellType := identifier.IdentifyCellType(cellValue)
|
||||
if cellType != nil {
|
||||
cellTypeResult = *cellType
|
||||
break
|
||||
}
|
||||
}
|
||||
return cellTypeResult
|
||||
}
|
||||
|
||||
func IdentifyNumber(cellValue string) *common.ColumnType {
|
||||
_, err := strconv.ParseInt(cellValue, 10, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return common.ColumnTypePtr(common.ColumnType_Number)
|
||||
}
|
||||
|
||||
func IdentifyFloat(cellValue string) *common.ColumnType {
|
||||
_, err := strconv.ParseFloat(cellValue, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return common.ColumnTypePtr(common.ColumnType_Float)
|
||||
}
|
||||
|
||||
func IdentifyBoolean(cellValue string) *common.ColumnType {
|
||||
lowerCellValue := strings.ToLower(cellValue)
|
||||
if lowerCellValue != "true" && lowerCellValue != "false" {
|
||||
return nil
|
||||
}
|
||||
return common.ColumnTypePtr(common.ColumnType_Boolean)
|
||||
}
|
||||
|
||||
func IdentifyDate(cellValue string) *common.ColumnType {
|
||||
matched, err := regexp.MatchString(dateTimePattern, cellValue)
|
||||
if err != nil || !matched {
|
||||
return nil
|
||||
}
|
||||
return common.ColumnTypePtr(common.ColumnType_Date)
|
||||
}
|
||||
|
||||
func IdentifyText(cellValue string) *common.ColumnType {
|
||||
return common.ColumnTypePtr(common.ColumnType_Text)
|
||||
}
|
||||
|
||||
func InitIdentifier() {
|
||||
initIdentifierOnce.Do(func() {
|
||||
identifierChain = []CellTypeIdentifier{
|
||||
{
|
||||
Priority: 5,
|
||||
IdentifyCellType: IdentifyNumber,
|
||||
TargetColumnType: common.ColumnType_Number,
|
||||
},
|
||||
{
|
||||
Priority: 4,
|
||||
IdentifyCellType: IdentifyFloat,
|
||||
TargetColumnType: common.ColumnType_Float,
|
||||
},
|
||||
{
|
||||
Priority: 3,
|
||||
IdentifyCellType: IdentifyBoolean,
|
||||
TargetColumnType: common.ColumnType_Boolean,
|
||||
},
|
||||
{
|
||||
Priority: 2,
|
||||
IdentifyCellType: IdentifyDate,
|
||||
TargetColumnType: common.ColumnType_Date,
|
||||
},
|
||||
{
|
||||
Priority: 1,
|
||||
IdentifyCellType: IdentifyText,
|
||||
TargetColumnType: common.ColumnType_Text,
|
||||
},
|
||||
}
|
||||
|
||||
sort.SliceStable(identifierChain, func(i, j int) bool {
|
||||
return identifierChain[i].Priority > identifierChain[j].Priority
|
||||
})
|
||||
|
||||
datePatternList := []string{
|
||||
"(\\d{2,4})\\/(0?[1-9]|1[0-2])\\/(0?[1-9]|[1-2]\\d|3[0-1])",
|
||||
"(\\d{2,4})-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2]\\d|3[0-1])",
|
||||
"(\\d{2,4})\\.(0?[1-9]|1[0-2])\\.(0?[1-9]|[1-2]\\d|3[0-1])",
|
||||
"(\\d{2,4})年(0?[1-9]|1[0-2])月(0?[1-9]|[1-2]\\d|3[0-1])日",
|
||||
}
|
||||
|
||||
timePatternList := []string{
|
||||
"([0-1]?\\d|2[0-3]):[0-5]\\d(:[0-5]\\d)?",
|
||||
"([0-1]?\\d|2[0-3])时[0-5]\\d分([0-5]\\d秒)?",
|
||||
"(0?\\d|1[0-2]):[0-5]\\d(:[0-5]\\d)? (am|AM|pm|PM)",
|
||||
"(0?\\d|1[0-2])时[0-5]\\d分([0-5]\\d秒)? (am|AM|pm|PM)",
|
||||
}
|
||||
|
||||
dateTimePattern = getDateTimeRegExp(datePatternList, timePatternList)
|
||||
})
|
||||
}
|
||||
|
||||
func getDateTimeRegExp(datePatternList []string, timePatterList []string) string {
|
||||
var (
|
||||
datePattern string
|
||||
timePattern string
|
||||
)
|
||||
|
||||
for _, p := range datePatternList {
|
||||
p = fmt.Sprintf("(%s)", p)
|
||||
if datePattern == "" {
|
||||
datePattern = p
|
||||
continue
|
||||
}
|
||||
datePattern = fmt.Sprintf("%s|%s", datePattern, p)
|
||||
}
|
||||
|
||||
for _, p := range timePatterList {
|
||||
p = fmt.Sprintf("(%s)", p)
|
||||
if timePattern == "" {
|
||||
timePattern = p
|
||||
continue
|
||||
}
|
||||
timePattern = fmt.Sprintf("%s|%s", timePattern, p)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("^(%s)( +(%s))?$", datePattern, timePattern)
|
||||
}
|
||||
|
||||
func (t *TosTableParser) TransferPreviewData(ctx context.Context, columns []*common.DocTableColumn, sampleData [][]string, previewLine int) (previewData []map[int64]string, err error) {
|
||||
previewData = make([]map[int64]string, 0)
|
||||
|
||||
for idx, line := range sampleData {
|
||||
if idx >= previewLine {
|
||||
break
|
||||
}
|
||||
lineValue := make(map[int64]string)
|
||||
if len(columns) != len(line) {
|
||||
logs.CtxWarnf(ctx, "[TransferPreviewData] sampleData's length:%d is not match with column count:%d", len(line), len(columns))
|
||||
}
|
||||
|
||||
for i, value := range line {
|
||||
if i >= len(columns) {
|
||||
break
|
||||
}
|
||||
lineValue[int64(i)] = value
|
||||
}
|
||||
previewData = append(previewData, lineValue)
|
||||
}
|
||||
|
||||
return previewData, nil
|
||||
}
|
||||
|
||||
func CheckSheetIsValid(fields []*database.FieldItem, parsedColumns []*common.DocTableColumn, sheet *entity.ExcelExtraInfo) (bool, *string) {
|
||||
if len(fields) != len(parsedColumns) {
|
||||
return false, ptr.Of("field number not match")
|
||||
}
|
||||
if len(parsedColumns) > 20 {
|
||||
return false, ptr.Of("field number exceed 20")
|
||||
}
|
||||
if sheet.Sheets[0].TotalRow > 100000 {
|
||||
return false, ptr.Of("data rows exceed 100000")
|
||||
}
|
||||
|
||||
equalColumns := map[string]int{}
|
||||
for _, field := range fields {
|
||||
for _, column := range parsedColumns {
|
||||
if field.Name == column.ColumnName {
|
||||
equalColumns[field.Name] = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(equalColumns) != len(fields) {
|
||||
return false, ptr.Of("field name not match")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
70
backend/domain/memory/database/repository/repository.go
Normal file
70
backend/domain/memory/database/repository/repository.go
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"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/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal/query"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/idgen"
|
||||
)
|
||||
|
||||
func NewAgentToDatabaseDAO(db *gorm.DB, idGen idgen.IDGenerator) AgentToDatabaseDAO {
|
||||
return dal.NewAgentToDatabaseDAO(db, idGen)
|
||||
}
|
||||
|
||||
type AgentToDatabaseDAO interface {
|
||||
BatchCreate(ctx context.Context, relations []*database.AgentToDatabase) ([]int64, error)
|
||||
BatchDelete(ctx context.Context, basicRelations []*database.AgentToDatabaseBasic) error
|
||||
ListByAgentID(ctx context.Context, agentID int64, tableType table.TableType) ([]*database.AgentToDatabase, error)
|
||||
}
|
||||
|
||||
func NewDraftDatabaseDAO(db *gorm.DB, idGen idgen.IDGenerator) DraftDAO {
|
||||
return dal.NewDraftDatabaseDAO(db, idGen)
|
||||
}
|
||||
|
||||
type DraftDAO interface {
|
||||
Get(ctx context.Context, id int64) (*entity.Database, error)
|
||||
List(ctx context.Context, filter *entity.DatabaseFilter, page *entity.Pagination, orderBy []*database.OrderBy) ([]*entity.Database, int64, error)
|
||||
MGet(ctx context.Context, ids []int64) ([]*entity.Database, error)
|
||||
|
||||
CreateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database, draftID, onlineID int64, physicalTableName string) (*entity.Database, error)
|
||||
UpdateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database) (*entity.Database, error)
|
||||
DeleteWithTX(ctx context.Context, tx *query.QueryTx, id int64) error
|
||||
BatchDeleteWithTX(ctx context.Context, tx *query.QueryTx, ids []int64) error
|
||||
}
|
||||
|
||||
func NewOnlineDatabaseDAO(db *gorm.DB, idGen idgen.IDGenerator) OnlineDAO {
|
||||
return dal.NewOnlineDatabaseDAO(db, idGen)
|
||||
}
|
||||
|
||||
type OnlineDAO interface {
|
||||
Get(ctx context.Context, id int64) (*entity.Database, error)
|
||||
MGet(ctx context.Context, ids []int64) ([]*entity.Database, error)
|
||||
List(ctx context.Context, filter *entity.DatabaseFilter, page *entity.Pagination, orderBy []*database.OrderBy) ([]*entity.Database, int64, error)
|
||||
|
||||
UpdateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database) (*entity.Database, error)
|
||||
CreateWithTX(ctx context.Context, tx *query.QueryTx, database *entity.Database, draftID, onlineID int64, physicalTableName string) (*entity.Database, error)
|
||||
DeleteWithTX(ctx context.Context, tx *query.QueryTx, id int64) error
|
||||
BatchDeleteWithTX(ctx context.Context, tx *query.QueryTx, ids []int64) error
|
||||
}
|
||||
258
backend/domain/memory/database/service/database.go
Normal file
258
backend/domain/memory/database/service/database.go
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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 service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/common"
|
||||
"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/entity"
|
||||
)
|
||||
|
||||
//go:generate mockgen -destination ../../../../internal/mock/domain/memory/database/database_mock.go --package database -source database.go
|
||||
type Database interface {
|
||||
CreateDatabase(ctx context.Context, req *CreateDatabaseRequest) (*CreateDatabaseResponse, error)
|
||||
UpdateDatabase(ctx context.Context, req *UpdateDatabaseRequest) (*UpdateDatabaseResponse, error)
|
||||
DeleteDatabase(ctx context.Context, req *DeleteDatabaseRequest) error
|
||||
MGetDatabase(ctx context.Context, req *MGetDatabaseRequest) (*MGetDatabaseResponse, error)
|
||||
ListDatabase(ctx context.Context, req *ListDatabaseRequest) (*ListDatabaseResponse, error)
|
||||
GetDraftDatabaseByOnlineID(ctx context.Context, req *GetDraftDatabaseByOnlineIDRequest) (*GetDraftDatabaseByOnlineIDResponse, error)
|
||||
DeleteDatabaseByAppID(ctx context.Context, req *DeleteDatabaseByAppIDRequest) (*DeleteDatabaseByAppIDResponse, error)
|
||||
GetAllDatabaseByAppID(ctx context.Context, req *GetAllDatabaseByAppIDRequest) (*GetAllDatabaseByAppIDResponse, error)
|
||||
|
||||
GetDatabaseTemplate(ctx context.Context, req *GetDatabaseTemplateRequest) (*GetDatabaseTemplateResponse, error)
|
||||
GetDatabaseTableSchema(ctx context.Context, req *GetDatabaseTableSchemaRequest) (*GetDatabaseTableSchemaResponse, error)
|
||||
ValidateDatabaseTableSchema(ctx context.Context, req *ValidateDatabaseTableSchemaRequest) (*ValidateDatabaseTableSchemaResponse, error)
|
||||
SubmitDatabaseInsertTask(ctx context.Context, req *SubmitDatabaseInsertTaskRequest) error
|
||||
GetDatabaseFileProgressData(ctx context.Context, req *GetDatabaseFileProgressDataRequest) (*GetDatabaseFileProgressDataResponse, error)
|
||||
|
||||
AddDatabaseRecord(ctx context.Context, req *AddDatabaseRecordRequest) error
|
||||
UpdateDatabaseRecord(ctx context.Context, req *UpdateDatabaseRecordRequest) error
|
||||
DeleteDatabaseRecord(ctx context.Context, req *DeleteDatabaseRecordRequest) error
|
||||
ListDatabaseRecord(ctx context.Context, req *ListDatabaseRecordRequest) (*ListDatabaseRecordResponse, error)
|
||||
|
||||
ExecuteSQL(ctx context.Context, req *ExecuteSQLRequest) (*ExecuteSQLResponse, error)
|
||||
|
||||
BindDatabase(ctx context.Context, req *BindDatabaseToAgentRequest) error
|
||||
UnBindDatabase(ctx context.Context, req *UnBindDatabaseToAgentRequest) error
|
||||
MGetDatabaseByAgentID(ctx context.Context, req *MGetDatabaseByAgentIDRequest) (*MGetDatabaseByAgentIDResponse, error)
|
||||
MGetRelationsByAgentID(ctx context.Context, req *MGetRelationsByAgentIDRequest) (*MGetRelationsByAgentIDResponse, error)
|
||||
|
||||
PublishDatabase(ctx context.Context, req *PublishDatabaseRequest) (*PublishDatabaseResponse, error)
|
||||
}
|
||||
|
||||
type CreateDatabaseRequest struct {
|
||||
Database *entity.Database
|
||||
}
|
||||
|
||||
type CreateDatabaseResponse struct {
|
||||
Database *entity.Database
|
||||
}
|
||||
|
||||
type UpdateDatabaseRequest struct {
|
||||
Database *entity.Database
|
||||
}
|
||||
|
||||
type UpdateDatabaseResponse struct {
|
||||
Database *entity.Database
|
||||
}
|
||||
|
||||
type MGetDatabaseRequest = database.MGetDatabaseRequest
|
||||
type MGetDatabaseResponse = database.MGetDatabaseResponse
|
||||
type GetDatabaseTemplateRequest struct {
|
||||
UserID int64
|
||||
TableName string
|
||||
FieldItems []*table.FieldItem
|
||||
}
|
||||
|
||||
type GetDatabaseTemplateResponse struct {
|
||||
Url string
|
||||
}
|
||||
type ListDatabaseRequest struct {
|
||||
CreatorID *int64
|
||||
SpaceID *int64
|
||||
ConnectorID *int64
|
||||
TableName *string
|
||||
AppID int64
|
||||
TableType table.TableType
|
||||
OrderBy []*database.OrderBy
|
||||
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
||||
type ListDatabaseResponse struct {
|
||||
Databases []*entity.Database
|
||||
|
||||
HasMore bool
|
||||
TotalCount int64
|
||||
}
|
||||
|
||||
type AddDatabaseRecordRequest struct {
|
||||
DatabaseID int64
|
||||
TableType table.TableType
|
||||
ConnectorID *int64
|
||||
UserID int64
|
||||
Records []map[string]string
|
||||
}
|
||||
|
||||
type UpdateDatabaseRecordRequest struct {
|
||||
DatabaseID int64
|
||||
TableType table.TableType
|
||||
ConnectorID *int64
|
||||
UserID int64
|
||||
Records []map[string]string
|
||||
}
|
||||
|
||||
type DeleteDatabaseRecordRequest struct {
|
||||
DatabaseID int64
|
||||
TableType table.TableType
|
||||
ConnectorID *int64
|
||||
UserID int64
|
||||
Records []map[string]string
|
||||
}
|
||||
|
||||
type ListDatabaseRecordRequest struct {
|
||||
DatabaseID int64
|
||||
ConnectorID *int64
|
||||
TableType table.TableType
|
||||
UserID int64
|
||||
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
||||
type ListDatabaseRecordResponse struct {
|
||||
Records []map[string]string
|
||||
FieldList []*database.FieldItem
|
||||
|
||||
HasMore bool
|
||||
TotalCount int64
|
||||
}
|
||||
|
||||
type ExecuteSQLRequest = database.ExecuteSQLRequest
|
||||
|
||||
type ExecuteSQLResponse = database.ExecuteSQLResponse
|
||||
|
||||
type BindDatabaseToAgentRequest = database.BindDatabaseToAgentRequest
|
||||
|
||||
type UnBindDatabaseToAgentRequest = database.UnBindDatabaseToAgentRequest
|
||||
|
||||
type MGetDatabaseByAgentIDRequest struct {
|
||||
AgentID int64
|
||||
TableType table.TableType
|
||||
NeedSysFields bool
|
||||
}
|
||||
|
||||
type MGetDatabaseByAgentIDResponse struct {
|
||||
Databases []*entity.Database
|
||||
}
|
||||
|
||||
type PublishDatabaseRequest = database.PublishDatabaseRequest
|
||||
|
||||
type PublishDatabaseResponse = database.PublishDatabaseResponse
|
||||
|
||||
type DeleteDatabaseRequest = database.DeleteDatabaseRequest
|
||||
|
||||
type MGetRelationsByAgentIDRequest struct {
|
||||
AgentID int64
|
||||
TableType table.TableType
|
||||
NeedSysFields bool
|
||||
}
|
||||
|
||||
type MGetRelationsByAgentIDResponse struct {
|
||||
Relations []*database.AgentToDatabase
|
||||
}
|
||||
type GetDatabaseTableSchemaRequest struct {
|
||||
TableSheet entity.TableSheet
|
||||
TableDataType table.TableDataType
|
||||
DatabaseID int64
|
||||
TosURL string
|
||||
UserID int64
|
||||
}
|
||||
|
||||
type GetDatabaseTableSchemaResponse struct {
|
||||
SheetList []*common.DocTableSheet
|
||||
TableMeta []*common.DocTableColumn
|
||||
PreviewData []map[int64]string
|
||||
}
|
||||
|
||||
type ValidateDatabaseTableSchemaRequest struct {
|
||||
TableSheet entity.TableSheet
|
||||
TableDataType table.TableDataType
|
||||
DatabaseID int64
|
||||
TosURL string
|
||||
UserID int64
|
||||
Fields []*database.FieldItem
|
||||
}
|
||||
|
||||
type ValidateDatabaseTableSchemaResponse struct {
|
||||
Valid bool
|
||||
InvalidMsg *string // if valid is false, it will be set
|
||||
}
|
||||
|
||||
func (r *ValidateDatabaseTableSchemaResponse) GetInvalidMsg() string {
|
||||
if r.Valid || r.InvalidMsg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return *r.InvalidMsg
|
||||
}
|
||||
|
||||
type SubmitDatabaseInsertTaskRequest struct {
|
||||
DatabaseID int64
|
||||
FileURI string
|
||||
TableType table.TableType
|
||||
TableSheet entity.TableSheet
|
||||
ConnectorID *int64
|
||||
UserID int64
|
||||
}
|
||||
|
||||
type GetDatabaseFileProgressDataRequest struct {
|
||||
DatabaseID int64
|
||||
TableType table.TableType
|
||||
UserID int64
|
||||
}
|
||||
|
||||
type GetDatabaseFileProgressDataResponse struct {
|
||||
FileName string
|
||||
Progress int32
|
||||
StatusDescript *string
|
||||
}
|
||||
|
||||
type GetDraftDatabaseByOnlineIDRequest struct {
|
||||
OnlineID int64
|
||||
}
|
||||
|
||||
type GetDraftDatabaseByOnlineIDResponse struct {
|
||||
Database *entity.Database
|
||||
}
|
||||
|
||||
type DeleteDatabaseByAppIDRequest struct {
|
||||
AppID int64
|
||||
}
|
||||
|
||||
type DeleteDatabaseByAppIDResponse struct {
|
||||
DeletedDatabaseIDs []int64 //online database ids
|
||||
}
|
||||
|
||||
type GetAllDatabaseByAppIDRequest = database.GetAllDatabaseByAppIDRequest
|
||||
|
||||
type GetAllDatabaseByAppIDResponse = database.GetAllDatabaseByAppIDResponse
|
||||
2195
backend/domain/memory/database/service/database_impl.go
Normal file
2195
backend/domain/memory/database/service/database_impl.go
Normal file
File diff suppressed because it is too large
Load Diff
874
backend/domain/memory/database/service/database_impl_test.go
Normal file
874
backend/domain/memory/database/service/database_impl_test.go
Normal file
@@ -0,0 +1,874 @@
|
||||
/*
|
||||
* 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 service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/database"
|
||||
"github.com/coze-dev/coze-studio/backend/api/model/table"
|
||||
entity2 "github.com/coze-dev/coze-studio/backend/domain/memory/database/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/memory/database/repository"
|
||||
"github.com/coze-dev/coze-studio/backend/infra/contract/rdb"
|
||||
rdb2 "github.com/coze-dev/coze-studio/backend/infra/impl/rdb"
|
||||
mock "github.com/coze-dev/coze-studio/backend/internal/mock/infra/contract/idgen"
|
||||
storageMock "github.com/coze-dev/coze-studio/backend/internal/mock/infra/contract/storage"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/slices"
|
||||
)
|
||||
|
||||
func setupTestEnv(t *testing.T) (*gorm.DB, rdb.RDB, *mock.MockIDGenerator, repository.DraftDAO, repository.OnlineDAO, Database) {
|
||||
dsn := "root:root@tcp(127.0.0.1:3306)/opencoze?charset=utf8mb4&parseTime=True&loc=Local"
|
||||
if os.Getenv("CI_JOB_NAME") != "" {
|
||||
dsn = strings.ReplaceAll(dsn, "127.0.0.1", "mysql")
|
||||
}
|
||||
gormDB, err := gorm.Open(mysql.Open(dsn))
|
||||
assert.NoError(t, err)
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
idGen := mock.NewMockIDGenerator(ctrl)
|
||||
|
||||
baseID := time.Now().UnixNano()
|
||||
idGen.EXPECT().GenID(gomock.Any()).DoAndReturn(func(ctx context.Context) (int64, error) {
|
||||
id := baseID
|
||||
baseID++
|
||||
return id, nil
|
||||
}).AnyTimes()
|
||||
|
||||
idGen.EXPECT().GenMultiIDs(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, count int) ([]int64, error) {
|
||||
ids := make([]int64, count)
|
||||
for i := 0; i < count; i++ {
|
||||
ids[i] = baseID
|
||||
baseID++
|
||||
}
|
||||
return ids, nil
|
||||
}).AnyTimes()
|
||||
|
||||
rdbService := rdb2.NewService(gormDB, idGen)
|
||||
draftDAO := dal.NewDraftDatabaseDAO(gormDB, idGen)
|
||||
onlineDAO := dal.NewOnlineDatabaseDAO(gormDB, idGen)
|
||||
|
||||
mockStorage := storageMock.NewMockStorage(ctrl)
|
||||
mockStorage.EXPECT().GetObjectUrl(gomock.Any(), gomock.Any()).Return("URL_ADDRESS", nil).AnyTimes()
|
||||
mockStorage.EXPECT().GetObject(gomock.Any(), gomock.Any()).Return([]byte("test text"), nil).AnyTimes()
|
||||
mockStorage.EXPECT().PutObject(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
||||
|
||||
dbService := NewService(rdbService, gormDB, idGen, mockStorage, nil)
|
||||
|
||||
return gormDB, rdbService, idGen, draftDAO, onlineDAO, dbService
|
||||
}
|
||||
|
||||
func cleanupTestEnv(t *testing.T, db *gorm.DB, additionalTables ...string) {
|
||||
sqlDB, err := db.DB()
|
||||
assert.NoError(t, err)
|
||||
|
||||
daosToClean := []string{"online_database_info", "draft_database_info"}
|
||||
for _, tableName := range daosToClean {
|
||||
_, err := sqlDB.Exec(fmt.Sprintf("DELETE FROM `%s` WHERE 1=1", tableName))
|
||||
if err != nil {
|
||||
t.Logf("Failed to clean table %s: %v", tableName, err)
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := sqlDB.Query("SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name LIKE 'table_%'")
|
||||
assert.NoError(t, err, "Failed to query tables")
|
||||
defer rows.Close()
|
||||
|
||||
var tablesToDrop []string
|
||||
for rows.Next() {
|
||||
var tableName string
|
||||
if err := rows.Scan(&tableName); err != nil {
|
||||
t.Logf("Error scanning table name: %v", err)
|
||||
continue
|
||||
}
|
||||
tablesToDrop = append(tablesToDrop, tableName)
|
||||
}
|
||||
|
||||
tablesToDrop = append(tablesToDrop, additionalTables...)
|
||||
|
||||
for _, tableName := range tablesToDrop {
|
||||
_, err := sqlDB.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", tableName))
|
||||
if err != nil {
|
||||
t.Logf("Failed to drop table %s: %v", tableName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDatabase(t *testing.T) {
|
||||
gormDB, _, _, _, onlineDAO, dbService := setupTestEnv(t)
|
||||
defer cleanupTestEnv(t, gormDB)
|
||||
|
||||
req := &CreateDatabaseRequest{
|
||||
Database: &database.Database{
|
||||
SpaceID: 1,
|
||||
CreatorID: 1001,
|
||||
|
||||
TableName: "test_db_create",
|
||||
FieldList: []*database.FieldItem{
|
||||
{
|
||||
Name: "id_custom",
|
||||
Type: table.FieldItemType_Number,
|
||||
MustRequired: true,
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Type: table.FieldItemType_Text,
|
||||
MustRequired: true,
|
||||
},
|
||||
{
|
||||
Name: "score",
|
||||
Type: table.FieldItemType_Float,
|
||||
},
|
||||
{
|
||||
Name: "date",
|
||||
Type: table.FieldItemType_Date,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := dbService.CreateDatabase(context.Background(), req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.NotNil(t, resp.Database)
|
||||
|
||||
assert.Equal(t, req.Database.TableName, resp.Database.TableName)
|
||||
assert.Equal(t, req.Database.TableType, resp.Database.TableType)
|
||||
assert.NotEmpty(t, resp.Database.ActualTableName)
|
||||
assert.Len(t, resp.Database.FieldList, 4)
|
||||
|
||||
savedDB, err := onlineDAO.Get(context.Background(), resp.Database.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, resp.Database.ID, savedDB.ID)
|
||||
assert.Equal(t, resp.Database.TableName, savedDB.TableName)
|
||||
}
|
||||
|
||||
func TestUpdateDatabase(t *testing.T) {
|
||||
gormDB, _, _, _, onlineDAO, dbService := setupTestEnv(t)
|
||||
defer cleanupTestEnv(t, gormDB)
|
||||
|
||||
resp, err := createDatabase(dbService)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
|
||||
databaseInfo := &entity2.Database{
|
||||
ID: resp.Database.ID,
|
||||
|
||||
SpaceID: 1,
|
||||
CreatorID: 1001,
|
||||
TableName: "test_db_update",
|
||||
TableType: ptr.Of(table.TableType_OnlineTable),
|
||||
FieldList: []*database.FieldItem{
|
||||
{
|
||||
Name: "age",
|
||||
Type: table.FieldItemType_Float,
|
||||
MustRequired: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
updateReq := &UpdateDatabaseRequest{
|
||||
Database: databaseInfo,
|
||||
}
|
||||
|
||||
res, err := dbService.UpdateDatabase(context.Background(), updateReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, res)
|
||||
|
||||
updatedDB, err := onlineDAO.Get(context.Background(), databaseInfo.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, updatedDB.FieldList, 1)
|
||||
}
|
||||
|
||||
func TestDeleteDatabase(t *testing.T) {
|
||||
gormDB, _, _, draftDAO, onlineDAO, dbService := setupTestEnv(t)
|
||||
defer cleanupTestEnv(t, gormDB)
|
||||
|
||||
resp, err := createDatabase(dbService)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
|
||||
deleteReq := &DeleteDatabaseRequest{
|
||||
ID: resp.Database.ID,
|
||||
}
|
||||
|
||||
err = dbService.DeleteDatabase(context.Background(), deleteReq)
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = draftDAO.Get(context.Background(), resp.Database.ID)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "not found")
|
||||
|
||||
_, err = onlineDAO.Get(context.Background(), resp.Database.ID)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "not found")
|
||||
}
|
||||
|
||||
func TestListDatabase(t *testing.T) {
|
||||
gormDB, _, _, _, _, dbService := setupTestEnv(t)
|
||||
defer cleanupTestEnv(t, gormDB)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
resp, err := createDatabase(dbService)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
}
|
||||
|
||||
spaceID := int64(1)
|
||||
tableType := table.TableType_OnlineTable
|
||||
listReq := &ListDatabaseRequest{
|
||||
SpaceID: &spaceID,
|
||||
TableType: tableType,
|
||||
Limit: 2,
|
||||
}
|
||||
resp, err := dbService.ListDatabase(context.Background(), listReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.GreaterOrEqual(t, len(resp.Databases), 2)
|
||||
|
||||
listReq = &ListDatabaseRequest{
|
||||
SpaceID: &spaceID,
|
||||
TableType: tableType,
|
||||
Limit: 2,
|
||||
Offset: 2,
|
||||
}
|
||||
resp, err = dbService.ListDatabase(context.Background(), listReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.GreaterOrEqual(t, len(resp.Databases), 1)
|
||||
}
|
||||
|
||||
func createDatabase(dbService Database) (*CreateDatabaseResponse, error) {
|
||||
req := &CreateDatabaseRequest{
|
||||
Database: &entity2.Database{
|
||||
SpaceID: 1,
|
||||
CreatorID: 1001,
|
||||
|
||||
TableName: "test_db_table_01",
|
||||
FieldList: []*database.FieldItem{
|
||||
{
|
||||
Name: "id_custom",
|
||||
Type: table.FieldItemType_Number,
|
||||
MustRequired: true,
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Type: table.FieldItemType_Text,
|
||||
MustRequired: true,
|
||||
},
|
||||
{
|
||||
Name: "score",
|
||||
Type: table.FieldItemType_Float,
|
||||
},
|
||||
{
|
||||
Name: "date",
|
||||
Type: table.FieldItemType_Date,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return dbService.CreateDatabase(context.Background(), req)
|
||||
}
|
||||
|
||||
func TestCRUDDatabaseRecord(t *testing.T) {
|
||||
gormDB, _, _, _, _, dbService := setupTestEnv(t)
|
||||
defer cleanupTestEnv(t, gormDB)
|
||||
|
||||
resp, err := createDatabase(dbService)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
|
||||
addRecordReq := &AddDatabaseRecordRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
UserID: 1001,
|
||||
Records: []map[string]string{
|
||||
{
|
||||
"id_custom": "1",
|
||||
"name": "John Doe",
|
||||
"score": "80.5",
|
||||
"date": "2025-01-01 00:00:00",
|
||||
},
|
||||
{
|
||||
"id_custom": "2",
|
||||
"name": "Jane Smith",
|
||||
"score": "90.5",
|
||||
"date": "2025-01-01 01:00:00",
|
||||
"bstudio_id": "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = dbService.AddDatabaseRecord(context.Background(), addRecordReq)
|
||||
assert.NoError(t, err)
|
||||
|
||||
listRecordReq := &ListDatabaseRecordRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
UserID: 1001,
|
||||
Limit: 50,
|
||||
}
|
||||
|
||||
listResp, err := dbService.ListDatabaseRecord(context.Background(), listRecordReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, listResp)
|
||||
assert.True(t, len(listResp.Records) == 2)
|
||||
|
||||
foundJohn := false
|
||||
foundSmith := false
|
||||
bsID := ""
|
||||
for _, record := range listResp.Records {
|
||||
if record["name"] == "John Doe" && record["score"] == "80.5" {
|
||||
foundJohn = true
|
||||
bsID = record[database.DefaultIDColName]
|
||||
}
|
||||
if record["name"] == "Jane Smith" && record["score"] == "90.5" {
|
||||
foundSmith = true
|
||||
}
|
||||
}
|
||||
assert.True(t, foundJohn, "John Doe record not found")
|
||||
assert.True(t, foundSmith, "Jane Smith record not found")
|
||||
|
||||
updateRecordReq := &UpdateDatabaseRecordRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
UserID: 1001,
|
||||
Records: []map[string]string{
|
||||
{
|
||||
database.DefaultIDColName: bsID,
|
||||
"name": "John Updated",
|
||||
"score": "90",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = dbService.UpdateDatabaseRecord(context.Background(), updateRecordReq)
|
||||
assert.NoError(t, err)
|
||||
|
||||
listReq := &ListDatabaseRecordRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
Limit: 50,
|
||||
UserID: 1001,
|
||||
}
|
||||
listRespAfterUpdate, err := dbService.ListDatabaseRecord(context.Background(), listReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, listRespAfterUpdate)
|
||||
assert.True(t, len(listRespAfterUpdate.Records) > 0)
|
||||
|
||||
foundJohnUpdate := false
|
||||
for _, record := range listRespAfterUpdate.Records {
|
||||
if record[database.DefaultIDColName] == bsID {
|
||||
foundJohnUpdate = true
|
||||
assert.Equal(t, "90", record["score"])
|
||||
}
|
||||
}
|
||||
assert.True(t, foundJohnUpdate, "John Doe update record not found")
|
||||
|
||||
deleteRecordReq := &DeleteDatabaseRecordRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
UserID: 1001,
|
||||
Records: []map[string]string{
|
||||
{
|
||||
database.DefaultIDColName: bsID,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = dbService.DeleteDatabaseRecord(context.Background(), deleteRecordReq)
|
||||
assert.NoError(t, err)
|
||||
|
||||
listRecordAfterDeleteReq := &ListDatabaseRecordRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
Limit: 50,
|
||||
UserID: 1001,
|
||||
}
|
||||
listRespAfterDelete, err := dbService.ListDatabaseRecord(context.Background(), listRecordAfterDeleteReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, listRespAfterDelete)
|
||||
assert.Equal(t, len(listRespAfterDelete.Records), 1)
|
||||
}
|
||||
|
||||
func TestExecuteSQLWithOperations(t *testing.T) {
|
||||
gormDB, _, _, _, _, dbService := setupTestEnv(t)
|
||||
defer cleanupTestEnv(t, gormDB)
|
||||
|
||||
resp, err := createDatabase(dbService)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
|
||||
fieldMap := slices.ToMap(resp.Database.FieldList, func(e *database.FieldItem) (string, *database.FieldItem) {
|
||||
return e.Name, e
|
||||
})
|
||||
|
||||
upsertRows := []*database.UpsertRow{
|
||||
{
|
||||
Records: []*database.Record{
|
||||
{
|
||||
FieldId: strconv.FormatInt(fieldMap["id_custom"].AlterID, 10),
|
||||
FieldValue: "?",
|
||||
},
|
||||
{
|
||||
FieldId: strconv.FormatInt(fieldMap["name"].AlterID, 10),
|
||||
FieldValue: "?",
|
||||
},
|
||||
{
|
||||
FieldId: strconv.FormatInt(fieldMap["score"].AlterID, 10),
|
||||
FieldValue: "?",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Records: []*database.Record{
|
||||
{
|
||||
FieldId: strconv.FormatInt(fieldMap["id_custom"].AlterID, 10),
|
||||
FieldValue: "?",
|
||||
},
|
||||
{
|
||||
FieldId: strconv.FormatInt(fieldMap["name"].AlterID, 10),
|
||||
FieldValue: "?",
|
||||
},
|
||||
{
|
||||
FieldId: strconv.FormatInt(fieldMap["score"].AlterID, 10),
|
||||
FieldValue: "?",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
executeInsertReq := &ExecuteSQLRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
OperateType: database.OperateType_Insert,
|
||||
UpsertRows: upsertRows,
|
||||
SQLParams: []*database.SQLParamVal{
|
||||
{
|
||||
Value: ptr.Of("111"),
|
||||
},
|
||||
{
|
||||
Value: ptr.Of("Alice"),
|
||||
},
|
||||
{
|
||||
Value: ptr.Of("85.5"),
|
||||
},
|
||||
{
|
||||
Value: ptr.Of("112"),
|
||||
},
|
||||
{
|
||||
Value: ptr.Of("Bob"),
|
||||
},
|
||||
{
|
||||
Value: ptr.Of("90.5"),
|
||||
},
|
||||
},
|
||||
UserID: "1001",
|
||||
SpaceID: 1,
|
||||
}
|
||||
|
||||
insertResp, err := dbService.ExecuteSQL(context.Background(), executeInsertReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, insertResp)
|
||||
assert.NotNil(t, insertResp.RowsAffected)
|
||||
assert.Equal(t, 2, len(insertResp.Records))
|
||||
assert.Equal(t, 1, len(insertResp.Records[0]))
|
||||
assert.Equal(t, int64(2), *insertResp.RowsAffected)
|
||||
|
||||
limit := int64(10)
|
||||
selectFields := &database.SelectFieldList{
|
||||
FieldID: []string{strconv.FormatInt(fieldMap["name"].AlterID, 10), strconv.FormatInt(fieldMap["score"].AlterID, 10)},
|
||||
}
|
||||
|
||||
executeSelectReq := &ExecuteSQLRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
OperateType: database.OperateType_Select,
|
||||
// SelectFieldList: selectFields,
|
||||
Limit: &limit,
|
||||
UserID: "1001",
|
||||
SpaceID: 1,
|
||||
OrderByList: []database.OrderBy{
|
||||
{
|
||||
Field: "id_custom",
|
||||
Direction: table.SortDirection_Desc,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
selectResp, err := dbService.ExecuteSQL(context.Background(), executeSelectReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, selectResp)
|
||||
assert.NotNil(t, selectResp.Records)
|
||||
assert.True(t, len(selectResp.Records) == 2)
|
||||
assert.Equal(t, string(selectResp.Records[0]["name"].([]uint8)), "Bob")
|
||||
assert.NotNil(t, selectResp.Records[0][database.DefaultUidDisplayColName])
|
||||
assert.NotNil(t, selectResp.Records[0][database.DefaultIDDisplayColName])
|
||||
assert.NotNil(t, selectResp.Records[0][database.DefaultCreateTimeDisplayColName])
|
||||
|
||||
executeNotNullSelectReq := &ExecuteSQLRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
OperateType: database.OperateType_Select,
|
||||
SelectFieldList: selectFields,
|
||||
Limit: &limit,
|
||||
UserID: "1001",
|
||||
SpaceID: 1,
|
||||
OrderByList: []database.OrderBy{
|
||||
{
|
||||
Field: "id_custom",
|
||||
Direction: table.SortDirection_Desc,
|
||||
},
|
||||
},
|
||||
Condition: &database.ComplexCondition{
|
||||
Conditions: []*database.Condition{
|
||||
{
|
||||
Left: "name",
|
||||
Operation: database.Operation_IS_NOT_NULL,
|
||||
},
|
||||
},
|
||||
Logic: database.Logic_And,
|
||||
},
|
||||
}
|
||||
|
||||
selectNotNullResp, err := dbService.ExecuteSQL(context.Background(), executeNotNullSelectReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, selectNotNullResp)
|
||||
assert.NotNil(t, selectNotNullResp.Records)
|
||||
assert.True(t, len(selectNotNullResp.Records) == 2)
|
||||
assert.Equal(t, string(selectNotNullResp.Records[0]["name"].([]uint8)), "Bob")
|
||||
|
||||
executeNullSelectReq := &ExecuteSQLRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
OperateType: database.OperateType_Select,
|
||||
SelectFieldList: selectFields,
|
||||
Limit: &limit,
|
||||
UserID: "1001",
|
||||
SpaceID: 1,
|
||||
OrderByList: []database.OrderBy{
|
||||
{
|
||||
Field: "id_custom",
|
||||
Direction: table.SortDirection_Desc,
|
||||
},
|
||||
},
|
||||
Condition: &database.ComplexCondition{
|
||||
Conditions: []*database.Condition{
|
||||
{
|
||||
Left: "name",
|
||||
Operation: database.Operation_IS_NULL,
|
||||
},
|
||||
},
|
||||
Logic: database.Logic_And,
|
||||
},
|
||||
}
|
||||
|
||||
selectNullResp, err := dbService.ExecuteSQL(context.Background(), executeNullSelectReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, selectNullResp)
|
||||
assert.True(t, len(selectNullResp.Records) == 0)
|
||||
|
||||
executeINSelectReq := &ExecuteSQLRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
OperateType: database.OperateType_Select,
|
||||
SelectFieldList: selectFields,
|
||||
Limit: &limit,
|
||||
UserID: "1001",
|
||||
SpaceID: 1,
|
||||
OrderByList: []database.OrderBy{
|
||||
{
|
||||
Field: "id_custom",
|
||||
Direction: table.SortDirection_Desc,
|
||||
},
|
||||
},
|
||||
SQLParams: []*database.SQLParamVal{
|
||||
{
|
||||
Value: ptr.Of("Alice"),
|
||||
},
|
||||
{
|
||||
Value: ptr.Of("Bob"),
|
||||
},
|
||||
},
|
||||
Condition: &database.ComplexCondition{
|
||||
Conditions: []*database.Condition{
|
||||
{
|
||||
Left: "name",
|
||||
Operation: database.Operation_IN,
|
||||
Right: "(?,?)",
|
||||
},
|
||||
},
|
||||
Logic: database.Logic_And,
|
||||
},
|
||||
}
|
||||
selectINResp, err := dbService.ExecuteSQL(context.Background(), executeINSelectReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, selectINResp)
|
||||
assert.True(t, len(selectINResp.Records) == 2)
|
||||
|
||||
updateRows := []*database.UpsertRow{
|
||||
{
|
||||
Records: []*database.Record{
|
||||
{
|
||||
FieldId: strconv.FormatInt(fieldMap["id_custom"].AlterID, 10),
|
||||
FieldValue: "?",
|
||||
},
|
||||
{
|
||||
FieldId: strconv.FormatInt(fieldMap["name"].AlterID, 10),
|
||||
FieldValue: "?",
|
||||
},
|
||||
{
|
||||
FieldId: strconv.FormatInt(fieldMap["score"].AlterID, 10),
|
||||
FieldValue: "?",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
executeUpdateReq := &ExecuteSQLRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
OperateType: database.OperateType_Update,
|
||||
UpsertRows: updateRows,
|
||||
Limit: &limit,
|
||||
SQLParams: []*database.SQLParamVal{
|
||||
{
|
||||
Value: ptr.Of("111"),
|
||||
},
|
||||
{
|
||||
Value: ptr.Of("Alice2"),
|
||||
},
|
||||
{
|
||||
Value: ptr.Of("99"),
|
||||
},
|
||||
{
|
||||
Value: ptr.Of("111"),
|
||||
},
|
||||
},
|
||||
UserID: "1001",
|
||||
SpaceID: 1,
|
||||
Condition: &database.ComplexCondition{
|
||||
Conditions: []*database.Condition{
|
||||
{
|
||||
Left: "id_custom",
|
||||
Operation: database.Operation_EQUAL,
|
||||
Right: "?",
|
||||
},
|
||||
},
|
||||
Logic: database.Logic_And,
|
||||
},
|
||||
}
|
||||
|
||||
updateResp, err := dbService.ExecuteSQL(context.Background(), executeUpdateReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, updateResp)
|
||||
assert.NotNil(t, updateResp.RowsAffected)
|
||||
|
||||
executeDeleteReq := &ExecuteSQLRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
OperateType: database.OperateType_Delete,
|
||||
Limit: &limit,
|
||||
UserID: "1001",
|
||||
SpaceID: 1,
|
||||
SQLParams: []*database.SQLParamVal{
|
||||
{
|
||||
Value: ptr.Of("111"),
|
||||
},
|
||||
},
|
||||
Condition: &database.ComplexCondition{
|
||||
Conditions: []*database.Condition{
|
||||
{
|
||||
Left: "id_custom",
|
||||
Operation: database.Operation_EQUAL,
|
||||
Right: "?",
|
||||
},
|
||||
},
|
||||
Logic: database.Logic_And,
|
||||
},
|
||||
}
|
||||
|
||||
dResp, err := dbService.ExecuteSQL(context.Background(), executeDeleteReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, dResp)
|
||||
assert.NotNil(t, dResp.RowsAffected)
|
||||
|
||||
selectCustom := &ExecuteSQLRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
OperateType: database.OperateType_Custom,
|
||||
Limit: &limit,
|
||||
UserID: "1001",
|
||||
SpaceID: 1,
|
||||
SQL: ptr.Of(fmt.Sprintf("SELECT * FROM %s WHERE score > ?", "test_db_table_01")),
|
||||
SQLParams: []*database.SQLParamVal{
|
||||
{
|
||||
Value: ptr.Of("85"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
selectCustomResp, err := dbService.ExecuteSQL(context.Background(), selectCustom)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, selectCustomResp)
|
||||
assert.NotNil(t, selectCustomResp.Records)
|
||||
assert.True(t, len(selectCustomResp.Records) > 0)
|
||||
|
||||
// Test custom SQL UPDATE
|
||||
updateCustom := &ExecuteSQLRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
OperateType: database.OperateType_Custom,
|
||||
UserID: "1001",
|
||||
SpaceID: 1,
|
||||
SQL: ptr.Of(fmt.Sprintf("UPDATE %s SET name = 'Bob Updated' WHERE id_custom = ?", "test_db_table_01")),
|
||||
SQLParams: []*database.SQLParamVal{
|
||||
{
|
||||
Value: ptr.Of("112"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
updateCustomResp, err := dbService.ExecuteSQL(context.Background(), updateCustom)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, updateCustomResp)
|
||||
assert.NotNil(t, updateCustomResp.RowsAffected)
|
||||
assert.Equal(t, *updateCustomResp.RowsAffected, int64(1))
|
||||
|
||||
// Test custom SQL DELETE
|
||||
deleteCustom := &ExecuteSQLRequest{
|
||||
DatabaseID: resp.Database.ID,
|
||||
TableType: table.TableType_OnlineTable,
|
||||
OperateType: database.OperateType_Custom,
|
||||
UserID: "1001",
|
||||
SpaceID: 1,
|
||||
SQL: ptr.Of(fmt.Sprintf("DELETE FROM %s WHERE id_custom = ?", "test_db_table_01")),
|
||||
SQLParams: []*database.SQLParamVal{
|
||||
{
|
||||
Value: ptr.Of("112"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
deleteCustomResp, err := dbService.ExecuteSQL(context.Background(), deleteCustom)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, deleteCustomResp)
|
||||
assert.NotNil(t, deleteCustomResp.RowsAffected)
|
||||
assert.Equal(t, *deleteCustomResp.RowsAffected, int64(1))
|
||||
}
|
||||
|
||||
func TestDeleteDatabaseByAppID(t *testing.T) {
|
||||
gormDB, _, _, draftDAO, onlineDAO, dbService := setupTestEnv(t)
|
||||
defer cleanupTestEnv(t, gormDB)
|
||||
|
||||
appID := int64(123456)
|
||||
dbCount := 3
|
||||
var onlineIDs []int64
|
||||
var draftIDs []int64
|
||||
var physicalTables []string
|
||||
|
||||
for i := 0; i < dbCount; i++ {
|
||||
dbName := fmt.Sprintf("test_appid_db_%d_%d", appID, i)
|
||||
req := &CreateDatabaseRequest{
|
||||
Database: &entity2.Database{
|
||||
AppID: appID,
|
||||
SpaceID: 1,
|
||||
CreatorID: 1001,
|
||||
TableName: dbName,
|
||||
FieldList: []*database.FieldItem{
|
||||
{
|
||||
Name: "id_custom",
|
||||
Type: table.FieldItemType_Number,
|
||||
MustRequired: true,
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Type: table.FieldItemType_Text,
|
||||
MustRequired: true,
|
||||
},
|
||||
{
|
||||
Name: "score",
|
||||
Type: table.FieldItemType_Float,
|
||||
},
|
||||
{
|
||||
Name: "date",
|
||||
Type: table.FieldItemType_Date,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
resp, err := dbService.CreateDatabase(context.Background(), req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
assert.NotNil(t, resp.Database)
|
||||
assert.Equal(t, appID, resp.Database.AppID)
|
||||
assert.NotEmpty(t, resp.Database.ActualTableName)
|
||||
physicalTables = append(physicalTables, resp.Database.ActualTableName)
|
||||
if resp.Database.ID != 0 {
|
||||
onlineIDs = append(onlineIDs, resp.Database.ID)
|
||||
}
|
||||
if resp.Database.DraftID != nil {
|
||||
draftIDs = append(draftIDs, *resp.Database.DraftID)
|
||||
}
|
||||
}
|
||||
|
||||
for _, id := range onlineIDs {
|
||||
_, err := onlineDAO.Get(context.Background(), id)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
for _, id := range draftIDs {
|
||||
_, err := draftDAO.Get(context.Background(), id)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
_, err := dbService.DeleteDatabaseByAppID(context.Background(), &DeleteDatabaseByAppIDRequest{AppID: appID})
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, id := range onlineIDs {
|
||||
_, err := onlineDAO.Get(context.Background(), id)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "not found")
|
||||
}
|
||||
for _, id := range draftIDs {
|
||||
_, err := draftDAO.Get(context.Background(), id)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "not found")
|
||||
}
|
||||
|
||||
sqlDB, err := gormDB.DB()
|
||||
assert.NoError(t, err)
|
||||
for _, tableName := range physicalTables {
|
||||
var cnt int
|
||||
err := sqlDB.QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = '%s'", tableName)).Scan(&cnt)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, cnt, "physical table %s should be deleted", tableName)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user