Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28b441dca8 | ||
|
|
936ee38878 | ||
|
|
a0df5e4e67 | ||
|
|
0302fe7595 | ||
|
|
5f67d49ab3 | ||
|
|
cf328abfb6 | ||
|
|
f6e05873e5 | ||
|
|
f19cd4d393 | ||
|
|
bc3d10a339 | ||
|
|
03d8045963 | ||
|
|
01911ebce2 | ||
|
|
fed86ab96c | ||
|
|
eda7e6e8e0 | ||
|
|
21aac3b374 | ||
|
|
8bd8844b8b | ||
|
|
3c5b4ece9d | ||
|
|
14f28821ce | ||
|
|
989296554b | ||
|
|
c38d16c780 |
@@ -1,23 +0,0 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: setup go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: Use Go Action
|
||||
id: use-go-action
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
api_key: '${{secrets.RELEASE_TOKEN}}'
|
||||
@@ -1,33 +0,0 @@
|
||||
name: test cockroach
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- donttrigger # disabled for now
|
||||
#- main
|
||||
#- v1
|
||||
#pull_request:
|
||||
|
||||
jobs:
|
||||
test-cockroach:
|
||||
name: test cockroach
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: test cockroach
|
||||
env:
|
||||
TEST_COCKROACH_HOST: "cockroach:26257"
|
||||
TEST_COCKROACH_DBNAME: xorm_test
|
||||
TEST_COCKROACH_USERNAME: root
|
||||
TEST_COCKROACH_PASSWORD:
|
||||
IGNORE_TEST_DELETE_LIMIT: true
|
||||
run: sleep 20 && make test-cockroach
|
||||
|
||||
services:
|
||||
cockroach:
|
||||
image: cockroachdb/cockroach:v19.2.4
|
||||
cmd:
|
||||
- 'start'
|
||||
- '--insecure'
|
||||
@@ -1,32 +0,0 @@
|
||||
name: test mariadb
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v1
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: test mariadb
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: test mariadb
|
||||
env:
|
||||
TEST_MYSQL_HOST: mariadb
|
||||
TEST_MYSQL_CHARSET: utf8mb4
|
||||
TEST_MYSQL_DBNAME: xorm_test
|
||||
TEST_MYSQL_USERNAME: root
|
||||
TEST_MYSQL_PASSWORD:
|
||||
run: TEST_QUOTE_POLICY=reserved make test-mysql
|
||||
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:10.4
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
MYSQL_DATABASE: xorm_test
|
||||
@@ -1,33 +0,0 @@
|
||||
name: test mssql
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v1
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test-mssql-collation:
|
||||
name: test mssql with collation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: test mssql with collation
|
||||
env:
|
||||
TEST_MSSQL_HOST: mssql2
|
||||
TEST_MSSQL_DBNAME: xorm_test
|
||||
TEST_MSSQL_USERNAME: sa
|
||||
TEST_MSSQL_PASSWORD: "yourStrong(!)Password"
|
||||
TEST_MSSQL_COLLATION: SQL_Latin1_General_CP1_CS_AS
|
||||
run: TEST_MSSQL_DEFAULT_VARCHAR=NVARCHAR TEST_MSSQL_DEFAULT_CHAR=NCHAR make test-mssql
|
||||
|
||||
services:
|
||||
mssql2:
|
||||
image: mcr.microsoft.com/mssql/server:latest
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
SA_PASSWORD: yourStrong(!)Password
|
||||
MSSQL_PID: Standard
|
||||
@@ -1,32 +0,0 @@
|
||||
name: test mssql
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v1
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test-mssql:
|
||||
name: test mssql
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: test mssql
|
||||
env:
|
||||
TEST_MSSQL_HOST: mssql
|
||||
TEST_MSSQL_DBNAME: xorm_test
|
||||
TEST_MSSQL_USERNAME: sa
|
||||
TEST_MSSQL_PASSWORD: "yourStrong(!)Password"
|
||||
run: TEST_MSSQL_DEFAULT_VARCHAR=NVARCHAR TEST_MSSQL_DEFAULT_CHAR=NCHAR make test-mssql
|
||||
|
||||
services:
|
||||
mssql:
|
||||
image: mcr.microsoft.com/mssql/server:latest
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
SA_PASSWORD: yourStrong(!)Password
|
||||
MSSQL_PID: Standard
|
||||
@@ -1,32 +0,0 @@
|
||||
name: test mysql
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v1
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test-mysql:
|
||||
name: test mysql
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: test mysql utf8mb4
|
||||
env:
|
||||
TEST_MYSQL_HOST: mysql
|
||||
TEST_MYSQL_CHARSET: utf8mb4
|
||||
TEST_MYSQL_DBNAME: xorm_test
|
||||
TEST_MYSQL_USERNAME: root
|
||||
TEST_MYSQL_PASSWORD:
|
||||
run: TEST_QUOTE_POLICY=reserved make test-mysql-tls
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
MYSQL_DATABASE: xorm_test
|
||||
@@ -1,32 +0,0 @@
|
||||
name: test mysql8
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v1
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: test mysql8
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: test mysql8
|
||||
env:
|
||||
TEST_MYSQL_HOST: mysql8
|
||||
TEST_MYSQL_CHARSET: utf8mb4
|
||||
TEST_MYSQL_DBNAME: xorm_test
|
||||
TEST_MYSQL_USERNAME: root
|
||||
TEST_MYSQL_PASSWORD:
|
||||
run: TEST_CACHE_ENABLE=true make test-mysql
|
||||
|
||||
services:
|
||||
mysql8:
|
||||
image: mysql:8.0
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
MYSQL_DATABASE: xorm_test
|
||||
@@ -1,55 +0,0 @@
|
||||
name: test postgres
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v1
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: test postgres
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: test postgres
|
||||
env:
|
||||
TEST_PGSQL_HOST: pgsql
|
||||
TEST_PGSQL_DBNAME: xorm_test
|
||||
TEST_PGSQL_USERNAME: postgres
|
||||
TEST_PGSQL_PASSWORD: postgres
|
||||
run: TEST_CACHE_ENABLE=true make test-postgres
|
||||
- name: test postgres with schema
|
||||
env:
|
||||
TEST_PGSQL_HOST: pgsql
|
||||
TEST_PGSQL_SCHEMA: xorm
|
||||
TEST_PGSQL_DBNAME: xorm_test
|
||||
TEST_PGSQL_USERNAME: postgres
|
||||
TEST_PGSQL_PASSWORD: postgres
|
||||
run: TEST_QUOTE_POLICY=reserved make test-postgres
|
||||
- name: test pgx
|
||||
env:
|
||||
TEST_PGSQL_HOST: pgsql
|
||||
TEST_PGSQL_DBNAME: xorm_test
|
||||
TEST_PGSQL_USERNAME: postgres
|
||||
TEST_PGSQL_PASSWORD: postgres
|
||||
run: TEST_CACHE_ENABLE=true make test-pgx
|
||||
- name: test pgx with schema
|
||||
env:
|
||||
TEST_PGSQL_HOST: pgsql
|
||||
TEST_PGSQL_SCHEMA: xorm
|
||||
TEST_PGSQL_DBNAME: xorm_test
|
||||
TEST_PGSQL_USERNAME: postgres
|
||||
TEST_PGSQL_PASSWORD: postgres
|
||||
run: TEST_QUOTE_POLICY=reserved make test-pgx
|
||||
|
||||
services:
|
||||
pgsql:
|
||||
image: postgres:9.5
|
||||
env:
|
||||
POSTGRES_DB: xorm_test
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -1,39 +0,0 @@
|
||||
name: test sqlite
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v1
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test-sqlite:
|
||||
name: unit test & test sqlite
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: vet
|
||||
run: make vet
|
||||
- name: format check
|
||||
run: make fmt-check
|
||||
- name: lint
|
||||
run: make lint
|
||||
- name: unit test
|
||||
run: make test
|
||||
- name: test sqlite3
|
||||
run: make test-sqlite3
|
||||
- name: test sqlite3 with cache
|
||||
run: TEST_CACHE_ENABLE=true make test-sqlite3
|
||||
|
||||
govulncheck_job:
|
||||
runs-on: ubuntu-latest
|
||||
name: Run govulncheck
|
||||
steps:
|
||||
- id: govulncheck
|
||||
uses: golang/govulncheck-action@v1
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
go-package: ./...
|
||||
@@ -1,28 +0,0 @@
|
||||
name: test tidb
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v1
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test-tidb:
|
||||
name: test tidb
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: test tidb
|
||||
env:
|
||||
TEST_TIDB_HOST: "tidb:4000"
|
||||
TEST_TIDB_DBNAME: xorm_test
|
||||
TEST_TIDB_USERNAME: root
|
||||
TEST_TIDB_PASSWORD:
|
||||
run: make test-tidb
|
||||
|
||||
services:
|
||||
tidb:
|
||||
image: pingcap/tidb:v3.0.3
|
||||
@@ -0,0 +1,8 @@
|
||||
# Instructions for agents
|
||||
|
||||
- Use `make help` to find available development targets
|
||||
- Before committing `.go` changes, run `make fmt` to format, and run `make lint` to lint
|
||||
- Before committing `go.mod` changes, run `go mod tidy`
|
||||
- Before committing new `.go` files, add the current year into the copyright header
|
||||
- Before committing any files, remove all trailing whitespace from source code lines
|
||||
- Add test code for newly added functions or new logic of old functions
|
||||
@@ -0,0 +1,37 @@
|
||||
Hanzo xorm
|
||||
Copyright (c) 2026 Hanzo AI, Inc.
|
||||
|
||||
This product includes software from xorm (the Go ORM), licensed under
|
||||
BSD-3-Clause:
|
||||
|
||||
Copyright (c) 2013 - 2015 The Xorm Authors
|
||||
|
||||
Forked from xorm.io/xorm (upstream repository https://gitea.com/xorm/xorm,
|
||||
mirrored at https://github.com/go-xorm/xorm) at commit
|
||||
ab3ffa8fa5d56bfae4cae9b0b5186c4f66cd84d1 (upstream PR #2569, "Fix bug when
|
||||
invoking session in the after load"). No upstream release tag is present in
|
||||
this fork's history; the commit above is the last upstream commit before the
|
||||
Hanzo fork.
|
||||
|
||||
DEVIATIONS
|
||||
|
||||
The following changes distinguish this fork from upstream xorm:
|
||||
|
||||
- Module path renamed from xorm.io/xorm to github.com/hanzoai/xorm; all
|
||||
internal import paths updated accordingly.
|
||||
- SQL builder dependency switched from xorm.io/builder to
|
||||
github.com/hanzoai/builder (v0.3.13).
|
||||
- CGo SQLite driver github.com/mattn/go-sqlite3 dropped in favor of the pure-Go
|
||||
github.com/hanzoai/csqlite (v0.1.0) alongside modernc.org/sqlite (v1.48.0).
|
||||
- Go language directive bumped to go 1.26.4.
|
||||
- Database driver dependencies bumped: github.com/go-sql-driver/mysql v1.8.1,
|
||||
github.com/jackc/pgx/v5 v5.6.0, github.com/microsoft/go-mssqldb v1.8.2,
|
||||
github.com/goccy/go-json v0.10.5, github.com/shopspring/decimal v1.4.0.
|
||||
- Test dependency github.com/stretchr/testify bumped to v1.11.1.
|
||||
- Hanzo project documentation added (AGENTS.md, LLM.md with a CLAUDE.md
|
||||
symlink, .changelog.yml).
|
||||
- Branding and CI/docs references migrated to Hanzo (git.hanzo.ai build
|
||||
badges, .github/hero.svg banner).
|
||||
|
||||
Dialect support (MySQL, Postgres, SQLite, MSSQL, Oracle, Dameng, GBase8s) is
|
||||
unchanged from upstream apart from the import-path rename above.
|
||||
@@ -1,10 +1,12 @@
|
||||
# xorm
|
||||
<p align="center"><img src=".github/hero.svg" alt="xorm" width="880"></p>
|
||||
|
||||
[中文](https://gitea.com/xorm/xorm/src/branch/v1/README_CN.md)
|
||||
# Hanzo xorm
|
||||
|
||||
Xorm is a simple and powerful ORM for Go.
|
||||
[中文](README_CN.md)
|
||||
|
||||
[](https://gitea.com/xorm/xorm/actions) [](https://gocover.io/xorm.io/xorm) [](https://goreportcard.com/report/xorm.io/xorm) [](https://discord.gg/HuR2CF3)
|
||||
Hanzo xorm is a simple and powerful ORM for Go — a fork of [xorm](https://gitea.com/xorm/xorm) by The Xorm Authors (BSD-3-Clause). See [NOTICE](NOTICE) for attribution and deviations.
|
||||
|
||||
[](https://git.hanzo.ai/hanzoai/xorm/actions) [](https://goreportcard.com/report/github.com/hanzoai/xorm) [](https://discord.gg/HuR2CF3)
|
||||
|
||||
## Notice
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ func (h *Hooks) BeforeProcess(c *ContextHook) (context.Context, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Ctx = ctx
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
+1
-2
@@ -14,8 +14,7 @@ import (
|
||||
"github.com/hanzoai/xorm/names"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "modernc.org/sqlite"
|
||||
_ "github.com/hanzoai/sqlite"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
Executable → Regular
@@ -1092,17 +1092,19 @@ func (db *postgres) IsColumnExist(queryer core.Queryer, ctx context.Context, tab
|
||||
func (db *postgres) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) {
|
||||
args := []any{tableName}
|
||||
s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, description,
|
||||
CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey,
|
||||
CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey
|
||||
FROM pg_attribute f
|
||||
COALESCE(bool_or(p.contype = 'p'), false) AS primarykey,
|
||||
COALESCE(bool_or(p.contype = 'u'), false) AS uniquekey
|
||||
FROM pg_attribute f
|
||||
JOIN pg_class c ON c.oid = f.attrelid JOIN pg_type t ON t.oid = f.atttypid
|
||||
LEFT JOIN pg_attrdef d ON d.adrelid = c.oid AND d.adnum = f.attnum
|
||||
LEFT JOIN pg_description de ON f.attrelid=de.objoid AND f.attnum=de.objsubid
|
||||
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
LEFT JOIN pg_constraint p ON p.conrelid = c.oid AND f.attnum = ANY (p.conkey)
|
||||
LEFT JOIN pg_class AS g ON p.confrelid = g.oid
|
||||
LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name
|
||||
WHERE n.nspname= s.table_schema AND c.relkind = 'r' AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.attnum;`
|
||||
LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name AND n.nspname = s.table_schema
|
||||
WHERE n.nspname = s.table_schema AND c.relkind = 'r' AND c.relname = $1%s AND f.attnum > 0
|
||||
GROUP BY s.column_name, s.column_default, s.is_nullable, s.data_type, s.character_maximum_length, de.description, f.attnum
|
||||
ORDER BY f.attnum;`
|
||||
|
||||
schema := db.getSchema()
|
||||
if schema != "" {
|
||||
|
||||
@@ -9,7 +9,7 @@ Package xorm is a simple and powerful ORM for Go.
|
||||
|
||||
Make sure you have installed Go 1.11+ and then:
|
||||
|
||||
go get xorm.io/xorm
|
||||
go get github.com/hanzoai/xorm
|
||||
|
||||
# Create Engine
|
||||
|
||||
@@ -205,7 +205,7 @@ Notice: the above 8 methods should be the last chainable method.
|
||||
|
||||
# Builder
|
||||
|
||||
xorm could work with xorm.io/builder directly.
|
||||
xorm could work with github.com/hanzoai/builder directly.
|
||||
|
||||
1. With Where
|
||||
|
||||
|
||||
@@ -503,6 +503,26 @@ func writeConcatWithChar(w io.Writer, value string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeSQLiteUniStr(w io.Writer, value string) error {
|
||||
var builder strings.Builder
|
||||
builder.WriteString("unistr('")
|
||||
for _, r := range value {
|
||||
switch {
|
||||
case r == '\'':
|
||||
builder.WriteString("''")
|
||||
case r == '\\':
|
||||
builder.WriteString("\\\\")
|
||||
case r < 0x20 || r == 0x7f:
|
||||
fmt.Fprintf(&builder, "\\u%04x", r)
|
||||
default:
|
||||
builder.WriteRune(r)
|
||||
}
|
||||
}
|
||||
builder.WriteString("')")
|
||||
_, err := io.WriteString(w, builder.String())
|
||||
return err
|
||||
}
|
||||
|
||||
// dumpTables dump database all table structs and data to w with specify db type
|
||||
func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error {
|
||||
var dstDialect dialects.Dialect
|
||||
@@ -548,7 +568,7 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w
|
||||
if table.Type != nil {
|
||||
dstTable, err = dstTableCache.Parse(reflect.New(table.Type).Elem())
|
||||
if err != nil {
|
||||
engine.logger.Errorf("Unable to infer table for %s in new dialect. Error: %v", table.Name)
|
||||
engine.logger.Errorf("Unable to infer table for %s in new dialect. Error: %v", table.Name, err)
|
||||
dstTable = table
|
||||
}
|
||||
}
|
||||
@@ -727,29 +747,13 @@ func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// SQLite concatentates strings using || (NOTE: a NUL byte in a text segment will fail)
|
||||
toCheck := strings.ReplaceAll(s.String, "'", "''")
|
||||
for len(toCheck) > 0 {
|
||||
loc := controlCharactersRe.FindStringIndex(toCheck)
|
||||
if loc == nil {
|
||||
if _, err := io.WriteString(w, "'"+toCheck+"'"); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
if loc[0] > 0 {
|
||||
if _, err := io.WriteString(w, "'"+toCheck[:loc[0]]+"' || "); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := fmt.Fprintf(w, "X'%x'", toCheck[loc[0]:loc[1]]); err != nil {
|
||||
if controlCharactersRe.MatchString(s.String) {
|
||||
if err := writeSQLiteUniStr(w, s.String); err != nil {
|
||||
return err
|
||||
}
|
||||
toCheck = toCheck[loc[1]:]
|
||||
if len(toCheck) > 0 {
|
||||
if _, err := io.WriteString(w, " || "); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := io.WriteString(w, "'"+strings.ReplaceAll(s.String, "'", "''")+"'"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
module github.com/hanzoai/xorm
|
||||
|
||||
go 1.20
|
||||
go 1.26.4
|
||||
|
||||
require (
|
||||
gitee.com/travelliu/dm v1.8.11192
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/goccy/go-json v0.10.5
|
||||
github.com/hanzoai/builder v0.3.13
|
||||
github.com/hanzoai/sqlite v0.3.2
|
||||
github.com/jackc/pgx/v5 v5.6.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mattn/go-sqlite3 v1.14.32
|
||||
github.com/microsoft/go-mssqldb v1.8.2
|
||||
github.com/shopspring/decimal v1.4.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/ziutek/mymysql v1.5.4
|
||||
modernc.org/sqlite v1.20.4
|
||||
github.com/hanzoai/builder v0.3.13
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -26,31 +25,21 @@ require (
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hanzoai/csqlite v0.1.0 // indirect
|
||||
github.com/hanzoai/sqlcipher v0.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rogpeppe/go-internal v1.6.1 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
golang.org/x/crypto v0.50.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.55.3 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
)
|
||||
|
||||
@@ -5,11 +5,17 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0p
|
||||
gitee.com/travelliu/dm v1.8.11192 h1:aqJT0xhodZjRutIfEXxKYv0CxqmHUHzsbz6SFaRL6OY=
|
||||
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -22,6 +28,7 @@ github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqw
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
@@ -31,10 +38,16 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hanzoai/builder v0.3.13 h1:tAOJ+0Q0xrrovk7lkvaZxuKZ4lqENIB6tE0Rr9+6Bo8=
|
||||
github.com/hanzoai/builder v0.3.13/go.mod h1:TWZaiP0Y9tCMwtLH2EvQqBAeT1f3aJI5Y0XPM8S0wcE=
|
||||
github.com/hanzoai/csqlite v0.1.0 h1:suwC3dh0INlfP/U0Es6cDf6JNQ+2+GVLLATPWCUux6k=
|
||||
github.com/hanzoai/csqlite v0.1.0/go.mod h1:H31a/O6VXuklR9UBkgY++bmAK5uzVfXPqU0F6P9Wsos=
|
||||
github.com/hanzoai/sqlcipher v0.1.0 h1:V9gKG3ZltN2ZCteDrOnXWfOeEe/YDhhUm9AorQEAuBo=
|
||||
github.com/hanzoai/sqlcipher v0.1.0/go.mod h1:F0soUYM1i4sawOZUpRvVnWoUayPbeGVlGq01VXy9Aqg=
|
||||
github.com/hanzoai/sqlite v0.3.2 h1:B/TRunlIDZECEypmr6rHeNyWf37YZXvPJHBDEFMKn/g=
|
||||
github.com/hanzoai/sqlite v0.3.2/go.mod h1:a3llsefKbu2Iq/0rJ1mlWCaU2t2cXh+aze85x+oW72k=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
@@ -45,31 +58,30 @@ github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/microsoft/go-mssqldb v1.8.2 h1:236sewazvC8FvG6Dr3bszrVhMkAl4KYImryLkRMCd0I=
|
||||
github.com/microsoft/go-mssqldb v1.8.2/go.mod h1:vp38dT33FGfVotRiTmDo3bFyaHq+p3LektQrjTULowo=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
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/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
@@ -87,29 +99,27 @@ github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFd
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
@@ -121,34 +131,3 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
|
||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
|
||||
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sqlite v1.20.4 h1:J8+m2trkN+KKoE7jglyHYYYiaq5xmz2HoHJIiBlRzbE=
|
||||
modernc.org/sqlite v1.20.4/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
|
||||
xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo=
|
||||
xorm.io/builder v0.3.13/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
|
||||
@@ -6,6 +6,7 @@ package statements
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/hanzoai/builder"
|
||||
@@ -22,6 +23,14 @@ func (n DateTimeString) Value() (driver.Value, error) {
|
||||
return n.Str, nil
|
||||
}
|
||||
|
||||
func (d DateTimeString) MarshalText() (text []byte, err error) {
|
||||
return []byte(d.Str), nil
|
||||
}
|
||||
|
||||
func (n DateTimeString) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(n.Str)
|
||||
}
|
||||
|
||||
// WriteArg writes an arg
|
||||
func (statement *Statement) WriteArg(w *builder.BytesWriter, arg any) error {
|
||||
switch argv := arg.(type) {
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"github.com/hanzoai/xorm/schemas"
|
||||
"github.com/hanzoai/xorm/tags"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "github.com/hanzoai/sqlite"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "github.com/hanzoai/sqlite"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/hanzoai/xorm"
|
||||
)
|
||||
|
||||
@@ -5,12 +5,167 @@
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/hanzoai/xorm/internal/utils"
|
||||
"github.com/hanzoai/xorm/schemas"
|
||||
)
|
||||
|
||||
var displayWidthNumericTypes = map[string]struct{}{
|
||||
schemas.BigInt: {},
|
||||
schemas.Int: {},
|
||||
schemas.Integer: {},
|
||||
schemas.MediumInt: {},
|
||||
schemas.SmallInt: {},
|
||||
schemas.TinyInt: {},
|
||||
}
|
||||
|
||||
func columnDefaultsMatch(col, oriCol *schemas.Column) bool {
|
||||
switch {
|
||||
case col.IsAutoIncrement:
|
||||
return true
|
||||
case col.DefaultIsEmpty && oriCol.DefaultIsEmpty:
|
||||
return true
|
||||
case col.DefaultIsEmpty:
|
||||
return nullableNullDefault(col, oriCol)
|
||||
case oriCol.DefaultIsEmpty:
|
||||
return nullableNullDefault(oriCol, col)
|
||||
}
|
||||
|
||||
return normalizeColumnDefaultValue(col.SQLType, col.Default) ==
|
||||
normalizeColumnDefaultValue(col.SQLType, oriCol.Default)
|
||||
}
|
||||
|
||||
func nullableNullDefault(emptyDefaultCol, explicitDefaultCol *schemas.Column) bool {
|
||||
if !emptyDefaultCol.Nullable || !explicitDefaultCol.Nullable {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.EqualFold(strings.TrimSpace(explicitDefaultCol.Default), "NULL")
|
||||
}
|
||||
|
||||
func normalizeColumnDefaultValue(sqlType schemas.SQLType, defaultValue string) string {
|
||||
normalized := strings.TrimSpace(defaultValue)
|
||||
if normalized == "" {
|
||||
return normalized
|
||||
}
|
||||
|
||||
if sqlType.IsBool() {
|
||||
switch strings.ToLower(trimDefaultQuotes(normalized)) {
|
||||
case "1", "true":
|
||||
return "true"
|
||||
case "0", "false":
|
||||
return "false"
|
||||
}
|
||||
}
|
||||
|
||||
if sqlType.IsNumeric() {
|
||||
if numeric, ok := normalizeNumericDefaultValue(normalized); ok {
|
||||
return numeric
|
||||
}
|
||||
}
|
||||
|
||||
return normalized
|
||||
}
|
||||
|
||||
func normalizeNumericDefaultValue(defaultValue string) (string, bool) {
|
||||
normalized := trimDefaultQuotes(defaultValue)
|
||||
rat, ok := new(big.Rat).SetString(normalized)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return rat.RatString(), true
|
||||
}
|
||||
|
||||
func trimDefaultQuotes(defaultValue string) string {
|
||||
if len(defaultValue) < 2 {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
if (defaultValue[0] == '\'' && defaultValue[len(defaultValue)-1] == '\'') ||
|
||||
(defaultValue[0] == '"' && defaultValue[len(defaultValue)-1] == '"') {
|
||||
return defaultValue[1 : len(defaultValue)-1]
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func normalizeColumnTypeForComparison(alias func(string) string, columnType string) string {
|
||||
normalized := strings.ToUpper(strings.TrimSpace(columnType))
|
||||
if normalized == "" {
|
||||
return normalized
|
||||
}
|
||||
|
||||
unsignedSuffix := ""
|
||||
if strings.HasSuffix(normalized, " UNSIGNED") {
|
||||
unsignedSuffix = " UNSIGNED"
|
||||
normalized = strings.TrimSpace(strings.TrimSuffix(normalized, unsignedSuffix))
|
||||
}
|
||||
|
||||
baseType := strings.ToUpper(strings.TrimSpace(schemas.SQLTypeName(normalized)))
|
||||
typeSuffix := strings.TrimPrefix(normalized, baseType)
|
||||
if _, ok := displayWidthNumericTypes[baseType]; ok {
|
||||
normalized = baseType
|
||||
} else {
|
||||
normalized = baseType + typeSuffix
|
||||
}
|
||||
|
||||
if alias != nil {
|
||||
aliasedBaseType := strings.ToUpper(alias(baseType))
|
||||
if _, ok := displayWidthNumericTypes[baseType]; ok {
|
||||
normalized = aliasedBaseType
|
||||
} else {
|
||||
normalized = aliasedBaseType + typeSuffix
|
||||
}
|
||||
}
|
||||
|
||||
if unsignedSuffix != "" && !strings.HasSuffix(normalized, unsignedSuffix) {
|
||||
normalized += unsignedSuffix
|
||||
}
|
||||
|
||||
return normalized
|
||||
}
|
||||
|
||||
func columnUsesCompatibleJSONType(col *schemas.Column, currentType string) bool {
|
||||
if col == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
currentBaseType := strings.ToUpper(strings.TrimSpace(schemas.SQLTypeName(currentType)))
|
||||
switch {
|
||||
case col.IsJSONB:
|
||||
return currentBaseType == schemas.Jsonb
|
||||
case col.IsJSON:
|
||||
return currentBaseType == schemas.Json
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func columnTypesMatch(alias func(string) string, expectedCol, currentCol *schemas.Column, expectedType, currentType string) bool {
|
||||
if alias == nil {
|
||||
alias = func(columnType string) string {
|
||||
return columnType
|
||||
}
|
||||
}
|
||||
|
||||
if expectedType == currentType {
|
||||
return true
|
||||
}
|
||||
|
||||
if columnUsesCompatibleJSONType(expectedCol, currentType) || columnUsesCompatibleJSONType(currentCol, expectedType) {
|
||||
return true
|
||||
}
|
||||
|
||||
if normalizeColumnTypeForComparison(alias, expectedType) == normalizeColumnTypeForComparison(alias, currentType) {
|
||||
return true
|
||||
}
|
||||
|
||||
return strings.EqualFold(schemas.SQLTypeName(currentType), alias(schemas.SQLTypeName(expectedType)))
|
||||
}
|
||||
|
||||
type SyncOptions struct {
|
||||
WarnIfDatabaseColumnMissed bool
|
||||
// IgnoreConstrains will not add, delete or update unique constrains
|
||||
@@ -158,7 +313,7 @@ func (session *Session) SyncWithOptions(opts SyncOptions, beans ...any) (*SyncRe
|
||||
expectedType := engine.dialect.SQLType(col)
|
||||
curType := engine.dialect.SQLType(oriCol)
|
||||
switch {
|
||||
case expectedType != curType:
|
||||
case !columnTypesMatch(engine.dialect.Alias, col, oriCol, expectedType, curType):
|
||||
switch {
|
||||
case expectedType == schemas.Text && strings.HasPrefix(curType, schemas.Varchar):
|
||||
// currently only support mysql & postgres
|
||||
@@ -182,7 +337,7 @@ func (session *Session) SyncWithOptions(opts SyncOptions, beans ...any) (*SyncRe
|
||||
}
|
||||
default:
|
||||
if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') {
|
||||
if !strings.EqualFold(schemas.SQLTypeName(curType), engine.dialect.Alias(schemas.SQLTypeName(expectedType))) {
|
||||
if !columnTypesMatch(engine.dialect.Alias, col, oriCol, expectedType, curType) {
|
||||
engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s",
|
||||
tbNameWithSchema, col.Name, curType, expectedType)
|
||||
}
|
||||
@@ -205,16 +360,9 @@ func (session *Session) SyncWithOptions(opts SyncOptions, beans ...any) (*SyncRe
|
||||
}
|
||||
}
|
||||
|
||||
if col.Default != oriCol.Default {
|
||||
switch {
|
||||
case col.IsAutoIncrement: // For autoincrement column, don't check default
|
||||
case (col.SQLType.Name == schemas.Bool || col.SQLType.Name == schemas.Boolean) &&
|
||||
((strings.EqualFold(col.Default, "true") && oriCol.Default == "1") ||
|
||||
(strings.EqualFold(col.Default, "false") && oriCol.Default == "0")):
|
||||
default:
|
||||
engine.logger.Warnf("Table %s Column %s db default is %s, struct default is %s",
|
||||
tbName, col.Name, oriCol.Default, col.Default)
|
||||
}
|
||||
if !columnDefaultsMatch(col, oriCol) {
|
||||
engine.logger.Warnf("Table %s Column %s db default is %s, struct default is %s",
|
||||
tbName, col.Name, oriCol.Default, col.Default)
|
||||
}
|
||||
if col.Nullable != oriCol.Nullable {
|
||||
engine.logger.Warnf("Table %s Column %s db nullable is %v, struct nullable is %v",
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
package xorm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
_ "github.com/hanzoai/sqlite"
|
||||
|
||||
"github.com/hanzoai/xorm/log"
|
||||
"github.com/hanzoai/xorm/schemas"
|
||||
)
|
||||
|
||||
func TestColumnDefaultsMatch(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
col *schemas.Column
|
||||
ori *schemas.Column
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "bool keyword casing",
|
||||
col: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Bool},
|
||||
Default: "TRUE",
|
||||
DefaultIsEmpty: false,
|
||||
},
|
||||
ori: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Bool},
|
||||
Default: "true",
|
||||
DefaultIsEmpty: false,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "bool numeric form",
|
||||
col: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Boolean},
|
||||
Default: "FALSE",
|
||||
DefaultIsEmpty: false,
|
||||
},
|
||||
ori: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Boolean},
|
||||
Default: "0",
|
||||
DefaultIsEmpty: false,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "numeric quoted in database",
|
||||
col: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Int},
|
||||
Default: "-1",
|
||||
DefaultIsEmpty: false,
|
||||
},
|
||||
ori: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Int},
|
||||
Default: "'-1'",
|
||||
DefaultIsEmpty: false,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "version helper default without schema default",
|
||||
col: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Int},
|
||||
Default: "1",
|
||||
DefaultIsEmpty: true,
|
||||
},
|
||||
ori: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Int},
|
||||
DefaultIsEmpty: true,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "autoincrement ignores default diff",
|
||||
col: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Int},
|
||||
Default: "1",
|
||||
DefaultIsEmpty: false,
|
||||
IsAutoIncrement: true,
|
||||
},
|
||||
ori: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Int},
|
||||
Default: "999",
|
||||
DefaultIsEmpty: false,
|
||||
IsAutoIncrement: true,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "nullable null default equals implicit null",
|
||||
col: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.BigInt},
|
||||
Default: "NULL",
|
||||
DefaultIsEmpty: false,
|
||||
Nullable: true,
|
||||
},
|
||||
ori: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.BigInt},
|
||||
DefaultIsEmpty: true,
|
||||
Nullable: true,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "quoted text null is not treated as sql null",
|
||||
col: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Varchar},
|
||||
Default: "'NULL'",
|
||||
DefaultIsEmpty: false,
|
||||
Nullable: true,
|
||||
},
|
||||
ori: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Varchar},
|
||||
DefaultIsEmpty: true,
|
||||
Nullable: true,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "not null column keeps explicit null mismatch",
|
||||
col: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Int},
|
||||
Default: "NULL",
|
||||
DefaultIsEmpty: false,
|
||||
Nullable: false,
|
||||
},
|
||||
ori: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Int},
|
||||
DefaultIsEmpty: true,
|
||||
Nullable: false,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "empty string still differs from no default",
|
||||
col: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Varchar},
|
||||
Default: "",
|
||||
DefaultIsEmpty: false,
|
||||
Nullable: true,
|
||||
},
|
||||
ori: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Varchar},
|
||||
DefaultIsEmpty: true,
|
||||
Nullable: true,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "real default still differs from no default",
|
||||
col: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Int},
|
||||
Default: "1",
|
||||
DefaultIsEmpty: false,
|
||||
},
|
||||
ori: &schemas.Column{
|
||||
SQLType: schemas.SQLType{Name: schemas.Int},
|
||||
DefaultIsEmpty: true,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := columnDefaultsMatch(tt.col, tt.ori); got != tt.want {
|
||||
t.Fatalf("columnDefaultsMatch() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeColumnDefaultValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
sqlType schemas.SQLType
|
||||
defaultValue string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "bool keeps canonical true",
|
||||
sqlType: schemas.SQLType{Name: schemas.Bool},
|
||||
defaultValue: "'TRUE'",
|
||||
want: "true",
|
||||
},
|
||||
{
|
||||
name: "bool keeps canonical false",
|
||||
sqlType: schemas.SQLType{Name: schemas.Boolean},
|
||||
defaultValue: " 0 ",
|
||||
want: "false",
|
||||
},
|
||||
{
|
||||
name: "numeric strips quotes and scale",
|
||||
sqlType: schemas.SQLType{Name: schemas.Decimal},
|
||||
defaultValue: "'1.00'",
|
||||
want: "1",
|
||||
},
|
||||
{
|
||||
name: "text keeps quoted literal",
|
||||
sqlType: schemas.SQLType{Name: schemas.Varchar},
|
||||
defaultValue: "'NULL'",
|
||||
want: "'NULL'",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := normalizeColumnDefaultValue(tt.sqlType, tt.defaultValue); got != tt.want {
|
||||
t.Fatalf("normalizeColumnDefaultValue() = %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeColumnTypeForComparison(t *testing.T) {
|
||||
alias := func(columnType string) string {
|
||||
if columnType == "NUMERIC" {
|
||||
return "DECIMAL"
|
||||
}
|
||||
return columnType
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
columnType string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "mysql integer display width is ignored",
|
||||
columnType: "INT(10) UNSIGNED",
|
||||
want: "INT UNSIGNED",
|
||||
},
|
||||
{
|
||||
name: "signed integer keeps base type only",
|
||||
columnType: "BIGINT(20)",
|
||||
want: "BIGINT",
|
||||
},
|
||||
{
|
||||
name: "alias still applies after normalization",
|
||||
columnType: "NUMERIC(10,2)",
|
||||
want: "DECIMAL(10,2)",
|
||||
},
|
||||
{
|
||||
name: "non integer length stays significant",
|
||||
columnType: "VARCHAR(255)",
|
||||
want: "VARCHAR(255)",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := normalizeColumnTypeForComparison(alias, tt.columnType); got != tt.want {
|
||||
t.Fatalf("normalizeColumnTypeForComparison() = %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestColumnTypesMatch(t *testing.T) {
|
||||
alias := func(columnType string) string {
|
||||
if columnType == "NUMERIC" {
|
||||
return "DECIMAL"
|
||||
}
|
||||
return columnType
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
expectedCol *schemas.Column
|
||||
currentCol *schemas.Column
|
||||
expectedType string
|
||||
currentType string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "mysql unsigned display width warning is suppressed",
|
||||
expectedCol: &schemas.Column{},
|
||||
currentCol: &schemas.Column{},
|
||||
expectedType: "INT UNSIGNED",
|
||||
currentType: "INT(10) UNSIGNED",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "mysql signedness mismatch still warns",
|
||||
expectedCol: &schemas.Column{},
|
||||
currentCol: &schemas.Column{},
|
||||
expectedType: "INT UNSIGNED",
|
||||
currentType: "INT(10)",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "type aliases still match",
|
||||
expectedCol: &schemas.Column{},
|
||||
currentCol: &schemas.Column{},
|
||||
expectedType: "DECIMAL(10,2)",
|
||||
currentType: "NUMERIC(10,2)",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "json-tagged text matches native json column",
|
||||
expectedCol: &schemas.Column{
|
||||
IsJSON: true,
|
||||
},
|
||||
currentCol: &schemas.Column{},
|
||||
expectedType: "TEXT",
|
||||
currentType: "JSON",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "jsonb-tagged text matches native jsonb column",
|
||||
expectedCol: &schemas.Column{
|
||||
IsJSON: true,
|
||||
IsJSONB: true,
|
||||
},
|
||||
currentCol: &schemas.Column{},
|
||||
expectedType: "TEXT",
|
||||
currentType: "JSONB",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "plain text still differs from native json column",
|
||||
expectedCol: &schemas.Column{},
|
||||
currentCol: &schemas.Column{},
|
||||
expectedType: "TEXT",
|
||||
currentType: "JSON",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := columnTypesMatch(alias, tt.expectedCol, tt.currentCol, tt.expectedType, tt.currentType); got != tt.want {
|
||||
t.Fatalf("columnTypesMatch() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type syncWarnMissingColumnInitial struct {
|
||||
ID int64
|
||||
LegacyCol string
|
||||
}
|
||||
|
||||
func (syncWarnMissingColumnInitial) TableName() string {
|
||||
return "sync_warn_missing_column"
|
||||
}
|
||||
|
||||
type syncWarnMissingColumnCurrent struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
func (syncWarnMissingColumnCurrent) TableName() string {
|
||||
return "sync_warn_missing_column"
|
||||
}
|
||||
|
||||
func TestSyncWithWarnIfDatabaseColumnMissed(t *testing.T) {
|
||||
engine, err := NewEngine("sqlite3", filepath.Join(t.TempDir(), "sync.db"))
|
||||
if err != nil {
|
||||
t.Fatalf("NewEngine() error = %v", err)
|
||||
}
|
||||
defer engine.Close()
|
||||
|
||||
assertNoError := func(err error) {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
assertNoError(engine.Sync(new(syncWarnMissingColumnInitial)))
|
||||
|
||||
var buf bytes.Buffer
|
||||
logger := log.NewSimpleLogger3(&buf, "", 0, log.LOG_WARNING)
|
||||
engine.SetLogger(logger)
|
||||
|
||||
_, err = engine.SyncWithOptions(SyncOptions{WarnIfDatabaseColumnMissed: true}, new(syncWarnMissingColumnCurrent))
|
||||
assertNoError(err)
|
||||
|
||||
if !strings.Contains(buf.String(), "Table sync_warn_missing_column has column legacy_col but struct has not related field") {
|
||||
t.Fatalf("expected missing-column warning, got %q", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncWithoutWarnIfDatabaseColumnMissed(t *testing.T) {
|
||||
engine, err := NewEngine("sqlite3", filepath.Join(t.TempDir(), "sync.db"))
|
||||
if err != nil {
|
||||
t.Fatalf("NewEngine() error = %v", err)
|
||||
}
|
||||
defer engine.Close()
|
||||
|
||||
if err = engine.Sync(new(syncWarnMissingColumnInitial)); err != nil {
|
||||
t.Fatalf("Sync() error = %v", err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
logger := log.NewSimpleLogger3(&buf, "", 0, log.LOG_WARNING)
|
||||
engine.SetLogger(logger)
|
||||
|
||||
_, err = engine.SyncWithOptions(SyncOptions{}, new(syncWarnMissingColumnCurrent))
|
||||
if err != nil {
|
||||
t.Fatalf("SyncWithOptions() error = %v", err)
|
||||
}
|
||||
|
||||
if strings.Contains(buf.String(), "struct has not related field") {
|
||||
t.Fatalf("unexpected missing-column warning, got %q", buf.String())
|
||||
}
|
||||
}
|
||||
+39
-2
@@ -9,6 +9,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -17,13 +18,12 @@ import (
|
||||
|
||||
_ "gitee.com/travelliu/dm"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/hanzoai/sqlite"
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
_ "github.com/lib/pq"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "github.com/microsoft/go-mssqldb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
_ "github.com/ziutek/mymysql/godrv"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func TestPing(t *testing.T) {
|
||||
@@ -137,6 +137,43 @@ func TestDump(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDumpSQLiteLargeText(t *testing.T) {
|
||||
assert.NoError(t, PrepareEngine())
|
||||
if testEngine.Dialect().URI().DBType != schemas.SQLITE {
|
||||
t.Skip("sqlite only")
|
||||
}
|
||||
|
||||
type TestDumpLargeText struct {
|
||||
Id int64
|
||||
Body string
|
||||
}
|
||||
|
||||
assertSync(t, new(TestDumpLargeText))
|
||||
|
||||
largeText := strings.Repeat("line\n", 1200)
|
||||
_, err := testEngine.Insert(&TestDumpLargeText{Body: largeText})
|
||||
assert.NoError(t, err)
|
||||
|
||||
fp := "sqlite_dump_large_text.sql"
|
||||
os.Remove(fp)
|
||||
assert.NoError(t, testEngine.DumpAllToFile(fp))
|
||||
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
sess := testEngine.NewSession()
|
||||
defer sess.Close()
|
||||
assert.NoError(t, sess.Begin())
|
||||
_, err = sess.ImportFile(fp)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, sess.Commit())
|
||||
|
||||
var got TestDumpLargeText
|
||||
has, err := testEngine.Get(&got)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.Equal(t, largeText, got.Body)
|
||||
}
|
||||
|
||||
var dbtypes = []schemas.DBType{schemas.SQLITE, schemas.MYSQL, schemas.POSTGRES, schemas.MSSQL}
|
||||
|
||||
func TestDumpTables(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user