Skip to content

Commit

Permalink
Support Oracle dialect (#995)
Browse files Browse the repository at this point in the history
* feat: support Oracle dialect

* feat: update README.md
  • Loading branch information
mattn authored Oct 26, 2024
1 parent 7c61172 commit a6f81ee
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 4 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SQL-first Golang ORM for PostgreSQL, MySQL, MSSQL, and SQLite
# SQL-first Golang ORM for PostgreSQL, MySQL, MSSQL, SQLite and Oracle

[![build workflow](https://github.com/uptrace/bun/actions/workflows/build.yml/badge.svg)](https://github.com/uptrace/bun/actions)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/uptrace/bun)](https://pkg.go.dev/github.com/uptrace/bun)
Expand All @@ -19,6 +19,7 @@
[MySQL](https://bun.uptrace.dev/guide/drivers.html#mysql) (including MariaDB),
[MSSQL](https://bun.uptrace.dev/guide/drivers.html#mssql),
[SQLite](https://bun.uptrace.dev/guide/drivers.html#sqlite).
[Oracle](https://bun.uptrace.dev/guide/drivers.html#oracle).
- [ORM-like](/example/basic/) experience using good old SQL. Bun supports structs, map, scalars, and
slices of map/structs/scalars.
- [Bulk inserts](https://bun.uptrace.dev/guide/query-insert.html).
Expand Down
3 changes: 3 additions & 0 deletions dialect/dialect.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ func (n Name) String() string {
return "mysql"
case MSSQL:
return "mssql"
case Oracle:
return "oracle"
default:
return "invalid"
}
Expand All @@ -23,4 +25,5 @@ const (
SQLite
MySQL
MSSQL
Oracle
)
126 changes: 126 additions & 0 deletions dialect/oracledialect/dialect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package oracledialect

import (
"database/sql"
"encoding/hex"
"fmt"
"time"

"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect"
"github.com/uptrace/bun/dialect/feature"
"github.com/uptrace/bun/dialect/sqltype"
"github.com/uptrace/bun/schema"
)

func init() {
if Version() != bun.Version() {
panic(fmt.Errorf("oracledialect and Bun must have the same version: v%s != v%s",
Version(), bun.Version()))
}
}

type Dialect struct {
schema.BaseDialect

tables *schema.Tables
features feature.Feature
}

func New() *Dialect {
d := new(Dialect)
d.tables = schema.NewTables(d)
d.features = feature.CTE |
feature.WithValues |
feature.Returning |
//feature.InsertReturning | // TODO
//feature.Output | // TODO
feature.InsertOnConflict |
//feature.TableNotExists |
feature.SelectExists |
feature.AutoIncrement |
feature.CompositeIn
return d
}

func (d *Dialect) Init(*sql.DB) {}

func (d *Dialect) Name() dialect.Name {
return dialect.Oracle
}

func (d *Dialect) Features() feature.Feature {
return d.features
}

func (d *Dialect) Tables() *schema.Tables {
return d.tables
}

func (d *Dialect) OnTable(table *schema.Table) {
for _, field := range table.FieldMap {
d.onField(field)
}
}

func (d *Dialect) onField(field *schema.Field) {
field.DiscoveredSQLType = fieldSQLType(field)
}

func (d *Dialect) IdentQuote() byte {
return '"'
}

func (*Dialect) AppendBytes(b, bs []byte) []byte {
if bs == nil {
return dialect.AppendNull(b)
}

b = append(b, "0x"...)

s := len(b)
b = append(b, make([]byte, hex.EncodedLen(len(bs)))...)
hex.Encode(b[s:], bs)

return b
}

func (d *Dialect) DefaultVarcharLen() int {
return 255
}

func (d *Dialect) AppendSequence(b []byte, table *schema.Table, field *schema.Field) []byte {
return append(b, " GENERATED BY DEFAULT AS IDENTITY"...)
}

func fieldSQLType(field *schema.Field) string {
switch field.DiscoveredSQLType {
case sqltype.SmallInt, sqltype.BigInt:
// INTEGER PRIMARY KEY is an alias for the ROWID.
// It is safe to convert all ints to INTEGER, because SQLite types don't have size.
return sqltype.Integer
case sqltype.Boolean:
return "number(1,0)"
default:
return field.DiscoveredSQLType
}
}

func (*Dialect) AppendTime(b []byte, tm time.Time) []byte {
if tm.IsZero() {
b = append(b, "NULL"...)
return b
}
b = append(b, "TO_TIMESTAMP('"...)
b = tm.AppendFormat(b, "2006-01-02 15:04:05.999999")
b = append(b, "', 'YYYY-MM-DD HH24:MI:SS.FF')"...)
return b
}

func (*Dialect) AppendBool(b []byte, v bool) []byte {
if v {
return append(b, '1')
}

return append(b, '0')
}
11 changes: 11 additions & 0 deletions dialect/oracledialect/scan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package oracledialect

import (
"reflect"

"github.com/uptrace/bun/schema"
)

func scanner(typ reflect.Type) schema.ScannerFunc {

Check failure on line 9 in dialect/oracledialect/scan.go

View workflow job for this annotation

GitHub Actions / lint

func `scanner` is unused (unused)
return schema.Scanner(typ)
}
6 changes: 6 additions & 0 deletions dialect/oracledialect/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package oracledialect

// Version is the current release version.
func Version() string {
return "1.2.1"
}
7 changes: 6 additions & 1 deletion query_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"time"

"github.com/uptrace/bun/dialect"
"github.com/uptrace/bun/dialect/feature"
"github.com/uptrace/bun/internal"
"github.com/uptrace/bun/schema"
Expand Down Expand Up @@ -418,7 +419,11 @@ func (q *baseQuery) _appendTables(
} else {
b = fmter.AppendQuery(b, string(q.table.SQLNameForSelects))
if withAlias && q.table.SQLAlias != q.table.SQLNameForSelects {
b = append(b, " AS "...)
if q.db.dialect.Name() == dialect.Oracle {
b = append(b, ' ')
} else {
b = append(b, " AS "...)
}
b = append(b, q.table.SQLAlias...)
}
}
Expand Down
9 changes: 7 additions & 2 deletions query_table_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strconv"
"strings"

"github.com/uptrace/bun/dialect"
"github.com/uptrace/bun/dialect/feature"
"github.com/uptrace/bun/dialect/sqltype"
"github.com/uptrace/bun/internal"
Expand Down Expand Up @@ -165,7 +166,7 @@ func (q *CreateTableQuery) AppendQuery(fmter schema.Formatter, b []byte) (_ []by
b = append(b, field.SQLName...)
b = append(b, " "...)
b = q.appendSQLType(b, field)
if field.NotNull {
if field.NotNull && q.db.dialect.Name() != dialect.Oracle {
b = append(b, " NOT NULL"...)
}

Expand Down Expand Up @@ -246,7 +247,11 @@ func (q *CreateTableQuery) appendSQLType(b []byte, field *schema.Field) []byte {
return append(b, field.CreateTableSQLType...)
}

b = append(b, sqltype.VarChar...)
if q.db.dialect.Name() == dialect.Oracle {
b = append(b, "VARCHAR2"...)
} else {
b = append(b, sqltype.VarChar...)
}
b = append(b, "("...)
b = strconv.AppendInt(b, int64(q.varchar), 10)
b = append(b, ")"...)
Expand Down

0 comments on commit a6f81ee

Please sign in to comment.