Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: Expose prepared statement column and parameter metadata #1743

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
kyleconroy wants to merge 1 commit into go-sql-driver:master
base: master
Choose a base branch
Loading
from sqlc-dev:expose_query_metadata
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions connection.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -224,20 +224,16 @@ func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
columnCount, err := stmt.readPrepareResultPacket()
if err == nil {
if stmt.paramCount > 0 {
if err = mc.skipColumns(stmt.paramCount); err != nil {
// Read parameter metadata instead of skipping it
if stmt.params, err = mc.readColumns(stmt.paramCount, nil); err != nil {
return nil, err
}
}

if columnCount > 0 {
if mc.extCapabilities&clientCacheMetadata != 0 {
if stmt.columns, err = mc.readColumns(int(columnCount), nil); err != nil {
return nil, err
}
} else {
if err = mc.skipColumns(int(columnCount)); err != nil {
return nil, err
}
// Always read column metadata
if stmt.columns, err = mc.readColumns(int(columnCount), stmt.columns); err != nil {
return nil, err
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions fields.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,38 @@ type mysqlField struct {
charSet uint8
}

// FieldMetadata represents metadata about a column or parameter from a prepared statement.
// This is a public struct that exposes the metadata from mysqlField.
type FieldMetadata struct {
// TableName is the name of the table this field belongs to (may be empty for expressions)
TableName string
// Name is the name or alias of the field
Name string
// Length is the maximum length of the field
Length uint32
// Decimals is the number of decimals for numeric types
Decimals byte
// DatabaseTypeName returns the MySQL type name (e.g., "INT", "VARCHAR", "TEXT")
DatabaseTypeName string
// Nullable indicates whether the field can be NULL
Nullable bool
// Unsigned indicates whether a numeric field is unsigned
Unsigned bool
}

// toFieldMetadata converts an internal mysqlField to a public FieldMetadata
func (mf *mysqlField) toFieldMetadata() FieldMetadata {
return FieldMetadata{
TableName: mf.tableName,
Name: mf.name,
Length: mf.length,
Decimals: mf.decimals,
DatabaseTypeName: mf.typeDatabaseName(),
Nullable: mf.flags&flagNotNULL == 0,
Unsigned: mf.flags&flagUnsigned != 0,
}
}

func (mf *mysqlField) scanType() reflect.Type {
switch mf.fieldType {
case fieldTypeTiny:
Expand Down
48 changes: 48 additions & 0 deletions statement.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,38 @@ import (
"reflect"
)

// StmtMetadata is an interface that provides access to prepared statement metadata.
// It can be used via type assertion on the driver.Stmt returned by Conn.Prepare.
//
// Example usage with database/sql:
//
// conn, _ := db.Conn(ctx)
// conn.Raw(func(driverConn any) error {
// if preparer, ok := driverConn.(driver.ConnPrepareContext); ok {
// stmt, _ := preparer.PrepareContext(ctx, query)
// if meta, ok := stmt.(mysql.StmtMetadata); ok {
// columns := meta.ColumnMetadata()
// params := meta.ParamMetadata()
// }
// }
// return nil
// })
type StmtMetadata interface {
// ColumnMetadata returns metadata about result columns
ColumnMetadata() []FieldMetadata
// ParamMetadata returns metadata about query parameters
ParamMetadata() []FieldMetadata
}

// Verify that mysqlStmt implements StmtMetadata
var _ StmtMetadata = (*mysqlStmt)(nil)

type mysqlStmt struct {
mc *mysqlConn
id uint32
paramCount int
columns []mysqlField
params []mysqlField
}

func (stmt *mysqlStmt) Close() error {
Expand All @@ -42,6 +69,27 @@ func (stmt *mysqlStmt) NumInput() int {
return stmt.paramCount
}

// ColumnMetadata returns metadata about the columns that will be returned by this prepared statement.
// This information is obtained from the MySQL server during the PREPARE phase.
func (stmt *mysqlStmt) ColumnMetadata() []FieldMetadata {
result := make([]FieldMetadata, len(stmt.columns))
for i, col := range stmt.columns {
result[i] = col.toFieldMetadata()
}
return result
}

// ParamMetadata returns metadata about the parameters expected by this prepared statement.
// This information is obtained from the MySQL server during the PREPARE phase.
// Note: MySQL may return limited parameter metadata depending on the query structure.
func (stmt *mysqlStmt) ParamMetadata() []FieldMetadata {
result := make([]FieldMetadata, len(stmt.params))
for i, param := range stmt.params {
result[i] = param.toFieldMetadata()
}
return result
}

func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
return converter{}
}
Expand Down

AltStyle によって変換されたページ (->オリジナル) /