diff --git a/.drone.yml b/.drone.yml index 7a18e0d6..d110fdef 100644 --- a/.drone.yml +++ b/.drone.yml @@ -109,6 +109,46 @@ steps: - push - pull_request +- name: test-oracle + pull: default + image: golang:1.12 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + TEST_ORACLE_HOST: oracle:1521 + TEST_ORACLE_DBNAME: xe + TEST_ORACLE_USERNAME: system + TEST_ORACLE_PASSWORD: oracle + TEST_CACHE_ENABLE: false + commands: + - make test-oracle + - TEST_CACHE_ENABLE=true make test-oracle + when: + event: + - push + - pull_request + +- name: test-oracle-schema + pull: default + image: golang:1.12 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + TEST_ORACLE_SCHEMA: xorm + TEST_ORACLE_HOST: oracle:1521 + TEST_ORACLE_DBNAME: xe + TEST_ORACLE_USERNAME: system + TEST_ORACLE_PASSWORD: oracle + TEST_CACHE_ENABLE: false + SCHEMA: xorm + commands: + - make test-oracle + - TEST_CACHE_ENABLE=true make test-oracle + when: + event: + - push + - pull_request + - name: test-postgres pull: default image: golang:1.12 @@ -292,6 +332,18 @@ services: - tag - pull_request +- name: oracle + pull: default + image: oracleinanutshell/oracle-xe-11g:latest + environment: + ORACLE_ALLOW_REMOTE: true + ORACLE_ENABLE_XDB: true + when: + event: + - push + - tag + - pull_request + - name: cockroach pull: default image: cockroachdb/cockroach:v19.2.4 diff --git a/Makefile b/Makefile index 4cccacd8..b513714b 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,12 @@ TEST_MYSQL_DBNAME ?= xorm_test TEST_MYSQL_USERNAME ?= root TEST_MYSQL_PASSWORD ?= +TEST_ORACLE_HOST ?= oracle:1521 +TEST_ORACLE_SCHEMA ?= +TEST_ORACLE_DBNAME ?= xe +TEST_ORACLE_USERNAME ?= system +TEST_ORACLE_PASSWORD ?= oracle + TEST_PGSQL_HOST ?= pgsql:5432 TEST_PGSQL_SCHEMA ?= TEST_PGSQL_DBNAME ?= xorm_test @@ -124,6 +130,23 @@ misspell-check: fi misspell -error -i unknwon,destory $(GOFILES) +.PHONY: install-instant-client +install-instant-client: +ifeq ("$(PKG_CONFIG_PATH)", ) + wget https://download.oracle.com/otn_software/linux/instantclient/19600/instantclient-basic-linux.x64-19.6.0.0.0dbru.zip + unzip instantclient-basic-linux.x64-19.6.0.0.0dbru.zip -d /usr/local/instantclient + echo "prefixdir=/usr/local/instantclient +libdir=${prefixdir} +includedir=${prefixdir}/sdk/include + +Name: OCI +Description: Oracle database driver +Version: 11.2 +Libs: -L${libdir} -lclntsh +Cflags: -I${includedir}" > /usr/local/instantclient/oci8.pc +export PKG_CONFIG_PATH=/usr/local/instantclient/oci8.pc +endif + .PHONY: test test: go-check $(GO) test $(PACKAGES) @@ -176,6 +199,33 @@ test-mysql\#%: go-check -conn_str="$(TEST_MYSQL_USERNAME):$(TEST_MYSQL_PASSWORD)@tcp($(TEST_MYSQL_HOST))/$(TEST_MYSQL_DBNAME)?charset=$(TEST_MYSQL_CHARSET)" \ -coverprofile=mysql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic +.PHONY: test-oracle +test-oracle: test-godror + +.PNONY: test-oci8 +test-oci8: go-check install-instant-client + $(GO) test $(INTEGRATION_PACKAGES) -v -race -tags=oracle -db=oci8 -schema='$(TEST_ORACLE_SCHEMA)' -cache=$(TEST_CACHE_ENABLE) \ + -conn_str="$(TEST_ORACLE_USERNAME):$(TEST_ORACLE_PASSWORD)@$(TEST_ORACLE_HOST)/$(TEST_ORACLE_DBNAME)" \ + -coverprofile=oracle.$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic + +.PHONY: test-oci8\#% +test-oci8\#%: go-check install-instant-client + $(GO) test $(INTEGRATION_PACKAGES) -v -race -run $* -tags=oracle -db=oci8 -schema='$(TEST_PGSQL_SCHEMA)' -cache=$(TEST_CACHE_ENABLE) \ + -conn_str="postgres://$(TEST_PGSQL_USERNAME):$(TEST_PGSQL_PASSWORD)@$(TEST_PGSQL_HOST)/$(TEST_PGSQL_DBNAME)" \ + -coverprofile=oracle.$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic + +.PHONY: test-godror +test-godror: go-check install-instant-client + $(GO) test $(INTEGRATION_PACKAGES) -v -race -tags=oracle -db=godror -schema='$(TEST_ORACLE_SCHEMA)' -cache=$(TEST_CACHE_ENABLE) \ + -conn_str="oracle://$(TEST_ORACLE_USERNAME):$(TEST_ORACLE_PASSWORD)@$(TEST_ORACLE_HOST)/$(TEST_ORACLE_DBNAME)" \ + -coverprofile=oracle.$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic + +.PHONY: test-godror\#% +test-godror\#%: go-check install-instant-client + $(GO) test $(INTEGRATION_PACKAGES) -v -race -run $* -tags=oracle -db=godror -schema='$(TEST_ORACLE_SCHEMA)' -cache=$(TEST_CACHE_ENABLE) \ + -conn_str="oracle://$(TEST_ORACLE_USERNAME):$(TEST_ORACLE_PASSWORD)@$(TEST_ORACLE_HOST)/$(TEST_ORACLE_DBNAME)" \ + -coverprofile=oracle.$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic + .PNONY: test-postgres test-postgres: go-check $(GO) test $(INTEGRATION_PACKAGES) -v -race -db=postgres -schema='$(TEST_PGSQL_SCHEMA)' -cache=$(TEST_CACHE_ENABLE) \ diff --git a/README.md b/README.md index ed866224..bf85e8be 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,9 @@ Drivers for Go's sql package which currently support database/sql includes: * MsSql - [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) -* Oracle - - [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment) +* Oracle (experiment) + - [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) + - [github.com/godror/godror](https://github.com/godror/godror) ## Installation diff --git a/core/db_test.go b/core/db_test.go index 777ab0ad..046b1e89 100644 --- a/core/db_test.go +++ b/core/db_test.go @@ -92,7 +92,6 @@ func BenchmarkOriQuery(b *testing.B) { if err != nil { b.Error(err) } - //fmt.Println(Id, Name, Title, Age, Alias, NickName) } rows.Close() } diff --git a/core/interface.go b/core/interface.go index a5c8e4e2..b2746ae0 100644 --- a/core/interface.go +++ b/core/interface.go @@ -7,6 +7,7 @@ import ( // Queryer represents an interface to query a SQL to get data from database type Queryer interface { + QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) } diff --git a/dialects/dialect.go b/dialects/dialect.go index dc96f73a..a1299bea 100644 --- a/dialects/dialect.go +++ b/dialects/dialect.go @@ -59,7 +59,7 @@ type Dialect interface { GetTables(queryer core.Queryer, ctx context.Context) ([]*schemas.Table, error) IsTableExist(queryer core.Queryer, ctx context.Context, tableName string) (bool, error) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) - DropTableSQL(tableName string) (string, bool) + DropTableSQL(tableName, autoincrCol string) ([]string, bool) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) IsColumnExist(queryer core.Queryer, ctx context.Context, tableName string, colName string) (bool, error) @@ -100,9 +100,9 @@ func (b *Base) FormatBytes(bs []byte) string { return fmt.Sprintf("0x%x", bs) } -func (db *Base) DropTableSQL(tableName string) (string, bool) { +func (db *Base) DropTableSQL(tableName, autoincrCol string) ([]string, bool) { quote := db.dialect.Quoter().Quote - return fmt.Sprintf("DROP TABLE IF EXISTS %s", quote(tableName)), true + return []string{fmt.Sprintf("DROP TABLE IF EXISTS %s", quote(tableName))}, true } func (db *Base) HasRecords(queryer core.Queryer, ctx context.Context, query string, args ...interface{}) (bool, error) { @@ -207,7 +207,7 @@ func regDrvsNDialects() bool { "pgx": {"postgres", func() Driver { return &pqDriverPgx{} }, func() Dialect { return &postgres{} }}, "sqlite3": {"sqlite3", func() Driver { return &sqlite3Driver{} }, func() Dialect { return &sqlite3{} }}, "oci8": {"oracle", func() Driver { return &oci8Driver{} }, func() Dialect { return &oracle{} }}, - "goracle": {"oracle", func() Driver { return &goracleDriver{} }, func() Dialect { return &oracle{} }}, + "godror": {"oracle", func() Driver { return &godrorDriver{} }, func() Dialect { return &oracle{} }}, } for driverName, v := range providedDrvsNDialects { diff --git a/dialects/mssql.go b/dialects/mssql.go index 8ef924b8..3894e105 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -311,10 +311,10 @@ func (db *mssql) AutoIncrStr() string { return "IDENTITY" } -func (db *mssql) DropTableSQL(tableName string) (string, bool) { - return fmt.Sprintf("IF EXISTS (SELECT * FROM sysobjects WHERE id = "+ +func (db *mssql) DropTableSQL(tableName, autoincrCol string) ([]string, bool) { + return []string{fmt.Sprintf("IF EXISTS (SELECT * FROM sysobjects WHERE id = "+ "object_id(N'%s') and OBJECTPROPERTY(id, N'IsUserTable') = 1) "+ - "DROP TABLE \"%s\"", tableName, tableName), true + "DROP TABLE \"%s\"", tableName, tableName)}, true } func (db *mssql) IndexCheckSQL(tableName, idxName string) (string, []interface{}) { diff --git a/dialects/oracle.go b/dialects/oracle.go index 91eed251..4c6035e6 100644 --- a/dialects/oracle.go +++ b/dialects/oracle.go @@ -6,8 +6,10 @@ package dialects import ( "context" + "database/sql" "errors" "fmt" + "net/url" "regexp" "strconv" "strings" @@ -523,9 +525,9 @@ func (db *oracle) SQLType(c *schemas.Column) string { case schemas.Binary, schemas.VarBinary, schemas.Blob, schemas.TinyBlob, schemas.MediumBlob, schemas.LongBlob, schemas.Bytea: return schemas.Blob case schemas.Time, schemas.DateTime, schemas.TimeStamp: - res = schemas.TimeStamp + res = schemas.Date case schemas.TimeStampz: - res = "TIMESTAMP WITH TIME ZONE" + res = "TIMESTAMP" case schemas.Float, schemas.Double, schemas.Numeric, schemas.Decimal: res = "NUMBER" case schemas.Text, schemas.MediumText, schemas.LongText, schemas.Json: @@ -556,8 +558,14 @@ func (db *oracle) IsReserved(name string) bool { return ok } -func (db *oracle) DropTableSQL(tableName string) (string, bool) { - return fmt.Sprintf("DROP TABLE `%s`", tableName), false +func (db *oracle) DropTableSQL(tableName, autoincrCol string) ([]string, bool) { + var sqls = []string{ + fmt.Sprintf("DROP TABLE %s", db.quoter.Quote(tableName)), + } + if autoincrCol != "" { + sqls = append(sqls, fmt.Sprintf("DROP SEQUENCE %s", seqName(tableName))) + } + return sqls, false } func (db *oracle) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { @@ -589,8 +597,19 @@ func (db *oracle) CreateTableSQL(table *schemas.Table, tableName string) ([]stri sql += " ), " } - sql = sql[:len(sql)-2] + ")" - return []string{sql}, false + var sqls = []string{sql[:len(sql)-2] + ")"} + + if table.AutoIncrColumn() != nil { + var sql2 = fmt.Sprintf(`CREATE sequence %s + minvalue 1 + nomaxvalue + start with 1 + increment by 1 + nocycle + nocache`, seqName(tableName)) + sqls = append(sqls, sql2) + } + return sqls, false } func (db *oracle) SetQuotePolicy(quotePolicy QuotePolicy) { @@ -627,12 +646,35 @@ func (db *oracle) IsColumnExist(queryer core.Queryer, ctx context.Context, table return db.HasRecords(queryer, ctx, query, args...) } -func (db *oracle) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) { - args := []interface{}{tableName} - s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," + - "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1" +func seqName(tableName string) string { + return "SEQ_" + strings.ToUpper(tableName) +} - rows, err := queryer.QueryContext(ctx, s, args...) +func (db *oracle) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) { + //s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," + + // "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1" + + s := `select column_name from user_cons_columns + where constraint_name = (select constraint_name from user_constraints + where table_name = :1 and constraint_type ='P')` + var pkName string + err := queryer.QueryRowContext(ctx, s, tableName).Scan(&pkName) + if err != nil { + if err == sql.ErrNoRows { + err = nil + } + return nil, nil, err + } + + s = `SELECT USER_TAB_COLS.COLUMN_NAME, USER_TAB_COLS.DATA_DEFAULT, USER_TAB_COLS.DATA_TYPE, USER_TAB_COLS.DATA_LENGTH, + USER_TAB_COLS.data_precision, USER_TAB_COLS.data_scale, USER_TAB_COLS.NULLABLE, + user_col_comments.comments + FROM USER_TAB_COLS + LEFT JOIN user_col_comments on user_col_comments.TABLE_NAME=USER_TAB_COLS.TABLE_NAME + AND user_col_comments.COLUMN_NAME=USER_TAB_COLS.COLUMN_NAME + WHERE USER_TAB_COLS.table_name = :1` + + rows, err := queryer.QueryContext(ctx, s, tableName) if err != nil { return nil, nil, err } @@ -644,11 +686,11 @@ func (db *oracle) GetColumns(queryer core.Queryer, ctx context.Context, tableNam col := new(schemas.Column) col.Indexes = make(map[string]int) - var colName, colDefault, nullable, dataType, dataPrecision, dataScale *string + var colName, colDefault, nullable, dataType, dataPrecision, dataScale, comment *string var dataLen int err = rows.Scan(&colName, &colDefault, &dataType, &dataLen, &dataPrecision, - &dataScale, &nullable) + &dataScale, &nullable, &comment) if err != nil { return nil, nil, err } @@ -665,10 +707,26 @@ func (db *oracle) GetColumns(queryer core.Queryer, ctx context.Context, tableNam col.Nullable = false } - var ignore bool + if comment != nil { + col.Comment = *comment + } + if pkName != "" && pkName == col.Name { + col.IsPrimaryKey = true - var dt string - var len1, len2 int + has, err := db.HasRecords(queryer, ctx, "SELECT * FROM USER_SEQUENCES WHERE SEQUENCE_NAME = :1", seqName(tableName)) + if err != nil { + return nil, nil, err + } + if has { + col.IsAutoIncrement = true + } + } + + var ( + ignore bool + dt string + len1, len2 int + ) dts := strings.Split(*dataType, "(") dt = dts[0] if len(dts) > 1 { @@ -690,7 +748,7 @@ func (db *oracle) GetColumns(queryer core.Queryer, ctx context.Context, tableNam col.SQLType = schemas.SQLType{Name: schemas.TimeStampz, DefaultLength: 0, DefaultLength2: 0} case "NUMBER": col.SQLType = schemas.SQLType{Name: schemas.Double, DefaultLength: len1, DefaultLength2: len2} - case "LONG", "LONG RAW": + case "LONG", "LONG RAW", "NCLOB", "CLOB": col.SQLType = schemas.SQLType{Name: schemas.Text, DefaultLength: 0, DefaultLength2: 0} case "RAW": col.SQLType = schemas.SQLType{Name: schemas.Binary, DefaultLength: 0, DefaultLength2: 0} @@ -721,12 +779,16 @@ func (db *oracle) GetColumns(queryer core.Queryer, ctx context.Context, tableNam colSeq = append(colSeq, col.Name) } + /*select * + from user_tab_comments + where Table_Name='用户表' */ + return colSeq, cols, nil } func (db *oracle) GetTables(queryer core.Queryer, ctx context.Context) ([]*schemas.Table, error) { - args := []interface{}{} - s := "SELECT table_name FROM user_tables" + s := "SELECT table_name FROM user_tables WHERE TABLESPACE_NAME = :1 AND table_name NOT LIKE :2" + args := []interface{}{strings.ToUpper(db.uri.User), "%$%"} rows, err := queryer.QueryContext(ctx, s, args...) if err != nil { @@ -802,38 +864,11 @@ func (db *oracle) Filters() []Filter { } } -type goracleDriver struct { +// https://github.com/godror/godror +type godrorDriver struct { } -func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*URI, error) { - db := &URI{DBType: schemas.ORACLE} - dsnPattern := regexp.MustCompile( - `^(?:(?P.*?)(?::(?P.*))?@)?` + // [user[:password]@] - `(?:(?P[^\(]*)(?:\((?P[^\)]*)\))?)?` + // [net[(addr)]] - `\/(?P.*?)` + // /dbname - `(?:\?(?P[^\?]*))?$`) // [?param1=value1¶mN=valueN] - matches := dsnPattern.FindStringSubmatch(dataSourceName) - // tlsConfigRegister := make(map[string]*tls.Config) - names := dsnPattern.SubexpNames() - - for i, match := range matches { - switch names[i] { - case "dbname": - db.DBName = match - } - } - if db.DBName == "" { - return nil, errors.New("dbname is empty") - } - return db, nil -} - -type oci8Driver struct { -} - -// dataSourceName=user/password@ipv4:port/dbname -// dataSourceName=user/password@[ipv6]:port/dbname -func (p *oci8Driver) Parse(driverName, dataSourceName string) (*URI, error) { +func parseNoProtocol(driverName, dataSourceName string) (*URI, error) { db := &URI{DBType: schemas.ORACLE} dsnPattern := regexp.MustCompile( `^(?P.*)\/(?P.*)@` + // user:password@ @@ -852,3 +887,45 @@ func (p *oci8Driver) Parse(driverName, dataSourceName string) (*URI, error) { } return db, nil } + +func parseOracle(driverName, dataSourceName string) (*URI, error) { + var connStr = dataSourceName + if !strings.HasPrefix(connStr, "oracle://") { + return parseNoProtocol(driverName, dataSourceName) + } + + u, err := url.Parse(connStr) + if err != nil { + return nil, err + } + + db := &URI{ + DBType: schemas.ORACLE, + Host: u.Hostname(), + Port: u.Port(), + DBName: strings.TrimLeft(u.RequestURI(), "/"), + } + + if u.User != nil { + db.User = u.User.Username() + db.Passwd, _ = u.User.Password() + } + + if db.DBName == "" { + return nil, errors.New("dbname is empty") + } + return db, nil +} + +func (cfg *godrorDriver) Parse(driverName, dataSourceName string) (*URI, error) { + return parseOracle(driverName, dataSourceName) +} + +type oci8Driver struct { +} + +// dataSourceName=user/password@ipv4:port/dbname +// dataSourceName=user/password@[ipv6]:port/dbname +func (p *oci8Driver) Parse(driverName, dataSourceName string) (*URI, error) { + return parseOracle(driverName, dataSourceName) +} diff --git a/dialects/time.go b/dialects/time.go index b0394745..d66dc8d5 100644 --- a/dialects/time.go +++ b/dialects/time.go @@ -17,7 +17,11 @@ func FormatTime(dialect Dialect, sqlTypeName string, t time.Time) (v interface{} s := t.Format("2006-01-02 15:04:05") // time.RFC3339 v = s[11:19] case schemas.Date: - v = t.Format("2006-01-02") + if dialect.URI().DBType == schemas.ORACLE { + v = t + } else { + v = t.Format("2006-01-02") + } case schemas.DateTime, schemas.TimeStamp, schemas.Varchar: // !DarthPestilane! format time when sqlTypeName is schemas.Varchar. v = t.Format("2006-01-02 15:04:05") case schemas.TimeStampz: diff --git a/go.mod b/go.mod index f6a98156..29ed9fee 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,13 @@ module xorm.io/xorm go 1.11 require ( - github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc - github.com/go-sql-driver/mysql v1.5.0 - github.com/lib/pq v1.7.0 - github.com/mattn/go-sqlite3 v2.0.3+incompatible + github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 + github.com/go-sql-driver/mysql v1.4.1 + github.com/godror/godror v0.12.1 + github.com/kr/pretty v0.1.0 // indirect + github.com/lib/pq v1.0.0 + github.com/mattn/go-oci8 v0.0.4 + github.com/mattn/go-sqlite3 v1.10.0 github.com/stretchr/testify v1.4.0 github.com/syndtr/goleveldb v1.0.0 github.com/ziutek/mymysql v1.5.4 diff --git a/go.sum b/go.sum index 2da01eeb..b84f3cd0 100644 --- a/go.sum +++ b/go.sum @@ -6,29 +6,60 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzq github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godror/godror v0.12.1 h1:NXKbt10PLKKsqP0GBbqXgUzE9O5SvppTeAx6jznuBE8= +github.com/godror/godror v0.12.1/go.mod h1:9MVLtu25FBJBMHkPs0m3Ngf/VmwGcLpM2HS8PlNGw9U= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= -github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= -github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-oci8 v0.0.4 h1:3h8d3VE8buPHoEcApdEoww7Gy3G0SWhwJ0UpniYxBJU= +github.com/mattn/go-oci8 v0.0.4/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= +github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -40,19 +71,47 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0F golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/integrations/cache_test.go b/integrations/cache_test.go index 44e817b1..08f2615b 100644 --- a/integrations/cache_test.go +++ b/integrations/cache_test.go @@ -62,7 +62,7 @@ func TestCacheFind(t *testing.T) { } boxes = make([]MailBox, 0, 2) - assert.NoError(t, testEngine.Alias("a").Where("a.id > -1").Asc("a.id").Find(&boxes)) + assert.NoError(t, testEngine.Alias("a").Where("`a`.`id` > -1").Asc("`a`.`id`").Find(&boxes)) assert.EqualValues(t, 2, len(boxes)) for i, box := range boxes { assert.Equal(t, inserts[i].Id, box.Id) @@ -77,7 +77,7 @@ func TestCacheFind(t *testing.T) { } boxes2 := make([]MailBox4, 0, 2) - assert.NoError(t, testEngine.Table("mail_box").Where("mail_box.id > -1").Asc("mail_box.id").Find(&boxes2)) + assert.NoError(t, testEngine.Table("mail_box").Where("`mail_box`.`id` > -1").Asc("mail_box.id").Find(&boxes2)) assert.EqualValues(t, 2, len(boxes2)) for i, box := range boxes2 { assert.Equal(t, inserts[i].Id, box.Id) @@ -164,14 +164,14 @@ func TestCacheGet(t *testing.T) { assert.NoError(t, err) var box1 MailBox3 - has, err := testEngine.Where("id = ?", inserts[0].Id).Get(&box1) + has, err := testEngine.Where("`id` = ?", inserts[0].Id).Get(&box1) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, "user1", box1.Username) assert.EqualValues(t, "pass1", box1.Password) var box2 MailBox3 - has, err = testEngine.Where("id = ?", inserts[0].Id).Get(&box2) + has, err = testEngine.Where("`id` = ?", inserts[0].Id).Get(&box2) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, "user1", box2.Username) diff --git a/integrations/engine_test.go b/integrations/engine_test.go index 19c5285d..646ddcdf 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -16,10 +16,12 @@ import ( _ "github.com/denisenkom/go-mssqldb" _ "github.com/go-sql-driver/mysql" + _ "github.com/godror/godror" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" - "github.com/stretchr/testify/assert" _ "github.com/ziutek/mymysql/godrv" + + "github.com/stretchr/testify/assert" ) func TestPing(t *testing.T) { @@ -58,8 +60,7 @@ func TestAutoTransaction(t *testing.T) { engine.Transaction(func(session *xorm.Session) (interface{}, error) { _, err := session.Insert(TestTx{Msg: "hi"}) assert.NoError(t, err) - - return nil, nil + return nil, err }) has, err := engine.Exist(&TestTx{Msg: "hi"}) diff --git a/integrations/oracle_test.go b/integrations/oracle_test.go new file mode 100644 index 00000000..56cd3b69 --- /dev/null +++ b/integrations/oracle_test.go @@ -0,0 +1,11 @@ +// Copyright 20190 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build oracle + +package integrations + +import ( + _ "github.com/mattn/go-oci8" +) diff --git a/integrations/session_cond_test.go b/integrations/session_cond_test.go index a0a91cad..493f914c 100644 --- a/integrations/session_cond_test.go +++ b/integrations/session_cond_test.go @@ -37,62 +37,62 @@ func TestBuilder(t *testing.T) { assert.NoError(t, err) var cond Condition - has, err := testEngine.Where(builder.Eq{"col_name": "col1"}).Get(&cond) + has, err := testEngine.Where(builder.Eq{"`col_name`": "col1"}).Get(&cond) assert.NoError(t, err) assert.Equal(t, true, has, "records should exist") - has, err = testEngine.Where(builder.Eq{"col_name": "col1"}. - And(builder.Eq{"op": OpEqual})). + has, err = testEngine.Where(builder.Eq{"`col_name`": "col1"}. + And(builder.Eq{"`op`": OpEqual})). NoAutoCondition(). Get(&cond) assert.NoError(t, err) assert.Equal(t, true, has, "records should exist") - has, err = testEngine.Where(builder.Eq{"col_name": "col1", "op": OpEqual, "value": "1"}). + has, err = testEngine.Where(builder.Eq{"`col_name`": "col1", "`op`": OpEqual, "`value`": "1"}). NoAutoCondition(). Get(&cond) assert.NoError(t, err) assert.Equal(t, true, has, "records should exist") - has, err = testEngine.Where(builder.Eq{"col_name": "col1"}. - And(builder.Neq{"op": OpEqual})). + has, err = testEngine.Where(builder.Eq{"`col_name`": "col1"}. + And(builder.Neq{"`op`": OpEqual})). NoAutoCondition(). Get(&cond) assert.NoError(t, err) assert.Equal(t, false, has, "records should not exist") var conds []Condition - err = testEngine.Where(builder.Eq{"col_name": "col1"}. - And(builder.Eq{"op": OpEqual})). + err = testEngine.Where(builder.Eq{"`col_name`": "col1"}. + And(builder.Eq{"`op`": OpEqual})). Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") conds = make([]Condition, 0) - err = testEngine.Where(builder.Like{"col_name", "col"}).Find(&conds) + err = testEngine.Where(builder.Like{"`col_name`", "col"}).Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") conds = make([]Condition, 0) - err = testEngine.Where(builder.Expr("col_name = ?", "col1")).Find(&conds) + err = testEngine.Where(builder.Expr("`col_name` = ?", "col1")).Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") conds = make([]Condition, 0) - err = testEngine.Where(builder.In("col_name", "col1", "col2")).Find(&conds) + err = testEngine.Where(builder.In("`col_name`", "col1", "col2")).Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") conds = make([]Condition, 0) - err = testEngine.NotIn("col_name", "col1", "col2").Find(&conds) + err = testEngine.NotIn("`col_name`", "col1", "col2").Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 0, len(conds), "records should not exist") // complex condtions var where = builder.NewCond() if true { - where = where.And(builder.Eq{"col_name": "col1"}) - where = where.Or(builder.And(builder.In("col_name", "col1", "col2"), builder.Expr("col_name = ?", "col1"))) + where = where.And(builder.Eq{"`col_name`": "col1"}) + where = where.Or(builder.And(builder.In("`col_name`", "col1", "col2"), builder.Expr("`col_name` = ?", "col1"))) } conds = make([]Condition, 0) diff --git a/integrations/tests.go b/integrations/tests.go index c8219935..a4105257 100644 --- a/integrations/tests.go +++ b/integrations/tests.go @@ -146,13 +146,12 @@ func createEngine(dbType, connStr string) error { if err != nil { return err } - var tableNames = make([]interface{}, 0, len(tables)) for _, table := range tables { - tableNames = append(tableNames, table.Name) - } - if err = testEngine.DropTables(tableNames...); err != nil { - return err + if err = testEngine.DropTables(table); err != nil { + return err + } } + return nil } diff --git a/internal/statements/insert.go b/internal/statements/insert.go index 6cbbbeda..7e894ae2 100644 --- a/internal/statements/insert.go +++ b/internal/statements/insert.go @@ -25,7 +25,7 @@ func (statement *Statement) writeInsertOutput(buf *strings.Builder, table *schem } // GenInsertSQL generates insert beans SQL -func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) (string, []interface{}, error) { +func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) (*builder.BytesWriter, error) { var ( buf = builder.NewWriter() exprs = statement.ExprColumns @@ -34,110 +34,129 @@ func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) ) if _, err := buf.WriteString("INSERT INTO "); err != nil { - return "", nil, err + return nil, err } if err := statement.dialect.Quoter().QuoteTo(buf.Builder, tableName); err != nil { - return "", nil, err + return nil, err } - if len(colNames) <= 0 { + var hasInsertColumns = len(colNames) > 0 + + if !hasInsertColumns && statement.dialect.URI().DBType != schemas.ORACLE { if statement.dialect.URI().DBType == schemas.MYSQL { if _, err := buf.WriteString(" VALUES ()"); err != nil { - return "", nil, err + return nil, err } } else { if err := statement.writeInsertOutput(buf.Builder, table); err != nil { - return "", nil, err + return nil, err } if _, err := buf.WriteString(" DEFAULT VALUES"); err != nil { - return "", nil, err + return nil, err } } } else { if _, err := buf.WriteString(" ("); err != nil { - return "", nil, err + return nil, err + } + + if len(table.AutoIncrement) > 0 && statement.dialect.URI().DBType == schemas.ORACLE { + colNames = append(colNames, table.AutoIncrement) } if err := statement.dialect.Quoter().JoinWrite(buf.Builder, append(colNames, exprs.ColNames...), ","); err != nil { - return "", nil, err + return nil, err } if _, err := buf.WriteString(")"); err != nil { - return "", nil, err + return nil, err } if err := statement.writeInsertOutput(buf.Builder, table); err != nil { - return "", nil, err + return nil, err } if statement.Conds().IsValid() { if _, err := buf.WriteString(" SELECT "); err != nil { - return "", nil, err + return nil, err } if err := statement.WriteArgs(buf, args); err != nil { - return "", nil, err + return nil, err } if len(exprs.Args) > 0 { if _, err := buf.WriteString(","); err != nil { - return "", nil, err + return nil, err } } if err := exprs.WriteArgs(buf); err != nil { - return "", nil, err + return nil, err } if _, err := buf.WriteString(" FROM "); err != nil { - return "", nil, err + return nil, err } if err := statement.dialect.Quoter().QuoteTo(buf.Builder, tableName); err != nil { - return "", nil, err + return nil, err } if _, err := buf.WriteString(" WHERE "); err != nil { - return "", nil, err + return nil, err } if err := statement.Conds().WriteTo(buf); err != nil { - return "", nil, err + return nil, err } } else { if _, err := buf.WriteString(" VALUES ("); err != nil { - return "", nil, err + return nil, err } if err := statement.WriteArgs(buf, args); err != nil { - return "", nil, err + return nil, err + } + + // Insert tablename (id) Values(seq_tablename.nextval) + if len(table.AutoIncrement) > 0 && statement.dialect.URI().DBType == schemas.ORACLE { + if hasInsertColumns { + if _, err := buf.WriteString(","); err != nil { + return nil, err + } + } + if _, err := buf.WriteString("seq_" + tableName + ".nextval"); err != nil { + return nil, err + } } if len(exprs.Args) > 0 { if _, err := buf.WriteString(","); err != nil { - return "", nil, err + return nil, err } } if err := exprs.WriteArgs(buf); err != nil { - return "", nil, err + return nil, err } if _, err := buf.WriteString(")"); err != nil { - return "", nil, err + return nil, err } } } - if len(table.AutoIncrement) > 0 && statement.dialect.URI().DBType == schemas.POSTGRES { + if len(table.AutoIncrement) > 0 && (statement.dialect.URI().DBType == schemas.POSTGRES || + statement.dialect.URI().DBType == schemas.ORACLE) { if _, err := buf.WriteString(" RETURNING "); err != nil { - return "", nil, err + return nil, err } if err := statement.dialect.Quoter().QuoteTo(buf.Builder, table.AutoIncrement); err != nil { - return "", nil, err + return nil, err } } - return buf.String(), buf.Args(), nil + return buf, nil } // GenInsertMapSQL generates insert map SQL diff --git a/internal/statements/statement.go b/internal/statements/statement.go index ed7bdaeb..f64fd085 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -90,6 +90,10 @@ func NewStatement(dialect dialects.Dialect, tagParser *tags.Parser, defaultTimeZ return statement } +func (statement *Statement) Dialect() dialects.Dialect { + return statement.dialect +} + func (statement *Statement) SetTableName(tableName string) { statement.tableName = tableName } diff --git a/session_insert.go b/session_insert.go index 1d99167b..1ad8cce6 100644 --- a/session_insert.go +++ b/session_insert.go @@ -119,6 +119,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error colMultiPlaces []string args []interface{} cols []*schemas.Column + insertCnt int ) for i := 0; i < size; i++ { @@ -151,6 +152,14 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error } fieldValue := *ptrFieldValue if col.IsAutoIncrement && utils.IsZero(fieldValue.Interface()) { + if session.engine.dialect.URI().DBType == schemas.ORACLE { + if i == 0 { + colNames = append(colNames, col.Name) + cols = append(cols, col) + } + colPlaces = append(colPlaces, fmt.Sprintf("seq_"+tableName+".nextval + %d", insertCnt)) + insertCnt++ + } continue } if col.MapType == schemas.ONLYFROMDB { @@ -280,7 +289,9 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { if err := session.statement.SetRefBean(bean); err != nil { return 0, err } - if len(session.statement.TableName()) <= 0 { + + var tableName = session.statement.TableName() + if tableName == "" { return 0, ErrTableNotFound } @@ -294,7 +305,6 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { processor.BeforeInsert() } - var tableName = session.statement.TableName() table := session.statement.RefTable colNames, args, err := session.genInsertColumns(bean) @@ -302,7 +312,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { return 0, err } - sqlStr, args, err := session.statement.GenInsertSQL(colNames, args) + buf, err := session.statement.GenInsertSQL(colNames, args) if err != nil { return 0, err } @@ -335,14 +345,28 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { cleanupProcessorsClosures(&session.afterClosures) // cleanup after used } + var dialect = session.statement.Dialect() + // for postgres, many of them didn't implement lastInsertId, so we should // implemented it ourself. - if session.engine.dialect.URI().DBType == schemas.ORACLE && len(table.AutoIncrement) > 0 { - res, err := session.queryBytes("select seq_atable.currval from dual", args...) + if len(table.AutoIncrement) > 0 && (dialect.URI().DBType == schemas.POSTGRES || + dialect.URI().DBType == schemas.MSSQL || + dialect.URI().DBType == schemas.ORACLE) { + var id int64 + if dialect.URI().DBType == schemas.ORACLE { + if _, err := buf.WriteString(" INTO :var_name"); err != nil { + return 0, err + } + buf.Append(&id) + } + + res, err := session.queryBytes(buf.String(), buf.Args()...) if err != nil { return 0, err } + fmt.Println("=====", id) + defer handleAfterInsertProcessorFunc(bean) session.cacheInsert(tableName) @@ -356,56 +380,16 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { } } - if len(res) < 1 { - return 0, errors.New("insert no error but not returned id") - } - - idByte := res[0][table.AutoIncrement] - id, err := strconv.ParseInt(string(idByte), 10, 64) - if err != nil || id <= 0 { - return 1, err - } - - aiValue, err := table.AutoIncrColumn().ValueOf(bean) - if err != nil { - session.engine.logger.Errorf("%v", err) - } - - if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() { - return 1, nil - } - - aiValue.Set(int64ToIntValue(id, aiValue.Type())) - - return 1, nil - } else if len(table.AutoIncrement) > 0 && (session.engine.dialect.URI().DBType == schemas.POSTGRES || - session.engine.dialect.URI().DBType == schemas.MSSQL) { - res, err := session.queryBytes(sqlStr, args...) - - if err != nil { - return 0, err - } - defer handleAfterInsertProcessorFunc(bean) - - session.cacheInsert(tableName) - - if table.Version != "" && session.statement.CheckVersion { - verValue, err := table.VersionColumn().ValueOf(bean) - if err != nil { - session.engine.logger.Errorf("%v", err) - } else if verValue.IsValid() && verValue.CanSet() { - session.incrVersionFieldValue(verValue) + if dialect.URI().DBType != schemas.ORACLE { + if len(res) < 1 { + return 0, errors.New("insert successfully but not returned id") } - } - if len(res) < 1 { - return 0, errors.New("insert successfully but not returned id") - } - - idByte := res[0][table.AutoIncrement] - id, err := strconv.ParseInt(string(idByte), 10, 64) - if err != nil || id <= 0 { - return 1, err + idByte := res[0][table.AutoIncrement] + id, err = strconv.ParseInt(string(idByte), 10, 64) + if err != nil || id <= 0 { + return 1, err + } } aiValue, err := table.AutoIncrColumn().ValueOf(bean) @@ -422,7 +406,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { return 1, nil } - res, err := session.exec(sqlStr, args...) + res, err := session.exec(buf.String(), buf.Args()...) if err != nil { return 0, err } @@ -537,10 +521,14 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac } } - if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { + if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime { // if time is non-empty, then set to auto time val, t := session.engine.nowTime(col) - args = append(args, val) + if session.engine.dialect.URI().DBType == schemas.ORACLE { + args = append(args, t) + } else { + args = append(args, val) + } var colName = col.Name session.afterClosures = append(session.afterClosures, func(bean interface{}) { diff --git a/session_schema.go b/session_schema.go index 9ccf8abe..7f02e453 100644 --- a/session_schema.go +++ b/session_schema.go @@ -131,8 +131,33 @@ func (session *Session) DropTable(beanOrTableName interface{}) error { } func (session *Session) dropTable(beanOrTableName interface{}) error { - tableName := session.engine.TableName(beanOrTableName) - sqlStr, checkIfExist := session.engine.dialect.DropTableSQL(session.engine.TableName(tableName, true)) + var tableName, autoIncrementCol string + switch beanOrTableName.(type) { + case *schemas.Table: + table := beanOrTableName.(*schemas.Table) + tableName = table.Name + if table.AutoIncrColumn() != nil { + autoIncrementCol = table.AutoIncrColumn().Name + } + case string: + tableName = beanOrTableName.(string) + default: + v := utils.ReflectValue(beanOrTableName) + table, err := session.engine.tagParser.ParseWithCache(v) + if err != nil { + return err + } + if session.statement.AltTableName != "" { + tableName = session.statement.AltTableName + } else { + tableName = table.Name + } + if table.AutoIncrColumn() != nil { + autoIncrementCol = table.AutoIncrColumn().Name + } + } + + sqlStrs, checkIfExist := session.engine.dialect.DropTableSQL(tableName, autoIncrementCol) if !checkIfExist { exist, err := session.engine.dialect.IsTableExist(session.getQueryer(), session.ctx, tableName) if err != nil { @@ -142,8 +167,11 @@ func (session *Session) dropTable(beanOrTableName interface{}) error { } if checkIfExist { - _, err := session.exec(sqlStr) - return err + for _, sqlStr := range sqlStrs { + if _, err := session.exec(sqlStr); err != nil { + return err + } + } } return nil }