From c84dd7dc916e0829cefa5dd72c309132375cc03b Mon Sep 17 00:00:00 2001 From: Hein Date: Fri, 7 Nov 2025 14:18:15 +0200 Subject: [PATCH] Lets try the model approach again --- pkg/common/adapters/database/bun.go | 19 +++++++++++++++---- pkg/resolvespec/handler.go | 10 +++++++--- pkg/restheadspec/handler.go | 10 +++++++--- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/pkg/common/adapters/database/bun.go b/pkg/common/adapters/database/bun.go index 9db5b90..8bbe0ba 100644 --- a/pkg/common/adapters/database/bun.go +++ b/pkg/common/adapters/database/bun.go @@ -82,6 +82,7 @@ func (b *BunAdapter) RunInTransaction(ctx context.Context, fn func(common.Databa type BunSelectQuery struct { query *bun.SelectQuery db bun.IDB // Store DB connection for count queries + hasModel bool // Track if Model() was called schema string // Separated schema name tableName string // Just the table name, without schema tableAlias string @@ -89,6 +90,7 @@ type BunSelectQuery struct { func (b *BunSelectQuery) Model(model interface{}) common.SelectQuery { b.query = b.query.Model(model) + b.hasModel = true // Mark that we have a model // Try to get table name from model if it implements TableNameProvider if provider, ok := model.(common.TableNameProvider); ok { @@ -232,10 +234,19 @@ func (b *BunSelectQuery) Scan(ctx context.Context, dest interface{}) error { } func (b *BunSelectQuery) Count(ctx context.Context) (int, error) { - // Bun's Count() method works properly and handles column selections automatically - // It builds: SELECT COUNT(*) FROM (original query) AS subquery - // This works with both Model() and Table() set - count, err := b.query.Count(ctx) + // If Model() was set, use bun's native Count() which works properly + if b.hasModel { + count, err := b.query.Count(ctx) + return count, err + } + + // Otherwise, wrap as subquery to avoid "Model(nil)" error + // This is needed when only Table() is set without a model + var count int + err := b.db.NewSelect(). + TableExpr("(?) AS subquery", b.query). + ColumnExpr("COUNT(*)"). + Scan(ctx, &count) return count, err } diff --git a/pkg/resolvespec/handler.go b/pkg/resolvespec/handler.go index 54d75ac..f82bdb6 100644 --- a/pkg/resolvespec/handler.go +++ b/pkg/resolvespec/handler.go @@ -172,12 +172,16 @@ func (h *Handler) handleRead(ctx context.Context, w common.ResponseWriter, id st logger.Info("Reading records from %s.%s", schema, entity) // Create the model pointer for Scan() operations - // We don't set it on the query to avoid table duplication in FROM clause sliceType := reflect.SliceOf(reflect.PointerTo(modelType)) modelPtr := reflect.New(sliceType).Interface() - // Use only Table() - model will be provided to Scan() directly - query := h.db.NewSelect().Table(tableName) + // Start with Model() to avoid "Model(nil)" errors in Count() + query := h.db.NewSelect().Model(model) + + // Only set Table() if the model doesn't provide a table name + if provider, ok := model.(common.TableNameProvider); !ok || provider.TableName() == "" { + query = query.Table(tableName) + } // Apply column selection if len(options.Columns) > 0 { diff --git a/pkg/restheadspec/handler.go b/pkg/restheadspec/handler.go index 712af0e..fd6737e 100644 --- a/pkg/restheadspec/handler.go +++ b/pkg/restheadspec/handler.go @@ -201,9 +201,13 @@ func (h *Handler) handleRead(ctx context.Context, w common.ResponseWriter, id st logger.Info("Reading records from %s.%s", schema, entity) - // Use Table() with the resolved table name - // Model will be provided to Scan() directly to avoid table duplication in FROM clause - query := h.db.NewSelect().Table(tableName) + // Start with Model() to avoid "Model(nil)" errors in Count() + query := h.db.NewSelect().Model(model) + + // Only set Table() if the model doesn't provide a table name + if provider, ok := model.(common.TableNameProvider); !ok || provider.TableName() == "" { + query = query.Table(tableName) + } // Apply column selection if len(options.Columns) > 0 {