Skip to content
Merged
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
10 changes: 5 additions & 5 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ func (ss *session) beginTx(ctx context.Context, w io.Writer) error {
}

func doDescTables(ctx context.Context, ss *session, commandIn commandIn) error {
if ss.Dialect.SqlForTab == "" {
if ss.Dialect.SQLForTables == "" {
return fmt.Errorf("desc: %w", ErrNotSupported)
}
query := ss.Dialect.SqlToQueryTables()
query := ss.Dialect.BuildSQLForTables()
var name string

handler := func(e *csvi.KeyEventArgs) (*csvi.CommandResult, error) {
Expand All @@ -106,7 +106,7 @@ func doDescTables(ctx context.Context, ss *session, commandIn commandIn) error {
}
header := e.Front()
for i, c := range header.Cell {
if strings.EqualFold(c.Text(), ss.Dialect.TableField) {
if strings.EqualFold(c.Text(), ss.Dialect.TableNameField) {
name = e.CursorRow.Cell[i].Text()
return &csvi.CommandResult{Quit: true}, nil
}
Expand Down Expand Up @@ -155,10 +155,10 @@ func doDescTables(ctx context.Context, ss *session, commandIn commandIn) error {
}

func doDescColumns(ctx context.Context, ss *session, table string) error {
if ss.Dialect.SqlForDesc == "" {
if ss.Dialect.SQLForColumns == "" {
return fmt.Errorf("desc table: %w", ErrNotSupported)
}
query := ss.Dialect.SqlToQueryColumns(table)
query := ss.Dialect.BuildSQLForColumns(table)
if ss.Debug {
fmt.Println(query)
}
Expand Down
55 changes: 37 additions & 18 deletions dialect/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,43 @@ type PlaceHolder interface {
}

type Entry struct {
Usage string
SqlForDesc string
SqlForTab string
TableField string
ColumnField string
DisplayDateTimeLayout string
PlaceHolder PlaceHolder
TypeNameToConv func(string) func(string) (any, error)
DSNFilter func(string) (string, error)
CanUseInTransaction func(string) bool
IsQuerySQL func(string) bool
}

func (D *Entry) TypeToConv(typeName string) func(string) (any, error) {
if D.TypeNameToConv == nil {
// Usage describes how to use this entry or what it represents.
Usage string

// SQLForColumns is the SQL query used to retrieve column information.
SQLForColumns string

// SQLForTables is the SQL query used to retrieve table information.
SQLForTables string

// TableNameField is the field name for table names in SQL results.
TableNameField string

// ColumnNameField is the field name for column names in SQL results.
ColumnNameField string

// PlaceHolder defines how to represent placeholders (e.g., ?, $1) in SQL.
PlaceHolder PlaceHolder

// TypeConverterFor returns a converter function for a given type name.
// The returned function converts a string literal to the corresponding Go value.
TypeConverterFor func(typeName string) func(literal string) (any, error)

// DSNFilter adjusts or validates a given DSN string before use.
DSNFilter func(dsn string) (string, error)

// IsTransactionSafe reports whether the given SQL statement is safe to run in a transaction.
IsTransactionSafe func(sql string) bool

// IsQuerySQL reports whether the given SQL statement is a query (e.g., SELECT) or not.
IsQuerySQL func(sql string) bool
}

func (D *Entry) LookupConverter(typeName string) func(string) (any, error) {
if D.TypeConverterFor == nil {
return nil
}
return D.TypeNameToConv(typeName)
return D.TypeConverterFor(typeName)
}

const (
Expand Down Expand Up @@ -86,8 +105,8 @@ func ParseAnyDateTime(s string) (time.Time, error) {

var registry = map[string]*Entry{}

func Register(name string, setting *Entry) {
registry[strings.ToUpper(name)] = setting
func (e *Entry) Register(name string) {
registry[strings.ToUpper(name)] = e
}

func Find(name string) (*Entry, bool) {
Expand Down
17 changes: 8 additions & 9 deletions dialect/mysql/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func mySQLDSNFilter(dsn string) (string, error) {

var mySqlSpec = &dialect.Entry{
Usage: `sqlbless mysql <USERNAME>:<PASSWORD>@/<DBNAME>`,
SqlForDesc: `
SQLForColumns: `
select ordinal_position as "ID",
column_name as "NAME",
case
Expand All @@ -82,19 +82,18 @@ var mySqlSpec = &dialect.Entry{
from information_schema.columns
where table_name = ?
order by ordinal_position`,
SqlForTab: `
SQLForTables: `
select * from information_schema.tables
where table_type = 'BASE TABLE'
and table_schema
not in ('mysql', 'information_schema', 'performance_schema', 'sys')`,
DisplayDateTimeLayout: dialect.DateTimeTzLayout,
TypeNameToConv: mySQLTypeNameToConv,
PlaceHolder: &dialect.PlaceHolderQuestion{},
DSNFilter: mySQLDSNFilter,
TableField: "TABLE_NAME",
ColumnField: "NAME",
TypeConverterFor: mySQLTypeNameToConv,
PlaceHolder: &dialect.PlaceHolderQuestion{},
DSNFilter: mySQLDSNFilter,
TableNameField: "TABLE_NAME",
ColumnNameField: "NAME",
}

func init() {
dialect.Register("MYSQL", mySqlSpec)
mySqlSpec.Register("MYSQL")
}
15 changes: 7 additions & 8 deletions dialect/oracle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func oracleTypeNameToConv(typeName string) func(string) (any, error) {

var oracleSpec = &dialect.Entry{
Usage: "sqlbless oracle://<USERNAME>:<PASSWORD>@<HOSTNAME>:<PORT>/<SERVICE>",
SqlForDesc: `
SQLForColumns: `
select column_id as "ID",
column_name as "NAME",
case
Expand All @@ -35,14 +35,13 @@ var oracleSpec = &dialect.Entry{
from all_tab_columns
where table_name = UPPER(:1)
order by column_id`,
SqlForTab: `select * from tab where tname not like 'BIN$%'`,
DisplayDateTimeLayout: dialect.DateTimeTzLayout,
TypeNameToConv: oracleTypeNameToConv,
TableField: "tname",
ColumnField: "name",
PlaceHolder: &dialect.PlaceHolderName{Prefix: ":", Format: "v"},
SQLForTables: `select * from tab where tname not like 'BIN$%'`,
TypeConverterFor: oracleTypeNameToConv,
TableNameField: "tname",
ColumnNameField: "name",
PlaceHolder: &dialect.PlaceHolderName{Prefix: ":", Format: "v"},
}

func init() {
dialect.Register("ORACLE", oracleSpec)
oracleSpec.Register("ORACLE")
}
17 changes: 8 additions & 9 deletions dialect/postgresql/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (ph *placeHolder) Values() (result []any) {

var postgresSpec = &dialect.Entry{
Usage: "sqlbless postgres://<USERNAME>:<PASSWORD>@<HOSTNAME>:<PORT>/<DBNAME>?sslmode=disable",
SqlForDesc: `
SQLForColumns: `
select a.attnum as "ID",
a.attname as "NAME",
case
Expand All @@ -63,17 +63,16 @@ var postgresSpec = &dialect.Entry{
and t.oid = a.atttypid
and a.attisdropped is false
order by a.attnum`,
SqlForTab: `
SQLForTables: `
select *
from information_schema.tables
where table_type = 'BASE TABLE'
and table_schema not in ('pg_catalog', 'information_schema')`,
DisplayDateTimeLayout: dialect.DateTimeTzLayout,
TypeNameToConv: postgresTypeNameToConv,
PlaceHolder: &placeHolder{},
TableField: "table_name",
ColumnField: "name",
CanUseInTransaction: canUseInTransaction,
TypeConverterFor: postgresTypeNameToConv,
PlaceHolder: &placeHolder{},
TableNameField: "table_name",
ColumnNameField: "name",
IsTransactionSafe: canUseInTransaction,
}

func canUseInTransaction(sql string) bool {
Expand All @@ -91,5 +90,5 @@ func canUseInTransaction(sql string) bool {
}

func init() {
dialect.Register("POSTGRES", postgresSpec)
postgresSpec.Register("POSTGRES")
}
23 changes: 15 additions & 8 deletions dialect/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,25 @@ func queryOneColumn(ctx context.Context, conn CanQuery, sqlStr, columnName strin
}
}

func (e *Entry) SqlToQueryTables() string {
return e.SqlForTab
// BuildSQLForTables returns the SQL statement used to retrieve the list of tables.
func (e *Entry) BuildSQLForTables() string {
return e.SQLForTables
}

func (e *Entry) SqlToQueryColumns(table string) string {
return strings.ReplaceAll(e.SqlForDesc, "{table_name}", table)
// BuildSQLForColumns returns the SQL statement used to retrieve the list of columns
// for the given table name. The placeholder "{table_name}" in the template will be replaced.
func (e *Entry) BuildSQLForColumns(table string) string {
return strings.ReplaceAll(e.SQLForColumns, "{table_name}", table)
}

func (e *Entry) Tables(ctx context.Context, conn CanQuery) ([]string, error) {
return queryOneColumn(ctx, conn, e.SqlToQueryTables(), e.TableField)
// Tables executes the SQL to list all table names defined by the dialect.
// It returns a slice of table names or an error if the query fails.
func (e *Entry) FetchTables(ctx context.Context, conn CanQuery) ([]string, error) {
return queryOneColumn(ctx, conn, e.BuildSQLForTables(), e.TableNameField)
}

func (e *Entry) Columns(ctx context.Context, conn CanQuery, table string) ([]string, error) {
return queryOneColumn(ctx, conn, e.SqlToQueryColumns(table), e.ColumnField, table)
// Columns executes the SQL to list column names for the specified table.
// It returns a slice of column names or an error if the query fails.
func (e *Entry) FetchColumns(ctx context.Context, conn CanQuery, table string) ([]string, error) {
return queryOneColumn(ctx, conn, e.BuildSQLForColumns(table), e.ColumnNameField, table)
}
17 changes: 8 additions & 9 deletions dialect/sqlite/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,18 @@ const dateTimeTzLayout = "2006-01-02 15:04:05.999999999 -07:00"

var Entry = &dialect.Entry{
Usage: "sqlbless sqlite3 :memory: OR <FILEPATH>",
SqlForTab: `
SQLForTables: `
select 'master' AS schema,name,rootpage,sql FROM sqlite_master
where type = 'table'
union all
select 'temp_schema' AS schema,name,rootpage,sql FROM sqlite_temp_schema
where type = 'temp_schema'`,
DisplayDateTimeLayout: dateTimeTzLayout,
TypeNameToConv: typeNameToConv,
PlaceHolder: &placeHolder{},
SqlForDesc: `PRAGMA table_info({table_name})`,
TableField: "name",
ColumnField: "name",
CanUseInTransaction: canUseInTransaction,
TypeConverterFor: typeNameToConv,
PlaceHolder: &placeHolder{},
SQLForColumns: `PRAGMA table_info({table_name})`,
TableNameField: "name",
ColumnNameField: "name",
IsTransactionSafe: canUseInTransaction,
IsQuerySQL: func(s string) bool {
s, _ = misc.CutField(s)
return strings.EqualFold(s, "PRAGMA")
Expand Down Expand Up @@ -94,5 +93,5 @@ func (ph *placeHolder) Values() (result []any) {
}

func init() {
dialect.Register("SQLITE3", Entry)
Entry.Register("SQLITE3")
}
15 changes: 7 additions & 8 deletions dialect/sqlserver/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func sqlServerTypeNameToConv(typeName string) func(string) (any, error) {

var sqlServerSpec = &dialect.Entry{
Usage: "sqlbless sqlserver://@<HOSTNAME>?database=<DBNAME>",
SqlForDesc: `
SQLForColumns: `
select c.column_id as "ID",
c.name as "NAME",
case
Expand All @@ -42,14 +42,13 @@ var sqlServerSpec = &dialect.Entry{
and o.name = @p1
and c.user_type_id = t.user_type_id
order by c.column_id`,
SqlForTab: `select * from sys.tables`,
DisplayDateTimeLayout: dialect.DateTimeTzLayout,
TypeNameToConv: sqlServerTypeNameToConv,
PlaceHolder: &dialect.PlaceHolderName{Prefix: "@", Format: "v"},
TableField: "name",
ColumnField: "name",
SQLForTables: `select * from sys.tables`,
TypeConverterFor: sqlServerTypeNameToConv,
PlaceHolder: &dialect.PlaceHolderName{Prefix: "@", Format: "v"},
TableNameField: "name",
ColumnNameField: "name",
}

func init() {
dialect.Register("SQLSERVER", sqlServerSpec)
sqlServerSpec.Register("SQLSERVER")
}
2 changes: 1 addition & 1 deletion edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func doEdit(ctx context.Context, ss *session, command string, pilot commandIn) e
// replace `edit ` to `select * from `
_, tableAndWhere := misc.CutField(command)
if tableAndWhere == "" {
tables, err := ss.Dialect.Tables(ctx, ss.conn)
tables, err := ss.Dialect.FetchTables(ctx, ss.conn)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions internal/sqlcompletion/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (C *completeType) getCandidates(ctx context.Context, fields []string) ([]st

func (C *completeType) tables(ctx context.Context) []string {
if len(C.tableCache) <= 0 {
C.tableCache, _ = C.Dialect.Tables(ctx, C.Conn)
C.tableCache, _ = C.Dialect.FetchTables(ctx, C.Conn)
}
return C.tableCache
}
Expand All @@ -138,7 +138,7 @@ func (C *completeType) columns(ctx context.Context, tables []string) (result []s
}
values, ok := C.columnCache[tableName]
if !ok {
values, _ = C.Dialect.Columns(ctx, C.Conn, tableName)
values, _ = C.Dialect.FetchColumns(ctx, C.Conn, tableName)
C.columnCache[tableName] = values
}
result = append(result, values...)
Expand Down
2 changes: 1 addition & 1 deletion loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func (ss *session) Loop(ctx context.Context, commandIn commandIn) error {
} else {
if ss.tx == nil {
_, err = ss.conn.ExecContext(ctx, query)
} else if f := ss.Dialect.CanUseInTransaction; f != nil && f(query) {
} else if f := ss.Dialect.IsTransactionSafe; f != nil && f(query) {
_, err = ss.tx.ExecContext(ctx, query)
} else {
err = ErrTransactionIsNotClosed
Expand Down
2 changes: 2 additions & 0 deletions release_note_en.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
( **English** / [Japanese](release_note_ja.md) )

- Refactor `dialect` subpackage: rename fields and methods for clarity (#8)

v0.25.0
=======
Nov 3, 2025
Expand Down
2 changes: 2 additions & 0 deletions release_note_ja.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
( [English](release_note_en.md) / **Japanese** )

- サブパッケージ `dialect` をリファクタリング: フィールド・メソッドを改名 (#8)

v0.25.0
=======
Nov 3, 2025
Expand Down
2 changes: 1 addition & 1 deletion spread/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func (editor *Editor) Edit(ctx context.Context, tableAndWhere string, termOut io
name := strings.ToUpper(ct.DatabaseTypeName())
var v func(string) (string, error)
_ct := ct
if conv := editor.TypeToConv(name); conv != nil {
if conv := editor.LookupConverter(name); conv != nil {
quoteFunc = append(quoteFunc, conv)
v = func(s string) (string, error) {
if s == editor.Null || s == "" {
Expand Down