feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View 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
}

View File

@@ -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
}

View 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
}
}

View 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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View 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
}

View File

@@ -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
}

View File

@@ -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,
}
}

View 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
}

View 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
}

View 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

File diff suppressed because it is too large Load Diff

View 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)
}
}