mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-02-05 17:16:08 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bf3d0224e |
@@ -168,7 +168,7 @@ func (b *BunAdapter) BeginTx(ctx context.Context) (common.Database, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// For Bun, we'll return a special wrapper that holds the transaction
|
// For Bun, we'll return a special wrapper that holds the transaction
|
||||||
return &BunTxAdapter{tx: tx}, nil
|
return &BunTxAdapter{tx: tx, driverName: b.DriverName()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BunAdapter) CommitTx(ctx context.Context) error {
|
func (b *BunAdapter) CommitTx(ctx context.Context) error {
|
||||||
@@ -191,7 +191,7 @@ func (b *BunAdapter) RunInTransaction(ctx context.Context, fn func(common.Databa
|
|||||||
}()
|
}()
|
||||||
return b.db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
|
return b.db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
|
||||||
// Create adapter with transaction
|
// Create adapter with transaction
|
||||||
adapter := &BunTxAdapter{tx: tx}
|
adapter := &BunTxAdapter{tx: tx, driverName: b.DriverName()}
|
||||||
return fn(adapter)
|
return fn(adapter)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -200,6 +200,17 @@ func (b *BunAdapter) GetUnderlyingDB() interface{} {
|
|||||||
return b.db
|
return b.db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BunAdapter) DriverName() string {
|
||||||
|
// Normalize Bun's dialect name to match the project's canonical vocabulary.
|
||||||
|
// Bun returns "pg" for PostgreSQL; the rest of the project uses "postgres".
|
||||||
|
switch name := b.db.Dialect().Name().String(); name {
|
||||||
|
case "pg":
|
||||||
|
return "postgres"
|
||||||
|
default:
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// BunSelectQuery implements SelectQuery for Bun
|
// BunSelectQuery implements SelectQuery for Bun
|
||||||
type BunSelectQuery struct {
|
type BunSelectQuery struct {
|
||||||
query *bun.SelectQuery
|
query *bun.SelectQuery
|
||||||
@@ -1478,6 +1489,7 @@ func (b *BunResult) LastInsertId() (int64, error) {
|
|||||||
// BunTxAdapter wraps a Bun transaction to implement the Database interface
|
// BunTxAdapter wraps a Bun transaction to implement the Database interface
|
||||||
type BunTxAdapter struct {
|
type BunTxAdapter struct {
|
||||||
tx bun.Tx
|
tx bun.Tx
|
||||||
|
driverName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BunTxAdapter) NewSelect() common.SelectQuery {
|
func (b *BunTxAdapter) NewSelect() common.SelectQuery {
|
||||||
@@ -1527,3 +1539,7 @@ func (b *BunTxAdapter) RunInTransaction(ctx context.Context, fn func(common.Data
|
|||||||
func (b *BunTxAdapter) GetUnderlyingDB() interface{} {
|
func (b *BunTxAdapter) GetUnderlyingDB() interface{} {
|
||||||
return b.tx
|
return b.tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BunTxAdapter) DriverName() string {
|
||||||
|
return b.driverName
|
||||||
|
}
|
||||||
|
|||||||
@@ -106,6 +106,20 @@ func (g *GormAdapter) GetUnderlyingDB() interface{} {
|
|||||||
return g.db
|
return g.db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *GormAdapter) DriverName() string {
|
||||||
|
if g.db.Dialector == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// Normalize GORM's dialector name to match the project's canonical vocabulary.
|
||||||
|
// GORM returns "sqlserver" for MSSQL; the rest of the project uses "mssql".
|
||||||
|
switch name := g.db.Name(); name {
|
||||||
|
case "sqlserver":
|
||||||
|
return "mssql"
|
||||||
|
default:
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GormSelectQuery implements SelectQuery for GORM
|
// GormSelectQuery implements SelectQuery for GORM
|
||||||
type GormSelectQuery struct {
|
type GormSelectQuery struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
|
|||||||
@@ -17,11 +17,18 @@ import (
|
|||||||
// This provides a lightweight PostgreSQL adapter without ORM overhead
|
// This provides a lightweight PostgreSQL adapter without ORM overhead
|
||||||
type PgSQLAdapter struct {
|
type PgSQLAdapter struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
|
driverName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPgSQLAdapter creates a new PostgreSQL adapter
|
// NewPgSQLAdapter creates a new adapter wrapping a standard sql.DB.
|
||||||
func NewPgSQLAdapter(db *sql.DB) *PgSQLAdapter {
|
// An optional driverName (e.g. "postgres", "sqlite", "mssql") can be provided;
|
||||||
return &PgSQLAdapter{db: db}
|
// it defaults to "postgres" when omitted.
|
||||||
|
func NewPgSQLAdapter(db *sql.DB, driverName ...string) *PgSQLAdapter {
|
||||||
|
name := "postgres"
|
||||||
|
if len(driverName) > 0 && driverName[0] != "" {
|
||||||
|
name = driverName[0]
|
||||||
|
}
|
||||||
|
return &PgSQLAdapter{db: db, driverName: name}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableQueryDebug enables query debugging for development
|
// EnableQueryDebug enables query debugging for development
|
||||||
@@ -98,7 +105,7 @@ func (p *PgSQLAdapter) BeginTx(ctx context.Context) (common.Database, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &PgSQLTxAdapter{tx: tx}, nil
|
return &PgSQLTxAdapter{tx: tx, driverName: p.driverName}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PgSQLAdapter) CommitTx(ctx context.Context) error {
|
func (p *PgSQLAdapter) CommitTx(ctx context.Context) error {
|
||||||
@@ -121,7 +128,7 @@ func (p *PgSQLAdapter) RunInTransaction(ctx context.Context, fn func(common.Data
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter := &PgSQLTxAdapter{tx: tx}
|
adapter := &PgSQLTxAdapter{tx: tx, driverName: p.driverName}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if p := recover(); p != nil {
|
if p := recover(); p != nil {
|
||||||
@@ -141,6 +148,10 @@ func (p *PgSQLAdapter) GetUnderlyingDB() interface{} {
|
|||||||
return p.db
|
return p.db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PgSQLAdapter) DriverName() string {
|
||||||
|
return p.driverName
|
||||||
|
}
|
||||||
|
|
||||||
// preloadConfig represents a relationship to be preloaded
|
// preloadConfig represents a relationship to be preloaded
|
||||||
type preloadConfig struct {
|
type preloadConfig struct {
|
||||||
relation string
|
relation string
|
||||||
@@ -836,6 +847,7 @@ func (p *PgSQLResult) LastInsertId() (int64, error) {
|
|||||||
// PgSQLTxAdapter wraps a PostgreSQL transaction
|
// PgSQLTxAdapter wraps a PostgreSQL transaction
|
||||||
type PgSQLTxAdapter struct {
|
type PgSQLTxAdapter struct {
|
||||||
tx *sql.Tx
|
tx *sql.Tx
|
||||||
|
driverName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PgSQLTxAdapter) NewSelect() common.SelectQuery {
|
func (p *PgSQLTxAdapter) NewSelect() common.SelectQuery {
|
||||||
@@ -912,6 +924,10 @@ func (p *PgSQLTxAdapter) GetUnderlyingDB() interface{} {
|
|||||||
return p.tx
|
return p.tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PgSQLTxAdapter) DriverName() string {
|
||||||
|
return p.driverName
|
||||||
|
}
|
||||||
|
|
||||||
// applyJoinPreloads adds JOINs for relationships that should use JOIN strategy
|
// applyJoinPreloads adds JOINs for relationships that should use JOIN strategy
|
||||||
func (p *PgSQLSelectQuery) applyJoinPreloads() {
|
func (p *PgSQLSelectQuery) applyJoinPreloads() {
|
||||||
for _, preload := range p.preloads {
|
for _, preload := range p.preloads {
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ type Database interface {
|
|||||||
// For Bun, this returns *bun.DB
|
// For Bun, this returns *bun.DB
|
||||||
// This is useful for provider-specific features like PostgreSQL NOTIFY/LISTEN
|
// This is useful for provider-specific features like PostgreSQL NOTIFY/LISTEN
|
||||||
GetUnderlyingDB() interface{}
|
GetUnderlyingDB() interface{}
|
||||||
|
|
||||||
|
// DriverName returns the canonical name of the underlying database driver.
|
||||||
|
// Possible values: "postgres", "sqlite", "mssql", "mysql".
|
||||||
|
// All adapters normalise vendor-specific strings (e.g. Bun's "pg", GORM's
|
||||||
|
// "sqlserver") to the values above before returning.
|
||||||
|
DriverName() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectQuery interface for building SELECT queries (compatible with both GORM and Bun)
|
// SelectQuery interface for building SELECT queries (compatible with both GORM and Bun)
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ func (m *mockDatabase) RollbackTx(ctx context.Context) error {
|
|||||||
func (m *mockDatabase) GetUnderlyingDB() interface{} {
|
func (m *mockDatabase) GetUnderlyingDB() interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (m *mockDatabase) DriverName() string {
|
||||||
|
return "postgres"
|
||||||
|
}
|
||||||
|
|
||||||
// Mock SelectQuery
|
// Mock SelectQuery
|
||||||
type mockSelectQuery struct{}
|
type mockSelectQuery struct{}
|
||||||
|
|||||||
@@ -467,13 +467,11 @@ func (c *sqlConnection) getNativeAdapter() (common.Database, error) {
|
|||||||
// Create a native adapter based on database type
|
// Create a native adapter based on database type
|
||||||
switch c.dbType {
|
switch c.dbType {
|
||||||
case DatabaseTypePostgreSQL:
|
case DatabaseTypePostgreSQL:
|
||||||
c.nativeAdapter = database.NewPgSQLAdapter(c.nativeDB)
|
c.nativeAdapter = database.NewPgSQLAdapter(c.nativeDB, string(c.dbType))
|
||||||
case DatabaseTypeSQLite:
|
case DatabaseTypeSQLite:
|
||||||
// For SQLite, we'll use the PgSQL adapter as it works with standard sql.DB
|
c.nativeAdapter = database.NewPgSQLAdapter(c.nativeDB, string(c.dbType))
|
||||||
c.nativeAdapter = database.NewPgSQLAdapter(c.nativeDB)
|
|
||||||
case DatabaseTypeMSSQL:
|
case DatabaseTypeMSSQL:
|
||||||
// For MSSQL, we'll use the PgSQL adapter as it works with standard sql.DB
|
c.nativeAdapter = database.NewPgSQLAdapter(c.nativeDB, string(c.dbType))
|
||||||
c.nativeAdapter = database.NewPgSQLAdapter(c.nativeDB)
|
|
||||||
default:
|
default:
|
||||||
return nil, ErrUnsupportedDatabase
|
return nil, ErrUnsupportedDatabase
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,12 +231,14 @@ func (m *connectionManager) Connect(ctx context.Context) error {
|
|||||||
|
|
||||||
// Close closes all database connections
|
// Close closes all database connections
|
||||||
func (m *connectionManager) Close() error {
|
func (m *connectionManager) Close() error {
|
||||||
|
// Stop the health checker before taking mu. performHealthCheck acquires
|
||||||
|
// a read lock, so waiting for the goroutine while holding the write lock
|
||||||
|
// would deadlock.
|
||||||
|
m.stopHealthChecker()
|
||||||
|
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
// Stop health checker
|
|
||||||
m.stopHealthChecker()
|
|
||||||
|
|
||||||
// Close all connections
|
// Close all connections
|
||||||
var errors []error
|
var errors []error
|
||||||
for name, conn := range m.connections {
|
for name, conn := range m.connections {
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ func (m *MockDatabase) GetUnderlyingDB() interface{} {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MockDatabase) DriverName() string {
|
||||||
|
return "postgres"
|
||||||
|
}
|
||||||
|
|
||||||
// MockResult implements common.Result interface for testing
|
// MockResult implements common.Result interface for testing
|
||||||
type MockResult struct {
|
type MockResult struct {
|
||||||
rows int64
|
rows int64
|
||||||
|
|||||||
@@ -645,12 +645,15 @@ func (h *Handler) getNotifyTopic(clientID, subscriptionID string) string {
|
|||||||
// Database operation helpers (adapted from websocketspec)
|
// Database operation helpers (adapted from websocketspec)
|
||||||
|
|
||||||
func (h *Handler) getTableName(schema, entity string, model interface{}) string {
|
func (h *Handler) getTableName(schema, entity string, model interface{}) string {
|
||||||
// Use entity as table name
|
|
||||||
tableName := entity
|
tableName := entity
|
||||||
|
|
||||||
if schema != "" {
|
if schema != "" {
|
||||||
|
if h.db.DriverName() == "sqlite" {
|
||||||
|
tableName = schema + "_" + tableName
|
||||||
|
} else {
|
||||||
tableName = schema + "." + tableName
|
tableName = schema + "." + tableName
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return tableName
|
return tableName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1380,10 +1380,16 @@ func (h *Handler) getSchemaAndTable(defaultSchema, entity string, model interfac
|
|||||||
return schema, entity
|
return schema, entity
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTableName returns the full table name including schema (schema.table)
|
// getTableName returns the full table name including schema.
|
||||||
|
// For most drivers the result is "schema.table". For SQLite, which does not
|
||||||
|
// support schema-qualified names, the schema and table are joined with an
|
||||||
|
// underscore: "schema_table".
|
||||||
func (h *Handler) getTableName(schema, entity string, model interface{}) string {
|
func (h *Handler) getTableName(schema, entity string, model interface{}) string {
|
||||||
schemaName, tableName := h.getSchemaAndTable(schema, entity, model)
|
schemaName, tableName := h.getSchemaAndTable(schema, entity, model)
|
||||||
if schemaName != "" {
|
if schemaName != "" {
|
||||||
|
if h.db.DriverName() == "sqlite" {
|
||||||
|
return fmt.Sprintf("%s_%s", schemaName, tableName)
|
||||||
|
}
|
||||||
return fmt.Sprintf("%s.%s", schemaName, tableName)
|
return fmt.Sprintf("%s.%s", schemaName, tableName)
|
||||||
}
|
}
|
||||||
return tableName
|
return tableName
|
||||||
|
|||||||
@@ -2015,11 +2015,18 @@ func (h *Handler) processChildRelationsForField(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTableNameForRelatedModel gets the table name for a related model
|
// getTableNameForRelatedModel gets the table name for a related model.
|
||||||
|
// If the model's TableName() is schema-qualified (e.g. "public.users") the
|
||||||
|
// separator is adjusted for the active driver: underscore for SQLite, dot otherwise.
|
||||||
func (h *Handler) getTableNameForRelatedModel(model interface{}, defaultName string) string {
|
func (h *Handler) getTableNameForRelatedModel(model interface{}, defaultName string) string {
|
||||||
if provider, ok := model.(common.TableNameProvider); ok {
|
if provider, ok := model.(common.TableNameProvider); ok {
|
||||||
tableName := provider.TableName()
|
tableName := provider.TableName()
|
||||||
if tableName != "" {
|
if tableName != "" {
|
||||||
|
if schema, table := h.parseTableName(tableName); schema != "" {
|
||||||
|
if h.db.DriverName() == "sqlite" {
|
||||||
|
return fmt.Sprintf("%s_%s", schema, table)
|
||||||
|
}
|
||||||
|
}
|
||||||
return tableName
|
return tableName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2264,10 +2271,16 @@ func (h *Handler) getSchemaAndTable(defaultSchema, entity string, model interfac
|
|||||||
return schema, entity
|
return schema, entity
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTableName returns the full table name including schema (schema.table)
|
// getTableName returns the full table name including schema.
|
||||||
|
// For most drivers the result is "schema.table". For SQLite, which does not
|
||||||
|
// support schema-qualified names, the schema and table are joined with an
|
||||||
|
// underscore: "schema_table".
|
||||||
func (h *Handler) getTableName(schema, entity string, model interface{}) string {
|
func (h *Handler) getTableName(schema, entity string, model interface{}) string {
|
||||||
schemaName, tableName := h.getSchemaAndTable(schema, entity, model)
|
schemaName, tableName := h.getSchemaAndTable(schema, entity, model)
|
||||||
if schemaName != "" {
|
if schemaName != "" {
|
||||||
|
if h.db.DriverName() == "sqlite" {
|
||||||
|
return fmt.Sprintf("%s_%s", schemaName, tableName)
|
||||||
|
}
|
||||||
return fmt.Sprintf("%s.%s", schemaName, tableName)
|
return fmt.Sprintf("%s.%s", schemaName, tableName)
|
||||||
}
|
}
|
||||||
return tableName
|
return tableName
|
||||||
|
|||||||
@@ -656,12 +656,15 @@ func (h *Handler) delete(hookCtx *HookContext) error {
|
|||||||
// Helper methods
|
// Helper methods
|
||||||
|
|
||||||
func (h *Handler) getTableName(schema, entity string, model interface{}) string {
|
func (h *Handler) getTableName(schema, entity string, model interface{}) string {
|
||||||
// Use entity as table name
|
|
||||||
tableName := entity
|
tableName := entity
|
||||||
|
|
||||||
if schema != "" {
|
if schema != "" {
|
||||||
|
if h.db.DriverName() == "sqlite" {
|
||||||
|
tableName = schema + "_" + tableName
|
||||||
|
} else {
|
||||||
tableName = schema + "." + tableName
|
tableName = schema + "." + tableName
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return tableName
|
return tableName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ func (m *MockDatabase) GetUnderlyingDB() interface{} {
|
|||||||
return args.Get(0)
|
return args.Get(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MockDatabase) DriverName() string {
|
||||||
|
return "postgres"
|
||||||
|
}
|
||||||
|
|
||||||
// MockSelectQuery is a mock implementation of common.SelectQuery
|
// MockSelectQuery is a mock implementation of common.SelectQuery
|
||||||
type MockSelectQuery struct {
|
type MockSelectQuery struct {
|
||||||
mock.Mock
|
mock.Mock
|
||||||
|
|||||||
Reference in New Issue
Block a user