diff --git a/fbxpool/rows.go b/fbxpool/rows.go index 491f1dc..1c5d923 100644 --- a/fbxpool/rows.go +++ b/fbxpool/rows.go @@ -3,6 +3,7 @@ package fbxpool import ( "github.com/firebirdsql/fbx" "github.com/firebirdsql/fbx/fbconn" + "github.com/firebirdsql/fbx/fbtype" ) type errRows struct { @@ -18,6 +19,7 @@ func (e errRows) Scan(dest ...any) error { return e.err } func (e errRows) Values() ([]any, error) { return nil, e.err } func (e errRows) RawValues() [][]byte { return nil } func (e errRows) Conn() *fbx.Conn { return nil } +func (e errRows) TypeMap() *fbtype.Map { return nil } type errRow struct { err error @@ -90,6 +92,10 @@ func (rows *poolRows) Conn() *fbx.Conn { return rows.r.Conn() } +func (rows *poolRows) TypeMap() *fbtype.Map { + return rows.r.TypeMap() +} + type poolRow struct { r fbx.Row c *Conn diff --git a/rows.go b/rows.go index 15da262..d2ad6e0 100644 --- a/rows.go +++ b/rows.go @@ -74,6 +74,11 @@ type Rows interface { // Conn returns the underlying *Conn on which the query was executed. This may return nil if Rows did not come from a // *Conn (e.g. if it was created by RowsFromResultReader) Conn() *Conn + + // TypeMap returns the [fbtype.Map] the values of this Rows are decoded with. It is available even when [Rows.Conn] + // is nil, such as for a Rows created by [RowsFromResultReader]. It may return nil if the Rows carries no values, + // such as one representing only an error. + TypeMap() *fbtype.Map } // Row is a convenience wrapper over Rows that is returned by QueryRow. @@ -424,6 +429,10 @@ func (rows *baseRows) Conn() *Conn { return rows.conn } +func (rows *baseRows) TypeMap() *fbtype.Map { + return rows.typeMap +} + func (rows *baseRows) scanBlobValue(fd *fbconn.FieldDescription, src []byte, dst any) error { if src == nil { switch d := dst.(type) { diff --git a/rows_test.go b/rows_test.go index 9a46630..d3402b5 100644 --- a/rows_test.go +++ b/rows_test.go @@ -11,6 +11,7 @@ import ( "github.com/firebirdsql/fbx" "github.com/firebirdsql/fbx/fbconn" "github.com/firebirdsql/fbx/fbproto" + "github.com/firebirdsql/fbx/fbtype" "github.com/firebirdsql/fbx/fbxtest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -1320,3 +1321,27 @@ recreate table products ( // Fries: $5 // Soft Drink: $3 } + +// Rows.TypeMap is available even when Rows.Conn is nil, so a caller can decode raw values itself. +func TestRowsTypeMapWithoutConn(t *testing.T) { + t.Parallel() + + defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *fbx.Conn) { + rr := conn.FbConn().Exec(ctx, `select cast('Adam' as varchar(5)) as name from rdb$database`) + + rows := fbx.RowsFromResultReader(conn.TypeMap(), rr) + defer func() { + rows.Close() + require.NoError(t, rows.Err()) + }() + require.Nil(t, rows.Conn(), "this Rows has no Conn, which is what makes TypeMap necessary") + require.Same(t, conn.TypeMap(), rows.TypeMap()) + + require.True(t, rows.Next()) + fd := rows.FieldDescriptions()[0] + var name string + typeMetadata := fbtype.NewTypeMetadata(fd.Type, fd.Subtype, fd.Length, fd.Scale) + require.NoError(t, rows.TypeMap().Scan(typeMetadata, rows.RawValues()[0], &name)) + require.Equal(t, "Adam", name) + }) +}