diff --git a/commands.go b/commands.go index ce57a58..759151c 100644 --- a/commands.go +++ b/commands.go @@ -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) { @@ -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 } @@ -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) } diff --git a/dialect/main.go b/dialect/main.go index 7a59dc4..fe2ba05 100644 --- a/dialect/main.go +++ b/dialect/main.go @@ -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 ( @@ -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) { diff --git a/dialect/mysql/main.go b/dialect/mysql/main.go index 2a187e2..3b78331 100644 --- a/dialect/mysql/main.go +++ b/dialect/mysql/main.go @@ -65,7 +65,7 @@ func mySQLDSNFilter(dsn string) (string, error) { var mySqlSpec = &dialect.Entry{ Usage: `sqlbless mysql :@/`, - SqlForDesc: ` + SQLForColumns: ` select ordinal_position as "ID", column_name as "NAME", case @@ -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") } diff --git a/dialect/oracle/main.go b/dialect/oracle/main.go index 1798e6c..d779075 100644 --- a/dialect/oracle/main.go +++ b/dialect/oracle/main.go @@ -19,7 +19,7 @@ func oracleTypeNameToConv(typeName string) func(string) (any, error) { var oracleSpec = &dialect.Entry{ Usage: "sqlbless oracle://:@:/", - SqlForDesc: ` + SQLForColumns: ` select column_id as "ID", column_name as "NAME", case @@ -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") } diff --git a/dialect/postgresql/main.go b/dialect/postgresql/main.go index 9a279e3..ef0de35 100644 --- a/dialect/postgresql/main.go +++ b/dialect/postgresql/main.go @@ -44,7 +44,7 @@ func (ph *placeHolder) Values() (result []any) { var postgresSpec = &dialect.Entry{ Usage: "sqlbless postgres://:@:/?sslmode=disable", - SqlForDesc: ` + SQLForColumns: ` select a.attnum as "ID", a.attname as "NAME", case @@ -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 { @@ -91,5 +90,5 @@ func canUseInTransaction(sql string) bool { } func init() { - dialect.Register("POSTGRES", postgresSpec) + postgresSpec.Register("POSTGRES") } diff --git a/dialect/query.go b/dialect/query.go index 47d681d..86c73fa 100644 --- a/dialect/query.go +++ b/dialect/query.go @@ -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) } diff --git a/dialect/sqlite/main.go b/dialect/sqlite/main.go index 6295d23..2bf1280 100644 --- a/dialect/sqlite/main.go +++ b/dialect/sqlite/main.go @@ -15,19 +15,18 @@ const dateTimeTzLayout = "2006-01-02 15:04:05.999999999 -07:00" var Entry = &dialect.Entry{ Usage: "sqlbless sqlite3 :memory: OR ", - 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") @@ -94,5 +93,5 @@ func (ph *placeHolder) Values() (result []any) { } func init() { - dialect.Register("SQLITE3", Entry) + Entry.Register("SQLITE3") } diff --git a/dialect/sqlserver/main.go b/dialect/sqlserver/main.go index bc51ba7..956a384 100644 --- a/dialect/sqlserver/main.go +++ b/dialect/sqlserver/main.go @@ -22,7 +22,7 @@ func sqlServerTypeNameToConv(typeName string) func(string) (any, error) { var sqlServerSpec = &dialect.Entry{ Usage: "sqlbless sqlserver://@?database=", - SqlForDesc: ` + SQLForColumns: ` select c.column_id as "ID", c.name as "NAME", case @@ -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") } diff --git a/edit.go b/edit.go index aa88acc..26dd3b2 100644 --- a/edit.go +++ b/edit.go @@ -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 } diff --git a/internal/sqlcompletion/main.go b/internal/sqlcompletion/main.go index 2498f57..26f31f3 100644 --- a/internal/sqlcompletion/main.go +++ b/internal/sqlcompletion/main.go @@ -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 } @@ -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...) diff --git a/loop.go b/loop.go index 54b0ccf..c0a0b08 100644 --- a/loop.go +++ b/loop.go @@ -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 diff --git a/release_note_en.md b/release_note_en.md index 76a07f1..034996c 100644 --- a/release_note_en.md +++ b/release_note_en.md @@ -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 diff --git a/release_note_ja.md b/release_note_ja.md index 1e659e2..00ca202 100644 --- a/release_note_ja.md +++ b/release_note_ja.md @@ -1,5 +1,7 @@ ( [English](release_note_en.md) / **Japanese** ) +- サブパッケージ `dialect` をリファクタリング: フィールド・メソッドを改名 (#8) + v0.25.0 ======= Nov 3, 2025 diff --git a/spread/edit.go b/spread/edit.go index 1e6b1d6..d60e244 100644 --- a/spread/edit.go +++ b/spread/edit.go @@ -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 == "" {