From bb9133ab9d7bfd7fe9a85115d80cf940dc3ea2cb Mon Sep 17 00:00:00 2001
From: hailaz <739476267@qq.com>
Date: Thu, 4 Dec 2025 11:35:32 +0800
Subject: [PATCH 1/4] fix: v2.9.6 (#4537)
---
README.MD | 2 +-
cmd/gf/go.mod | 14 +++++++-------
contrib/config/apollo/go.mod | 2 +-
contrib/config/consul/go.mod | 2 +-
contrib/config/kubecm/go.mod | 2 +-
contrib/config/nacos/go.mod | 2 +-
contrib/config/polaris/go.mod | 2 +-
contrib/drivers/clickhouse/go.mod | 2 +-
contrib/drivers/dm/go.mod | 2 +-
contrib/drivers/mssql/go.mod | 2 +-
contrib/drivers/mysql/go.mod | 2 +-
contrib/drivers/oracle/go.mod | 2 +-
contrib/drivers/pgsql/go.mod | 2 +-
contrib/drivers/sqlite/go.mod | 2 +-
contrib/drivers/sqlitecgo/go.mod | 2 +-
contrib/metric/otelmetric/go.mod | 2 +-
contrib/nosql/redis/go.mod | 2 +-
contrib/registry/consul/go.mod | 2 +-
contrib/registry/etcd/go.mod | 2 +-
contrib/registry/file/go.mod | 2 +-
contrib/registry/nacos/go.mod | 2 +-
contrib/registry/polaris/go.mod | 2 +-
contrib/registry/zookeeper/go.mod | 2 +-
contrib/rpc/grpcx/go.mod | 4 ++--
contrib/sdk/httpclient/go.mod | 2 +-
contrib/trace/otlpgrpc/go.mod | 2 +-
contrib/trace/otlphttp/go.mod | 2 +-
version.go | 2 +-
28 files changed, 35 insertions(+), 35 deletions(-)
diff --git a/README.MD b/README.MD
index 4a879635e..8eebc5ec9 100644
--- a/README.MD
+++ b/README.MD
@@ -38,7 +38,7 @@ A powerful framework for faster, easier, and more efficient project development.
💖 [Thanks to all the contributors who made GoFrame possible](https://github.com/gogf/gf/graphs/contributors) 💖
-
+
## License
diff --git a/cmd/gf/go.mod b/cmd/gf/go.mod
index 1b1f29b01..a46f18746 100644
--- a/cmd/gf/go.mod
+++ b/cmd/gf/go.mod
@@ -3,13 +3,13 @@ module github.com/gogf/gf/cmd/gf/v2
go 1.23.0
require (
- github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.9.5
- github.com/gogf/gf/contrib/drivers/mssql/v2 v2.9.5
- github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.5
- github.com/gogf/gf/contrib/drivers/oracle/v2 v2.9.5
- github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.9.5
- github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.9.5
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.9.6
+ github.com/gogf/gf/contrib/drivers/mssql/v2 v2.9.6
+ github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.6
+ github.com/gogf/gf/contrib/drivers/oracle/v2 v2.9.6
+ github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.9.6
+ github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.9.6
+ github.com/gogf/gf/v2 v2.9.6
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f
github.com/olekukonko/tablewriter v1.1.0
github.com/schollz/progressbar/v3 v3.15.0
diff --git a/contrib/config/apollo/go.mod b/contrib/config/apollo/go.mod
index 90e22c357..9d13209e8 100644
--- a/contrib/config/apollo/go.mod
+++ b/contrib/config/apollo/go.mod
@@ -4,7 +4,7 @@ go 1.23.0
require (
github.com/apolloconfig/agollo/v4 v4.3.1
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
)
require (
diff --git a/contrib/config/consul/go.mod b/contrib/config/consul/go.mod
index 20728b658..0244e1e89 100644
--- a/contrib/config/consul/go.mod
+++ b/contrib/config/consul/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/config/consul/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/hashicorp/consul/api v1.24.0
github.com/hashicorp/go-cleanhttp v0.5.2
)
diff --git a/contrib/config/kubecm/go.mod b/contrib/config/kubecm/go.mod
index d2dda1651..d16e11550 100644
--- a/contrib/config/kubecm/go.mod
+++ b/contrib/config/kubecm/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/config/kubecm/v2
go 1.24.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
k8s.io/api v0.33.4
k8s.io/apimachinery v0.33.4
k8s.io/client-go v0.33.4
diff --git a/contrib/config/nacos/go.mod b/contrib/config/nacos/go.mod
index 76187eb87..d692cf7ee 100644
--- a/contrib/config/nacos/go.mod
+++ b/contrib/config/nacos/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/config/nacos/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/nacos-group/nacos-sdk-go/v2 v2.3.3
)
diff --git a/contrib/config/polaris/go.mod b/contrib/config/polaris/go.mod
index d3bc7f564..9b0e00a35 100644
--- a/contrib/config/polaris/go.mod
+++ b/contrib/config/polaris/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/config/polaris/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/polarismesh/polaris-go v1.6.1
)
diff --git a/contrib/drivers/clickhouse/go.mod b/contrib/drivers/clickhouse/go.mod
index ba86211a3..8920bad47 100644
--- a/contrib/drivers/clickhouse/go.mod
+++ b/contrib/drivers/clickhouse/go.mod
@@ -4,7 +4,7 @@ go 1.23.0
require (
github.com/ClickHouse/clickhouse-go/v2 v2.0.15
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/google/uuid v1.6.0
github.com/shopspring/decimal v1.3.1
)
diff --git a/contrib/drivers/dm/go.mod b/contrib/drivers/dm/go.mod
index 864d53dba..da297a72b 100644
--- a/contrib/drivers/dm/go.mod
+++ b/contrib/drivers/dm/go.mod
@@ -6,7 +6,7 @@ replace github.com/gogf/gf/v2 => ../../../
require (
gitee.com/chunanyong/dm v1.8.12
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
)
require (
diff --git a/contrib/drivers/mssql/go.mod b/contrib/drivers/mssql/go.mod
index d071b7a8c..cc77d48ed 100644
--- a/contrib/drivers/mssql/go.mod
+++ b/contrib/drivers/mssql/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/drivers/mssql/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/microsoft/go-mssqldb v1.7.1
)
diff --git a/contrib/drivers/mysql/go.mod b/contrib/drivers/mysql/go.mod
index f741a7e9b..5b36bd192 100644
--- a/contrib/drivers/mysql/go.mod
+++ b/contrib/drivers/mysql/go.mod
@@ -4,7 +4,7 @@ go 1.23.0
require (
github.com/go-sql-driver/mysql v1.7.1
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
)
require (
diff --git a/contrib/drivers/oracle/go.mod b/contrib/drivers/oracle/go.mod
index 229c678ff..578c5e616 100644
--- a/contrib/drivers/oracle/go.mod
+++ b/contrib/drivers/oracle/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/drivers/oracle/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/sijms/go-ora/v2 v2.7.10
)
diff --git a/contrib/drivers/pgsql/go.mod b/contrib/drivers/pgsql/go.mod
index 6b37c177b..5676c2842 100644
--- a/contrib/drivers/pgsql/go.mod
+++ b/contrib/drivers/pgsql/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/drivers/pgsql/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/lib/pq v1.10.9
)
diff --git a/contrib/drivers/sqlite/go.mod b/contrib/drivers/sqlite/go.mod
index f57729db3..fb479c02d 100644
--- a/contrib/drivers/sqlite/go.mod
+++ b/contrib/drivers/sqlite/go.mod
@@ -4,7 +4,7 @@ go 1.23.0
require (
github.com/glebarez/go-sqlite v1.21.2
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
)
require (
diff --git a/contrib/drivers/sqlitecgo/go.mod b/contrib/drivers/sqlitecgo/go.mod
index 22e68b2de..5ebc70529 100644
--- a/contrib/drivers/sqlitecgo/go.mod
+++ b/contrib/drivers/sqlitecgo/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/drivers/sqlitecgo/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/mattn/go-sqlite3 v1.14.17
)
diff --git a/contrib/metric/otelmetric/go.mod b/contrib/metric/otelmetric/go.mod
index 96c35b7ba..05059cbab 100644
--- a/contrib/metric/otelmetric/go.mod
+++ b/contrib/metric/otelmetric/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/metric/otelmetric/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/prometheus/client_golang v1.23.2
go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0
go.opentelemetry.io/otel v1.38.0
diff --git a/contrib/nosql/redis/go.mod b/contrib/nosql/redis/go.mod
index bee27405d..1b7d12652 100644
--- a/contrib/nosql/redis/go.mod
+++ b/contrib/nosql/redis/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/nosql/redis/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/redis/go-redis/v9 v9.12.1
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel/trace v1.38.0
diff --git a/contrib/registry/consul/go.mod b/contrib/registry/consul/go.mod
index 8bb65ccaa..b3e89e802 100644
--- a/contrib/registry/consul/go.mod
+++ b/contrib/registry/consul/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/registry/consul/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/hashicorp/consul/api v1.26.1
)
diff --git a/contrib/registry/etcd/go.mod b/contrib/registry/etcd/go.mod
index 389704ebc..0b87ad0be 100644
--- a/contrib/registry/etcd/go.mod
+++ b/contrib/registry/etcd/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/registry/etcd/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
go.etcd.io/etcd/client/v3 v3.5.17
google.golang.org/grpc v1.59.0
)
diff --git a/contrib/registry/file/go.mod b/contrib/registry/file/go.mod
index 58ccf4fbc..153eedb78 100644
--- a/contrib/registry/file/go.mod
+++ b/contrib/registry/file/go.mod
@@ -2,7 +2,7 @@ module github.com/gogf/gf/contrib/registry/file/v2
go 1.23.0
-require github.com/gogf/gf/v2 v2.9.5
+require github.com/gogf/gf/v2 v2.9.6
require (
github.com/BurntSushi/toml v1.5.0 // indirect
diff --git a/contrib/registry/nacos/go.mod b/contrib/registry/nacos/go.mod
index 1c3e28b0a..1068b7600 100644
--- a/contrib/registry/nacos/go.mod
+++ b/contrib/registry/nacos/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/registry/nacos/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/nacos-group/nacos-sdk-go/v2 v2.3.3
)
diff --git a/contrib/registry/polaris/go.mod b/contrib/registry/polaris/go.mod
index 9fa657b64..6980d6502 100644
--- a/contrib/registry/polaris/go.mod
+++ b/contrib/registry/polaris/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/registry/polaris/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
github.com/polarismesh/polaris-go v1.6.1
)
diff --git a/contrib/registry/zookeeper/go.mod b/contrib/registry/zookeeper/go.mod
index fdca1dd3a..be03cd1e5 100644
--- a/contrib/registry/zookeeper/go.mod
+++ b/contrib/registry/zookeeper/go.mod
@@ -4,7 +4,7 @@ go 1.23.0
require (
github.com/go-zookeeper/zk v1.0.3
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
golang.org/x/sync v0.16.0
)
diff --git a/contrib/rpc/grpcx/go.mod b/contrib/rpc/grpcx/go.mod
index 5bc98ceb9..2df2b6ca5 100644
--- a/contrib/rpc/grpcx/go.mod
+++ b/contrib/rpc/grpcx/go.mod
@@ -3,8 +3,8 @@ module github.com/gogf/gf/contrib/rpc/grpcx/v2
go 1.23.0
require (
- github.com/gogf/gf/contrib/registry/file/v2 v2.9.5
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/contrib/registry/file/v2 v2.9.6
+ github.com/gogf/gf/v2 v2.9.6
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel/trace v1.38.0
google.golang.org/grpc v1.64.1
diff --git a/contrib/sdk/httpclient/go.mod b/contrib/sdk/httpclient/go.mod
index 0780cf4b0..eb3221bb0 100644
--- a/contrib/sdk/httpclient/go.mod
+++ b/contrib/sdk/httpclient/go.mod
@@ -2,7 +2,7 @@ module github.com/gogf/gf/contrib/sdk/httpclient/v2
go 1.23.0
-require github.com/gogf/gf/v2 v2.9.5
+require github.com/gogf/gf/v2 v2.9.6
require (
github.com/BurntSushi/toml v1.5.0 // indirect
diff --git a/contrib/trace/otlpgrpc/go.mod b/contrib/trace/otlpgrpc/go.mod
index 0eaa451cd..dacbc96ee 100644
--- a/contrib/trace/otlpgrpc/go.mod
+++ b/contrib/trace/otlpgrpc/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/trace/otlpgrpc/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
diff --git a/contrib/trace/otlphttp/go.mod b/contrib/trace/otlphttp/go.mod
index 31a8071b8..595b34ed1 100644
--- a/contrib/trace/otlphttp/go.mod
+++ b/contrib/trace/otlphttp/go.mod
@@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/trace/otlphttp/v2
go 1.23.0
require (
- github.com/gogf/gf/v2 v2.9.5
+ github.com/gogf/gf/v2 v2.9.6
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0
diff --git a/version.go b/version.go
index fbcf4ce79..8dbd72f20 100644
--- a/version.go
+++ b/version.go
@@ -2,5 +2,5 @@ package gf
const (
// VERSION is the current GoFrame version.
- VERSION = "v2.9.5"
+ VERSION = "v2.9.6"
)
From 1650aab340052872438746c97d7b34ce5b495509 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 4 Dec 2025 11:44:05 +0800
Subject: [PATCH 2/4] fix: update gf cli to v2.9.6 (#4538)
Automated changes by
[create-pull-request](https://github.com/peter-evans/create-pull-request)
GitHub action
Co-authored-by: hailaz
---
cmd/gf/go.sum | 14 ++++++++++++++
cmd/gf/internal/cmd/testdata/build/varmap/go.mod | 2 +-
cmd/gf/internal/cmd/testdata/build/varmap/go.sum | 4 ++--
3 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/cmd/gf/go.sum b/cmd/gf/go.sum
index dd9c07361..bc47437f9 100644
--- a/cmd/gf/go.sum
+++ b/cmd/gf/go.sum
@@ -46,6 +46,20 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.9.6 h1:rJzRmA5TGWMeKDebdDosYODoUrMUHqfA5pWO1MBC5b0=
+github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.9.6/go.mod h1:u+bUsuftf8qpKpPZPdOFhzh3F5KQzo6Wqa9JFTCLFqg=
+github.com/gogf/gf/contrib/drivers/mssql/v2 v2.9.6 h1:3QTlIbSdrVYvRMNUF6nckspA6Eh5Uy2NqwB3/auxIwk=
+github.com/gogf/gf/contrib/drivers/mssql/v2 v2.9.6/go.mod h1:oMteYgkWImPpUVe1aqPKtZ8jX1dG3v60lS7IA87MwFQ=
+github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.6 h1:BY1ThxMo0bTx2P18PuCe57ARmjHuEithSdob/CbH/rw=
+github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.6/go.mod h1:v/jKO9JJdLctlPlnUSnnG0SNSEpElM51Qx3KoI5crkU=
+github.com/gogf/gf/contrib/drivers/oracle/v2 v2.9.6 h1:12+sWI/hm1D4KxG+1FMZpfoU3PwtSLJ9KbLNa20roLg=
+github.com/gogf/gf/contrib/drivers/oracle/v2 v2.9.6/go.mod h1:gjjhgxqjafnORK0F4Fa5W8TJlassw7svKy7RFj5GKss=
+github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.9.6 h1:LG/bTOJEpyNu6+IdREqFyi6J8LdZIeceeyxhuyV58LQ=
+github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.9.6/go.mod h1:Ekd5IgUGyBlbfqKD/69hkIL9vHF6F4V2FeEP3h/pH08=
+github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.9.6 h1:3QZvWIlz3dLjNELQU+5ZZZWuzEx9gsRFLU+qIKVUG6M=
+github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.9.6/go.mod h1:7EEAe8UYI5dLeuwCWN3HgC62OhjIYbkynaoavw1U/k4=
+github.com/gogf/gf/v2 v2.9.6 h1:fQ6uPtS1Ra8qY+OuzPPZTlgksJ4eOXmTZ1/a2l3Idog=
+github.com/gogf/gf/v2 v2.9.6/go.mod h1:Svl1N+E8G/QshU2DUbh/3J/AJauqCgUnxHurXWR4Qx0=
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f h1:7xfXR/BhG3JDqO1s45n65Oyx9t4E/UqDOXep6jXdLCM=
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f/go.mod h1:HnYoio6S7VaFJdryKcD/r9HgX+4QzYfr00XiXUo/xz0=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
diff --git a/cmd/gf/internal/cmd/testdata/build/varmap/go.mod b/cmd/gf/internal/cmd/testdata/build/varmap/go.mod
index 05d198d5f..87b3f34f8 100644
--- a/cmd/gf/internal/cmd/testdata/build/varmap/go.mod
+++ b/cmd/gf/internal/cmd/testdata/build/varmap/go.mod
@@ -4,7 +4,7 @@ go 1.23.0
toolchain go1.24.6
-require github.com/gogf/gf/v2 v2.9.5
+require github.com/gogf/gf/v2 v2.9.6
require (
go.opentelemetry.io/otel v1.38.0 // indirect
diff --git a/cmd/gf/internal/cmd/testdata/build/varmap/go.sum b/cmd/gf/internal/cmd/testdata/build/varmap/go.sum
index 16671703d..949b5632f 100644
--- a/cmd/gf/internal/cmd/testdata/build/varmap/go.sum
+++ b/cmd/gf/internal/cmd/testdata/build/varmap/go.sum
@@ -4,8 +4,8 @@ github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyM
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
-github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
+github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU=
+github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
From 6e0ba551f973879842bb1a565574ffb81319c8d3 Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Thu, 4 Dec 2025 14:27:01 +0800
Subject: [PATCH 3/4] ci(release): disable go module caching in release
workflow (#4539)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Resolves TODO comment requesting cache to be disabled for the
`actions/setup-go` step in the release workflow.
- Add `cache: false` to `actions/setup-go@v5` configuration
- Remove the now-completed TODO comment
Original prompt
> 处理 TODO: 禁用缓存 (来自 .github/workflows/release.yml)
Created from VS Code via the [GitHub Pull
Request](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github)
extension.
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
---------
Co-authored-by: hailaz <739476267@qq.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: hailaz <29968474+hailaz@users.noreply.github.com>
---
.github/workflows/release.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c74f995a2..fdf1234ff 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,11 +17,12 @@ jobs:
steps:
- name: Checkout Github Code
uses: actions/checkout@v5
-
+
- name: Set Up Golang Environment
uses: actions/setup-go@v5
with:
go-version: 1.25
+ cache: false
- name: Build CLI Binary
run: |
From baf30a0e992ab47b3f6ec84deeb95a502e494889 Mon Sep 17 00:00:00 2001
From: John Guo
Date: Thu, 4 Dec 2025 20:12:12 +0800
Subject: [PATCH 4/4] feat(contrib/drivers/dm): add `Replace/InsertIgnore`
support and field type/length enhancements for dm database (#4541)
This pull request introduces significant improvements to the DM database
driver, especially around insert operations, and refines documentation
and tests to reflect these changes. The main focus is enabling support
for "replace" and "insert ignore" operations using DM's `MERGE`
statement, improving type reporting for table fields, and updating
documentation for clarity and accuracy.
### DM Driver Insert Operations
* Added support for `Replace` and `InsertIgnore` operations in the DM
driver by internally mapping them to DM's `MERGE` statement. This
enables upsert and insert-ignore behavior for DM databases, improving
compatibility with other drivers.
* Implemented helper methods (`doMergeInsert`, `doInsertIgnore`, and
`getPrimaryKeys`) to generate correct `MERGE` SQL statements and
automatically detect primary keys when needed.
[[1]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL31-R94)
[[2]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL115-R212)
* Updated the logic for building update values and SQL generation to
ensure correct behavior for both upsert and insert-ignore cases.
[[1]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL61-R109)
[[2]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL89-R132)
[[3]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL100-R144)
[[4]](diffhunk://#diff-f51b30e3f0b0f1284b905385a89992efd0de2fe9ff8c5a4062344dfab17d428eL115-R212)
### Table Field Type Reporting
* Improved the DM driver's `TableFields` method to report column types
with length/precision (e.g., `VARCHAR(128)` instead of just `VARCHAR`),
aligning with expectations and other drivers.
[[1]](diffhunk://#diff-40a365112421ae1967bd960f8acefcc91ddb8180865b78bc49cd090fbf4883daL26-R26)
[[2]](diffhunk://#diff-40a365112421ae1967bd960f8acefcc91ddb8180865b78bc49cd090fbf4883daR88-R105)
* Updated related unit tests to expect the new type format for DM table
fields.
### Documentation Updates
* Removed outdated or redundant documentation in both English and
Chinese driver README files, and clarified supported features and
limitations for DM and other drivers.
[[1]](diffhunk://#diff-d49f5bc3a34b11a6ccb82cc54675b06a7dea5f0a943ae91c4ca0d28bd5003299L1)
[[2]](diffhunk://#diff-d49f5bc3a34b11a6ccb82cc54675b06a7dea5f0a943ae91c4ca0d28bd5003299L47-R46)
[[3]](diffhunk://#diff-d49f5bc3a34b11a6ccb82cc54675b06a7dea5f0a943ae91c4ca0d28bd5003299L119-L122)
[[4]](diffhunk://#diff-05411a14e9c7ca235f7f436bfde732853aa93b364361fe80d65ac768f4e4d613L1-L126)
### Test Suite Enhancements
* Refactored and restored unit tests for DM driver insert operations,
including tests for `Save`, `Insert`, and the new `InsertIgnore`
functionality to ensure correct behavior and compatibility.
[[1]](diffhunk://#diff-2b1a59b8b2adaa1ca3074629374ab122929e4d4fbb4cc794b8e1db60ebf8d4c2L143-L245)
[[2]](diffhunk://#diff-2b1a59b8b2adaa1ca3074629374ab122929e4d4fbb4cc794b8e1db60ebf8d4c2R512-R632)
* Minor adjustments to DM test initialization for improved clarity.
### Core Insert Logic Minor Refactoring
* Minor variable renaming for clarity in the core insert logic
(`gdb_core.go`), improving code readability.
[[1]](diffhunk://#diff-b1bbe5e3995261813e4e0ac6ffee8a37c236eaa2759f2bd82e211711695a70bcL449-R452)
[[2]](diffhunk://#diff-b1bbe5e3995261813e4e0ac6ffee8a37c236eaa2759f2bd82e211711695a70bcL466-R474)
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
contrib/drivers/README.MD | 7 +-
contrib/drivers/README.zh_CN.MD | 126 --
contrib/drivers/dm/dm.go | 1 +
contrib/drivers/dm/dm_do_insert.go | 145 +-
contrib/drivers/dm/dm_table_fields.go | 18 +-
contrib/drivers/dm/dm_z_unit_basic_test.go | 236 +--
.../dm/dm_z_unit_feature_soft_time_test.go | 1400 +++++++++++++++++
contrib/drivers/dm/dm_z_unit_init_test.go | 4 +-
database/gdb/gdb_core.go | 14 +-
database/gdb/gdb_model_soft_time.go | 1 +
10 files changed, 1659 insertions(+), 293 deletions(-)
delete mode 100644 contrib/drivers/README.zh_CN.MD
create mode 100644 contrib/drivers/dm/dm_z_unit_feature_soft_time_test.go
diff --git a/contrib/drivers/README.MD b/contrib/drivers/README.MD
index ef3bd361c..c10d58d66 100644
--- a/contrib/drivers/README.MD
+++ b/contrib/drivers/README.MD
@@ -1,4 +1,3 @@
-English | [简体中文](README.zh_CN.MD)
# Database drivers
@@ -44,7 +43,7 @@ func main() {
## Supported Drivers
-### MySQL/MariaDB/TiDB
+### MySQL/MariaDB/TiDB/OceanBase
```go
import _ "github.com/gogf/gf/contrib/drivers/mysql/v2"
@@ -116,10 +115,6 @@ Note:
import _ "github.com/gogf/gf/contrib/drivers/dm/v2"
```
-Note:
-
-- It does not support `Replace` features.
-
## Custom Drivers
It's quick and easy, please refer to current driver source.
diff --git a/contrib/drivers/README.zh_CN.MD b/contrib/drivers/README.zh_CN.MD
deleted file mode 100644
index 0d5b1d214..000000000
--- a/contrib/drivers/README.zh_CN.MD
+++ /dev/null
@@ -1,126 +0,0 @@
-[English](README.MD) | 简体中文
-
-# 数据库驱动程序
-
-用于gdb包的数据库驱动程序。
-
-## 安装
-
-以 `mysql` 为例。
-
-```shell
-go get github.com/gogf/gf/contrib/drivers/mysql/v2@latest
-# 方便复制
-go get github.com/gogf/gf/contrib/drivers/clickhouse/v2@latest
-go get github.com/gogf/gf/contrib/drivers/dm/v2@latest
-go get github.com/gogf/gf/contrib/drivers/mssql/v2@latest
-go get github.com/gogf/gf/contrib/drivers/oracle/v2@latest
-go get github.com/gogf/gf/contrib/drivers/pgsql/v2@latest
-go get github.com/gogf/gf/contrib/drivers/sqlite/v2@latest
-go get github.com/gogf/gf/contrib/drivers/sqlitecgo/v2@latest
-```
-
-选择并将驱动程序导入到您的项目中:
-
-```go
-import _ "github.com/gogf/gf/contrib/drivers/mysql/v2"
-```
-
-通常在 `main.go` 的顶部导入:
-
-```go
-package main
-
-import (
- _ "github.com/gogf/gf/contrib/drivers/mysql/v2"
-
- // 其他导入的包。
-)
-
-func main() {
- // 主要逻辑。
-}
-```
-
-## 支持的驱动程序
-
-### MySQL/MariaDB/TiDB
-
-```go
-import _ "github.com/gogf/gf/contrib/drivers/mysql/v2"
-```
-
-### SQLite
-
-```go
-import _ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
-```
-
-#### cgo 版本
-
-32位Windows请使用cgo版本
-
-```go
-import _ "github.com/gogf/gf/contrib/drivers/sqlitecgo/v2"
-```
-
-### PostgreSQL
-
-```go
-import _ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
-```
-
-注意:
-
-- 不支持 `Replace` 功能。
-
-### SQL Server
-
-```go
-import _ "github.com/gogf/gf/contrib/drivers/mssql/v2"
-```
-
-注意:
-
-- 不支持 `Replace` 功能。
-- 仅支持服务器版本 >= `SQL Server2005`
-- 仅支持 datetime2 和 datetimeoffset 类型来自动处理 created_at/updated_at/deleted_at 列,因为 datetime 类型在将列值作为字符串传递时不支持微秒精度。
-
-### Oracle
-
-```go
-import _ "github.com/gogf/gf/contrib/drivers/oracle/v2"
-```
-
-注意:
-
-- 不支持 `Replace` 功能。
-- 不支持 `LastInsertId`。
-
-### ClickHouse
-
-```go
-import _ "github.com/gogf/gf/contrib/drivers/clickhouse/v2"
-```
-
-注意:
-
-- 不支持 `InsertIgnore/InsertGetId` 功能。
-- 不支持 `Save/Replace` 功能。
-- 不支持 `Transaction` 功能。
-- 不支持 `RowsAffected` 功能。
-
-### DM
-
-```go
-import _ "github.com/gogf/gf/contrib/drivers/dm/v2"
-```
-
-注意:
-
-- 不支持 `Replace` 功能。
-
-## 自定义驱动程序
-
-自定义驱动程序非常快速和简单,您可以参考当前驱动程序的源代码来进行开发。
-如果您有关于支持新驱动程序的PR(Pull Request),我们将非常感激地接受您的提交到当前仓库。
\ No newline at end of file
diff --git a/contrib/drivers/dm/dm.go b/contrib/drivers/dm/dm.go
index 3bfb01cb9..6b45a51de 100644
--- a/contrib/drivers/dm/dm.go
+++ b/contrib/drivers/dm/dm.go
@@ -14,6 +14,7 @@ import (
"github.com/gogf/gf/v2/frame/g"
)
+// Driver is the driver for dm database.
type Driver struct {
*gdb.Core
}
diff --git a/contrib/drivers/dm/dm_do_insert.go b/contrib/drivers/dm/dm_do_insert.go
index 1218ab900..538340ffa 100644
--- a/contrib/drivers/dm/dm_do_insert.go
+++ b/contrib/drivers/dm/dm_do_insert.go
@@ -28,28 +28,70 @@ func (d *Driver) DoInsert(
return d.doSave(ctx, link, table, list, option)
case gdb.InsertOptionReplace:
- // TODO:: Should be Supported
- return nil, gerror.NewCode(
- gcode.CodeNotSupported, `Replace operation is not supported by dm driver`,
- )
- }
+ // dm does not support REPLACE INTO syntax, use SAVE instead.
+ return d.doSave(ctx, link, table, list, option)
- return d.Core.DoInsert(ctx, link, table, list, option)
+ case gdb.InsertOptionIgnore:
+ // dm does not support INSERT IGNORE syntax, use MERGE instead.
+ return d.doInsertIgnore(ctx, link, table, list, option)
+
+ default:
+ return d.Core.DoInsert(ctx, link, table, list, option)
+ }
}
// doSave support upsert for dm
func (d *Driver) doSave(ctx context.Context,
link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption,
) (result sql.Result, err error) {
- if len(option.OnConflict) == 0 {
- return nil, gerror.NewCode(
- gcode.CodeMissingParameter, `Please specify conflict columns`,
- )
+ return d.doMergeInsert(ctx, link, table, list, option, true)
+}
+
+// doInsertIgnore implements INSERT IGNORE operation using MERGE statement for DM database.
+// It only inserts records when there's no conflict on primary/unique keys.
+func (d *Driver) doInsertIgnore(ctx context.Context,
+ link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption,
+) (result sql.Result, err error) {
+ return d.doMergeInsert(ctx, link, table, list, option, false)
+}
+
+// doMergeInsert implements MERGE-based insert operations for DM database.
+// When withUpdate is true, it performs upsert (insert or update).
+// When withUpdate is false, it performs insert ignore (insert only when no conflict).
+func (d *Driver) doMergeInsert(
+ ctx context.Context,
+ link gdb.Link,
+ table string,
+ list gdb.List,
+ option gdb.DoInsertOption,
+ withUpdate bool,
+) (result sql.Result, err error) {
+ // If OnConflict is not specified, automatically get the primary key of the table
+ conflictKeys := option.OnConflict
+ if len(conflictKeys) == 0 {
+ conflictKeys, err = d.getPrimaryKeys(ctx, table)
+ if err != nil {
+ return nil, gerror.WrapCode(
+ gcode.CodeInternalError,
+ err,
+ `failed to get primary keys for table`,
+ )
+ }
+ if len(conflictKeys) == 0 {
+ return nil, gerror.NewCode(
+ gcode.CodeMissingParameter,
+ `Please specify conflict columns or ensure the table has a primary key`,
+ )
+ }
}
if len(list) == 0 {
- return nil, gerror.NewCode(
- gcode.CodeInvalidRequest, `Save operation list is empty by oracle driver`,
+ opName := "Save"
+ if !withUpdate {
+ opName = "InsertIgnore"
+ }
+ return nil, gerror.NewCodef(
+ gcode.CodeInvalidRequest, `%s operation list is empty by dm driver`, opName,
)
}
@@ -58,14 +100,13 @@ func (d *Driver) doSave(ctx context.Context,
oneLen = len(one)
charL, charR = d.GetChars()
- conflictKeys = option.OnConflict
conflictKeySet = gset.New(false)
- // queryHolders: Handle data with Holder that need to be upsert
- // queryValues: Handle data that need to be upsert
+ // queryHolders: Handle data with Holder that need to be merged
+ // queryValues: Handle data that need to be merged
// insertKeys: Handle valid keys that need to be inserted
// insertValues: Handle values that need to be inserted
- // updateValues: Handle values that need to be updated
+ // updateValues: Handle values that need to be updated (only when withUpdate=true)
queryHolders = make([]string, oneLen)
queryValues = make([]any, oneLen)
insertKeys = make([]string, oneLen)
@@ -86,9 +127,9 @@ func (d *Driver) doSave(ctx context.Context,
insertKeys[index] = keyWithChar
insertValues[index] = fmt.Sprintf("T2.%s", keyWithChar)
- // filter conflict keys in updateValues.
- // And the key is not a soft created field.
- if !(conflictKeySet.Contains(key) || d.Core.IsSoftCreatedFieldName(key)) {
+ // Build updateValues only when withUpdate is true
+ // Filter conflict keys and soft created fields from updateValues
+ if withUpdate && !(conflictKeySet.Contains(key) || d.Core.IsSoftCreatedFieldName(key)) {
updateValues = append(
updateValues,
fmt.Sprintf(`T1.%s = T2.%s`, keyWithChar, keyWithChar),
@@ -97,8 +138,10 @@ func (d *Driver) doSave(ctx context.Context,
index++
}
- batchResult := new(gdb.SqlResult)
- sqlStr := parseSqlForUpsert(table, queryHolders, insertKeys, insertValues, updateValues, conflictKeys)
+ var (
+ batchResult = new(gdb.SqlResult)
+ sqlStr = parseSqlForMerge(table, queryHolders, insertKeys, insertValues, updateValues, conflictKeys)
+ )
r, err := d.DoExec(ctx, link, sqlStr, queryValues...)
if err != nil {
return r, err
@@ -112,40 +155,58 @@ func (d *Driver) doSave(ctx context.Context,
return batchResult, nil
}
-// parseSqlForUpsert
-// MERGE INTO {{table}} T1
-// USING ( SELECT {{queryHolders}} FROM DUAL T2
-// ON (T1.{{duplicateKey}} = T2.{{duplicateKey}} AND ...)
-// WHEN NOT MATCHED THEN
-// INSERT {{insertKeys}} VALUES {{insertValues}}
-// WHEN MATCHED THEN
-// UPDATE SET {{updateValues}}
-func parseSqlForUpsert(table string,
+// getPrimaryKeys retrieves the primary key field names of the table as a slice of strings.
+// This method extracts primary key information from TableFields.
+func (d *Driver) getPrimaryKeys(ctx context.Context, table string) ([]string, error) {
+ tableFields, err := d.TableFields(ctx, table)
+ if err != nil {
+ return nil, err
+ }
+
+ var primaryKeys []string
+ for _, field := range tableFields {
+ if field.Key == "PRI" {
+ primaryKeys = append(primaryKeys, field.Name)
+ }
+ }
+
+ return primaryKeys, nil
+}
+
+// parseSqlForMerge generates MERGE statement for DM database.
+// When updateValues is empty, it only inserts (INSERT IGNORE behavior).
+// When updateValues is provided, it performs upsert (INSERT or UPDATE).
+// Examples:
+// - INSERT IGNORE: MERGE INTO table T1 USING (...) T2 ON (...) WHEN NOT MATCHED THEN INSERT(...) VALUES (...)
+// - UPSERT: MERGE INTO table T1 USING (...) T2 ON (...) WHEN NOT MATCHED THEN INSERT(...) VALUES (...) WHEN MATCHED THEN UPDATE SET ...
+func parseSqlForMerge(table string,
queryHolders, insertKeys, insertValues, updateValues, duplicateKey []string,
) (sqlStr string) {
var (
queryHolderStr = strings.Join(queryHolders, ",")
insertKeyStr = strings.Join(insertKeys, ",")
insertValueStr = strings.Join(insertValues, ",")
- updateValueStr = strings.Join(updateValues, ",")
duplicateKeyStr string
- pattern = gstr.Trim(`MERGE INTO %s T1 USING (SELECT %s FROM DUAL) T2 ON (%s) WHEN NOT MATCHED THEN INSERT(%s) VALUES (%s) WHEN MATCHED THEN UPDATE SET %s;`)
)
+ // Build ON condition
for index, keys := range duplicateKey {
if index != 0 {
duplicateKeyStr += " AND "
}
- duplicateTmp := fmt.Sprintf("T1.%s = T2.%s", keys, keys)
- duplicateKeyStr += duplicateTmp
+ duplicateKeyStr += fmt.Sprintf("T1.%s = T2.%s", keys, keys)
}
- return fmt.Sprintf(pattern,
- table,
- queryHolderStr,
- duplicateKeyStr,
- insertKeyStr,
- insertValueStr,
- updateValueStr,
- )
+ // Build SQL based on whether UPDATE is needed
+ pattern := gstr.Trim(`MERGE INTO %s T1 USING (SELECT %s FROM DUAL) T2 ON (%s) WHEN NOT MATCHED THEN INSERT(%s) VALUES (%s)`)
+ if len(updateValues) > 0 {
+ // Upsert: INSERT or UPDATE
+ pattern += gstr.Trim(`WHEN MATCHED THEN UPDATE SET %s`)
+ return fmt.Sprintf(
+ pattern, table, queryHolderStr, duplicateKeyStr, insertKeyStr, insertValueStr,
+ strings.Join(updateValues, ","),
+ )
+ }
+ // Insert Ignore: INSERT only
+ return fmt.Sprintf(pattern, table, queryHolderStr, duplicateKeyStr, insertKeyStr, insertValueStr)
}
diff --git a/contrib/drivers/dm/dm_table_fields.go b/contrib/drivers/dm/dm_table_fields.go
index 9dc3c6c8c..137047366 100644
--- a/contrib/drivers/dm/dm_table_fields.go
+++ b/contrib/drivers/dm/dm_table_fields.go
@@ -23,7 +23,7 @@ func escapeSingleQuote(s string) string {
}
const (
- tableFieldsSqlTmp = `SELECT c.COLUMN_NAME, c.DATA_TYPE, c.DATA_DEFAULT, c.NULLABLE, cc.COMMENTS FROM ALL_TAB_COLUMNS c LEFT JOIN ALL_COL_COMMENTS cc ON c.COLUMN_NAME = cc.COLUMN_NAME AND c.TABLE_NAME = cc.TABLE_NAME AND c.OWNER = cc.OWNER WHERE c.TABLE_NAME = '%s' AND c.OWNER = '%s'`
+ tableFieldsSqlTmp = `SELECT c.COLUMN_NAME, c.DATA_TYPE, c.DATA_LENGTH, c.DATA_DEFAULT, c.NULLABLE, cc.COMMENTS FROM ALL_TAB_COLUMNS c LEFT JOIN ALL_COL_COMMENTS cc ON c.COLUMN_NAME = cc.COLUMN_NAME AND c.TABLE_NAME = cc.TABLE_NAME AND c.OWNER = cc.OWNER WHERE c.TABLE_NAME = '%s' AND c.OWNER = '%s'`
tableFieldsPkSqlSchemaTmp = `SELECT COLS.COLUMN_NAME AS PRIMARY_KEY_COLUMN FROM USER_CONSTRAINTS CONS JOIN USER_CONS_COLUMNS COLS ON CONS.CONSTRAINT_NAME = COLS.CONSTRAINT_NAME WHERE CONS.TABLE_NAME = '%s' AND CONS.CONSTRAINT_TYPE = 'P'`
tableFieldsPkSqlDBATmp = `SELECT COLS.COLUMN_NAME AS PRIMARY_KEY_COLUMN FROM DBA_CONSTRAINTS CONS JOIN DBA_CONS_COLUMNS COLS ON CONS.CONSTRAINT_NAME = COLS.CONSTRAINT_NAME WHERE CONS.TABLE_NAME = '%s' AND CONS.OWNER = '%s' AND CONS.CONSTRAINT_TYPE = 'P'`
)
@@ -85,10 +85,24 @@ func (d *Driver) TableFields(
if m["NULLABLE"].String() != "N" {
nullable = true
}
+
+ // Build field type with length/precision
+ // For NUMBER(p,s): use DATA_PRECISION and DATA_SCALE
+ // For VARCHAR2/CHAR: use DATA_LENGTH
+ var (
+ fieldType string
+ dataType = m["DATA_TYPE"].String()
+ dataLength = m["DATA_LENGTH"].Int()
+ )
+ if dataLength > 0 {
+ fieldType = fmt.Sprintf("%s(%d)", dataType, dataLength)
+ } else {
+ fieldType = dataType
+ }
fields[m["COLUMN_NAME"].String()] = &gdb.TableField{
Index: i,
Name: m["COLUMN_NAME"].String(),
- Type: m["DATA_TYPE"].String(),
+ Type: fieldType,
Null: nullable,
Default: m["DATA_DEFAULT"].Val(),
Key: pkFields.Get(m["COLUMN_NAME"].String()),
diff --git a/contrib/drivers/dm/dm_z_unit_basic_test.go b/contrib/drivers/dm/dm_z_unit_basic_test.go
index aeb83a70a..da9ab215b 100644
--- a/contrib/drivers/dm/dm_z_unit_basic_test.go
+++ b/contrib/drivers/dm/dm_z_unit_basic_test.go
@@ -80,12 +80,12 @@ func TestTableFields(t *testing.T) {
createInitTable(tables)
gtest.C(t, func(t *gtest.T) {
var expect = map[string][]any{
- "ID": {"BIGINT", false},
- "ACCOUNT_NAME": {"VARCHAR", false},
- "PWD_RESET": {"TINYINT", false},
- "ATTR_INDEX": {"INT", true},
- "DELETED": {"INT", false},
- "CREATED_TIME": {"TIMESTAMP", false},
+ "ID": {"BIGINT(8)", false},
+ "ACCOUNT_NAME": {"VARCHAR(128)", false},
+ "PWD_RESET": {"TINYINT(1)", false},
+ "ATTR_INDEX": {"INT(4)", true},
+ "DELETED": {"INT(4)", false},
+ "CREATED_TIME": {"TIMESTAMP(8)", false},
}
res, err := db.TableFields(ctx, tables)
@@ -140,109 +140,6 @@ func Test_DB_Query(t *testing.T) {
})
}
-func TestModelSave(t *testing.T) {
- table := createTable()
- defer dropTable(table)
- gtest.C(t, func(t *gtest.T) {
- type User struct {
- Id int
- AccountName string
- AttrIndex int
- }
- var (
- user User
- count int
- result sql.Result
- err error
- )
-
- result, err = db.Model(table).Data(g.Map{
- "id": 1,
- "accountName": "ac1",
- "attrIndex": 100,
- }).OnConflict("id").Save()
-
- t.AssertNil(err)
- n, _ := result.RowsAffected()
- t.Assert(n, 1)
-
- err = db.Model(table).Scan(&user)
- t.AssertNil(err)
- t.Assert(user.Id, 1)
- t.Assert(user.AccountName, "ac1")
- t.Assert(user.AttrIndex, 100)
-
- _, err = db.Model(table).Data(g.Map{
- "id": 1,
- "accountName": "ac2",
- "attrIndex": 200,
- }).OnConflict("id").Save()
- t.AssertNil(err)
-
- err = db.Model(table).Scan(&user)
- t.AssertNil(err)
- t.Assert(user.AccountName, "ac2")
- t.Assert(user.AttrIndex, 200)
-
- count, err = db.Model(table).Count()
- t.AssertNil(err)
- t.Assert(count, 1)
- })
-}
-
-func TestModelInsert(t *testing.T) {
- // g.Model.insert not lost default not null coloumn
- table := "A_tables"
- createInitTable(table)
- gtest.C(t, func(t *gtest.T) {
- i := 200
- data := User{
- ID: int64(i),
- AccountName: fmt.Sprintf(`A%dtwo`, i),
- PwdReset: 0,
- AttrIndex: 99,
- CreatedTime: time.Now(),
- UpdatedTime: time.Now(),
- }
- // _, err := db.Schema(TestDBName).Model(table).Data(data).Insert()
- _, err := db.Model(table).Insert(&data)
- gtest.AssertNil(err)
- })
-
- gtest.C(t, func(t *gtest.T) {
- i := 201
- data := User{
- ID: int64(i),
- AccountName: fmt.Sprintf(`A%dtwoONE`, i),
- PwdReset: 1,
- CreatedTime: time.Now(),
- AttrIndex: 98,
- UpdatedTime: time.Now(),
- }
- // _, err := db.Schema(TestDBName).Model(table).Data(data).Insert()
- _, err := db.Model(table).Data(&data).Insert()
- gtest.AssertNil(err)
- })
-}
-
-func TestDBInsert(t *testing.T) {
- table := "A_tables"
- createInitTable("A_tables")
- gtest.C(t, func(t *gtest.T) {
- i := 300
- data := g.Map{
- "ID": i,
- "ACCOUNT_NAME": fmt.Sprintf(`A%dthress`, i),
- "PWD_RESET": 3,
- "ATTR_INDEX": 98,
- "CREATED_TIME": gtime.Now(),
- "UPDATED_TIME": gtime.Now(),
- }
- _, err := db.Insert(ctx, table, &data)
- gtest.AssertNil(err)
- })
-}
-
func Test_DB_Exec(t *testing.T) {
createInitTable("A_tables")
gtest.C(t, func(t *gtest.T) {
@@ -612,3 +509,124 @@ func Test_Empty_Slice_Argument(t *testing.T) {
t.Assert(len(result), 0)
})
}
+
+func TestModelSave(t *testing.T) {
+ table := createTable()
+ defer dropTable(table)
+ gtest.C(t, func(t *gtest.T) {
+ type User struct {
+ Id int
+ AccountName string
+ AttrIndex int
+ }
+ var (
+ user User
+ count int
+ result sql.Result
+ err error
+ )
+
+ result, err = db.Model(table).Data(g.Map{
+ "id": 1,
+ "accountName": "ac1",
+ "attrIndex": 100,
+ }).OnConflict("id").Save()
+
+ t.AssertNil(err)
+ n, _ := result.RowsAffected()
+ t.Assert(n, 1)
+
+ err = db.Model(table).Scan(&user)
+ t.AssertNil(err)
+ t.Assert(user.Id, 1)
+ t.Assert(user.AccountName, "ac1")
+ t.Assert(user.AttrIndex, 100)
+
+ _, err = db.Model(table).Data(g.Map{
+ "id": 1,
+ "accountName": "ac2",
+ "attrIndex": 200,
+ }).OnConflict("id").Save()
+ t.AssertNil(err)
+
+ err = db.Model(table).Scan(&user)
+ t.AssertNil(err)
+ t.Assert(user.AccountName, "ac2")
+ t.Assert(user.AttrIndex, 200)
+
+ count, err = db.Model(table).Count()
+ t.AssertNil(err)
+ t.Assert(count, 1)
+ })
+}
+
+func TestModelInsert(t *testing.T) {
+ // g.Model.insert not lost default not null column
+ table := "A_tables"
+ createInitTable(table)
+ gtest.C(t, func(t *gtest.T) {
+ i := 200
+ data := User{
+ ID: int64(i),
+ AccountName: fmt.Sprintf(`A%dtwo`, i),
+ PwdReset: 0,
+ AttrIndex: 99,
+ CreatedTime: time.Now(),
+ UpdatedTime: time.Now(),
+ }
+ // _, err := db.Schema(TestDBName).Model(table).Data(data).Insert()
+ _, err := db.Model(table).Insert(&data)
+ gtest.AssertNil(err)
+ })
+
+ gtest.C(t, func(t *gtest.T) {
+ i := 201
+ data := User{
+ ID: int64(i),
+ AccountName: fmt.Sprintf(`A%dtwoONE`, i),
+ PwdReset: 1,
+ CreatedTime: time.Now(),
+ AttrIndex: 98,
+ UpdatedTime: time.Now(),
+ }
+ // _, err := db.Schema(TestDBName).Model(table).Data(data).Insert()
+ _, err := db.Model(table).Data(&data).Insert()
+ gtest.AssertNil(err)
+ })
+}
+
+func Test_Model_InsertIgnore(t *testing.T) {
+ table := createInitTable()
+ defer dropTable(table)
+
+ // db.SetDebug(true)
+
+ gtest.C(t, func(t *gtest.T) {
+ data := User{
+ ID: int64(666),
+ AccountName: fmt.Sprintf(`name_%d`, 666),
+ PwdReset: 0,
+ AttrIndex: 99,
+ CreatedTime: time.Now(),
+ UpdatedTime: time.Now(),
+ }
+ _, err := db.Model(table).Data(data).Insert()
+ t.AssertNil(err)
+ })
+ gtest.C(t, func(t *gtest.T) {
+ data := User{
+ ID: int64(666),
+ AccountName: fmt.Sprintf(`name_%d`, 777),
+ PwdReset: 0,
+ AttrIndex: 99,
+ CreatedTime: time.Now(),
+ UpdatedTime: time.Now(),
+ }
+ _, err := db.Model(table).Data(data).InsertIgnore()
+ t.AssertNil(err)
+
+ one, err := db.Model(table).Where("id", 666).One()
+ t.AssertNil(err)
+ t.Assert(one["ACCOUNT_NAME"].String(), "name_666")
+ })
+}
diff --git a/contrib/drivers/dm/dm_z_unit_feature_soft_time_test.go b/contrib/drivers/dm/dm_z_unit_feature_soft_time_test.go
new file mode 100644
index 000000000..d66630f7f
--- /dev/null
+++ b/contrib/drivers/dm/dm_z_unit_feature_soft_time_test.go
@@ -0,0 +1,1400 @@
+// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://github.com/gogf/gf.
+
+package dm_test
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gtime"
+ "github.com/gogf/gf/v2/test/gtest"
+)
+
+// CreateAt/UpdateAt/DeleteAt.
+func Test_SoftTime_CreateUpdateDelete1(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ create_at TIMESTAMP(6) DEFAULT NULL,
+ update_at TIMESTAMP(6) DEFAULT NULL,
+ delete_at TIMESTAMP(6) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ gtest.C(t, func(t *gtest.T) {
+ // Insert
+ dataInsert := g.Map{
+ "id": 1,
+ "name": "name_1",
+ }
+ r, err := db.Model(table).Data(dataInsert).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneInsert, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneInsert["ID"].Int(), 1)
+ t.Assert(oneInsert["NAME"].String(), "name_1")
+ t.Assert(oneInsert["DELETE_AT"].String(), "")
+ t.AssertGE(oneInsert["CREATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+ t.AssertGE(oneInsert["UPDATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Save
+ dataSave := g.Map{
+ "id": 1,
+ "name": "name_10",
+ }
+ r, err = db.Model(table).Data(dataSave).Save()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneSave, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneSave["ID"].Int(), 1)
+ t.Assert(oneSave["NAME"].String(), "name_10")
+ t.Assert(oneSave["DELETE_AT"].String(), "")
+ t.Assert(oneSave["CREATE_AT"].GTime().Timestamp(), oneInsert["CREATE_AT"].GTime().Timestamp())
+ t.AssertNE(oneSave["UPDATE_AT"].GTime().Timestamp(), oneInsert["UPDATE_AT"].GTime().Timestamp())
+ t.AssertGE(oneSave["UPDATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Update
+ dataUpdate := g.Map{
+ "name": "name_1000",
+ }
+ r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneUpdate, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneUpdate["ID"].Int(), 1)
+ t.Assert(oneUpdate["NAME"].String(), "name_1000")
+ t.Assert(oneUpdate["DELETE_AT"].String(), "")
+ t.Assert(oneUpdate["CREATE_AT"].GTime().Timestamp(), oneInsert["CREATE_AT"].GTime().Timestamp())
+ t.AssertGE(oneUpdate["UPDATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // Replace
+ dataReplace := g.Map{
+ "id": 1,
+ "name": "name_100",
+ }
+ r, err = db.Model(table).Data(dataReplace).Replace()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneReplace, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneReplace["ID"].Int(), 1)
+ t.Assert(oneReplace["NAME"].String(), "name_100")
+ t.Assert(oneReplace["DELETE_AT"].String(), "")
+ t.AssertGE(oneReplace["CREATE_AT"].GTime().Timestamp(), oneInsert["CREATE_AT"].GTime().Timestamp())
+ t.AssertGE(oneReplace["UPDATE_AT"].GTime().Timestamp(), oneInsert["UPDATE_AT"].GTime().Timestamp())
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Delete
+ r, err = db.Model(table).Delete("id", 1)
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+ // Delete Select
+ one4, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one4), 0)
+ one5, err := db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one5["ID"].Int(), 1)
+ t.AssertGE(one5["DELETE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+ // Delete Count
+ i, err := db.Model(table).Count()
+ t.AssertNil(err)
+ t.Assert(i, 0)
+ i, err = db.Model(table).Unscoped().Count()
+ t.AssertNil(err)
+ t.Assert(i, 1)
+
+ // Delete Unscoped
+ r, err = db.Model(table).Unscoped().Delete("id", 1)
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+ one6, err := db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one6), 0)
+ i, err = db.Model(table).Unscoped().Count()
+ t.AssertNil(err)
+ t.Assert(i, 0)
+ })
+}
+
+// CreateAt/UpdateAt/DeleteAt.
+func Test_SoftTime_CreateUpdateDelete2(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ create_at TIMESTAMP(0) DEFAULT NULL,
+ update_at TIMESTAMP(0) DEFAULT NULL,
+ delete_at TIMESTAMP(0) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ gtest.C(t, func(t *gtest.T) {
+ // Insert
+ dataInsert := g.Map{
+ "id": 1,
+ "name": "name_1",
+ }
+ r, err := db.Model(table).Data(dataInsert).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneInsert, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneInsert["ID"].Int(), 1)
+ t.Assert(oneInsert["NAME"].String(), "name_1")
+ t.Assert(oneInsert["DELETE_AT"].String(), "")
+ t.AssertGE(oneInsert["CREATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+ t.AssertGE(oneInsert["UPDATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Save
+ dataSave := g.Map{
+ "id": 1,
+ "name": "name_10",
+ }
+ r, err = db.Model(table).Data(dataSave).Save()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneSave, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneSave["ID"].Int(), 1)
+ t.Assert(oneSave["NAME"].String(), "name_10")
+ t.Assert(oneSave["DELETE_AT"].String(), "")
+ t.Assert(oneSave["CREATE_AT"].GTime().Timestamp(), oneInsert["CREATE_AT"].GTime().Timestamp())
+ t.AssertNE(oneSave["UPDATE_AT"].GTime().Timestamp(), oneInsert["UPDATE_AT"].GTime().Timestamp())
+ t.AssertGE(oneSave["UPDATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Update
+ dataUpdate := g.Map{
+ "name": "name_1000",
+ }
+ r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneUpdate, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneUpdate["ID"].Int(), 1)
+ t.Assert(oneUpdate["NAME"].String(), "name_1000")
+ t.Assert(oneUpdate["DELETE_AT"].String(), "")
+ t.Assert(oneUpdate["CREATE_AT"].GTime().Timestamp(), oneInsert["CREATE_AT"].GTime().Timestamp())
+ t.AssertGE(oneUpdate["UPDATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // Replace
+ dataReplace := g.Map{
+ "id": 1,
+ "name": "name_100",
+ }
+ r, err = db.Model(table).Data(dataReplace).Replace()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneReplace, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneReplace["ID"].Int(), 1)
+ t.Assert(oneReplace["NAME"].String(), "name_100")
+ t.Assert(oneReplace["DELETE_AT"].String(), "")
+ t.AssertGE(oneReplace["CREATE_AT"].GTime().Timestamp(), oneInsert["CREATE_AT"].GTime().Timestamp())
+ t.AssertGE(oneReplace["UPDATE_AT"].GTime().Timestamp(), oneInsert["UPDATE_AT"].GTime().Timestamp())
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Delete
+ r, err = db.Model(table).Delete("id", 1)
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+ // Delete Select
+ one4, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one4), 0)
+ one5, err := db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one5["ID"].Int(), 1)
+ t.AssertGE(one5["DELETE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+ // Delete Count
+ i, err := db.Model(table).Count()
+ t.AssertNil(err)
+ t.Assert(i, 0)
+ i, err = db.Model(table).Unscoped().Count()
+ t.AssertNil(err)
+ t.Assert(i, 1)
+
+ // Delete Unscoped
+ r, err = db.Model(table).Unscoped().Delete("id", 1)
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+ one6, err := db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one6), 0)
+ i, err = db.Model(table).Unscoped().Count()
+ t.AssertNil(err)
+ t.Assert(i, 0)
+ })
+}
+
+// CreatedAt/UpdatedAt/DeletedAt.
+func Test_SoftTime_CreatedUpdatedDeleted_Map(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ created_at TIMESTAMP(6) DEFAULT NULL,
+ updated_at TIMESTAMP(6) DEFAULT NULL,
+ deleted_at TIMESTAMP(6) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ gtest.C(t, func(t *gtest.T) {
+ // Insert
+ dataInsert := g.Map{
+ "id": 1,
+ "name": "name_1",
+ }
+ r, err := db.Model(table).Data(dataInsert).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneInsert, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneInsert["ID"].Int(), 1)
+ t.Assert(oneInsert["NAME"].String(), "name_1")
+ t.Assert(oneInsert["DELETED_AT"].String(), "")
+ t.AssertGE(oneInsert["CREATED_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+ t.AssertGE(oneInsert["UPDATED_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Save
+ dataSave := g.Map{
+ "id": 1,
+ "name": "name_10",
+ }
+ r, err = db.Model(table).Data(dataSave).Save()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneSave, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneSave["ID"].Int(), 1)
+ t.Assert(oneSave["NAME"].String(), "name_10")
+ t.Assert(oneSave["DELETED_AT"].String(), "")
+ t.Assert(oneSave["CREATED_AT"].GTime().Timestamp(), oneInsert["CREATED_AT"].GTime().Timestamp())
+ t.AssertNE(oneSave["UPDATED_AT"].GTime().Timestamp(), oneInsert["UPDATED_AT"].GTime().Timestamp())
+ t.AssertGE(oneSave["UPDATED_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Update
+ dataUpdate := g.Map{
+ "name": "name_1000",
+ }
+ r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneUpdate, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneUpdate["ID"].Int(), 1)
+ t.Assert(oneUpdate["NAME"].String(), "name_1000")
+ t.Assert(oneUpdate["DELETED_AT"].String(), "")
+ t.Assert(oneUpdate["CREATED_AT"].GTime().Timestamp(), oneInsert["CREATED_AT"].GTime().Timestamp())
+ t.AssertGE(oneUpdate["UPDATED_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // Replace
+ dataReplace := g.Map{
+ "id": 1,
+ "name": "name_100",
+ }
+ r, err = db.Model(table).Data(dataReplace).Replace()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneReplace, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneReplace["ID"].Int(), 1)
+ t.Assert(oneReplace["NAME"].String(), "name_100")
+ t.Assert(oneReplace["DELETED_AT"].String(), "")
+ t.AssertGE(oneReplace["CREATED_AT"].GTime().Timestamp(), oneInsert["CREATED_AT"].GTime().Timestamp())
+ t.AssertGE(oneReplace["UPDATED_AT"].GTime().Timestamp(), oneInsert["UPDATED_AT"].GTime().Timestamp())
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Delete
+ r, err = db.Model(table).Delete("id", 1)
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+ // Delete Select
+ one4, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one4), 0)
+ one5, err := db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one5["ID"].Int(), 1)
+ t.AssertGE(one5["DELETED_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+ // Delete Count
+ i, err := db.Model(table).Count()
+ t.AssertNil(err)
+ t.Assert(i, 0)
+ i, err = db.Model(table).Unscoped().Count()
+ t.AssertNil(err)
+ t.Assert(i, 1)
+
+ // Delete Unscoped
+ r, err = db.Model(table).Unscoped().Delete("id", 1)
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+ one6, err := db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one6), 0)
+ i, err = db.Model(table).Unscoped().Count()
+ t.AssertNil(err)
+ t.Assert(i, 0)
+ })
+}
+
+// CreatedAt/UpdatedAt/DeletedAt.
+func Test_SoftTime_CreatedUpdatedDeleted_Struct(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ created_at TIMESTAMP(6) DEFAULT NULL,
+ updated_at TIMESTAMP(6) DEFAULT NULL,
+ deleted_at TIMESTAMP(6) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ type User struct {
+ Id int
+ Name string
+ CreatedAT *gtime.Time
+ UpdatedAT *gtime.Time
+ DeletedAT *gtime.Time
+ }
+ gtest.C(t, func(t *gtest.T) {
+ // Insert
+ dataInsert := User{
+ Id: 1,
+ Name: "name_1",
+ }
+ r, err := db.Model(table).Data(dataInsert).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneInsert, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneInsert["ID"].Int(), 1)
+ t.Assert(oneInsert["NAME"].String(), "name_1")
+ t.Assert(oneInsert["DELETED_AT"].String(), "")
+ t.AssertGE(oneInsert["CREATED_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+ t.AssertGE(oneInsert["UPDATED_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Save
+ dataSave := User{
+ Id: 1,
+ Name: "name_10",
+ }
+ r, err = db.Model(table).Data(dataSave).OmitEmpty().Save()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneSave, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneSave["ID"].Int(), 1)
+ t.Assert(oneSave["NAME"].String(), "name_10")
+ t.Assert(oneSave["DELETED_AT"].String(), "")
+ t.Assert(oneSave["CREATED_AT"].GTime().Timestamp(), oneInsert["CREATED_AT"].GTime().Timestamp())
+ t.AssertNE(oneSave["UPDATED_AT"].GTime().Timestamp(), oneInsert["UPDATED_AT"].GTime().Timestamp())
+ t.AssertGE(oneSave["UPDATED_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Update
+ dataUpdate := User{
+ Name: "name_1000",
+ }
+ r, err = db.Model(table).Data(dataUpdate).OmitEmpty().WherePri(1).Update()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneUpdate, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneUpdate["ID"].Int(), 1)
+ t.Assert(oneUpdate["NAME"].String(), "name_1000")
+ t.Assert(oneUpdate["DELETED_AT"].String(), "")
+ t.Assert(oneUpdate["CREATED_AT"].GTime().Timestamp(), oneInsert["CREATED_AT"].GTime().Timestamp())
+ t.AssertGE(oneUpdate["UPDATED_AT"].GTime().Timestamp(), gtime.Timestamp()-4)
+
+ // Replace
+ dataReplace := User{
+ Id: 1,
+ Name: "name_100",
+ }
+ r, err = db.Model(table).Data(dataReplace).OmitEmpty().Replace()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneReplace, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneReplace["ID"].Int(), 1)
+ t.Assert(oneReplace["NAME"].String(), "name_100")
+ t.Assert(oneReplace["DELETED_AT"].String(), "")
+ t.AssertGE(oneReplace["CREATED_AT"].GTime().Timestamp(), oneInsert["CREATED_AT"].GTime().Timestamp())
+ t.AssertGE(oneReplace["UPDATED_AT"].GTime().Timestamp(), oneInsert["UPDATED_AT"].GTime().Timestamp())
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Delete
+ r, err = db.Model(table).Delete("id", 1)
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+ // Delete Select
+ one4, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one4), 0)
+ one5, err := db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one5["ID"].Int(), 1)
+ t.AssertGE(one5["DELETED_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+ // Delete Count
+ i, err := db.Model(table).Count()
+ t.AssertNil(err)
+ t.Assert(i, 0)
+ i, err = db.Model(table).Unscoped().Count()
+ t.AssertNil(err)
+ t.Assert(i, 1)
+
+ // Delete Unscoped
+ r, err = db.Model(table).Unscoped().Delete("id", 1)
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+ one6, err := db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one6), 0)
+ i, err = db.Model(table).Unscoped().Count()
+ t.AssertNil(err)
+ t.Assert(i, 0)
+ })
+}
+
+func Test_SoftUpdateTime(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ num INT DEFAULT NULL,
+ create_at TIMESTAMP(6) DEFAULT NULL,
+ update_at TIMESTAMP(6) DEFAULT NULL,
+ delete_at TIMESTAMP(6) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ gtest.C(t, func(t *gtest.T) {
+ // Insert
+ dataInsert := g.Map{
+ "id": 1,
+ "num": 10,
+ }
+ r, err := db.Model(table).Data(dataInsert).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneInsert, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneInsert["ID"].Int(), 1)
+ t.Assert(oneInsert["NUM"].Int(), 10)
+
+ // Update.
+ r, err = db.Model(table).Data("num=num+1").Where("id=?", 1).Update()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+ })
+}
+
+func Test_SoftUpdateTime_WithDO(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ num INT DEFAULT NULL,
+ created_at TIMESTAMP(6) DEFAULT NULL,
+ updated_at TIMESTAMP(6) DEFAULT NULL,
+ deleted_at TIMESTAMP(6) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ gtest.C(t, func(t *gtest.T) {
+ // Insert
+ dataInsert := g.Map{
+ "id": 1,
+ "num": 10,
+ }
+ r, err := db.Model(table).Data(dataInsert).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneInserted, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneInserted["ID"].Int(), 1)
+ t.Assert(oneInserted["NUM"].Int(), 10)
+
+ // Update.
+ time.Sleep(2 * time.Second)
+ type User struct {
+ g.Meta `orm:"do:true"`
+ Id any
+ Num any
+ CreatedAt any
+ UpdatedAt any
+ DeletedAt any
+ }
+ r, err = db.Model(table).Data(User{
+ Num: 100,
+ }).Where("id=?", 1).Update()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneUpdated, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneUpdated["NUM"].Int(), 100)
+ t.Assert(oneUpdated["CREATED_AT"].String(), oneInserted["CREATED_AT"].String())
+ t.AssertNE(oneUpdated["UPDATED_AT"].String(), oneInserted["UPDATED_AT"].String())
+ })
+}
+
+func Test_SoftDelete(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ create_at TIMESTAMP(6) DEFAULT NULL,
+ update_at TIMESTAMP(6) DEFAULT NULL,
+ delete_at TIMESTAMP(6) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+ // db.SetDebug(true)
+ gtest.C(t, func(t *gtest.T) {
+ for i := 1; i <= 10; i++ {
+ data := g.Map{
+ "id": i,
+ "name": fmt.Sprintf("name_%d", i),
+ }
+ r, err := db.Model(table).Data(data).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+ }
+ })
+ gtest.C(t, func(t *gtest.T) {
+ one, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.AssertNE(one["CREATE_AT"].String(), "")
+ t.AssertNE(one["UPDATE_AT"].String(), "")
+ t.Assert(one["DELETE_AT"].String(), "")
+ })
+ gtest.C(t, func(t *gtest.T) {
+ one, err := db.Model(table).WherePri(10).One()
+ t.AssertNil(err)
+ t.AssertNE(one["CREATE_AT"].String(), "")
+ t.AssertNE(one["UPDATE_AT"].String(), "")
+ t.Assert(one["DELETE_AT"].String(), "")
+ })
+ gtest.C(t, func(t *gtest.T) {
+ ids := g.SliceInt{1, 3, 5}
+ r, err := db.Model(table).Where("id", ids).Delete()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 3)
+
+ count, err := db.Model(table).Where("id", ids).Count()
+ t.AssertNil(err)
+ t.Assert(count, 0)
+
+ all, err := db.Model(table).Unscoped().Where("id", ids).All()
+ t.AssertNil(err)
+ t.Assert(len(all), 3)
+ t.AssertNE(all[0]["CREATE_AT"].String(), "")
+ t.AssertNE(all[0]["UPDATE_AT"].String(), "")
+ t.AssertNE(all[0]["DELETE_AT"].String(), "")
+ t.AssertNE(all[1]["CREATE_AT"].String(), "")
+ t.AssertNE(all[1]["UPDATE_AT"].String(), "")
+ t.AssertNE(all[1]["DELETE_AT"].String(), "")
+ t.AssertNE(all[2]["CREATE_AT"].String(), "")
+ t.AssertNE(all[2]["UPDATE_AT"].String(), "")
+ t.AssertNE(all[2]["DELETE_AT"].String(), "")
+ })
+}
+
+func Test_SoftDelete_Join(t *testing.T) {
+ table1 := "time_test_table1"
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ create_at TIMESTAMP(6) DEFAULT NULL,
+ update_at TIMESTAMP(6) DEFAULT NULL,
+ delete_at TIMESTAMP(6) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table1)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table1)
+
+ table2 := "time_test_table2"
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ createat TIMESTAMP(6) DEFAULT NULL,
+ updateat TIMESTAMP(6) DEFAULT NULL,
+ deleteat TIMESTAMP(6) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table2)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table2)
+
+ gtest.C(t, func(t *gtest.T) {
+ // db.SetDebug(true)
+ dataInsert1 := g.Map{
+ "id": 1,
+ "name": "name_1",
+ }
+ r, err := db.Model(table1).Data(dataInsert1).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ dataInsert2 := g.Map{
+ "id": 1,
+ "name": "name_2",
+ }
+ r, err = db.Model(table2).Data(dataInsert2).Insert()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err := db.Model(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").One()
+ t.AssertNil(err)
+ t.Assert(one["NAME"], "name_1")
+
+ // Soft deleting.
+ r, err = db.Model(table1).Where(1).Delete()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err = db.Model(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").One()
+ t.AssertNil(err)
+ t.Assert(one.IsEmpty(), true)
+
+ one, err = db.Model(table2, "t2").LeftJoin(table1, "t1", "t2.id=t1.id").Fields("t2.name").One()
+ t.AssertNil(err)
+ t.Assert(one.IsEmpty(), true)
+ })
+}
+
+func Test_SoftDelete_WhereAndOr(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ create_at TIMESTAMP(6) DEFAULT NULL,
+ update_at TIMESTAMP(6) DEFAULT NULL,
+ delete_at TIMESTAMP(6) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+ // db.SetDebug(true)
+ // Add datas.
+ gtest.C(t, func(t *gtest.T) {
+ for i := 1; i <= 10; i++ {
+ data := g.Map{
+ "id": i,
+ "name": fmt.Sprintf("name_%d", i),
+ }
+ r, err := db.Model(table).Data(data).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+ }
+ })
+ gtest.C(t, func(t *gtest.T) {
+ ids := g.SliceInt{1, 3, 5}
+ r, err := db.Model(table).Where("id", ids).Delete()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 3)
+
+ count, err := db.Model(table).Where("id", 1).WhereOr("id", 3).Count()
+ t.AssertNil(err)
+ t.Assert(count, 0)
+ })
+}
+
+func Test_CreateUpdateTime_Struct(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ create_at TIMESTAMP(6) DEFAULT NULL,
+ update_at TIMESTAMP(6) DEFAULT NULL,
+ delete_at TIMESTAMP(6) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ // db.SetDebug(true)
+ // defer db.SetDebug(false)
+
+ type Entity struct {
+ Id uint64 `orm:"id,primary" json:"id"`
+ Name string `orm:"name" json:"name"`
+ CreateAt *gtime.Time `orm:"create_at" json:"create_at"`
+ UpdateAt *gtime.Time `orm:"update_at" json:"update_at"`
+ DeleteAt *gtime.Time `orm:"delete_at" json:"delete_at"`
+ }
+ gtest.C(t, func(t *gtest.T) {
+ // Insert
+ dataInsert := &Entity{
+ Id: 1,
+ Name: "name_1",
+ CreateAt: nil,
+ UpdateAt: nil,
+ DeleteAt: nil,
+ }
+ r, err := db.Model(table).Data(dataInsert).OmitEmpty().Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneInsert, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneInsert["ID"].Int(), 1)
+ t.Assert(oneInsert["NAME"].String(), "name_1")
+ t.Assert(oneInsert["DELETE_AT"].String(), "")
+ t.AssertGE(oneInsert["CREATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+ t.AssertGE(oneInsert["UPDATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ time.Sleep(2 * time.Second)
+
+ // Save
+ dataSave := &Entity{
+ Id: 1,
+ Name: "name_10",
+ CreateAt: nil,
+ UpdateAt: nil,
+ DeleteAt: nil,
+ }
+ r, err = db.Model(table).Data(dataSave).OmitEmpty().Save()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneSave, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneSave["ID"].Int(), 1)
+ t.Assert(oneSave["NAME"].String(), "name_10")
+ t.Assert(oneSave["DELETE_AT"].String(), "")
+ t.Assert(oneSave["CREATE_AT"].GTime().Timestamp(), oneInsert["CREATE_AT"].GTime().Timestamp())
+ t.AssertNE(oneSave["UPDATE_AT"].GTime().Timestamp(), oneInsert["UPDATE_AT"].GTime().Timestamp())
+ t.AssertGE(oneSave["UPDATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ time.Sleep(2 * time.Second)
+
+ // Update
+ dataUpdate := &Entity{
+ Id: 1,
+ Name: "name_1000",
+ CreateAt: nil,
+ UpdateAt: nil,
+ DeleteAt: nil,
+ }
+ r, err = db.Model(table).Data(dataUpdate).WherePri(1).OmitEmpty().Update()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneUpdate, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneUpdate["ID"].Int(), 1)
+ t.Assert(oneUpdate["NAME"].String(), "name_1000")
+ t.Assert(oneUpdate["DELETE_AT"].String(), "")
+ t.Assert(oneUpdate["CREATE_AT"].GTime().Timestamp(), oneInsert["CREATE_AT"].GTime().Timestamp())
+ t.AssertGE(oneUpdate["UPDATE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+
+ // Replace
+ dataReplace := &Entity{
+ Id: 1,
+ Name: "name_100",
+ CreateAt: nil,
+ UpdateAt: nil,
+ DeleteAt: nil,
+ }
+ r, err = db.Model(table).Data(dataReplace).OmitEmpty().Replace()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneReplace, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneReplace["ID"].Int(), 1)
+ t.Assert(oneReplace["NAME"].String(), "name_100")
+ t.Assert(oneReplace["DELETE_AT"].String(), "")
+ t.AssertGE(oneReplace["CREATE_AT"].GTime().Timestamp(), oneInsert["CREATE_AT"].GTime().Timestamp())
+ t.AssertGE(oneReplace["UPDATE_AT"].GTime().Timestamp(), oneInsert["UPDATE_AT"].GTime().Timestamp())
+
+ time.Sleep(2 * time.Second)
+
+ // Delete
+ r, err = db.Model(table).Delete("id", 1)
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+ // Delete Select
+ one4, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one4), 0)
+ one5, err := db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one5["ID"].Int(), 1)
+ t.AssertGE(one5["DELETE_AT"].GTime().Timestamp(), gtime.Timestamp()-2)
+ // Delete Count
+ i, err := db.Model(table).Count()
+ t.AssertNil(err)
+ t.Assert(i, 0)
+ i, err = db.Model(table).Unscoped().Count()
+ t.AssertNil(err)
+ t.Assert(i, 1)
+
+ // Delete Unscoped
+ r, err = db.Model(table).Unscoped().Delete("id", 1)
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+ one6, err := db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one6), 0)
+ i, err = db.Model(table).Unscoped().Count()
+ t.AssertNil(err)
+ t.Assert(i, 0)
+ })
+}
+
+func Test_SoftTime_CreateUpdateDelete_UnixTimestamp(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ create_at INT DEFAULT NULL,
+ update_at INT DEFAULT NULL,
+ delete_at INT DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ // insert
+ gtest.C(t, func(t *gtest.T) {
+ dataInsert := g.Map{
+ "id": 1,
+ "name": "name_1",
+ }
+ r, err := db.Model(table).Data(dataInsert).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one["NAME"].String(), "name_1")
+ t.AssertGT(one["CREATE_AT"].Int64(), 0)
+ t.AssertGT(one["UPDATE_AT"].Int64(), 0)
+ t.Assert(one["DELETE_AT"].Int64(), 0)
+ t.Assert(len(one["CREATE_AT"].String()), 10)
+ t.Assert(len(one["UPDATE_AT"].String()), 10)
+ })
+
+ // sleep some seconds to make update time greater than create time.
+ time.Sleep(2 * time.Second)
+
+ // update
+ gtest.C(t, func(t *gtest.T) {
+ // update: map
+ dataInsert := g.Map{
+ "name": "name_11",
+ }
+ r, err := db.Model(table).Data(dataInsert).WherePri(1).Update()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one["NAME"].String(), "name_11")
+ t.AssertGT(one["CREATE_AT"].Int64(), 0)
+ t.AssertGT(one["UPDATE_AT"].Int64(), 0)
+ t.Assert(one["DELETE_AT"].Int64(), 0)
+ t.Assert(len(one["CREATE_AT"].String()), 10)
+ t.Assert(len(one["UPDATE_AT"].String()), 10)
+
+ var (
+ lastCreateTime = one["CREATE_AT"].Int64()
+ lastUpdateTime = one["UPDATE_AT"].Int64()
+ )
+
+ time.Sleep(2 * time.Second)
+
+ // update: string
+ r, err = db.Model(table).Data("name='name_111'").WherePri(1).Update()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err = db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one["NAME"].String(), "name_111")
+ t.Assert(one["CREATE_AT"].Int64(), lastCreateTime)
+ t.AssertGT(one["UPDATE_AT"].Int64(), lastUpdateTime)
+ t.Assert(one["DELETE_AT"].Int64(), 0)
+ })
+
+ // delete
+ gtest.C(t, func(t *gtest.T) {
+ r, err := db.Model(table).WherePri(1).Delete()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one), 0)
+
+ one, err = db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one["NAME"].String(), "name_111")
+ t.AssertGT(one["CREATE_AT"].Int64(), 0)
+ t.AssertGT(one["UPDATE_AT"].Int64(), 0)
+ t.AssertGT(one["DELETE_AT"].Int64(), 0)
+ })
+}
+
+func Test_SoftTime_CreateUpdateDelete_Bool_Deleted(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ // do not use BIT(1) but use BIT in dm database as bool type.
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ create_at INT DEFAULT NULL,
+ update_at INT DEFAULT NULL,
+ delete_at BIT DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ // db.SetDebug(true)
+ // insert
+ gtest.C(t, func(t *gtest.T) {
+ dataInsert := g.Map{
+ "id": 1,
+ "name": "name_1",
+ }
+ r, err := db.Model(table).Data(dataInsert).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one["NAME"].String(), "name_1")
+ t.AssertGT(one["CREATE_AT"].Int64(), 0)
+ t.AssertGT(one["UPDATE_AT"].Int64(), 0)
+ t.Assert(one["DELETE_AT"].Int64(), 0)
+ t.Assert(len(one["CREATE_AT"].String()), 10)
+ t.Assert(len(one["UPDATE_AT"].String()), 10)
+ })
+
+ // delete
+ gtest.C(t, func(t *gtest.T) {
+ r, err := db.Model(table).WherePri(1).Delete()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one), 0)
+
+ one, err = db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one["NAME"].String(), "name_1")
+ t.AssertGT(one["CREATE_AT"].Int64(), 0)
+ t.AssertGT(one["UPDATE_AT"].Int64(), 0)
+ t.Assert(one["DELETE_AT"].Int64(), 1)
+ })
+}
+
+func Test_SoftTime_CreateUpdateDelete_Option_SoftTimeTypeTimestampMilli(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ create_at BIGINT DEFAULT NULL,
+ update_at BIGINT DEFAULT NULL,
+ delete_at BIT DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ var softTimeOption = gdb.SoftTimeOption{
+ SoftTimeType: gdb.SoftTimeTypeTimestampMilli,
+ }
+
+ // insert
+ gtest.C(t, func(t *gtest.T) {
+ dataInsert := g.Map{
+ "id": 1,
+ "name": "name_1",
+ }
+ r, err := db.Model(table).SoftTime(softTimeOption).Data(dataInsert).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one["NAME"].String(), "name_1")
+ t.Assert(len(one["CREATE_AT"].String()), 13)
+ t.Assert(len(one["UPDATE_AT"].String()), 13)
+ t.Assert(one["DELETE_AT"].Int64(), 0)
+ })
+
+ // delete
+ gtest.C(t, func(t *gtest.T) {
+ r, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).Delete()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one), 0)
+
+ one, err = db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one["NAME"].String(), "name_1")
+ t.AssertGT(one["CREATE_AT"].Int64(), 0)
+ t.AssertGT(one["UPDATE_AT"].Int64(), 0)
+ t.Assert(one["DELETE_AT"].Int64(), 1)
+ })
+}
+
+func Test_SoftTime_CreateUpdateDelete_Option_SoftTimeTypeTimestampNano(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ create_at BIGINT DEFAULT NULL,
+ update_at BIGINT DEFAULT NULL,
+ delete_at BIT DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ var softTimeOption = gdb.SoftTimeOption{
+ SoftTimeType: gdb.SoftTimeTypeTimestampNano,
+ }
+
+ // insert
+ gtest.C(t, func(t *gtest.T) {
+ dataInsert := g.Map{
+ "id": 1,
+ "name": "name_1",
+ }
+ r, err := db.Model(table).SoftTime(softTimeOption).Data(dataInsert).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one["NAME"].String(), "name_1")
+ t.Assert(len(one["CREATE_AT"].String()), 19)
+ t.Assert(len(one["UPDATE_AT"].String()), 19)
+ t.Assert(one["DELETE_AT"].Int64(), 0)
+ })
+
+ // delete
+ gtest.C(t, func(t *gtest.T) {
+ r, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).Delete()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(len(one), 0)
+
+ one, err = db.Model(table).Unscoped().WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(one["NAME"].String(), "name_1")
+ t.AssertGT(one["CREATE_AT"].Int64(), 0)
+ t.AssertGT(one["UPDATE_AT"].Int64(), 0)
+ t.Assert(one["DELETE_AT"].Int64(), 1)
+ })
+}
+
+func Test_SoftTime_CreateUpdateDelete_Specified(t *testing.T) {
+ table := "soft_time_test_table_" + gtime.TimestampNanoStr()
+ if _, err := db.Exec(ctx, fmt.Sprintf(`
+CREATE TABLE %s (
+ id INT NOT NULL,
+ name VARCHAR(45) DEFAULT NULL,
+ create_at TIMESTAMP(0) DEFAULT NULL,
+ update_at TIMESTAMP(0) DEFAULT NULL,
+ delete_at TIMESTAMP(0) DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+ `, table)); err != nil {
+ gtest.Error(err)
+ }
+ defer dropTable(table)
+
+ gtest.C(t, func(t *gtest.T) {
+ // Insert
+ dataInsert := g.Map{
+ "id": 1,
+ "name": "name_1",
+ "create_at": gtime.NewFromStr("2024-05-30 20:00:00"),
+ "update_at": gtime.NewFromStr("2024-05-30 20:00:00"),
+ }
+ r, err := db.Model(table).Data(dataInsert).Insert()
+ t.AssertNil(err)
+ n, _ := r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneInsert, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneInsert["ID"].Int(), 1)
+ t.Assert(oneInsert["NAME"].String(), "name_1")
+ t.Assert(oneInsert["DELETE_AT"].String(), "")
+ t.Assert(oneInsert["CREATE_AT"].String(), "2024-05-30 20:00:00")
+ t.Assert(oneInsert["UPDATE_AT"].String(), "2024-05-30 20:00:00")
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Save
+ dataSave := g.Map{
+ "id": 1,
+ "name": "name_10",
+ "update_at": gtime.NewFromStr("2024-05-30 20:15:00"),
+ }
+ r, err = db.Model(table).Data(dataSave).Save()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneSave, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneSave["ID"].Int(), 1)
+ t.Assert(oneSave["NAME"].String(), "name_10")
+ t.Assert(oneSave["DELETE_AT"].String(), "")
+ t.Assert(oneSave["CREATE_AT"].String(), "2024-05-30 20:00:00")
+ t.Assert(oneSave["UPDATE_AT"].String(), "2024-05-30 20:15:00")
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Update
+ dataUpdate := g.Map{
+ "name": "name_1000",
+ "update_at": gtime.NewFromStr("2024-05-30 20:30:00"),
+ }
+ r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneUpdate, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneUpdate["ID"].Int(), 1)
+ t.Assert(oneUpdate["NAME"].String(), "name_1000")
+ t.Assert(oneUpdate["DELETE_AT"].String(), "")
+ t.Assert(oneUpdate["CREATE_AT"].String(), "2024-05-30 20:00:00")
+ t.Assert(oneUpdate["UPDATE_AT"].String(), "2024-05-30 20:30:00")
+
+ // Replace
+ dataReplace := g.Map{
+ "id": 1,
+ "name": "name_100",
+ "create_at": gtime.NewFromStr("2024-05-30 21:00:00"),
+ "update_at": gtime.NewFromStr("2024-05-30 21:00:00"),
+ }
+ r, err = db.Model(table).Data(dataReplace).Replace()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ oneReplace, err := db.Model(table).WherePri(1).One()
+ t.AssertNil(err)
+ t.Assert(oneReplace["ID"].Int(), 1)
+ t.Assert(oneReplace["NAME"].String(), "name_100")
+ t.Assert(oneReplace["DELETE_AT"].String(), "")
+ t.AssertGE(oneReplace["CREATE_AT"].GTime().Timestamp(), oneInsert["CREATE_AT"].GTime().Timestamp())
+ t.AssertGE(oneReplace["UPDATE_AT"].GTime().Timestamp(), oneInsert["UPDATE_AT"].GTime().Timestamp())
+
+ // For time asserting purpose.
+ time.Sleep(2 * time.Second)
+
+ // Insert with delete_at
+ dataInsertDelete := g.Map{
+ "id": 2,
+ "name": "name_2",
+ "create_at": gtime.NewFromStr("2024-05-30 20:00:00"),
+ "update_at": gtime.NewFromStr("2024-05-30 20:00:00"),
+ "delete_at": gtime.NewFromStr("2024-05-30 20:00:00"),
+ }
+ r, err = db.Model(table).Data(dataInsertDelete).Insert()
+ t.AssertNil(err)
+ n, _ = r.RowsAffected()
+ t.Assert(n, 1)
+
+ // Delete Select
+ oneDelete, err := db.Model(table).WherePri(2).One()
+ t.AssertNil(err)
+ t.Assert(len(oneDelete), 0)
+ oneDeleteUnscoped, err := db.Model(table).Unscoped().WherePri(2).One()
+ t.AssertNil(err)
+ t.Assert(oneDeleteUnscoped["ID"].Int(), 2)
+ t.Assert(oneDeleteUnscoped["NAME"].String(), "name_2")
+ t.Assert(oneDeleteUnscoped["DELETE_AT"].String(), "2024-05-30 20:00:00")
+ t.Assert(oneDeleteUnscoped["CREATE_AT"].String(), "2024-05-30 20:00:00")
+ t.Assert(oneDeleteUnscoped["UPDATE_AT"].String(), "2024-05-30 20:00:00")
+ })
+}
diff --git a/contrib/drivers/dm/dm_z_unit_init_test.go b/contrib/drivers/dm/dm_z_unit_init_test.go
index 94d056716..100329a70 100644
--- a/contrib/drivers/dm/dm_z_unit_init_test.go
+++ b/contrib/drivers/dm/dm_z_unit_init_test.go
@@ -63,8 +63,8 @@ func init() {
Weight: 1,
MaxIdleConnCount: 10,
MaxOpenConnCount: 10,
- CreatedAt: "created_time",
- UpdatedAt: "updated_time",
+ // CreatedAt: "created_time",
+ // UpdatedAt: "updated_time",
}
nodeLink := gdb.ConfigNode{
diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go
index d34c58ed3..56d10bd35 100644
--- a/database/gdb/gdb_core.go
+++ b/database/gdb/gdb_core.go
@@ -446,8 +446,10 @@ func (c *Core) DoInsert(ctx context.Context, link Link, table string, list List,
// Group the list by fields. Different fields to different list.
// It here uses ListMap to keep sequence for data inserting.
// ============================================================================================
- var keyListMap = gmap.NewListMap()
- var tmpkeyListMap = make(map[string]List)
+ var (
+ keyListMap = gmap.NewListMap()
+ tmpKeyListMap = make(map[string]List)
+ )
for _, item := range list {
mapLen := len(item)
if mapLen == 0 {
@@ -463,13 +465,13 @@ func (c *Core) DoInsert(ctx context.Context, link Link, table string, list List,
keys = tmpKeys // for fieldsToSequence
tmpKeysInSequenceStr := gstr.Join(tmpKeys, ",")
- if tmpkeyListMapItem, ok := tmpkeyListMap[tmpKeysInSequenceStr]; ok {
- tmpkeyListMap[tmpKeysInSequenceStr] = append(tmpkeyListMapItem, item)
+ if tmpKeyListMapItem, ok := tmpKeyListMap[tmpKeysInSequenceStr]; ok {
+ tmpKeyListMap[tmpKeysInSequenceStr] = append(tmpKeyListMapItem, item)
} else {
- tmpkeyListMap[tmpKeysInSequenceStr] = List{item}
+ tmpKeyListMap[tmpKeysInSequenceStr] = List{item}
}
}
- for tmpKeysInSequenceStr, itemList := range tmpkeyListMap {
+ for tmpKeysInSequenceStr, itemList := range tmpKeyListMap {
keyListMap.Set(tmpKeysInSequenceStr, itemList)
}
if keyListMap.Size() > 1 {
diff --git a/database/gdb/gdb_model_soft_time.go b/database/gdb/gdb_model_soft_time.go
index 3972bf647..1ddcdcb12 100644
--- a/database/gdb/gdb_model_soft_time.go
+++ b/database/gdb/gdb_model_soft_time.go
@@ -380,6 +380,7 @@ func (m *softTimeMaintainer) GetValueByFieldTypeForCreateOrUpdate(
ctx context.Context, fieldType LocalType, isDeletedField bool,
) any {
var value any
+ // for create or update procedure, the deleted field is always set to non-deleted value.
if isDeletedField {
switch fieldType {
case LocalTypeDate, LocalTypeTime, LocalTypeDatetime: