diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a2e929..33ec8e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Changelog (English) - Upgrade MySQL driver to v1.10.0 - Upgrade Microsoft SQL Server driver to v1.10.0 - Upgrade PostgreSQL driver to v1.12.3 +- Oracle datetime values are now passed as strings and converted using `TO_DATE`/`TO_TIMESTAMP` to avoid timezone-related comparison issues in sijms/go-ora. (#55) v0.27.6 ------- diff --git a/CHANGELOG_ja.md b/CHANGELOG_ja.md index fc0b66a..29e3b00 100644 --- a/CHANGELOG_ja.md +++ b/CHANGELOG_ja.md @@ -7,6 +7,7 @@ Changelog (Japanese) - Upgrade MySQL driver to v1.10.0 - Upgrade Microsoft SQL Server driver to v1.10.0 - Upgrade PostgreSQL driver to v1.12.3 +- sijms/go-ora でのタイムゾーンに関する比較問題を回避するため、Oracle の日時値を文字列として引き渡し、`TO_DATE`/`TO_TIMESTAMP` を使ってコンバートするようにした (#55) v0.27.6 ------- diff --git a/dialect/oracle/main.go b/dialect/oracle/main.go index d779075..8f919f3 100644 --- a/dialect/oracle/main.go +++ b/dialect/oracle/main.go @@ -1,6 +1,8 @@ package sqlbless import ( + "database/sql" + "fmt" "strings" _ "github.com/sijms/go-ora/v2" @@ -8,15 +10,6 @@ import ( "github.com/hymkor/sqlbless/dialect" ) -func oracleTypeNameToConv(typeName string) func(string) (any, error) { - if strings.HasPrefix(typeName, "TIMESTAMP") || typeName == "DATE" { - return func(s string) (any, error) { - return dialect.ParseAnyDateTime(s) - } - } - return nil -} - var oracleSpec = &dialect.Entry{ Usage: "sqlbless oracle://:@:/", SQLForColumns: ` @@ -39,7 +32,62 @@ var oracleSpec = &dialect.Entry{ TypeConverterFor: oracleTypeNameToConv, TableNameField: "tname", ColumnNameField: "name", - PlaceHolder: &dialect.PlaceHolderName{Prefix: ":", Format: "v"}, + PlaceHolder: new(placeHolder), +} + +type withFormat struct { + format string + value any +} + +func oracleTypeNameToConv(typeName string) func(string) (any, error) { + var format string + var layout string + + if typeName == "DATE" { + format = "TO_DATE(:v%d,'YYYY/MM/DD HH24:MI:SS')" + layout = "2006/01/02 15:04:05" + } else if strings.HasPrefix(typeName, "TIMESTAMP") { + format = "TO_TIMESTAMP(:v%d,'YYYY/MM/DD HH24:MI:SS.FF')" + layout = "2006/01/02 15:04:05.999999" + } else { + return nil + } + return func(s string) (any, error) { + dt, err := dialect.ParseAnyDateTime(s) + if err != nil { + return s, nil + } + return &withFormat{ + format: format, + value: dt.Format(layout), + }, nil + } +} + +type placeHolder struct { + values []any +} + +func (ph *placeHolder) Make(v any) string { + if w, ok := v.(*withFormat); ok { + ph.values = append(ph.values, w.value) + return fmt.Sprintf(w.format, len(ph.values)) + } + ph.values = append(ph.values, v) + return fmt.Sprintf(":v%d", len(ph.values)) +} + +func (ph *placeHolder) NormalizeColumnForWhere(value any, columnName string) string { + return columnName +} + +func (ph *placeHolder) Values() (result []any) { + for i, v := range ph.values { + result = append(result, sql.Named(fmt.Sprintf("v%d", i+1), v)) + } + ph.values = ph.values[:0] + return } func init() {