feat(db): 添加对达梦数据库的支持

- 在应用程序中新增对达梦数据库(DM)的配置和连接支持
- 实现 SetDmDB 函数以配置达梦数据库连接
- 更新数据库操作逻辑,支持达梦特有的 SQL 语法和功能
- 在相关文件中添加达梦数据库的处理逻辑,包括表创建、数据插入和查询
- 更新 go.mod 和 go.sum 文件以引入达梦数据库驱动
- 增强文档,详细说明达梦数据库的配置和使用方法
This commit is contained in:
2026-03-20 10:46:51 +08:00
parent 7ab803c5cc
commit b43f968b6c
194 changed files with 211502 additions and 2328 deletions
@@ -0,0 +1,67 @@
---
name: 修复路径和测试数据
overview: 修复测试时 swagger/config 生成到错误目录的问题(根因是 Go test CWD 在 app/ 下),并为 DM 和 MySQL 两个测试数据库创建完善的测试数据表和种子数据。
todos:
- id: fix-cwd
content: 修复 app_test.go 中的工作目录问题:添加 os.Chdir("..") 并修改 config path
status: completed
- id: cleanup-wrong-dirs
content: 清理错误生成的 example/app/tpt/ 和 example/app/config/ 目录
status: completed
- id: setup-mysql
content: 创建 setup_mysql.goMySQL DDL 建表逻辑(与 setup_dm.go 对应)
status: completed
- id: extract-seed
content: 提取通用种子数据函数 seedTestDataDM 和 MySQL 共用
status: completed
- id: update-testmain
content: 更新 app_test.go 增加 SetupMySQLDatabase 调用
status: completed
- id: sql-scripts
content: 在 example/sql/ 下提供 MySQL 和 DM 的原始 DDL+DML SQL 脚本
status: completed
- id: verify-tests
content: 运行测试验证 swagger 输出到正确位置,且 DM 测试全部通过
status: completed
isProject: false
---
# 修复 Swagger 路径问题 + 完善双数据库测试数据
## 问题 1Swagger 和 config 生成到错误目录
**根因**`go test ./app/...` 时,Go test 将工作目录设为包目录 `example/app/`。配置文件中 `"tpt": "tpt"``codeConfig` 中的 `"config/admin.json"` 等相对路径,都从 `example/app/` 解析,导致文件生成到 `example/app/tpt/``example/app/config/` 而非预期的 `example/tpt/``example/config/`
**修复方案**:在 [example/app/app_test.go](example/app/app_test.go) 的 `TestMain` 中,`NewTestApp` 之前先 `os.Chdir("..")`,将工作目录切回 `example/`,然后将配置路径从 `"../config/config.json"` 改为 `"config/config.json"`
```go
func TestMain(m *testing.M) {
os.Chdir("..") // 切回 example/ 目录,使 tpt、config 等相对路径正确解析
testApp = NewTestApp("config/config.json", ...)
...
}
```
同时清理已错误生成的 `example/app/tpt/``example/app/config/` 目录。
## 问题 2:双数据库测试数据
现有 [example/app/setup_dm.go](example/app/setup_dm.go) 仅处理 DM。需要为 MySQL 创建类似的初始化逻辑,并使种子数据逻辑可复用。
**数据库结构**(DM 和 MySQL 共用同一套表):
- `admin` — 管理员表(id, name, phone, state, password, role_id, title, create_time, modify_time
- `ctg` — 分类表(id, name, state, create_time, modify_time
- `article` — 文章表(id, title, author, content, state, click_num, sort, img, ctg_id, admin_id, create_time, modify_time
- `test_batch` — 批量测试表(id, name, title, state, create_time
- `cached` — 老版本缓存表(id, key, value, endtime, time
- `hotime_cache` — 新版缓存表(id, key, value, end_time, state, create_time, modify_time
**方案**:
1. 创建 [example/app/setup_mysql.go](example/app/setup_mysql.go):包含 MySQL DDL 建表和种子数据灌入,类似 `setup_dm.go` 的结构
2. 在 [example/app/app_test.go](example/app/app_test.go) 的 `TestMain` 中增加 `SetupMySQLDatabase(&testApp.Db)` 调用(根据 `db.Type` 自动判断是否执行)
3. 种子数据使用 ORM 的 `db.Insert()` 方法,与数据库类型无关,可直接复用现有 `setup_dm.go``seedDMData` 的逻辑,提取为通用的 `seedTestData` 函数
4. 同时提供 MySQL 和 DM 的原始 DDL/DML SQL 脚本,放在 `example/sql/` 目录下,方便手动执行
**config.json 切换方式**:当前 MySQL 的 key 是 `"mysql-"`(带尾缀表示禁用),DM 的 key 是 `"dm"`(活跃)。切换数据库只需互换后缀:`"mysql"` / `"dm-"`
@@ -0,0 +1,328 @@
---
name: 支持达梦数据库
overview: 在 HoTime 框架的现有 Dialect 方言体系下,新增达梦(DM8)数据库方言实现,涉及驱动引入、方言适配、连接配置、缓存层/代码生成/备份模块的 DM 分支补充。
todos:
- id: dm-driver
content: 引入达梦 Go 驱动依赖(go.mod + import),确定驱动引入方式
status: pending
- id: dm-dialect
content: 在 db/dialect.go 中实现 DMDialectQuote/Placeholder/UpsertSQL 等全部方法)
status: pending
- id: dm-init
content: 在 db/db.go 的 initDialect() 中注册 dm 类型
status: pending
- id: dm-connect
content: 在 application.go 中添加 SetDmDB() 连接函数和 SetDB() 中的 dm 分支
status: pending
- id: dm-config
content: 在 var.go 中添加 dm 数据库配置说明
status: pending
- id: dm-cache
content: 在 cache/cache_db.go 中为 tableExists/createMainTable/createHistoryTable/migrateFromCached/set 添加 dm 分支
status: pending
- id: dm-makecode
content: 在 code/makecode.go 中添加 dm 的表结构查询逻辑
status: pending
- id: dm-insert-returning
content: 修改 db/crud.go 的 Insert() 函数,当 SupportsLastInsertId()=false 时使用 RETURNING + QueryRow 获取插入 ID
status: pending
- id: dm-upsert
content: 在 db/crud.go 中新增 buildDMUpsert() 方法(MERGE INTO 语法),并在 Upsert() 中添加 dm 分支
status: pending
- id: dm-backup
content: 在 db/backup.go 中适配达梦的 DDL 导出和标识符引号
status: pending
- id: dm-query-types
content: 在 db/query.go 的 classifyDBType() 中补充达梦特有数据类型(NUMBER 等)
status: pending
- id: refactor-example
content: 重构 example/main.go,最小化启动配置,拆分到 example/app/ 目录
status: pending
- id: example-test-ctr
content: 创建 example/app/test.go(通用 ORM 测试 Ctr+ test_test.go(测试定义),含数据库无关的 CRUD/条件/JOIN/聚合/分页/批量/Upsert/事务
status: pending
- id: example-mysql-ctr
content: 创建 example/app/mysql.goMySQL 特有 Ctr+ mysql_test.go,含 DDL/SHOW TABLES/DESCRIBE/原生SQL
status: pending
- id: example-dmdb-ctr
content: 创建 example/app/dmdb.go(达梦特有 Ctr+ dmdb_test.go,含达梦 DDL/USER_TABLES/元数据/原生SQL
status: pending
- id: example-app-test
content: 创建 example/app/init.go(路由注册)+ app_test.goTestMain+TestApi),集成测试框架
status: pending
- id: example-config
content: 修改 example/config/config.jsonmysql 改为 mysql-(假注释禁用),新增 dm 配置
status: pending
isProject: false
---
# 支持达梦(DM8)数据库
## 现状分析
项目已有完善的 Dialect 接口体系([db/dialect.go](db/dialect.go)),以及 `initDialect()` 工厂方法([db/db.go](db/db.go)),当前支持 MySQL、PostgreSQL、SQLite 三种方言。需要新增达梦方言并在各处补充 `case "dm"` 分支。
```mermaid
flowchart TB
subgraph currentState [当前架构]
DialectIf["Dialect 接口<br/>(db/dialect.go)"]
MySQL["MySQLDialect"]
PG["PostgreSQLDialect"]
SQLite["SQLiteDialect"]
DialectIf --> MySQL
DialectIf --> PG
DialectIf --> SQLite
end
subgraph newState [新增]
DM["DMDialect"]
DialectIf --> DM
end
```
## 达梦 vs MySQL 关键 SQL 差异
- **标识符引号**: 达梦使用双引号 `"name"`(同 PostgreSQL),MySQL 用反引号
- **自增列**: 达梦用 `IDENTITY(1,1)`MySQL 用 `AUTO_INCREMENT`
- **占位符**: 达梦使用 `?`(同 MySQL
- **LastInsertId**: 达梦不支持 `LAST_INSERT_ID()`,需用 `RETURNING "id"` 子句获取插入 ID(同 PostgreSQL
- **Upsert**: 达梦使用 `MERGE INTO ... USING ... WHEN MATCHED ... WHEN NOT MATCHED ...`Oracle 风格)
- **表存在检查**: `SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME='xxx'`
- **列信息查询**: `SELECT COLUMN_NAME, DATA_TYPE, COMMENTS FROM USER_COL_COMMENTS ...`
- **时间戳转换**: 无 `FROM_UNIXTIME`,需用 `DATEADD(SECOND, ts, TIMESTAMP '1970-01-01 00:00:00')`
- **INSERT IGNORE**: 不支持,需用 `MERGE INTO` 替代
- **DDL 导出**: 无 `SHOW CREATE TABLE`,使用系统视图组合
## 涉及修改的文件
### 1. 新增达梦 Go 驱动(go module 方式)
使用达梦官方 module 化驱动 `gitee.com/chunanyong/dm`,通过标准 go module 流程引入:
```bash
go get gitee.com/chunanyong/dm
go mod vendor
```
- [go.mod](go.mod) - 自动添加 `gitee.com/chunanyong/dm` 依赖
- [db/db.go](db/db.go) - 添加 `import _ "gitee.com/chunanyong/dm"` 并在 `initDialect()` 添加 `case "dm"`
- `vendor/` 目录通过 `go mod vendor` 自动同步
驱动注册名为 `"dm"`,连接串格式:`dm://user:password@host:port?schema=dbName`
### 2. 新增 DMDialect 方言
在 [db/dialect.go](db/dialect.go) 末尾新增 `DMDialect` 结构体,实现 `Dialect` 接口全部方法:
- `Quote` / `QuoteIdentifier` / `QuoteChar` - 使用双引号
- `Placeholder` - 返回 `?`
- `Placeholders` - 返回逗号分隔的 `?`
- `SupportsLastInsertId` - 返回 `false`(达梦不支持 MySQL 的 `LAST_INSERT_ID()`
- `ReturningClause` - 返回 `RETURNING "id"`(用于 Insert 后获取自增 ID)
- `UpsertSQL` - 生成 `MERGE INTO ... USING (SELECT ? AS col1, ...) src ON (...) WHEN MATCHED THEN UPDATE SET ... WHEN NOT MATCHED THEN INSERT ...`
### 3. 连接配置
- [application.go](application.go) - 新增 `SetDmDB()` 函数,连接串格式: `dm://user:password@host:port?schema=dbName`
- [application.go](application.go) 的 `SetDB()` 中添加 `dbDm := db.GetMap("dm")` 分支
- [var.go](var.go) - 在 `Config``"db"``ConfigNote``"db"` 下新增 `"dm"` 配置项
`var.go``ConfigNote` 新增的达梦配置说明如下:
```go
"dm": Map{
"注释": "达梦数据库配置,除prefix及主从数据库slave项,其他全部必须",
"host": "默认127.0.0.1,必须,数据库ip地址",
"name": "默认SYSDBA,必须,数据库schema名称",
"user": "默认SYSDBA,必须,数据库用户名",
"password": "默认SYSDBA,必须,数据库密码",
"port": "默认5236,必须,数据库端口",
"prefix": "默认空,非必须,数据表前缀",
"slave": Map{
"注释": "从数据库配置,dm里配置slave项即启用主从读写,减少数据库压力",
"host": "默认127.0.0.1,必须,数据库ip地址",
"name": "默认SYSDBA,必须,数据库schema名称",
"user": "默认SYSDBA,必须,数据库用户名",
"password": "默认SYSDBA,必须,数据库密码",
"port": "默认5236,必须,数据库端口",
},
},
```
### 4. Insert 返回 ID 适配(关键兼容点)
当前 [db/crud.go](db/crud.go) 的 `Insert()` 直接调用 `res.LastInsertId()` 获取插入 ID**但达梦不支持 `LAST_INSERT_ID()`**。Dialect 接口已预留了 `SupportsLastInsertId()``ReturningClause()` 两个方法但 Insert() 未使用。
修改 `Insert()` 函数逻辑:
```go
if that.Dialect != nil && !that.Dialect.SupportsLastInsertId() {
// 达梦/PostgreSQL: 用 RETURNING 子句 + QueryRow 获取 ID
returningClause := that.Dialect.ReturningClause("id")
query = strings.TrimSuffix(query, ";") + returningClause + ";"
var insertedId int64
err := that.QueryRow(query, values...).Scan(&insertedId)
// ...
id = insertedId
} else {
// MySQL/SQLite: 继续使用 Exec + LastInsertId
res, err := that.Exec(query, values...)
id1, _ := res.LastInsertId()
id = id1
}
```
同理,`Upsert()` 函数也需在 `switch dbType` 中增加 `case "dm"` 分支,调用新增的 `buildDMUpsert()` 方法生成 `MERGE INTO` 语法。
**RowsAffected 无需额外处理**`Update()``Delete()` 使用的 `res.RowsAffected()` 在达梦中正常工作。
### 5. 缓存层适配
[cache/cache_db.go](cache/cache_db.go) 中需在以下函数增加 `case "dm"` 分支:
- `tableExists()` - 使用 `SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME='...'`
- `createMainTable()` - 达梦建表 DDL`IDENTITY(1,1)` 代替 `AUTO_INCREMENT`
- `createHistoryTable()` - 同上
- `migrateFromCached()` - 使用 `MERGE INTO` 代替 `INSERT IGNORE`,用 `DATEADD` 代替 `FROM_UNIXTIME`
- `set()` - 使用 `MERGE INTO` 实现 upsert
### 6. 代码生成适配
[code/makecode.go](code/makecode.go) 第 160-165 行,增加 `case "dm"` 分支:
```go
if db.Type == "dm" {
tableInfo = db.Select("USER_TAB_COLUMNS",
"COLUMN_NAME AS name, DATA_TYPE AS type, COMMENTS AS label, NULLABLE AS must, DATA_DEFAULT AS dflt_value",
Map{"TABLE_NAME": v.GetString("name")})
}
```
(注:需要 JOIN `USER_COL_COMMENTS` 获取列注释)
### 7. 备份模块适配
[db/backup.go](db/backup.go) 中:
- `backupDdl()` - `SHOW CREATE TABLE` 替换为通过系统视图(`USER_CONS_COLUMNS` + `USER_TAB_COLUMNS`)拼接 DDL
- `backupSave()` / `backupCol()` - 将反引号替换为通过 Dialect 的 `Quote()` 方法动态生成
### 8. initDialect 注册
[db/db.go](db/db.go) 的 `initDialect()` 添加:
```go
case "dm", "dameng":
that.Dialect = &DMDialect{}
```
## 驱动引入与 vendor 更新流程
采用标准 go module 方式,驱动包为 `gitee.com/chunanyong/dm`(达梦官方维护的 module 化版本):
```bash
# 1. 添加依赖到 go.mod
go get gitee.com/chunanyong/dm
# 2. 同步到 vendor 目录
go mod vendor
```
`go mod vendor` 会自动将 `gitee.com/chunanyong/dm` 及其传递依赖(`github.com/golang/snappy``golang.org/x/text`)下载并复制到 `vendor/` 目录。后续只需提交 `go.mod``go.sum``vendor/` 的变更即可。
### 9. 数据类型映射补充
[db/query.go](db/query.go) 的 `classifyDBType()` 函数(第 388-417 行)需要补充达梦特有类型:
- `NUMBER` -> `dbTypeDecimal`(达梦/Oracle 的通用数值类型)
- `VARCHAR2` -> `dbTypeUnknown`(字符串类型,保持默认即可)
```go
case "DECIMAL", "NUMERIC", "NEWDECIMAL", "NUMBER":
return dbTypeDecimal
```
### 10. 重构 example 目录 + 集成测试框架
**当前问题**[example/main.go](example/main.go) 有 1427 行,所有测试逻辑堆在一个文件。
**重构后目录结构**
```
example/
main.go -- 最小化:只有 Init + Run + 路由引用
config/
config.json -- mysql- (假注释禁用) + dm (启用)
app/
init.go -- Project 路由注册
test.go -- TestCtr: 通用 ORM 测试(数据库无关)
test_test.go -- TestTest: 测试定义
mysql.go -- MysqlCtr: MySQL 特有测试
mysql_test.go -- MysqlTest: MySQL 测试定义
dmdb.go -- DmdbCtr: 达梦特有测试
dmdb_test.go -- DmdbTest: 达梦测试定义
cache.go -- CacheCtr: 缓存测试(共用)
cache_test.go -- CacheTest: 缓存测试定义
app_test.go -- TestMain + TestApi + ProjectTest
```
**拆分原则**
- **TestCtr**(通用 ORM 测试,数据库无关):`init`(根据 `that.Db.Type` 建表)、`crud``condition``chain``join``aggregate``pagination``batch``upsert``transaction`
- **MysqlCtr**MySQL 特有):`tables`SHOW TABLES)、`describe`DESCRIBE)、`rawsql`(反引号原生 SQL
- **DmdbCtr**(达梦特有):`tables`USER_TABLES)、`describe`USER_TAB_COLUMNS)、`rawsql`(双引号原生 SQL
- **CacheCtr**(缓存测试,共用):`all``compat``batch`
**main.go 最小化**
```go
package main
import (
. "code.hoteas.com/golang/hotime"
"code.hoteas.com/golang/hotime/example/app"
)
func main() {
appIns := Init("config/config.json")
appIns.Run(Router{"app": app.Project})
}
```
**config.json 修改**`mysql` 改为 `mysql-` 假注释禁用,新增 `dm`):
```json
{
"db": {
"mysql-": {
"host": "192.168.6.253",
"name": "dgs-cms2601301",
"password": "dasda8454456",
"port": "3306",
"user": "root"
},
"dm": {
"host": "172.31.31.5",
"port": "5236",
"user": "SYSDBA",
"password": "SC_sysdba2026",
"name": "SYSDBA"
}
},
"mode": 2,
"port": "8081"
}
```
**测试框架集成**`app_test.go`),运行 `go test ./example/app/... -v`
## 注意事项
- 达梦默认对标识符大小写敏感(默认转大写),建表和查询时需注意统一使用双引号包裹
- 达梦的 `MERGE INTO` 语法与 Oracle 基本一致,这是替代 MySQL `ON DUPLICATE KEY UPDATE``INSERT IGNORE` 的标准方式
- 达梦默认端口为 5236,默认管理员账户为 SYSDBA
- 达梦的 `NOW()` 函数在 DM8 中可用,`SYSDATE``CURRENT_TIMESTAMP` 也可用
- 达梦支持 `LIMIT` 语法,分页查询无需额外适配
- 达梦 Go 驱动使用 `?` 作为参数占位符,与 MySQL/SQLite 一致
+32
View File
@@ -627,12 +627,16 @@ func SetDB(appIns *Application) {
db := appIns.Config.GetMap("db")
dbSqlite := db.GetMap("sqlite")
dbMysql := db.GetMap("mysql")
dbDm := db.GetMap("dm")
if db != nil && dbSqlite != nil {
SetSqliteDB(appIns, dbSqlite)
}
if db != nil && dbMysql != nil {
SetMysqlDB(appIns, dbMysql)
}
if db != nil && dbDm != nil {
SetDmDB(appIns, dbDm)
}
}
func SetMysqlDB(appIns *Application, config Map) {
@@ -683,6 +687,34 @@ func SetSqliteDB(appIns *Application, config Map) {
})
}
func SetDmDB(appIns *Application, config Map) {
appIns.Db.Type = "dm"
appIns.Db.DBName = config.GetString("name")
appIns.Db.Prefix = config.GetString("prefix")
appIns.Db.Log = appIns.Log
appIns.Db.Mode = appIns.Config.GetCeilInt("mode")
appIns.SetConnectDB(func(err ...*Error) (master, slave *sql.DB) {
query := "dm://" + config.GetString("user") + ":" + config.GetString("password") +
"@" + config.GetString("host") + ":" + config.GetString("port") + "?schema=" + config.GetString("name")
DB, e := sql.Open("dm", query)
if e != nil && len(err) != 0 {
err[0].SetError(e)
}
master = DB
configSlave := config.GetMap("slave")
if configSlave != nil {
querySlave := "dm://" + configSlave.GetString("user") + ":" + configSlave.GetString("password") +
"@" + configSlave.GetString("host") + ":" + configSlave.GetString("port") + "?schema=" + configSlave.GetString("name")
DB1, e := sql.Open("dm", querySlave)
if e != nil && len(err) != 0 {
err[0].SetError(e)
}
slave = DB1
}
return master, slave
})
}
func setMakeCodeListener(name string, appIns *Application) {
appIns.SetConnectListener(func(context *Context) (isFinished bool) {
+54 -3
View File
@@ -155,6 +155,11 @@ func (that *CacheDb) tableExists(tableName string) bool {
case "postgres":
res := that.Db.Query(`SELECT tablename FROM pg_tables WHERE schemaname='public' AND tablename='` + tableName + `'`)
return len(res) != 0
case "dm", "dameng":
// 双引号创建的表名保留原始大小写,不能 ToUpper
res := that.Db.Query(`SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME='` + tableName + `'`)
return len(res) != 0
}
return false
@@ -201,7 +206,21 @@ func (that *CacheDb) createMainTable(dbType, tableName string) {
"modify_time" TIMESTAMP
)`
that.Db.Exec(createSQL)
// 创建索引
that.Db.Exec(`CREATE INDEX "idx_` + tableName + `_end_time" ON "` + tableName + `" ("end_time")`)
return
case "dm", "dameng":
createSQL = `CREATE TABLE "` + tableName + `" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"key" VARCHAR(64) NOT NULL,
"value" TEXT,
"end_time" TIMESTAMP,
"state" INT DEFAULT 0,
"create_time" TIMESTAMP,
"modify_time" TIMESTAMP,
CONSTRAINT "uk_` + tableName + `_key" UNIQUE ("key")
)`
that.Db.Exec(createSQL)
that.Db.Exec(`CREATE INDEX "idx_` + tableName + `_end_time" ON "` + tableName + `" ("end_time")`)
return
}
@@ -252,7 +271,21 @@ func (that *CacheDb) createHistoryTable(dbType, tableName string) {
"modify_time" TIMESTAMP
)`
that.Db.Exec(createSQL)
// 创建索引
that.Db.Exec(`CREATE INDEX "idx_` + tableName + `_cache_id" ON "` + tableName + `" ("hotime_cache_id")`)
return
case "dm", "dameng":
createSQL = `CREATE TABLE "` + tableName + `" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"hotime_cache_id" INT,
"key" VARCHAR(64),
"value" TEXT,
"end_time" TIMESTAMP,
"state" INT DEFAULT 0,
"create_time" TIMESTAMP,
"modify_time" TIMESTAMP
)`
that.Db.Exec(createSQL)
that.Db.Exec(`CREATE INDEX "idx_` + tableName + `_cache_id" ON "` + tableName + `" ("hotime_cache_id")`)
return
}
@@ -291,9 +324,19 @@ func (that *CacheDb) migrateFromCached(dbType, oldTableName, newTableName string
`INNER JOIN (SELECT "key", MAX(id) as max_id FROM "` + oldTableName + `" GROUP BY "key") m ` +
`ON c.id = m.max_id ` +
`ON CONFLICT ("key") DO NOTHING`
case "dm", "dameng":
migrateSQL = `MERGE INTO "` + newTableName + `" t USING (` +
`SELECT c."key", c."value", DATEADD(SECOND, c."endtime", TIMESTAMP '1970-01-01 00:00:00') AS "end_time", ` +
`DATEADD(SECOND, c."time" / 1000000000, TIMESTAMP '1970-01-01 00:00:00') AS "create_time" ` +
`FROM "` + oldTableName + `" c ` +
`INNER JOIN (SELECT "key", MAX("id") AS max_id FROM "` + oldTableName + `" GROUP BY "key") m ` +
`ON c."id" = m.max_id` +
`) src ON (t."key" = src."key") ` +
`WHEN NOT MATCHED THEN INSERT ("key", "value", "end_time", "state", "create_time", "modify_time") ` +
`VALUES (src."key", src."value", src."end_time", 0, src."create_time", src."create_time")`
}
// 执行迁移,不删除老表(由人工确认后删除,更安全)
that.Db.Exec(migrateSQL)
}
@@ -455,6 +498,14 @@ func (that *CacheDb) set(key string, value interface{}, endTime time.Time) {
`ON CONFLICT ("key") DO UPDATE SET "value"=EXCLUDED."value", "end_time"=EXCLUDED."end_time", "modify_time"=EXCLUDED."modify_time"`
that.Db.Exec(upsertSQL, key, string(bte), endTimeStr, nowTime, nowTime)
case "dm", "dameng":
upsertSQL := `MERGE INTO "` + tableName + `" t USING (SELECT ? AS "key", ? AS "value", ? AS "end_time", ? AS "create_time", ? AS "modify_time") src ` +
`ON (t."key" = src."key") ` +
`WHEN MATCHED THEN UPDATE SET "value"=src."value", "end_time"=src."end_time", "modify_time"=src."modify_time" ` +
`WHEN NOT MATCHED THEN INSERT ("key", "value", "end_time", "state", "create_time", "modify_time") ` +
`VALUES (src."key", src."value", src."end_time", 0, src."create_time", src."modify_time")`
that.Db.Exec(upsertSQL, key, string(bte), endTimeStr, nowTime, nowTime)
default:
// 兼容其他数据库:使用 Update + Insert
num := that.Db.Update(tableName, Map{
+6
View File
@@ -118,6 +118,9 @@ func (that *MakeCode) Db2JSON(db *db.HoTimeDB, config Map) {
if db.Type == "sqlite" {
nowTables = db.Select("sqlite_master", "name", Map{"type": "table"})
}
if db.Type == "dm" || db.Type == "dameng" {
nowTables = db.Query(`SELECT TABLE_NAME AS "name", COMMENTS AS "label" FROM USER_TAB_COMMENTS WHERE TABLE_TYPE='TABLE'`)
}
//idSlice=append(idSlice,nowTables)
for _, v := range nowTables {
// if v.GetString("name") == "cached" {
@@ -163,6 +166,9 @@ func (that *MakeCode) Db2JSON(db *db.HoTimeDB, config Map) {
if db.Type == "sqlite" {
tableInfo = db.Query("pragma table_info([" + v.GetString("name") + "]);")
}
if db.Type == "dm" || db.Type == "dameng" {
tableInfo = db.Query(`SELECT c.COLUMN_NAME AS "name", c.DATA_TYPE AS "type", m.COMMENTS AS "label", c.NULLABLE AS "must", c.DATA_DEFAULT AS "dflt_value" FROM USER_TAB_COLUMNS c LEFT JOIN USER_COL_COMMENTS m ON c.TABLE_NAME=m.TABLE_NAME AND c.COLUMN_NAME=m.COLUMN_NAME WHERE c.TABLE_NAME='` + v.GetString("name") + `' ORDER BY c.COLUMN_ID`)
}
idSlice = append(idSlice, tableInfo)
for _, info1 := range tableInfo {
+64 -6
View File
@@ -14,13 +14,18 @@ func (that *HoTimeDB) backupSave(path string, tt string, code int) {
fd, _ := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
defer fd.Close()
q := "`"
if that.Dialect != nil {
q = that.Dialect.QuoteChar()
}
str := "\r\n"
if code == 0 || code == 2 {
str += that.backupDdl(tt)
}
if code == 0 || code == 1 {
str += "insert into `" + tt + "`\r\n\r\n("
str += "insert into " + q + tt + q + "\r\n\r\n("
str += that.backupCol(tt)
}
@@ -29,16 +34,69 @@ func (that *HoTimeDB) backupSave(path string, tt string, code int) {
// backupDdl 备份表结构(DDL
func (that *HoTimeDB) backupDdl(tt string) string {
data := that.Query("show create table " + tt)
if len(data) == 0 {
switch that.Type {
case "dm", "dameng":
return that.backupDdlDM(tt)
default:
data := that.Query("show create table " + tt)
if len(data) == 0 {
return ""
}
return ObjToStr(data[0]["Create Table"]) + ";\r\n\r\n"
}
}
// backupDdlDM 达梦数据库 DDL 导出
func (that *HoTimeDB) backupDdlDM(tt string) string {
cols := that.Query(`SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE, NULLABLE, DATA_DEFAULT FROM USER_TAB_COLUMNS WHERE TABLE_NAME='` + tt + `' ORDER BY COLUMN_ID`)
if len(cols) == 0 {
return ""
}
return ObjToStr(data[0]["Create Table"]) + ";\r\n\r\n"
ddl := "CREATE TABLE \"" + tt + "\" (\r\n"
for i, col := range cols {
name := col.GetString("COLUMN_NAME")
dtype := col.GetString("DATA_TYPE")
nullable := col.GetString("NULLABLE")
defVal := col.GetString("DATA_DEFAULT")
colDef := " \"" + name + "\" " + dtype
prec := col.GetInt("DATA_PRECISION")
scale := col.GetInt("DATA_SCALE")
length := col.GetInt("DATA_LENGTH")
if prec > 0 {
colDef += "(" + ObjToStr(prec)
if scale > 0 {
colDef += "," + ObjToStr(scale)
}
colDef += ")"
} else if length > 0 && (strings.Contains(strings.ToUpper(dtype), "CHAR") || strings.Contains(strings.ToUpper(dtype), "BINARY")) {
colDef += "(" + ObjToStr(length) + ")"
}
if nullable == "N" {
colDef += " NOT NULL"
}
if defVal != "" {
colDef += " DEFAULT " + strings.TrimSpace(defVal)
}
if i < len(cols)-1 {
colDef += ","
}
ddl += colDef + "\r\n"
}
ddl += ");\r\n\r\n"
return ddl
}
// backupCol 备份表数据
func (that *HoTimeDB) backupCol(tt string) string {
q := "`"
if that.Dialect != nil {
q = that.Dialect.QuoteChar()
}
str := ""
data := that.Select(tt, "*")
@@ -54,9 +112,9 @@ func (that *HoTimeDB) backupCol(tt string) string {
for k := range data[0] {
if tempLthData == lthCol-1 {
str += "`" + k + "`) "
str += q + k + q + ") "
} else {
str += "`" + k + "`,"
str += q + k + q + ","
}
col[tempLthData] = k
tempLthData++
+62 -6
View File
@@ -285,13 +285,21 @@ func (that *HoTimeDB) Insert(table string, data map[string]interface{}) int64 {
query := "INSERT INTO " + processor.ProcessTableName(table) + " " + queryString + "VALUES" + valueString
res, err := that.Exec(query, values...)
id := int64(0)
if err.GetError() == nil && res != nil {
id1, err := res.LastInsertId()
that.LastErr.SetError(err)
id = id1
if that.Dialect != nil && !that.Dialect.SupportsLastInsertId() {
returningClause := that.Dialect.ReturningClause("id")
query = strings.TrimSuffix(query, ";") + returningClause
rows := that.Query(query, values...)
if len(rows) > 0 {
id = rows[0].GetInt64("id")
}
} else {
res, err := that.Exec(query, values...)
if err.GetError() == nil && res != nil {
id1, e := res.LastInsertId()
that.LastErr.SetError(e)
id = id1
}
}
// 如果插入成功,删除缓存
@@ -475,6 +483,8 @@ func (that *HoTimeDB) Upsert(table string, data Map, uniqueKeys Slice, updateCol
query = that.buildPostgresUpsert(table, columns, uniqueKeyStrs, updateColumnStrs, rawValues)
case "sqlite3", "sqlite":
query = that.buildSQLiteUpsert(table, columns, uniqueKeyStrs, updateColumnStrs, rawValues)
case "dm", "dameng":
query = that.buildDMUpsert(table, columns, uniqueKeyStrs, updateColumnStrs, rawValues)
default: // mysql
query = that.buildMySQLUpsert(table, columns, uniqueKeyStrs, updateColumnStrs, rawValues)
}
@@ -608,6 +618,52 @@ func (that *HoTimeDB) buildSQLiteUpsert(table string, columns []string, uniqueKe
") DO UPDATE SET " + strings.Join(updateParts, ", ")
}
// buildDMUpsert 构建达梦的 Upsert 语句(MERGE INTO
func (that *HoTimeDB) buildDMUpsert(table string, columns []string, uniqueKeys []string, updateColumns []string, rawValues map[string]string) string {
processor := that.GetProcessor()
dialect := that.GetDialect()
quotedTable := processor.ProcessTableName(table)
srcParts := make([]string, len(columns))
for i, col := range columns {
if raw, ok := rawValues[col]; ok {
srcParts[i] = raw + " AS " + dialect.QuoteIdentifier(col)
} else {
srcParts[i] = "? AS " + dialect.QuoteIdentifier(col)
}
}
onParts := make([]string, len(uniqueKeys))
for i, key := range uniqueKeys {
qk := dialect.QuoteIdentifier(key)
onParts[i] = quotedTable + "." + qk + " = src." + qk
}
updateParts := make([]string, len(updateColumns))
for i, col := range updateColumns {
qc := dialect.QuoteIdentifier(col)
if raw, ok := rawValues[col]; ok {
updateParts[i] = qc + " = " + raw
} else {
updateParts[i] = qc + " = src." + qc
}
}
insertCols := make([]string, len(columns))
insertVals := make([]string, len(columns))
for i, col := range columns {
qc := dialect.QuoteIdentifier(col)
insertCols[i] = qc
insertVals[i] = "src." + qc
}
return "MERGE INTO " + quotedTable + " USING (SELECT " + strings.Join(srcParts, ", ") +
") src ON (" + strings.Join(onParts, " AND ") +
") WHEN MATCHED THEN UPDATE SET " + strings.Join(updateParts, ", ") +
" WHEN NOT MATCHED THEN INSERT (" + strings.Join(insertCols, ", ") +
") VALUES (" + strings.Join(insertVals, ", ") + ")"
}
// Update 更新数据
func (that *HoTimeDB) Update(table string, data Map, where Map) int64 {
processor := that.GetProcessor()
+3
View File
@@ -7,6 +7,7 @@ import (
"strings"
"sync"
_ "gitee.com/chunanyong/dm"
_ "github.com/go-sql-driver/mysql"
_ "github.com/mattn/go-sqlite3"
"github.com/sirupsen/logrus"
@@ -75,6 +76,8 @@ func (that *HoTimeDB) initDialect() {
that.Dialect = &PostgreSQLDialect{}
case "sqlite3", "sqlite":
that.Dialect = &SQLiteDialect{}
case "dm", "dameng":
that.Dialect = &DMDialect{}
default:
that.Dialect = &MySQLDialect{}
}
+94
View File
@@ -206,6 +206,100 @@ func (d *PostgreSQLDialect) UpsertSQL(table string, columns []string, uniqueKeys
strings.Join(updateParts, ", "))
}
// DMDialect 达梦数据库方言实现
type DMDialect struct{}
func (d *DMDialect) GetName() string {
return "dm"
}
func (d *DMDialect) Quote(name string) string {
if strings.Contains(name, ".") || strings.Contains(name, " ") {
return name
}
return "\"" + name + "\""
}
func (d *DMDialect) QuoteIdentifier(name string) string {
name = strings.Trim(name, "`\"")
return "\"" + name + "\""
}
func (d *DMDialect) QuoteChar() string {
return "\""
}
func (d *DMDialect) Placeholder(index int) string {
return "?"
}
func (d *DMDialect) Placeholders(count int, startIndex int) string {
if count <= 0 {
return ""
}
placeholders := make([]string, count)
for i := 0; i < count; i++ {
placeholders[i] = "?"
}
return strings.Join(placeholders, ",")
}
func (d *DMDialect) SupportsLastInsertId() bool {
return true // DM Go 驱动原生支持 IDENTITY 列的 LastInsertId
}
func (d *DMDialect) ReturningClause(column string) string {
return " RETURNING " + d.Quote(column)
}
func (d *DMDialect) UpsertSQL(table string, columns []string, uniqueKeys []string, updateColumns []string) string {
// MERGE INTO table USING (SELECT ? AS col1, ? AS col2) src
// ON (table.uk = src.uk) WHEN MATCHED THEN UPDATE SET col1 = src.col1
// WHEN NOT MATCHED THEN INSERT (col1, col2) VALUES (src.col1, src.col2)
quotedTable := d.Quote(table)
srcParts := make([]string, len(columns))
for i, col := range columns {
if strings.HasSuffix(col, "[#]") {
srcParts[i] = col
} else {
srcParts[i] = "? AS " + d.Quote(col)
}
}
onParts := make([]string, len(uniqueKeys))
for i, key := range uniqueKeys {
qk := d.Quote(key)
onParts[i] = quotedTable + "." + qk + " = src." + qk
}
updateParts := make([]string, len(updateColumns))
for i, col := range updateColumns {
if strings.HasSuffix(col, "[#]") {
updateParts[i] = col
} else {
qc := d.Quote(col)
updateParts[i] = qc + " = src." + qc
}
}
insertCols := make([]string, len(columns))
insertVals := make([]string, len(columns))
for i, col := range columns {
qc := d.Quote(col)
insertCols[i] = qc
insertVals[i] = "src." + qc
}
return fmt.Sprintf("MERGE INTO %s USING (SELECT %s) src ON (%s) WHEN MATCHED THEN UPDATE SET %s WHEN NOT MATCHED THEN INSERT (%s) VALUES (%s)",
quotedTable,
strings.Join(srcParts, ", "),
strings.Join(onParts, " AND "),
strings.Join(updateParts, ", "),
strings.Join(insertCols, ", "),
strings.Join(insertVals, ", "))
}
// SQLiteDialect SQLite 方言实现
type SQLiteDialect struct{}
+10 -26
View File
@@ -83,41 +83,33 @@ func (p *IdentifierProcessor) ProcessTableNameNoPrefix(name string) string {
// ProcessColumn 处理 table.column 格式
// 输入: "name" 或 "order.name" 或 "`order`.name" 或 "`order`.`name`"
// 输出: "`name`" 或 "app_order.name"
// 注意: 单独的列名加引号(避免关键字冲突),table.column 格式不加引号
// 输出: "`name`" 或 "`app_order`.`name`" (MySQL) / "\"app_order\".\"name\"" (DM/PG)
func (p *IdentifierProcessor) ProcessColumn(name string) string {
// 检查是否包含点号
if !strings.Contains(name, ".") {
// 单独的列名,需要加引号(避免关键字冲突)
return p.dialect.QuoteIdentifier(p.stripQuotes(name))
}
// 处理 table.column 格式,不加引号,只添加前缀
parts := p.splitTableColumn(name)
if len(parts) == 2 {
tableName := p.stripQuotes(parts[0])
columnName := p.stripQuotes(parts[1])
// table.column 格式不加反引号
return p.prefix + tableName + "." + columnName
return p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(columnName)
}
// 无法解析,返回原样但转换引号
return p.convertQuotes(name)
}
// ProcessColumnNoPrefix 处理 table.column 格式(不添加前缀)
func (p *IdentifierProcessor) ProcessColumnNoPrefix(name string) string {
if !strings.Contains(name, ".") {
// 单独的列名,需要加引号(避免关键字冲突)
return p.dialect.QuoteIdentifier(p.stripQuotes(name))
}
// table.column 格式不加引号
parts := p.splitTableColumn(name)
if len(parts) == 2 {
tableName := p.stripQuotes(parts[0])
columnName := p.stripQuotes(parts[1])
return tableName + "." + columnName
return p.dialect.QuoteIdentifier(tableName) + "." + p.dialect.QuoteIdentifier(columnName)
}
return p.convertQuotes(name)
@@ -125,9 +117,8 @@ func (p *IdentifierProcessor) ProcessColumnNoPrefix(name string) string {
// ProcessConditionString 智能解析条件字符串(如 ON 条件)
// 输入: "user.id = order.user_id AND order.status = 1"
// 输出: "app_user.id = app_order.user_id AND app_order.status = 1"
// 注意: table.column 格式不加反引号,因为 MySQL/SQLite/PostgreSQL 都能正确解析
// 这样可以保持返回的列名与原始列名一致,便于聚合函数等场景读取结果
// 输出: "`app_user`.`id` = `app_order`.`user_id` AND `app_order`.`status` = 1" (MySQL)
// 输出: "\"app_user\".\"id\" = \"app_order\".\"user_id\" AND \"app_order\".\"status\" = 1" (DM/PG)
func (p *IdentifierProcessor) ProcessConditionString(condition string) string {
if condition == "" {
return condition
@@ -136,28 +127,24 @@ func (p *IdentifierProcessor) ProcessConditionString(condition string) string {
result := condition
// 首先处理已有完整引号的情况 `table`.`column` 或 "table"."column"
// 去除引号,只添加前缀
fullyQuotedPattern := regexp.MustCompile("[`\"]([a-zA-Z_][a-zA-Z0-9_]*)[`\"]\\.[`\"]([a-zA-Z_][a-zA-Z0-9_]*)[`\"]")
result = fullyQuotedPattern.ReplaceAllStringFunc(result, func(match string) string {
parts := fullyQuotedPattern.FindStringSubmatch(match)
if len(parts) == 3 {
tableName := parts[1]
colName := parts[2]
// table.column 格式不加反引号,只添加前缀
return p.prefix + tableName + "." + colName
return p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(colName)
}
return match
})
// 然后处理部分引号的情况 `table`.column 或 "table".column
// 去除引号,只添加前缀
quotedTablePattern := regexp.MustCompile("[`\"]([a-zA-Z_][a-zA-Z0-9_]*)[`\"]\\.([a-zA-Z_][a-zA-Z0-9_]*)(?:[^`\"]|$)")
result = quotedTablePattern.ReplaceAllStringFunc(result, func(match string) string {
parts := quotedTablePattern.FindStringSubmatch(match)
if len(parts) >= 3 {
tableName := parts[1]
colName := parts[2]
// 保留末尾字符(如果有)
suffix := ""
if len(match) > len(parts[0])-1 {
lastChar := match[len(match)-1]
@@ -165,24 +152,21 @@ func (p *IdentifierProcessor) ProcessConditionString(condition string) string {
suffix = string(lastChar)
}
}
// table.column 格式不加反引号,只添加前缀
return p.prefix + tableName + "." + colName + suffix
return p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(colName) + suffix
}
return match
})
// 最后处理无引号的情况 table.column
// 使用更精确的正则,确保不匹配已处理的内容
unquotedPattern := regexp.MustCompile(`([^` + "`" + `"\w]|^)([a-zA-Z_][a-zA-Z0-9_]*)\.([a-zA-Z_][a-zA-Z0-9_]*)([^` + "`" + `"\w(]|$)`)
result = unquotedPattern.ReplaceAllStringFunc(result, func(match string) string {
parts := unquotedPattern.FindStringSubmatch(match)
if len(parts) >= 5 {
prefix := parts[1] // 前面的边界字符
prefix := parts[1]
tableName := parts[2]
colName := parts[3]
suffix := parts[4] // 后面的边界字符
// table.column 格式不加反引号,只添加前缀
return prefix + p.prefix + tableName + "." + colName + suffix
suffix := parts[4]
return prefix + p.dialect.QuoteIdentifier(p.prefix+tableName) + "." + p.dialect.QuoteIdentifier(colName) + suffix
}
return match
})
+1 -1
View File
@@ -399,7 +399,7 @@ func classifyDBType(typeName string) int {
"SERIAL", "BIGSERIAL", "SMALLSERIAL",
"YEAR", "BOOL", "BOOLEAN", "OID":
return dbTypeInteger
case "DECIMAL", "NUMERIC", "NEWDECIMAL":
case "DECIMAL", "NUMERIC", "NEWDECIMAL", "NUMBER":
return dbTypeDecimal
case "FLOAT", "DOUBLE", "REAL", "FLOAT4", "FLOAT8", "DOUBLE PRECISION":
return dbTypeFloat
+27 -27
View File
@@ -244,39 +244,12 @@ func (that *HoTimeDB) varCond(k string, v interface{}) (string, []interface{}) {
k = strings.Replace(k, "[#]", "", -1)
k = processor.ProcessColumn(k) + " "
where += " " + k + "=" + ObjToStr(v) + " "
case "[##]": // 直接添加value到sql,需要考虑防注入
where += " " + ObjToStr(v)
case "[#!]":
k = strings.Replace(k, "[#!]", "", -1)
k = processor.ProcessColumn(k) + " "
where += " " + k + "!=" + ObjToStr(v) + " "
case "[!#]":
k = strings.Replace(k, "[!#]", "", -1)
k = processor.ProcessColumn(k) + " "
where += " " + k + "!=" + ObjToStr(v) + " "
case "[~]":
k = strings.Replace(k, "[~]", "", -1)
k = processor.ProcessColumn(k) + " "
where += k + " LIKE ? "
v = "%" + ObjToStr(v) + "%"
res = append(res, v)
case "[!~]": // 左边任意
k = strings.Replace(k, "[!~]", "", -1)
k = processor.ProcessColumn(k) + " "
where += k + " LIKE ? "
v = "%" + ObjToStr(v) + ""
res = append(res, v)
case "[~!]": // 右边任意
k = strings.Replace(k, "[~!]", "", -1)
k = processor.ProcessColumn(k) + " "
where += k + " LIKE ? "
v = ObjToStr(v) + "%"
res = append(res, v)
case "[~~]": // 手动任意
k = strings.Replace(k, "[~~]", "", -1)
k = processor.ProcessColumn(k) + " "
where += k + " LIKE ? "
res = append(res, v)
default:
def = true
}
@@ -307,6 +280,33 @@ func (that *HoTimeDB) varCond(k string, v interface{}) (string, []interface{}) {
vs := ObjToSlice(v)
res = append(res, vs[0])
res = append(res, vs[1])
case "[##]":
where += " " + ObjToStr(v)
case "[#!]":
k = strings.Replace(k, "[#!]", "", -1)
k = processor.ProcessColumn(k) + " "
where += " " + k + "!=" + ObjToStr(v) + " "
case "[!#]":
k = strings.Replace(k, "[!#]", "", -1)
k = processor.ProcessColumn(k) + " "
where += " " + k + "!=" + ObjToStr(v) + " "
case "[!~]": // 左边任意
k = strings.Replace(k, "[!~]", "", -1)
k = processor.ProcessColumn(k) + " "
where += k + " LIKE ? "
v = "%" + ObjToStr(v) + ""
res = append(res, v)
case "[~!]": // 右边任意
k = strings.Replace(k, "[~!]", "", -1)
k = processor.ProcessColumn(k) + " "
where += k + " LIKE ? "
v = ObjToStr(v) + "%"
res = append(res, v)
case "[~~]": // 手动任意
k = strings.Replace(k, "[~~]", "", -1)
k = processor.ProcessColumn(k) + " "
where += k + " LIKE ? "
res = append(res, v)
default:
where, res = that.handleDefaultCondition(k, v, where, res)
}
+43
View File
@@ -0,0 +1,43 @@
package app
import (
"os"
"testing"
. "code.hoteas.com/golang/hotime"
)
var ProjectTest = ProjTest{
"test": TestTest,
"mysql": MysqlTest,
"dmdb": DmdbTest,
"cache": CacheTest,
}
var testApp *TestApp
func TestMain(m *testing.M) {
// Go test 的 CWD 是包目录 example/app/,切回 example/ 使相对路径正确
_ = os.Chdir("..")
testApp = NewTestApp("config/config.json",
TestProj{
"app": {
Proj: Project,
Tests: ProjectTest,
},
},
)
// 在测试前初始化各数据库的测试表和种子数据
SetupDatabase(&testApp.Db)
code := m.Run()
testApp.PrintCoverage()
testApp.GenerateSwagger("My API", "1.0.0", testApp.Config.GetString("tpt"))
os.Exit(code)
}
func TestApi(t *testing.T) {
testApp.RunTests(t)
}
+294
View File
@@ -0,0 +1,294 @@
package app
import (
"fmt"
"time"
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
// CacheCtr 缓存测试控制器(数据库无关)
var CacheCtr = Ctr{
"all": func(that *Context) { that.Display(0, testCacheAll(that)) },
"compat": func(that *Context) { that.Display(0, testCacheCompatible(that)) },
"batch": func(that *Context) {
that.Display(0, Map{"message": "批量缓存测试完成,请查看控制台输出和日志文件"})
},
}
func testCacheAll(that *Context) Map {
result := Map{"name": "数据库缓存测试", "tests": Slice{}}
tests := Slice{}
cacheMode := "unknown"
if that.Application.HoTimeCache != nil && that.Application.HoTimeCache.Config != nil {
dbConfig := that.Application.HoTimeCache.Config.GetMap("db")
if dbConfig != nil {
cacheMode = dbConfig.GetString("mode")
if cacheMode == "" {
cacheMode = "new"
}
}
}
result["cache_mode"] = cacheMode
testPrefix := fmt.Sprintf("cache_test_%d_", time.Now().UnixNano())
// 1. 基础读写测试
test1 := Map{"name": "1. 基础 set/get 测试"}
testKey1 := testPrefix + "basic"
testValue1 := Map{"name": "测试数据", "count": 123, "active": true}
that.Application.Cache(testKey1, testValue1)
cached1 := that.Application.Cache(testKey1)
if cached1.Data != nil {
cachedMap := cached1.ToMap()
test1["result"] = cachedMap.GetString("name") == "测试数据" && cachedMap.GetInt("count") == 123
test1["cached_value"] = cachedMap
} else {
test1["result"] = false
test1["error"] = "缓存读取返回 nil"
}
tests = append(tests, test1)
// 2. 删除缓存测试
test2 := Map{"name": "2. delete 删除缓存测试"}
testKey2 := testPrefix + "delete"
that.Application.Cache(testKey2, "删除测试值")
before := that.Application.Cache(testKey2)
beforeExists := before.Data != nil
that.Application.Cache(testKey2, nil)
after := that.Application.Cache(testKey2)
afterExists := after.Data != nil
test2["result"] = beforeExists && !afterExists
test2["before_exists"] = beforeExists
test2["after_exists"] = afterExists
tests = append(tests, test2)
// 3. 过期时间测试
test3 := Map{"name": "3. 过期时间测试(短超时)"}
testKey3 := testPrefix + "expire"
that.Application.Cache(testKey3, "短期数据", 2)
immediate := that.Application.Cache(testKey3)
immediateExists := immediate.Data != nil
test3["result"] = immediateExists
test3["immediate_exists"] = immediateExists
test3["note"] = "设置了2秒过期,可等待后再次访问验证过期"
tests = append(tests, test3)
// 4. 不存在的 key 读取测试
test4 := Map{"name": "4. 不存在的 key 读取测试"}
nonExistKey := testPrefix + "non_exist_key_" + fmt.Sprintf("%d", time.Now().UnixNano())
nonExist := that.Application.Cache(nonExistKey)
test4["result"] = nonExist.Data == nil
test4["value"] = nonExist.Data
tests = append(tests, test4)
// 5. 重复 set 同一个 key 测试
test5 := Map{"name": "5. 重复 set 同一个 key 测试"}
testKey5 := testPrefix + "repeat"
that.Application.Cache(testKey5, "第一次值")
first := that.Application.Cache(testKey5).ToStr()
that.Application.Cache(testKey5, "第二次值")
second := that.Application.Cache(testKey5).ToStr()
that.Application.Cache(testKey5, Map{"version": 3})
third := that.Application.Cache(testKey5).ToMap()
test5["result"] = first == "第一次值" && second == "第二次值" && third.GetInt("version") == 3
test5["first"] = first
test5["second"] = second
test5["third"] = third
tests = append(tests, test5)
// 6. 通配删除测试
test6 := Map{"name": "6. 通配删除测试 (key*)"}
wildcardPrefix := testPrefix + "wildcard_"
that.Application.Cache(wildcardPrefix+"a", "值A")
that.Application.Cache(wildcardPrefix+"b", "值B")
that.Application.Cache(wildcardPrefix+"c", "值C")
aExists := that.Application.Cache(wildcardPrefix+"a").Data != nil
bExists := that.Application.Cache(wildcardPrefix+"b").Data != nil
cExists := that.Application.Cache(wildcardPrefix+"c").Data != nil
allExistBefore := aExists && bExists && cExists
that.Application.Cache(wildcardPrefix+"*", nil)
aAfter := that.Application.Cache(wildcardPrefix+"a").Data != nil
bAfter := that.Application.Cache(wildcardPrefix+"b").Data != nil
cAfter := that.Application.Cache(wildcardPrefix+"c").Data != nil
allDeletedAfter := !aAfter && !bAfter && !cAfter
test6["result"] = allExistBefore && allDeletedAfter
test6["before"] = Map{"a": aExists, "b": bExists, "c": cExists}
test6["after"] = Map{"a": aAfter, "b": bAfter, "c": cAfter}
tests = append(tests, test6)
// 7. 不同数据类型测试
test7 := Map{"name": "7. 不同数据类型存储测试"}
that.Application.Cache(testPrefix+"type_string", "字符串值")
typeString := that.Application.Cache(testPrefix + "type_string").ToStr()
that.Application.Cache(testPrefix+"type_int", 12345)
typeInt := that.Application.Cache(testPrefix + "type_int").ToInt()
that.Application.Cache(testPrefix+"type_float", 3.14159)
typeFloat := that.Application.Cache(testPrefix + "type_float").ToFloat64()
that.Application.Cache(testPrefix+"type_bool", true)
typeBoolData := that.Application.Cache(testPrefix + "type_bool").Data
typeBool := typeBoolData == true || typeBoolData == "true" || typeBoolData == 1.0
that.Application.Cache(testPrefix+"type_map", Map{"key": "value", "num": 100})
typeMap := that.Application.Cache(testPrefix + "type_map").ToMap()
that.Application.Cache(testPrefix+"type_slice", Slice{1, 2, 3, "four", Map{"five": 5}})
typeSlice := that.Application.Cache(testPrefix + "type_slice").ToSlice()
test7["result"] = typeString == "字符串值" &&
typeInt == 12345 &&
typeFloat > 3.14 && typeFloat < 3.15 &&
typeBool == true &&
typeMap.GetString("key") == "value" &&
len(typeSlice) == 5
test7["string"] = typeString
test7["int"] = typeInt
test7["float"] = typeFloat
test7["bool"] = typeBool
test7["map"] = typeMap
test7["slice"] = typeSlice
tests = append(tests, test7)
// 8. 自定义超时时间测试
test8 := Map{"name": "8. 自定义超时时间参数测试"}
testKey8 := testPrefix + "custom_timeout"
that.Application.Cache(testKey8, "长期数据", 3600)
longTerm := that.Application.Cache(testKey8)
test8["result"] = longTerm.Data != nil
test8["value"] = longTerm.ToStr()
tests = append(tests, test8)
// 9. 查询缓存表状态
test9 := Map{"name": "9. 缓存表状态查询"}
prefix := that.Db.GetPrefix()
newTableName := prefix + "hotime_cache"
newCount := that.Db.Count(newTableName)
test9["new_table_count"] = newCount
test9["new_table_name"] = newTableName
test9["result"] = newCount >= 0
tests = append(tests, test9)
that.Application.Cache(testPrefix+"*", nil)
result["tests"] = tests
result["success"] = true
result["cleanup"] = "已清理所有测试缓存数据"
return result
}
func testCacheCompatible(that *Context) Map {
result := Map{
"test_name": "兼容模式白盒测试",
"timestamp": time.Now().Format("2006-01-02 15:04:05"),
}
prefix := that.Db.GetPrefix()
newTableName := prefix + "hotime_cache"
legacyTableName := prefix + "cached"
tests := Slice{}
test1 := Map{"name": "1. 查询当前缓存模式"}
cacheConfig := that.Application.Config.GetMap("cache")
dbConfig := cacheConfig.GetMap("db")
mode := dbConfig.GetString("mode")
if mode == "" {
mode = "默认(compatible)"
}
test1["mode"] = mode
test1["result"] = true
tests = append(tests, test1)
// 根据数据库类型使用不同的引号
q := "`"
if that.Db.Dialect != nil {
q = that.Db.Dialect.QuoteChar()
}
test2 := Map{"name": "2. 查询老表cached现有数据"}
legacyData := that.Db.Query("SELECT * FROM " + q + legacyTableName + q + " LIMIT 5")
test2["legacy_table"] = legacyTableName
test2["count"] = len(legacyData)
test2["data"] = legacyData
test2["result"] = true
tests = append(tests, test2)
test3 := Map{"name": "3. 查询新表hotime_cache现有数据"}
newData := that.Db.Query("SELECT * FROM " + q + newTableName + q + " LIMIT 5")
test3["new_table"] = newTableName
test3["count"] = len(newData)
test3["data"] = newData
test3["result"] = true
tests = append(tests, test3)
test4 := Map{"name": "4. 测试兼容模式老表回退读取"}
testKey4 := "test_compat_fallback_" + ObjToStr(time.Now().UnixNano())
testValue4 := Map{"admin_id": 999, "admin_name": "测试老数据"}
testValueJson4 := ObjToStr(Map{"data": testValue4})
that.Db.Insert(legacyTableName, Map{
"key": testKey4,
"value": testValueJson4,
"endtime": time.Now().Unix() + 3600,
"time": time.Now().UnixNano(),
})
test4["test_key"] = testKey4
newExists := that.Db.Get(newTableName, "*", Map{"key": testKey4})
test4["key_in_new_table"] = newExists != nil
cacheValue := that.Application.Cache(testKey4)
test4["cache_api_result"] = cacheValue.Data
test4["result"] = newExists == nil && cacheValue.Data != nil
tests = append(tests, test4)
test5 := Map{"name": "5. 测试兼容模式写新删老"}
testKey5 := "test_compat_write_" + ObjToStr(time.Now().UnixNano())
testValue5 := "兼容模式测试数据"
that.Db.Insert(legacyTableName, Map{
"key": testKey5,
"value": `{"data":"老表原始数据"}`,
"endtime": time.Now().Unix() + 3600,
"time": time.Now().UnixNano(),
})
legacyBefore := that.Db.Get(legacyTableName, "*", Map{"key": testKey5})
test5["step1_legacy_before"] = legacyBefore != nil
that.Application.Cache(testKey5, testValue5)
newAfter := that.Db.Get(newTableName, "*", Map{"key": testKey5})
test5["step2_new_after"] = newAfter != nil
if newAfter != nil {
test5["new_value"] = newAfter.GetString("value")
}
legacyAfter := that.Db.Get(legacyTableName, "*", Map{"key": testKey5})
test5["step3_legacy_after_deleted"] = legacyAfter == nil
test5["result"] = legacyBefore != nil && newAfter != nil && legacyAfter == nil
tests = append(tests, test5)
test6 := Map{"name": "6. 测试兼容模式删除(删除两表)"}
testKey6 := "test_compat_delete_" + ObjToStr(time.Now().UnixNano())
that.Db.Insert(legacyTableName, Map{
"key": testKey6,
"value": `{"data":"待删除老数据"}`,
"endtime": time.Now().Unix() + 3600,
"time": time.Now().UnixNano(),
})
that.Db.Insert(newTableName, Map{
"key": testKey6,
"value": `"待删除新数据"`,
"end_time": time.Now().Add(time.Hour).Format("2006-01-02 15:04:05"),
"state": 0,
"create_time": time.Now().Format("2006-01-02 15:04:05"),
"modify_time": time.Now().Format("2006-01-02 15:04:05"),
})
test6["before_legacy"] = that.Db.Get(legacyTableName, "*", Map{"key": testKey6}) != nil
test6["before_new"] = that.Db.Get(newTableName, "*", Map{"key": testKey6}) != nil
that.Application.Cache(testKey6, nil)
test6["after_legacy_deleted"] = that.Db.Get(legacyTableName, "*", Map{"key": testKey6}) == nil
test6["after_new_deleted"] = that.Db.Get(newTableName, "*", Map{"key": testKey6}) == nil
test6["result"] = test6.GetBool("before_legacy") && test6.GetBool("before_new") &&
test6.GetBool("after_legacy_deleted") && test6.GetBool("after_new_deleted")
tests = append(tests, test6)
that.Db.Delete(newTableName, Map{"key[~]": "test_compat_%"})
that.Db.Delete(legacyTableName, Map{"key[~]": "test_compat_%"})
result["tests"] = tests
result["success"] = true
return result
}
+11
View File
@@ -0,0 +1,11 @@
package app
import (
. "code.hoteas.com/golang/hotime"
)
var CacheTest = CtrTest{
"all": {Desc: "缓存全部测试", Func: func(a *Api) { a.Get("缓存全部测试", 0) }},
"compat": {Desc: "缓存兼容模式测试", Func: func(a *Api) { a.Get("兼容模式测试", 0) }},
"batch": {Desc: "批量缓存测试", Func: func(a *Api) { a.Get("批量缓存测试", 0) }},
}
+88
View File
@@ -0,0 +1,88 @@
package app
import (
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
// DmdbCtr 达梦数据库特有测试控制器
var DmdbCtr = Ctr{
"tables": func(that *Context) {
if that.Db.Type != "dm" && that.Db.Type != "dameng" {
that.Display(0, Map{"skip": true, "reason": "当前非达梦数据库"})
return
}
tables := that.Db.Query(`SELECT TABLE_NAME AS "name", COMMENTS AS "label" FROM USER_TAB_COMMENTS WHERE TABLE_TYPE='TABLE'`)
that.Display(0, Map{"tables": tables})
},
"describe": func(that *Context) {
if that.Db.Type != "dm" && that.Db.Type != "dameng" {
that.Display(0, Map{"skip": true, "reason": "当前非达梦数据库"})
return
}
tableName := that.Req.FormValue("table")
if tableName == "" {
that.Display(1, "请提供 table 参数")
return
}
columns := that.Db.Query(`SELECT c.COLUMN_NAME AS "name", c.DATA_TYPE AS "type", m.COMMENTS AS "label", c.NULLABLE AS "must" FROM USER_TAB_COLUMNS c LEFT JOIN USER_COL_COMMENTS m ON c.TABLE_NAME=m.TABLE_NAME AND c.COLUMN_NAME=m.COLUMN_NAME WHERE c.TABLE_NAME='` + tableName + `' ORDER BY c.COLUMN_ID`)
data := that.Db.Select(tableName, "*", Map{"LIMIT": 10})
that.Display(0, Map{"table": tableName, "columns": columns, "sample_data": data})
},
"rawsql": func(that *Context) {
if that.Db.Type != "dm" && that.Db.Type != "dameng" {
that.Display(0, Map{"skip": true, "reason": "当前非达梦数据库"})
return
}
result := testDmRawSQL(that)
that.Display(0, result)
},
}
func testDmRawSQL(that *Context) Map {
result := Map{"name": "达梦原生SQL测试", "tests": Slice{}}
tests := Slice{}
test1 := Map{"name": "Query 原生查询 (双引号)"}
articles1 := that.Db.Query(`SELECT "id", "title", "author" FROM "article" WHERE "state" = ? LIMIT ?`, 0, 5)
test1["result"] = len(articles1) >= 0
test1["count"] = len(articles1)
tests = append(tests, test1)
test2 := Map{"name": "Exec 原生执行 (双引号)"}
testArticle := that.Db.Get("article", "id", Map{"state": 0})
if testArticle != nil {
res, err := that.Db.Exec(`UPDATE "article" SET "modify_time" = NOW() WHERE "id" = ?`, testArticle.GetInt64("id"))
if err.GetError() == nil && res != nil {
affected, _ := res.RowsAffected()
test2["result"] = affected >= 0
test2["affected"] = affected
} else {
test2["result"] = false
test2["error"] = err.GetError()
}
} else {
test2["result"] = true
test2["note"] = "无可用测试数据"
}
tests = append(tests, test2)
test3 := Map{"name": "USER_TABLES 查询"}
tables := that.Db.Query(`SELECT TABLE_NAME FROM USER_TABLES`)
test3["result"] = len(tables) >= 0
test3["count"] = len(tables)
tests = append(tests, test3)
test4 := Map{"name": "USER_TAB_COLUMNS 查询"}
cols := that.Db.Query(`SELECT COLUMN_NAME, DATA_TYPE FROM USER_TAB_COLUMNS WHERE TABLE_NAME='admin' ORDER BY COLUMN_ID`)
test4["result"] = len(cols) >= 0
test4["count"] = len(cols)
test4["columns"] = cols
tests = append(tests, test4)
result["tests"] = tests
result["success"] = true
return result
}
+12
View File
@@ -0,0 +1,12 @@
package app
import (
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
var DmdbTest = CtrTest{
"tables": {Desc: "查询达梦所有表", Func: func(a *Api) { a.Get("USER_TABLES 查询", 0) }},
"describe": {Desc: "查询达梦表结构", Func: func(a *Api) { a.Query(Map{"table": "admin"}).Get("USER_TAB_COLUMNS 查询", 0) }},
"rawsql": {Desc: "达梦原生 SQL 测试", Func: func(a *Api) { a.Get("达梦原生SQL", 0) }},
}
+12
View File
@@ -0,0 +1,12 @@
package app
import (
. "code.hoteas.com/golang/hotime"
)
var Project = Proj{
"test": TestCtr,
"mysql": MysqlCtr,
"dmdb": DmdbCtr,
"cache": CacheCtr,
}
+96
View File
@@ -0,0 +1,96 @@
package app
import (
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
// MysqlCtr MySQL 特有测试控制器
var MysqlCtr = Ctr{
"tables": func(that *Context) {
if that.Db.Type != "mysql" {
that.Display(0, Map{"skip": true, "reason": "当前非 MySQL 数据库"})
return
}
tables := that.Db.Query("SHOW TABLES")
that.Display(0, Map{"tables": tables})
},
"describe": func(that *Context) {
if that.Db.Type != "mysql" {
that.Display(0, Map{"skip": true, "reason": "当前非 MySQL 数据库"})
return
}
tableName := that.Req.FormValue("table")
if tableName == "" {
that.Display(1, "请提供 table 参数")
return
}
columns := that.Db.Query("DESCRIBE " + tableName)
data := that.Db.Select(tableName, Map{"LIMIT": 10})
that.Display(0, Map{"table": tableName, "columns": columns, "sample_data": data})
},
"rawsql": func(that *Context) {
if that.Db.Type != "mysql" {
that.Display(0, Map{"skip": true, "reason": "当前非 MySQL 数据库"})
return
}
result := testMysqlRawSQL(that)
that.Display(0, result)
},
}
func testMysqlRawSQL(that *Context) Map {
result := Map{"name": "MySQL原生SQL测试", "tests": Slice{}}
tests := Slice{}
test1 := Map{"name": "Query 原生查询 (反引号)"}
articles1 := that.Db.Query("SELECT id, title, author FROM `article` WHERE state = ? LIMIT ?", 0, 5)
test1["result"] = len(articles1) >= 0
test1["count"] = len(articles1)
tests = append(tests, test1)
test2 := Map{"name": "Exec 原生执行 (反引号)"}
testArticle := that.Db.Get("article", "id", Map{"state": 0})
if testArticle != nil {
res, err := that.Db.Exec("UPDATE `article` SET modify_time = NOW() WHERE id = ?", testArticle.GetInt64("id"))
if err.GetError() == nil && res != nil {
affected, _ := res.RowsAffected()
test2["result"] = affected >= 0
test2["affected"] = affected
} else {
test2["result"] = false
test2["error"] = err.GetError()
}
} else {
test2["result"] = true
test2["note"] = "无可用测试数据"
}
tests = append(tests, test2)
test3 := Map{"name": "IN (?) 非空数组展开"}
articles3 := that.Db.Query("SELECT id, title FROM `article` WHERE id IN (?) LIMIT 10", []int{1, 2, 3, 4, 5})
test3["result"] = len(articles3) >= 0
test3["count"] = len(articles3)
test3["lastQuery"] = that.Db.LastQuery
tests = append(tests, test3)
test4 := Map{"name": "IN (?) 空数组替换为1=0"}
articles4 := that.Db.Query("SELECT id, title FROM `article` WHERE id IN (?) LIMIT 10", []int{})
test4["result"] = len(articles4) == 0
test4["count"] = len(articles4)
test4["lastQuery"] = that.Db.LastQuery
tests = append(tests, test4)
test5 := Map{"name": "NOT IN (?) 空数组替换为1=1"}
articles5 := that.Db.Query("SELECT id, title FROM `article` WHERE id NOT IN (?) LIMIT 10", []int{})
test5["result"] = len(articles5) > 0
test5["count"] = len(articles5)
test5["lastQuery"] = that.Db.LastQuery
tests = append(tests, test5)
result["tests"] = tests
result["success"] = true
return result
}
+12
View File
@@ -0,0 +1,12 @@
package app
import (
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
var MysqlTest = CtrTest{
"tables": {Desc: "查询 MySQL 所有表", Func: func(a *Api) { a.Get("SHOW TABLES", 0) }},
"describe": {Desc: "查询 MySQL 表结构", Func: func(a *Api) { a.Query(Map{"table": "admin"}).Get("DESCRIBE admin", 0) }},
"rawsql": {Desc: "MySQL 原生 SQL 测试", Func: func(a *Api) { a.Get("MySQL原生SQL", 0) }},
}
+126
View File
@@ -0,0 +1,126 @@
package app
import (
"fmt"
. "code.hoteas.com/golang/hotime/db"
)
// SetupDMDatabase 为达梦数据库创建测试表并灌入测试数据
func SetupDMDatabase(db *HoTimeDB) {
if db.Type != "dm" && db.Type != "dameng" {
return
}
fmt.Println("[DM Setup] 开始初始化达梦数据库测试环境...")
prefix := db.GetPrefix()
createDMTables(db, prefix)
seedTestData(db, "DM")
fmt.Println("[DM Setup] 达梦数据库测试环境初始化完成")
}
func dmTableExists(db *HoTimeDB, tableName string) bool {
// COUNT(*) 即使表为空也会返回一行;若表不存在则报错返回空切片。
// 相比 USER_TABLES,这里直接检测当前 schema 下表的可访问性,避免
// 连接 schema?schema=TEST)与 USER_TABLESSYSDBA 自身 schema)错配。
res := db.Query(`SELECT COUNT(*) as cnt FROM "` + tableName + `"`)
return len(res) > 0
}
func createDMTables(db *HoTimeDB, prefix string) {
// admin 表
tbl := prefix + "admin"
if !dmTableExists(db, tbl) {
fmt.Println("[DM Setup] 创建表:", tbl)
db.Exec(`CREATE TABLE "` + tbl + `" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"name" VARCHAR(100),
"phone" VARCHAR(20),
"state" INT DEFAULT 0,
"password" VARCHAR(100),
"role_id" INT DEFAULT 0,
"title" VARCHAR(100),
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"modify_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`)
db.Exec(`CREATE UNIQUE INDEX "uk_` + tbl + `_phone" ON "` + tbl + `" ("phone")`)
}
// ctg 分类表
tbl = prefix + "ctg"
if !dmTableExists(db, tbl) {
fmt.Println("[DM Setup] 创建表:", tbl)
db.Exec(`CREATE TABLE "` + tbl + `" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"name" VARCHAR(100),
"state" INT DEFAULT 0,
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"modify_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`)
}
// article 文章表
tbl = prefix + "article"
if !dmTableExists(db, tbl) {
fmt.Println("[DM Setup] 创建表:", tbl)
db.Exec(`CREATE TABLE "` + tbl + `" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"title" VARCHAR(200),
"author" VARCHAR(100),
"content" CLOB,
"state" INT DEFAULT 0,
"click_num" INT DEFAULT 0,
"sort" INT DEFAULT 0,
"img" VARCHAR(500),
"ctg_id" INT DEFAULT 0,
"admin_id" INT DEFAULT 0,
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"modify_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`)
}
// test_batch 测试批量表
tbl = prefix + "test_batch"
if !dmTableExists(db, tbl) {
fmt.Println("[DM Setup] 创建表:", tbl)
db.Exec(`CREATE TABLE "` + tbl + `" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"name" VARCHAR(100),
"title" VARCHAR(200),
"state" INT DEFAULT 0,
"create_time" TIMESTAMP
)`)
}
// cached 老版本缓存表(兼容模式测试需要)
tbl = prefix + "cached"
if !dmTableExists(db, tbl) {
fmt.Println("[DM Setup] 创建表:", tbl)
db.Exec(`CREATE TABLE "` + tbl + `" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"key" VARCHAR(64),
"value" CLOB,
"endtime" BIGINT,
"time" BIGINT
)`)
}
// hotime_cache 新版缓存表
tbl = prefix + "hotime_cache"
if !dmTableExists(db, tbl) {
fmt.Println("[DM Setup] 创建表:", tbl)
db.Exec(`CREATE TABLE "` + tbl + `" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"key" VARCHAR(64) NOT NULL,
"value" TEXT,
"end_time" TIMESTAMP,
"state" INT DEFAULT 0,
"create_time" TIMESTAMP,
"modify_time" TIMESTAMP,
CONSTRAINT "uk_` + tbl + `_key" UNIQUE ("key")
)`)
db.Exec(`CREATE INDEX "idx_` + tbl + `_end_time" ON "` + tbl + `" ("end_time")`)
}
}
+131
View File
@@ -0,0 +1,131 @@
package app
import (
"fmt"
. "code.hoteas.com/golang/hotime/db"
)
// SetupMySQLDatabase 为 MySQL 数据库创建测试表并灌入测试数据
func SetupMySQLDatabase(db *HoTimeDB) {
if db.Type != "mysql" {
return
}
fmt.Println("[MySQL Setup] 开始初始化 MySQL 数据库测试环境...")
prefix := db.GetPrefix()
createMySQLTables(db, prefix)
seedTestData(db, "MySQL")
fmt.Println("[MySQL Setup] MySQL 数据库测试环境初始化完成")
}
func mysqlTableExists(db *HoTimeDB, tableName string) bool {
rows := db.Query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?", tableName)
return len(rows) > 0
}
func createMySQLTables(db *HoTimeDB, prefix string) {
// admin 表
tbl := prefix + "admin"
if !mysqlTableExists(db, tbl) {
fmt.Println("[MySQL Setup] 创建表:", tbl)
db.Exec("CREATE TABLE `" + tbl + "` (" +
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
"`name` varchar(100) DEFAULT NULL," +
"`phone` varchar(20) DEFAULT NULL," +
"`state` int(2) DEFAULT '0'," +
"`password` varchar(100) DEFAULT NULL," +
"`role_id` int(11) DEFAULT '0'," +
"`title` varchar(100) DEFAULT NULL," +
"`create_time` datetime DEFAULT NULL," +
"`modify_time` datetime DEFAULT NULL," +
"PRIMARY KEY (`id`)," +
"UNIQUE KEY `uk_phone` (`phone`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
}
// ctg 分类表
tbl = prefix + "ctg"
if !mysqlTableExists(db, tbl) {
fmt.Println("[MySQL Setup] 创建表:", tbl)
db.Exec("CREATE TABLE `" + tbl + "` (" +
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
"`name` varchar(100) DEFAULT NULL," +
"`state` int(2) DEFAULT '0'," +
"`create_time` datetime DEFAULT NULL," +
"`modify_time` datetime DEFAULT NULL," +
"PRIMARY KEY (`id`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
}
// article 文章表
tbl = prefix + "article"
if !mysqlTableExists(db, tbl) {
fmt.Println("[MySQL Setup] 创建表:", tbl)
db.Exec("CREATE TABLE `" + tbl + "` (" +
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
"`title` varchar(200) DEFAULT NULL," +
"`author` varchar(100) DEFAULT NULL," +
"`content` text," +
"`state` int(2) DEFAULT '0'," +
"`click_num` int(11) DEFAULT '0'," +
"`sort` int(11) DEFAULT '0'," +
"`img` varchar(500) DEFAULT NULL," +
"`ctg_id` int(11) DEFAULT '0'," +
"`admin_id` int(11) DEFAULT '0'," +
"`create_time` datetime DEFAULT NULL," +
"`modify_time` datetime DEFAULT NULL," +
"PRIMARY KEY (`id`)," +
"KEY `idx_state` (`state`)," +
"KEY `idx_ctg_id` (`ctg_id`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
}
// test_batch 测试批量表
tbl = prefix + "test_batch"
if !mysqlTableExists(db, tbl) {
fmt.Println("[MySQL Setup] 创建表:", tbl)
db.Exec("CREATE TABLE `" + tbl + "` (" +
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
"`name` varchar(100) DEFAULT NULL," +
"`title` varchar(200) DEFAULT NULL," +
"`state` int(2) DEFAULT '0'," +
"`create_time` datetime DEFAULT NULL," +
"PRIMARY KEY (`id`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
}
// cached 老版本缓存表(兼容模式测试需要)
tbl = prefix + "cached"
if !mysqlTableExists(db, tbl) {
fmt.Println("[MySQL Setup] 创建表:", tbl)
db.Exec("CREATE TABLE `" + tbl + "` (" +
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
"`key` varchar(64) DEFAULT NULL," +
"`value` text," +
"`endtime` bigint(20) DEFAULT NULL," +
"`time` bigint(20) DEFAULT NULL," +
"PRIMARY KEY (`id`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
}
// hotime_cache 新版缓存表
tbl = prefix + "hotime_cache"
if !mysqlTableExists(db, tbl) {
fmt.Println("[MySQL Setup] 创建表:", tbl)
db.Exec("CREATE TABLE `" + tbl + "` (" +
"`id` int(11) unsigned NOT NULL AUTO_INCREMENT," +
"`key` varchar(64) NOT NULL," +
"`value` text," +
"`end_time` datetime DEFAULT NULL," +
"`state` int(2) DEFAULT '0'," +
"`create_time` datetime DEFAULT NULL," +
"`modify_time` datetime DEFAULT NULL," +
"PRIMARY KEY (`id`)," +
"UNIQUE KEY `uk_key` (`key`)," +
"KEY `idx_end_time` (`end_time`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
}
}
+79
View File
@@ -0,0 +1,79 @@
package app
import (
"fmt"
. "code.hoteas.com/golang/hotime/db"
)
// SetupDatabase 根据数据库类型自动初始化测试表和种子数据
// 在 TestMain 中调用一次即可
func SetupDatabase(db *HoTimeDB) {
switch db.Type {
case "dm", "dameng":
SetupDMDatabase(db)
case "mysql":
SetupMySQLDatabase(db)
}
}
// seedTestData 通用种子数据灌入(DM 和 MySQL 共用)
// 使用 ORM 的 Insert 方法,与数据库方言无关
func seedTestData(db *HoTimeDB, tag string) {
// 已有数据则跳过
if db.Count("ctg") > 0 {
fmt.Printf("[%s Setup] 表中已有数据,跳过数据灌入\n", tag)
return
}
fmt.Printf("[%s Setup] 灌入测试数据...\n", tag)
// 分类数据
ctgs := []map[string]interface{}{
{"name": "新闻资讯", "state": 0},
{"name": "技术文档", "state": 0},
{"name": "公告通知", "state": 0},
{"name": "产品介绍", "state": 0},
{"name": "已归档", "state": 1},
}
for _, c := range ctgs {
c["create_time[#]"] = "NOW()"
c["modify_time[#]"] = "NOW()"
db.Insert("ctg", c)
}
// 管理员数据
admins := []map[string]interface{}{
{"name": "超级管理员", "phone": "13800000001", "state": 1, "password": "admin123", "role_id": 1, "title": "系统管理员"},
{"name": "编辑员", "phone": "13800000002", "state": 1, "password": "editor123", "role_id": 2, "title": "内容编辑"},
{"name": "审核员", "phone": "13800000003", "state": 1, "password": "review123", "role_id": 3, "title": "内容审核"},
}
for _, a := range admins {
a["create_time[#]"] = "NOW()"
a["modify_time[#]"] = "NOW()"
db.Insert("admin", a)
}
// 文章数据 — 覆盖不同状态、分类、点击数、排序
articles := []map[string]interface{}{
{"title": "数据库入门指南", "author": "管理员", "content": "国产关系型数据库管理系统入门...", "state": 0, "click_num": 128, "sort": 10, "ctg_id": 1, "admin_id": 1},
{"title": "新闻发布系统上线公告", "author": "编辑员", "content": "本系统已正式上线运行...", "state": 0, "click_num": 256, "sort": 5, "ctg_id": 1, "admin_id": 2},
{"title": "Go语言最佳实践", "author": "管理员", "content": "Go语言在后端开发中的最佳实践总结...", "state": 0, "click_num": 512, "sort": 8, "ctg_id": 2, "admin_id": 1},
{"title": "API接口设计规范", "author": "审核员", "content": "RESTful API设计的基本规范和标准...", "state": 0, "click_num": 64, "sort": 15, "ctg_id": 2, "admin_id": 3},
{"title": "系统维护通知", "author": "管理员", "content": "系统将于本周日凌晨进行维护升级...", "state": 0, "click_num": 32, "sort": 1, "ctg_id": 3, "admin_id": 1},
{"title": "产品功能更新说明", "author": "编辑员", "content": "新版本增加了以下功能...", "state": 0, "click_num": 96, "sort": 12, "ctg_id": 4, "admin_id": 2},
{"title": "数据库性能优化指南", "author": "管理员", "content": "数据库性能优化的常见方法和技巧...", "state": 0, "click_num": 1024, "sort": 3, "ctg_id": 2, "admin_id": 1},
{"title": "缓存机制深入分析", "author": "审核员", "content": "缓存在Web应用中的重要作用...", "state": 0, "click_num": 200, "sort": 7, "ctg_id": 2, "admin_id": 3},
{"title": "旧版公告(已归档)", "author": "管理员", "content": "此公告已过期归档...", "state": 1, "click_num": 10, "sort": 0, "ctg_id": 5, "admin_id": 1},
{"title": "测试文章(隐藏)", "author": "编辑员", "content": "测试用文章不公开显示...", "state": 2, "click_num": 0, "sort": 0, "ctg_id": 1, "admin_id": 2},
{"title": "高点击量文章", "author": "编辑员", "content": "这是一篇点击量很高的文章...", "state": 0, "click_num": 9999, "sort": 2, "ctg_id": 1, "admin_id": 2},
}
for _, a := range articles {
a["create_time[#]"] = "NOW()"
a["modify_time[#]"] = "NOW()"
db.Insert("article", a)
}
fmt.Printf("[%s Setup] 数据灌入完成: ctg=%d, admin=%d, article=%d\n",
tag, db.Count("ctg"), db.Count("admin"), db.Count("article"))
}
+747
View File
@@ -0,0 +1,747 @@
package app
import (
"fmt"
"time"
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
. "code.hoteas.com/golang/hotime/db"
)
// initTestTables 根据数据库类型初始化测试表
func initTestTables(that *Context) {
switch that.Db.Type {
case "dm", "dameng":
// 用 COUNT(*) 直接探查表可访问性,而非 USER_TABLES(后者受 schema 错配影响)
exists := that.Db.Query(`SELECT COUNT(*) as cnt FROM "test_batch"`)
if len(exists) == 0 {
that.Db.Exec(`CREATE TABLE "test_batch" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"name" VARCHAR(100),
"title" VARCHAR(200),
"state" INT DEFAULT 0,
"create_time" TIMESTAMP
)`)
}
default:
that.Db.Exec(`CREATE TABLE IF NOT EXISTS test_batch (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
title VARCHAR(200),
state INT DEFAULT 0,
create_time DATETIME
)`)
}
_ = that.Db.Count("admin")
_ = that.Db.Count("article")
}
// nowFunc 返回当前数据库的 NOW() 函数表达式
func nowFunc(that *Context) string {
return "NOW()"
}
// TestCtr 通用 ORM 测试控制器(数据库无关)
var TestCtr = Ctr{
"test": func(that *Context) {
that.Display(2, "dsadasd")
},
"all": func(that *Context) {
results := Map{}
initTestTables(that)
results["1_basic_crud"] = testBasicCRUD(that)
results["2_condition_syntax"] = testConditionSyntax(that)
results["3_chain_query"] = testChainQuery(that)
results["4_join_query"] = testJoinQuery(that)
results["5_aggregate"] = testAggregate(that)
results["6_pagination"] = testPagination(that)
results["7_batch_insert"] = testInserts(that)
results["8_upsert"] = testUpsert(that)
results["9_transaction"] = testTransaction(that)
that.Display(0, results)
},
"crud": func(that *Context) { initTestTables(that); that.Display(0, testBasicCRUD(that)) },
"condition": func(that *Context) { that.Display(0, testConditionSyntax(that)) },
"chain": func(that *Context) { that.Display(0, testChainQuery(that)) },
"join": func(that *Context) { that.Display(0, testJoinQuery(that)) },
"aggregate": func(that *Context) { that.Display(0, testAggregate(that)) },
"pagination": func(that *Context) { that.Display(0, testPagination(that)) },
"batch": func(that *Context) { initTestTables(that); that.Display(0, testInserts(that)) },
"upsert": func(that *Context) { that.Display(0, testUpsert(that)) },
"transaction": func(that *Context) { initTestTables(that); that.Display(0, testTransaction(that)) },
}
func testBasicCRUD(that *Context) Map {
result := Map{"name": "基础CRUD测试", "tests": Slice{}}
tests := Slice{}
now := nowFunc(that)
insertTest := Map{"name": "Insert 插入测试 (admin表)"}
adminId := that.Db.Insert("admin", Map{
"name": "测试管理员_" + fmt.Sprintf("%d", time.Now().Unix()),
"phone": fmt.Sprintf("138%d", time.Now().Unix()%100000000),
"state": 1,
"password": "test123456",
"role_id": 1,
"title": "测试职位",
"create_time[#]": now,
"modify_time[#]": now,
})
insertTest["result"] = adminId > 0
insertTest["adminId"] = adminId
insertTest["lastQuery"] = that.Db.LastQuery
tests = append(tests, insertTest)
getTest := Map{"name": "Get 获取单条记录测试"}
admin := that.Db.Get("admin", "*", Map{"id": adminId})
getTest["result"] = admin != nil && admin.GetInt64("id") == adminId
getTest["admin"] = admin
tests = append(tests, getTest)
selectTest1 := Map{"name": "Select 单条件查询测试"}
admins1 := that.Db.Select("admin", "*", Map{"state": 1, "LIMIT": 5})
selectTest1["result"] = len(admins1) >= 0
selectTest1["count"] = len(admins1)
tests = append(tests, selectTest1)
selectTest2 := Map{"name": "Select 多条件自动AND测试"}
admins2 := that.Db.Select("admin", "*", Map{
"state": 1,
"role_id[>]": 0,
"ORDER": "id DESC",
"LIMIT": 5,
})
selectTest2["result"] = true
selectTest2["count"] = len(admins2)
selectTest2["lastQuery"] = that.Db.LastQuery
tests = append(tests, selectTest2)
updateTest := Map{"name": "Update 更新测试"}
affected := that.Db.Update("admin", Map{
"title": "更新后的职位",
"modify_time[#]": now,
}, Map{"id": adminId})
updateTest["result"] = affected > 0
updateTest["affected"] = affected
tests = append(tests, updateTest)
deleteTest := Map{"name": "Delete 删除测试"}
tempId := that.Db.Insert("test_batch", Map{
"name": "临时删除测试",
"title": "测试标题",
"state": 1,
"create_time[#]": now,
})
deleteAffected := that.Db.Delete("test_batch", Map{"id": tempId})
deleteTest["result"] = deleteAffected > 0
deleteTest["affected"] = deleteAffected
tests = append(tests, deleteTest)
result["tests"] = tests
result["success"] = true
return result
}
func testConditionSyntax(that *Context) Map {
result := Map{"name": "条件查询语法测试", "tests": Slice{}}
tests := Slice{}
test1 := Map{"name": "等于条件 (=)"}
articles1 := that.Db.Select("article", "id,title", Map{"state": 0, "LIMIT": 3})
test1["result"] = true
test1["count"] = len(articles1)
tests = append(tests, test1)
test2 := Map{"name": "不等于条件 ([!])"}
articles2 := that.Db.Select("article", "id,title,state", Map{"state[!]": -1, "LIMIT": 3})
test2["result"] = true
test2["count"] = len(articles2)
test2["lastQuery"] = that.Db.LastQuery
tests = append(tests, test2)
test3 := Map{"name": "大于小于条件 ([>], [<])"}
articles3 := that.Db.Select("article", "id,title,click_num", Map{
"click_num[>]": 0,
"click_num[<]": 100000,
"LIMIT": 3,
})
test3["result"] = true
test3["count"] = len(articles3)
tests = append(tests, test3)
test4 := Map{"name": "大于等于小于等于条件 ([>=], [<=])"}
articles4 := that.Db.Select("article", "id,title,sort", Map{
"sort[>=]": 0,
"sort[<=]": 100,
"LIMIT": 3,
})
test4["result"] = true
test4["count"] = len(articles4)
tests = append(tests, test4)
test5 := Map{"name": "LIKE 模糊查询 ([~])"}
articles5 := that.Db.Select("article", "id,title", Map{"title[~]": "新闻", "LIMIT": 3})
test5["result"] = true
test5["count"] = len(articles5)
test5["lastQuery"] = that.Db.LastQuery
tests = append(tests, test5)
test6 := Map{"name": "右模糊查询 ([~!])"}
articles6 := that.Db.Select("admin", "id,name", Map{"name[~!]": "管理", "LIMIT": 3})
test6["result"] = true
test6["count"] = len(articles6)
tests = append(tests, test6)
test7 := Map{"name": "BETWEEN 区间查询 ([<>])"}
articles7 := that.Db.Select("article", "id,title,click_num", Map{"click_num[<>]": Slice{0, 1000}, "LIMIT": 3})
test7["result"] = true
test7["count"] = len(articles7)
test7["lastQuery"] = that.Db.LastQuery
tests = append(tests, test7)
test8 := Map{"name": "NOT BETWEEN 查询 ([><])"}
articles8 := that.Db.Select("article", "id,title,sort", Map{"sort[><]": Slice{-10, 0}, "LIMIT": 3})
test8["result"] = true
test8["count"] = len(articles8)
tests = append(tests, test8)
test9 := Map{"name": "IN 查询"}
articles9 := that.Db.Select("article", "id,title", Map{"id": Slice{1, 2, 3, 4, 5}, "LIMIT": 5})
test9["result"] = true
test9["count"] = len(articles9)
test9["lastQuery"] = that.Db.LastQuery
tests = append(tests, test9)
test10 := Map{"name": "NOT IN 查询 ([!])"}
articles10 := that.Db.Select("article", "id,title", Map{"id[!]": Slice{1, 2, 3}, "LIMIT": 5})
test10["result"] = true
test10["count"] = len(articles10)
tests = append(tests, test10)
test11 := Map{"name": "IS NULL 查询"}
articles11 := that.Db.Select("article", "id,title,img", Map{"img": nil, "LIMIT": 3})
test11["result"] = true
test11["count"] = len(articles11)
tests = append(tests, test11)
test12 := Map{"name": "IS NOT NULL 查询 ([!])"}
articles12 := that.Db.Select("article", "id,title,create_time", Map{"create_time[!]": nil, "LIMIT": 3})
test12["result"] = true
test12["count"] = len(articles12)
tests = append(tests, test12)
// SQL 片段查询根据数据库类型适配
test13 := Map{"name": "直接 SQL 片段查询 ([##])"}
var sqlFragment string
switch that.Db.Type {
case "dm", "dameng":
sqlFragment = "\"create_time\" > DATEADD(DAY, -365, NOW())"
default:
sqlFragment = "create_time > DATE_SUB(NOW(), INTERVAL 365 DAY)"
}
articles13 := that.Db.Select("article", "id,title,create_time", Map{
"[##]": sqlFragment,
"LIMIT": 3,
})
test13["result"] = true
test13["count"] = len(articles13)
test13["lastQuery"] = that.Db.LastQuery
tests = append(tests, test13)
test14 := Map{"name": "显式 AND 条件"}
articles14 := that.Db.Select("article", "id,title,state,click_num", Map{
"AND": Map{
"state": 0,
"click_num[>=]": 0,
},
"LIMIT": 3,
})
test14["result"] = true
test14["count"] = len(articles14)
tests = append(tests, test14)
test15 := Map{"name": "OR 条件"}
articles15 := that.Db.Select("article", "id,title,sort,click_num", Map{
"OR": Map{
"sort": 0,
"click_num[>]": 10,
},
"LIMIT": 5,
})
test15["result"] = true
test15["count"] = len(articles15)
tests = append(tests, test15)
test16 := Map{"name": "嵌套 AND/OR 条件"}
articles16 := that.Db.Select("article", "id,title,sort,state", Map{
"AND": Map{
"state": 0,
"OR": Map{
"sort[>=]": 0,
"click_num[>]": 0,
},
},
"LIMIT": 5,
})
test16["result"] = true
test16["count"] = len(articles16)
test16["lastQuery"] = that.Db.LastQuery
tests = append(tests, test16)
result["tests"] = tests
result["success"] = true
return result
}
func testChainQuery(that *Context) Map {
result := Map{"name": "链式查询测试", "tests": Slice{}}
tests := Slice{}
test1 := Map{"name": "基本链式查询 Table().Where().Select()"}
articles1 := that.Db.Table("article").
Where("state", 0).
Select("id,title,author")
test1["result"] = len(articles1) >= 0
test1["count"] = len(articles1)
tests = append(tests, test1)
test2 := Map{"name": "链式 And 条件"}
articles2 := that.Db.Table("article").
Where("state", 0).
And("click_num[>=]", 0).
And("sort[>=]", 0).
Select("id,title,click_num")
test2["result"] = len(articles2) >= 0
test2["count"] = len(articles2)
tests = append(tests, test2)
test3 := Map{"name": "链式 Or 条件"}
articles3 := that.Db.Table("article").
Where("state", 0).
Or(Map{
"sort": 0,
"click_num[>]": 10,
}).
Select("id,title,sort,click_num")
test3["result"] = len(articles3) >= 0
test3["count"] = len(articles3)
tests = append(tests, test3)
test4 := Map{"name": "链式 Order 排序"}
articles4 := that.Db.Table("article").
Where("state", 0).
Order("create_time DESC", "id ASC").
Limit(0, 5).
Select("id,title,create_time")
test4["result"] = len(articles4) >= 0
test4["count"] = len(articles4)
tests = append(tests, test4)
test5 := Map{"name": "链式 Limit 限制"}
articles5 := that.Db.Table("article").
Where("state", 0).
Limit(0, 3).
Select("id,title")
test5["result"] = len(articles5) <= 3
test5["count"] = len(articles5)
tests = append(tests, test5)
test6 := Map{"name": "链式 Get 获取单条"}
article6 := that.Db.Table("article").
Where("state", 0).
Get("id,title,author")
test6["result"] = article6 != nil || true
test6["article"] = article6
tests = append(tests, test6)
test7 := Map{"name": "链式 Count 统计"}
count7 := that.Db.Table("article").
Where("state", 0).
Count()
test7["result"] = count7 >= 0
test7["count"] = count7
tests = append(tests, test7)
test8 := Map{"name": "链式 Page 分页"}
articles8 := that.Db.Table("article").
Where("state", 0).
Page(1, 5).
Select("id,title")
test8["result"] = len(articles8) <= 5
test8["count"] = len(articles8)
tests = append(tests, test8)
test9 := Map{"name": "链式 Group 分组"}
stats9 := that.Db.Table("article").
Where("state", 0).
Group("ctg_id").
Select("ctg_id, COUNT(*) as cnt")
test9["result"] = len(stats9) >= 0
test9["stats"] = stats9
tests = append(tests, test9)
test10 := Map{"name": "链式 Update 更新"}
testArticle := that.Db.Table("article").Where("state", 0).Get("id")
if testArticle != nil {
affected := that.Db.Table("article").
Where("id", testArticle.GetInt64("id")).
Update(Map{"modify_time[#]": nowFunc(that)})
test10["result"] = affected >= 0
test10["affected"] = affected
} else {
test10["result"] = true
test10["note"] = "无可用测试数据"
}
tests = append(tests, test10)
result["tests"] = tests
result["success"] = true
return result
}
func testJoinQuery(that *Context) Map {
result := Map{"name": "JOIN查询测试", "tests": Slice{}}
tests := Slice{}
test1 := Map{"name": "LEFT JOIN 链式查询"}
articles1 := that.Db.Table("article").
LeftJoin("ctg", "article.ctg_id = ctg.id").
Where("article.state", 0).
Limit(0, 5).
Select("article.id, article.title, ctg.name as ctg_name")
test1["result"] = len(articles1) >= 0
test1["count"] = len(articles1)
test1["data"] = articles1
tests = append(tests, test1)
test2 := Map{"name": "传统 JOIN 语法"}
articles2 := that.Db.Select("article",
Slice{
Map{"[>]ctg": "article.ctg_id = ctg.id"},
},
"article.id, article.title, ctg.name as ctg_name",
Map{
"article.state": 0,
"LIMIT": 5,
})
test2["result"] = len(articles2) >= 0
test2["count"] = len(articles2)
test2["lastQuery"] = that.Db.LastQuery
tests = append(tests, test2)
test3 := Map{"name": "多表 JOIN"}
articles3 := that.Db.Table("article").
LeftJoin("ctg", "article.ctg_id = ctg.id").
LeftJoin("admin", "article.admin_id = admin.id").
Where("article.state", 0).
Limit(0, 5).
Select("article.id, article.title, ctg.name as ctg_name, admin.name as admin_name")
test3["result"] = len(articles3) >= 0
test3["count"] = len(articles3)
test3["data"] = articles3
tests = append(tests, test3)
test4 := Map{"name": "INNER JOIN"}
articles4 := that.Db.Table("article").
InnerJoin("ctg", "article.ctg_id = ctg.id").
Where("ctg.state", 0).
Limit(0, 5).
Select("article.id, article.title, ctg.name as ctg_name")
test4["result"] = len(articles4) >= 0
test4["count"] = len(articles4)
tests = append(tests, test4)
result["tests"] = tests
result["success"] = true
return result
}
func testAggregate(that *Context) Map {
result := Map{"name": "聚合函数测试", "tests": Slice{}}
tests := Slice{}
test1 := Map{"name": "Count 总数统计"}
count1 := that.Db.Count("article")
test1["result"] = count1 >= 0
test1["count"] = count1
tests = append(tests, test1)
test2 := Map{"name": "Count 条件统计"}
count2 := that.Db.Count("article", Map{"state": 0})
test2["result"] = count2 >= 0
test2["count"] = count2
tests = append(tests, test2)
test3 := Map{"name": "Sum 求和 (单字段名)"}
sum3 := that.Db.Sum("article", "click_num", Map{"state": 0})
test3["result"] = sum3 >= 0
test3["sum"] = sum3
test3["lastQuery"] = that.Db.LastQuery
tests = append(tests, test3)
test4 := Map{"name": "Avg 平均值 (单字段名)"}
avg4 := that.Db.Avg("article", "click_num", Map{"state": 0})
test4["result"] = avg4 >= 0
test4["avg"] = avg4
test4["lastQuery"] = that.Db.LastQuery
tests = append(tests, test4)
test5 := Map{"name": "Max 最大值 (单字段名)"}
max5 := that.Db.Max("article", "click_num", Map{"state": 0})
test5["result"] = max5 >= 0
test5["max"] = max5
test5["lastQuery"] = that.Db.LastQuery
tests = append(tests, test5)
test6 := Map{"name": "Min 最小值 (单字段名)"}
min6 := that.Db.Min("article", "sort", Map{"state": 0})
test6["result"] = true
test6["min"] = min6
test6["lastQuery"] = that.Db.LastQuery
tests = append(tests, test6)
test7 := Map{"name": "GROUP BY 分组统计"}
stats7 := that.Db.Select("article",
"ctg_id, COUNT(*) as article_count, AVG(click_num) as avg_clicks, SUM(click_num) as total_clicks",
Map{
"state": 0,
"GROUP": "ctg_id",
"ORDER": "article_count DESC",
"LIMIT": 10,
})
test7["result"] = len(stats7) >= 0
test7["stats"] = stats7
tests = append(tests, test7)
test8 := Map{"name": "Sum 求和 (table.column 格式)"}
sum8 := that.Db.Sum("article", "article.click_num", Map{"state": 0})
test8["result"] = sum8 >= 0
test8["sum"] = sum8
test8["match_single_field"] = sum8 == sum3
test8["lastQuery"] = that.Db.LastQuery
tests = append(tests, test8)
test9 := Map{"name": "聚合函数一致性验证"}
avg9 := that.Db.Avg("article", "article.click_num", Map{"state": 0})
max10 := that.Db.Max("article", "article.click_num", Map{"state": 0})
min11 := that.Db.Min("article", "article.sort", Map{"state": 0})
allMatch := (sum8 == sum3) && (avg9 == avg4) && (max10 == max5) && (min11 == min6)
test9["result"] = allMatch
test9["summary"] = fmt.Sprintf("Sum: %v=%v, Avg: %v=%v, Max: %v=%v, Min: %v=%v",
sum3, sum8, avg4, avg9, max5, max10, min6, min11)
tests = append(tests, test9)
joinSlice := Slice{
Map{"[>]ctg": "article.ctg_id = ctg.id"},
}
test10 := Map{"name": "Sum 带 JOIN (table.column 格式)"}
sum10 := that.Db.Sum("article", "article.click_num", joinSlice, Map{"article.state": 0})
test10["result"] = sum10 >= 0
test10["sum"] = sum10
test10["lastQuery"] = that.Db.LastQuery
tests = append(tests, test10)
test11 := Map{"name": "Count 带 JOIN"}
count11 := that.Db.Count("article", joinSlice, Map{"article.state": 0})
test11["result"] = count11 >= 0
test11["count"] = count11
test11["lastQuery"] = that.Db.LastQuery
tests = append(tests, test11)
result["tests"] = tests
result["success"] = true
return result
}
func testPagination(that *Context) Map {
result := Map{"name": "分页查询测试", "tests": Slice{}}
tests := Slice{}
test1 := Map{"name": "PageSelect 分页查询"}
articles1 := that.Db.Page(1, 5).PageSelect("article", "*", Map{
"state": 0,
"ORDER": "id DESC",
})
test1["result"] = len(articles1) <= 5
test1["count"] = len(articles1)
tests = append(tests, test1)
test2 := Map{"name": "PageSelect 第二页"}
articles2 := that.Db.Page(2, 5).PageSelect("article", "*", Map{
"state": 0,
"ORDER": "id DESC",
})
test2["result"] = len(articles2) <= 5
test2["count"] = len(articles2)
tests = append(tests, test2)
test3 := Map{"name": "链式 Page 分页"}
articles3 := that.Db.Table("article").
Where("state", 0).
Order("id DESC").
Page(1, 3).
Select("id,title,author")
test3["result"] = len(articles3) <= 3
test3["count"] = len(articles3)
tests = append(tests, test3)
test4 := Map{"name": "Offset 偏移查询"}
articles4 := that.Db.Table("article").
Where("state", 0).
Limit(3).
Offset(2).
Select("id,title")
test4["result"] = len(articles4) <= 3
test4["count"] = len(articles4)
test4["lastQuery"] = that.Db.LastQuery
tests = append(tests, test4)
result["tests"] = tests
result["success"] = true
return result
}
func testInserts(that *Context) Map {
result := Map{"name": "批量插入测试", "tests": Slice{}}
tests := Slice{}
now := nowFunc(that)
test1 := Map{"name": "Inserts 批量插入"}
timestamp := time.Now().UnixNano()
affected1 := that.Db.Inserts("test_batch", []Map{
{"name": fmt.Sprintf("批量测试1_%d", timestamp), "title": "标题1", "state": 1},
{"name": fmt.Sprintf("批量测试2_%d", timestamp), "title": "标题2", "state": 1},
{"name": fmt.Sprintf("批量测试3_%d", timestamp), "title": "标题3", "state": 1},
})
test1["result"] = affected1 >= 0
test1["affected"] = affected1
test1["lastQuery"] = that.Db.LastQuery
tests = append(tests, test1)
test2 := Map{"name": "Inserts 带 [#] 标记"}
timestamp2 := time.Now().UnixNano()
affected2 := that.Db.Inserts("test_batch", []Map{
{"name": fmt.Sprintf("带时间测试1_%d", timestamp2), "title": "标题带时间1", "state": 1, "create_time[#]": now},
{"name": fmt.Sprintf("带时间测试2_%d", timestamp2), "title": "标题带时间2", "state": 1, "create_time[#]": now},
})
test2["result"] = affected2 >= 0
test2["affected"] = affected2
tests = append(tests, test2)
that.Db.Delete("test_batch", Map{"name[~]": fmt.Sprintf("_%d", timestamp)})
that.Db.Delete("test_batch", Map{"name[~]": fmt.Sprintf("_%d", timestamp2)})
result["tests"] = tests
result["success"] = true
return result
}
func testUpsert(that *Context) Map {
result := Map{"name": "Upsert测试", "tests": Slice{}}
tests := Slice{}
now := nowFunc(that)
timestamp := time.Now().Unix()
testPhone := fmt.Sprintf("199%08d", timestamp%100000000)
test1 := Map{"name": "Upsert 插入新记录 (admin表)"}
affected1 := that.Db.Upsert("admin",
Map{
"name": "Upsert测试管理员",
"phone": testPhone,
"state": 1,
"password": "test123",
"role_id": 1,
"title": "测试职位",
"create_time[#]": now,
"modify_time[#]": now,
},
Slice{"phone"},
Slice{"name", "state", "title", "modify_time"},
)
test1["result"] = affected1 >= 0
test1["affected"] = affected1
test1["lastQuery"] = that.Db.LastQuery
tests = append(tests, test1)
test2 := Map{"name": "Upsert 更新已存在记录"}
affected2 := that.Db.Upsert("admin",
Map{
"name": "Upsert更新后管理员",
"phone": testPhone,
"state": 1,
"password": "updated123",
"role_id": 2,
"title": "更新后职位",
"create_time[#]": now,
"modify_time[#]": now,
},
Slice{"phone"},
Slice{"name", "title", "role_id", "modify_time"},
)
test2["result"] = affected2 >= 0
test2["affected"] = affected2
tests = append(tests, test2)
updatedAdmin := that.Db.Get("admin", "*", Map{"phone": testPhone})
test2["updatedAdmin"] = updatedAdmin
that.Db.Delete("admin", Map{"phone": testPhone})
result["tests"] = tests
result["success"] = true
return result
}
func testTransaction(that *Context) Map {
result := Map{"name": "事务测试", "tests": Slice{}}
tests := Slice{}
now := nowFunc(that)
timestamp := time.Now().Unix()
testName1 := fmt.Sprintf("事务测试_%d", timestamp)
test1 := Map{"name": "事务成功提交"}
success1 := that.Db.Action(func(tx HoTimeDB) bool {
recordId := tx.Insert("test_batch", Map{
"name": testName1,
"title": "事务提交测试",
"state": 1,
"create_time[#]": now,
})
return recordId != 0
})
test1["result"] = success1
checkRecord := that.Db.Get("test_batch", "*", Map{"name": testName1})
test1["recordExists"] = checkRecord != nil
tests = append(tests, test1)
test2 := Map{"name": "事务回滚"}
testName2 := fmt.Sprintf("事务回滚测试_%d", timestamp)
success2 := that.Db.Action(func(tx HoTimeDB) bool {
_ = tx.Insert("test_batch", Map{
"name": testName2,
"title": "事务回滚测试",
"state": 1,
"create_time[#]": now,
})
return false
})
test2["result"] = !success2
checkRecord2 := that.Db.Get("test_batch", "*", Map{"name": testName2})
test2["recordRolledBack"] = checkRecord2 == nil
tests = append(tests, test2)
that.Db.Delete("test_batch", Map{"name": testName1})
result["tests"] = tests
result["success"] = true
return result
}
+18
View File
@@ -0,0 +1,18 @@
package app
import (
. "code.hoteas.com/golang/hotime"
)
var TestTest = CtrTest{
"all": {Desc: "运行全部通用测试", Func: func(a *Api) { a.Get("全部测试", 0) }},
"crud": {Desc: "基础 CRUD 测试", Func: func(a *Api) { a.Get("CRUD测试", 0) }},
"condition": {Desc: "条件查询语法测试", Func: func(a *Api) { a.Get("条件查询", 0) }},
"chain": {Desc: "链式查询测试", Func: func(a *Api) { a.Get("链式查询", 0) }},
"join": {Desc: "JOIN 查询测试", Func: func(a *Api) { a.Get("JOIN查询", 0) }},
"aggregate": {Desc: "聚合函数测试", Func: func(a *Api) { a.Get("聚合函数", 0) }},
"pagination": {Desc: "分页查询测试", Func: func(a *Api) { a.Get("分页查询", 0) }},
"batch": {Desc: "批量插入测试", Func: func(a *Api) { a.Get("批量插入", 0) }},
"upsert": {Desc: "Upsert 测试", Func: func(a *Api) { a.Get("Upsert测试", 0) }},
"transaction": {Desc: "事务测试", Func: func(a *Api) { a.Get("事务测试", 0) }},
}
+2 -1411
View File
File diff suppressed because it is too large Load Diff
+94
View File
@@ -0,0 +1,94 @@
-- 达梦数据库测试环境初始化脚本
-- Schema: TEST(在 config.json 中配置 "dm": {"name": "TEST", ...}
-- 执行前确保已切换到 TEST Schema,或以 SYSDBA 身份执行
-- 连接串示例: dm://SYSDBA:SC_sysdba2026@172.31.18.249:5236?schema=TEST
-- ===================== DDL =====================
CREATE TABLE "admin" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"name" VARCHAR(100),
"phone" VARCHAR(20),
"state" INT DEFAULT 0,
"password" VARCHAR(100),
"role_id" INT DEFAULT 0,
"title" VARCHAR(100),
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"modify_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX "uk_admin_phone" ON "admin" ("phone");
CREATE TABLE "ctg" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"name" VARCHAR(100),
"state" INT DEFAULT 0,
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"modify_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE "article" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"title" VARCHAR(200),
"author" VARCHAR(100),
"content" CLOB,
"state" INT DEFAULT 0,
"click_num" INT DEFAULT 0,
"sort" INT DEFAULT 0,
"img" VARCHAR(500),
"ctg_id" INT DEFAULT 0,
"admin_id" INT DEFAULT 0,
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"modify_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE "test_batch" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"name" VARCHAR(100),
"title" VARCHAR(200),
"state" INT DEFAULT 0,
"create_time" TIMESTAMP
);
CREATE TABLE "cached" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"key" VARCHAR(64),
"value" CLOB,
"endtime" BIGINT,
"time" BIGINT
);
CREATE TABLE "hotime_cache" (
"id" INT IDENTITY(1,1) PRIMARY KEY,
"key" VARCHAR(64) NOT NULL,
"value" TEXT,
"end_time" TIMESTAMP,
"state" INT DEFAULT 0,
"create_time" TIMESTAMP,
"modify_time" TIMESTAMP,
CONSTRAINT "uk_hotime_cache_key" UNIQUE ("key")
);
CREATE INDEX "idx_hotime_cache_end_time" ON "hotime_cache" ("end_time");
-- ===================== DML 种子数据 =====================
INSERT INTO "ctg" ("name","state","create_time","modify_time") VALUES ('新闻资讯', 0, NOW(), NOW());
INSERT INTO "ctg" ("name","state","create_time","modify_time") VALUES ('技术文档', 0, NOW(), NOW());
INSERT INTO "ctg" ("name","state","create_time","modify_time") VALUES ('公告通知', 0, NOW(), NOW());
INSERT INTO "ctg" ("name","state","create_time","modify_time") VALUES ('产品介绍', 0, NOW(), NOW());
INSERT INTO "ctg" ("name","state","create_time","modify_time") VALUES ('已归档', 1, NOW(), NOW());
INSERT INTO "admin" ("name","phone","state","password","role_id","title","create_time","modify_time") VALUES ('超级管理员','13800000001',1,'admin123', 1,'系统管理员',NOW(),NOW());
INSERT INTO "admin" ("name","phone","state","password","role_id","title","create_time","modify_time") VALUES ('编辑员', '13800000002',1,'editor123',2,'内容编辑', NOW(),NOW());
INSERT INTO "admin" ("name","phone","state","password","role_id","title","create_time","modify_time") VALUES ('审核员', '13800000003',1,'review123',3,'内容审核', NOW(),NOW());
INSERT INTO "article" ("title","author","content","state","click_num","sort","ctg_id","admin_id","create_time","modify_time") VALUES ('数据库入门指南', '管理员','国产关系型数据库管理系统入门...', 0, 128, 10,1,1,NOW(),NOW());
INSERT INTO "article" ("title","author","content","state","click_num","sort","ctg_id","admin_id","create_time","modify_time") VALUES ('新闻发布系统上线公告', '编辑员','本系统已正式上线运行...', 0, 256, 5,1,2,NOW(),NOW());
INSERT INTO "article" ("title","author","content","state","click_num","sort","ctg_id","admin_id","create_time","modify_time") VALUES ('Go语言最佳实践', '管理员','Go语言在后端开发中的最佳实践总结...',0, 512, 8,2,1,NOW(),NOW());
INSERT INTO "article" ("title","author","content","state","click_num","sort","ctg_id","admin_id","create_time","modify_time") VALUES ('API接口设计规范', '审核员','RESTful API设计的基本规范和标准...', 0, 64, 15,2,3,NOW(),NOW());
INSERT INTO "article" ("title","author","content","state","click_num","sort","ctg_id","admin_id","create_time","modify_time") VALUES ('系统维护通知', '管理员','系统将于本周日凌晨进行维护升级...', 0, 32, 1,3,1,NOW(),NOW());
INSERT INTO "article" ("title","author","content","state","click_num","sort","ctg_id","admin_id","create_time","modify_time") VALUES ('产品功能更新说明', '编辑员','新版本增加了以下功能...', 0, 96, 12,4,2,NOW(),NOW());
INSERT INTO "article" ("title","author","content","state","click_num","sort","ctg_id","admin_id","create_time","modify_time") VALUES ('数据库性能优化指南', '管理员','数据库性能优化的常见方法和技巧...', 0,1024, 3,2,1,NOW(),NOW());
INSERT INTO "article" ("title","author","content","state","click_num","sort","ctg_id","admin_id","create_time","modify_time") VALUES ('缓存机制深入分析', '审核员','缓存在Web应用中的重要作用...', 0, 200, 7,2,3,NOW(),NOW());
INSERT INTO "article" ("title","author","content","state","click_num","sort","ctg_id","admin_id","create_time","modify_time") VALUES ('旧版公告(已归档)', '管理员','此公告已过期归档...', 1, 10, 0,5,1,NOW(),NOW());
INSERT INTO "article" ("title","author","content","state","click_num","sort","ctg_id","admin_id","create_time","modify_time") VALUES ('测试文章(隐藏)', '编辑员','测试用文章不公开显示...', 2, 0, 0,1,2,NOW(),NOW());
INSERT INTO "article" ("title","author","content","state","click_num","sort","ctg_id","admin_id","create_time","modify_time") VALUES ('高点击量文章', '编辑员','这是一篇点击量很高的文章...', 0,9999, 2,1,2,NOW(),NOW());
+104
View File
@@ -0,0 +1,104 @@
-- MySQL 测试数据库初始化脚本
-- 数据库: test(在 config.json 中配置 "mysql": {"name": "test", ...}
-- 执行前确保已连接到 test 数据库:USE test;
-- ===================== DDL =====================
CREATE TABLE IF NOT EXISTS `admin` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`state` int(2) DEFAULT '0',
`password` varchar(100) DEFAULT NULL,
`role_id` int(11) DEFAULT '0',
`title` varchar(100) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`modify_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员表';
CREATE TABLE IF NOT EXISTS `ctg` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`state` int(2) DEFAULT '0',
`create_time` datetime DEFAULT NULL,
`modify_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分类表';
CREATE TABLE IF NOT EXISTS `article` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(200) DEFAULT NULL,
`author` varchar(100) DEFAULT NULL,
`content` text,
`state` int(2) DEFAULT '0' COMMENT '0正常 1归档 2隐藏',
`click_num` int(11) DEFAULT '0',
`sort` int(11) DEFAULT '0',
`img` varchar(500) DEFAULT NULL,
`ctg_id` int(11) DEFAULT '0',
`admin_id` int(11) DEFAULT '0',
`create_time` datetime DEFAULT NULL,
`modify_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_state` (`state`),
KEY `idx_ctg_id` (`ctg_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章表';
CREATE TABLE IF NOT EXISTS `test_batch` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`title` varchar(200) DEFAULT NULL,
`state` int(2) DEFAULT '0',
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='批量测试表';
CREATE TABLE IF NOT EXISTS `cached` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`key` varchar(64) DEFAULT NULL,
`value` text,
`endtime` bigint(20) DEFAULT NULL,
`time` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='老版本缓存表';
CREATE TABLE IF NOT EXISTS `hotime_cache` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`key` varchar(64) NOT NULL COMMENT '缓存键',
`value` text DEFAULT NULL COMMENT '缓存值',
`end_time` datetime DEFAULT NULL COMMENT '过期时间',
`state` int(2) DEFAULT '0',
`create_time` datetime DEFAULT NULL,
`modify_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_key` (`key`),
KEY `idx_end_time` (`end_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='缓存管理';
-- ===================== DML 种子数据 =====================
INSERT IGNORE INTO `ctg` (`name`, `state`, `create_time`, `modify_time`) VALUES
('新闻资讯', 0, NOW(), NOW()),
('技术文档', 0, NOW(), NOW()),
('公告通知', 0, NOW(), NOW()),
('产品介绍', 0, NOW(), NOW()),
('已归档', 1, NOW(), NOW());
INSERT IGNORE INTO `admin` (`name`, `phone`, `state`, `password`, `role_id`, `title`, `create_time`, `modify_time`) VALUES
('超级管理员', '13800000001', 1, 'admin123', 1, '系统管理员', NOW(), NOW()),
('编辑员', '13800000002', 1, 'editor123', 2, '内容编辑', NOW(), NOW()),
('审核员', '13800000003', 1, 'review123', 3, '内容审核', NOW(), NOW());
INSERT INTO `article` (`title`, `author`, `content`, `state`, `click_num`, `sort`, `ctg_id`, `admin_id`, `create_time`, `modify_time`) VALUES
('数据库入门指南', '管理员', '国产关系型数据库管理系统入门...', 0, 128, 10, 1, 1, NOW(), NOW()),
('新闻发布系统上线公告', '编辑员', '本系统已正式上线运行...', 0, 256, 5, 1, 2, NOW(), NOW()),
('Go语言最佳实践', '管理员', 'Go语言在后端开发中的最佳实践总结...', 0, 512, 8, 2, 1, NOW(), NOW()),
('API接口设计规范', '审核员', 'RESTful API设计的基本规范和标准...', 0, 64, 15, 2, 3, NOW(), NOW()),
('系统维护通知', '管理员', '系统将于本周日凌晨进行维护升级...', 0, 32, 1, 3, 1, NOW(), NOW()),
('产品功能更新说明', '编辑员', '新版本增加了以下功能...', 0, 96, 12, 4, 2, NOW(), NOW()),
('数据库性能优化指南', '管理员', '数据库性能优化的常见方法和技巧...', 0, 1024, 3, 2, 1, NOW(), NOW()),
('缓存机制深入分析', '审核员', '缓存在Web应用中的重要作用...', 0, 200, 7, 2, 3, NOW(), NOW()),
('旧版公告(已归档)', '管理员', '此公告已过期归档...', 1, 10, 0, 5, 1, NOW(), NOW()),
('测试文章(隐藏)', '编辑员', '测试用文章不公开显示...', 2, 0, 0, 1, 2, NOW(), NOW()),
('高点击量文章', '编辑员', '这是一篇点击量很高的文章...', 0, 9999, 2, 1, 2, NOW(), NOW());
File diff suppressed because it is too large Load Diff
+481
View File
@@ -0,0 +1,481 @@
<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8"><title>API 调试控制台</title>
<style>
:root{--bg:#1a1a2e;--bg2:#16213e;--bg3:#0f3460;--fg:#e0e0e0;--fg2:#888;--accent:#4fc3f7;--green:#66bb6a;--red:#ef5350;--border:#2a2a4a;--r:4px}
*{box-sizing:border-box;margin:0;padding:0}body{display:flex;height:100vh;background:var(--bg);color:var(--fg);font:13px/1.5 -apple-system,sans-serif}
input,select,textarea,button{font:inherit}pre{margin:0}
#side{width:260px;min-width:260px;display:flex;flex-direction:column;border-right:1px solid var(--border);overflow:hidden}
.shd{padding:10px;border-bottom:1px solid var(--border)}.shd h3{font-size:13px;color:var(--accent);margin-bottom:6px}
.shd input{width:100%;padding:5px 8px;border:1px solid var(--border);border-radius:var(--r);background:var(--bg2);color:var(--fg);font-size:12px;outline:none}
.shd input:focus{border-color:var(--accent)}.sft{display:flex;gap:2px;margin-top:5px}
.sft button{flex:1;padding:3px;border:1px solid var(--border);border-radius:var(--r);background:transparent;color:var(--fg2);font-size:11px;cursor:pointer}
.sft button.on{background:var(--bg3);color:var(--accent);border-color:var(--accent)}
#tree{flex:1;overflow-y:auto;min-height:0}#tree::-webkit-scrollbar{width:4px}#tree::-webkit-scrollbar-thumb{background:#444;border-radius:2px}
.l1{border-bottom:1px solid var(--border)}.l1h,.l2h{display:flex;align-items:center;cursor:pointer}
.l1h{padding:5px 8px;font-weight:700;color:var(--accent);font-size:13px}.l1h:hover,.l2h:hover{background:var(--bg2)}
.l2h{padding:3px 8px 3px 20px;color:#81d4fa;font-size:12px}
.ar{margin-right:4px;font-size:8px;transition:transform .1s;color:#555}.ar.o{transform:rotate(90deg);color:var(--accent)}
.cn{margin-left:auto;font-size:10px;color:#888;background:rgba(255,255,255,.06);padding:0 5px;border-radius:8px;min-width:18px;text-align:center}.sub{display:none}.sub.o{display:block}
.ep{display:flex;align-items:center;padding:3px 6px 3px 32px;cursor:pointer;gap:4px;color:var(--fg2)}
.ep:hover{background:var(--bg2);color:var(--fg)}.ep.act{background:var(--bg3);color:var(--accent)}
.ep .nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mt{font-size:9px;padding:1px 4px;border-radius:2px;font-weight:700;flex-shrink:0}
.mt-get{background:#1b5e20;color:#a5d6a7}.mt-post{background:#0d47a1;color:#90caf9}.mt-put{background:#e65100;color:#ffcc80}.mt-delete{background:#b71c1c;color:#ef9a9a}
.tg{font-size:9px;padding:1px 4px;border-radius:2px;flex-shrink:0}
.tg-ok{background:#1b5e20;color:#a5d6a7}.tg-err{background:#b71c1c;color:#ef9a9a}.tg-no{background:#333;color:#555}
#main{flex:1;display:flex;flex-direction:column;overflow:hidden}
#bar{padding:5px 12px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:6px;flex-shrink:0}
#bar label{color:var(--fg2);font-size:11px}
#bar select,#bar input{padding:3px 6px;border:1px solid var(--border);border-radius:var(--r);background:var(--bg2);color:var(--fg);font-size:12px;outline:none}
#bar input{flex:1;max-width:280px}#bar select{width:auto}
#ct{flex:1;display:flex;flex-direction:column;overflow:hidden;padding:0}
.ct-hd{padding:6px 12px;border-bottom:1px solid var(--border);flex-shrink:0}
.ehd{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.ehd .mt{font-size:13px;padding:3px 10px}.ehd .pa{font-size:15px;font-weight:600}.ehd .ds{color:var(--fg2);font-size:13px}
.ehd .nt{color:var(--red);font-size:11px;padding:2px 6px;border:1px solid var(--red);border-radius:var(--r)}
.case-cnt{font-size:11px;color:var(--fg2);padding:1px 8px;border:1px solid var(--border);border-radius:10px;white-space:nowrap;flex-shrink:0}
.pf-wrap{position:relative;flex:1;min-width:0}.pf-btn{width:100%;display:flex;align-items:center;gap:6px;padding:4px 8px;border:1px solid var(--border);border-radius:var(--r);background:var(--bg2);cursor:pointer;font-size:12px;color:var(--fg);outline:none;box-sizing:border-box}.pf-btn:hover{border-color:#555}.pf-dd{position:absolute;top:calc(100% + 2px);left:0;right:0;background:#161625;border:1px solid var(--border);border-radius:var(--r);z-index:200;list-style:none;padding:3px 0;margin:0;max-height:200px;overflow-y:auto;display:none;box-shadow:0 4px 12px rgba(0,0,0,.5)}.pf-dd.on{display:block}.pf-opt{display:flex;align-items:center;gap:6px;padding:5px 10px;cursor:pointer;font-size:12px}.pf-opt:hover{background:var(--bg3)}.pf-opt.sel{color:var(--accent)}
.split{display:flex;flex:1;min-height:0;overflow:hidden}
.split-l{width:560px;min-width:220px;overflow-y:auto;padding:10px 12px;border-right:1px solid var(--border);flex-shrink:0}
.split-l::-webkit-scrollbar{width:4px}.split-l::-webkit-scrollbar-thumb{background:#444;border-radius:2px}
.split-r{flex:1;display:flex;flex-direction:column;min-width:0;overflow:hidden}
.rtabs{display:flex;border-bottom:1px solid var(--border);flex-shrink:0;padding:0 4px}
.rtab{padding:6px 14px;cursor:pointer;color:var(--fg2);border-bottom:2px solid transparent;font-size:12px}
.rtab:hover{color:var(--fg)}.rtab.on{color:var(--accent);border-color:var(--accent)}
.rpn{display:none;overflow-y:auto;padding:12px;flex:1}.rpn.on{display:block}
.rpn::-webkit-scrollbar{width:4px}.rpn::-webkit-scrollbar-thumb{background:#444;border-radius:2px}
.cs{border:1px solid var(--border);border-radius:var(--r);margin-bottom:6px;overflow:hidden}
.csh{padding:6px 10px;display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px;background:var(--bg2)}
.csh:hover{background:var(--bg3)}.dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0}
.dot.ok{background:var(--green)}.dot.er{background:var(--red)}.csh .ml{margin-left:auto;color:#555;font-size:11px}
.csb{display:none;border-top:1px solid var(--border);font-size:12px}.csb.o{display:block}
.csr{display:flex}.csr>.csc{flex:1;padding:8px 10px;min-width:0}.csr>.csc+.csc{border-left:1px solid var(--border)}
.csc h5{color:var(--accent);margin:0 0 4px;font-size:11px;letter-spacing:.5px;display:flex;align-items:center}
.csc h5 .cp-sm{margin-left:auto}.csc h5:not(:first-child){margin-top:8px}
pre.j{background:#0a0a1a;padding:6px 8px;border-radius:var(--r);font-size:12px;line-height:1.4;color:#aaa;white-space:pre-wrap;word-break:break-all;max-height:260px;overflow:auto;font-family:Consolas,Monaco,monospace}
.sec{margin-top:10px;padding-top:10px;border-top:1px solid var(--border)}.sec-title{color:var(--fg2);font-size:11px;letter-spacing:.5px;margin-bottom:4px}
.row-hd{display:flex;align-items:center;margin-bottom:4px;padding:4px 8px;background:var(--bg2);border-radius:var(--r)}.row-hd .sec-title{flex:1;margin:0}
.kv{display:flex;gap:4px;margin-bottom:3px;align-items:center}.kv input{flex:1;padding:4px 6px;border:1px solid var(--border);border-radius:var(--r);background:var(--bg2);color:var(--fg);font-size:12px;outline:none}
.kv input[type="file"]{padding:3px 4px}
.kv input:focus{border-color:var(--accent)}.kv button{padding:2px 8px;background:var(--bg3);border:1px solid var(--border);color:var(--red);border-radius:var(--r);cursor:pointer}
.addbtn{padding:2px 10px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);color:var(--fg);cursor:pointer;font-size:10px;white-space:nowrap}
.addbtn:hover{border-color:var(--accent);color:var(--accent)}
.sbtn{padding:6px 18px;background:var(--accent);color:var(--bg);border:none;border-radius:var(--r);cursor:pointer;font-size:12px;font-weight:600;white-space:nowrap}
.sbtn:hover{opacity:.9}.sbtn:disabled{opacity:.4;cursor:not-allowed}
.curl{background:#0a0a1a;padding:6px 8px;border-radius:var(--r);font-size:11px;color:#888;white-space:pre-wrap;word-break:break-all;font-family:Consolas,Monaco,monospace}
.cp-sm{padding:1px 6px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);color:var(--fg2);cursor:pointer;font-size:10px}
.cp-sm:hover{color:var(--accent);border-color:var(--accent)}
.pc{border:1px solid var(--border);border-radius:var(--r);margin-bottom:6px;overflow:hidden}
.pc-hd{padding:3px 8px;background:var(--bg2);color:var(--fg2);font-size:10px;letter-spacing:.5px;border-bottom:1px solid var(--border)}.pc-bd{padding:6px 8px}
.kv-ro{display:flex;gap:4px;margin-bottom:3px;align-items:center}
.kv-ro .kk{min-width:80px;max-width:140px;padding:4px 6px;border:1px solid var(--border);border-radius:var(--r);background:var(--bg3);color:var(--accent);font-size:12px;word-break:break-all}
.kv-ro .vv{flex:1;padding:4px 6px;border:1px solid var(--border);border-radius:var(--r);background:var(--bg2);color:var(--fg);font-size:12px;word-break:break-all}
.bd-acc{border:1px solid var(--border);border-radius:var(--r);overflow:hidden}
.bd-hd{padding:6px 8px;cursor:pointer;display:flex;align-items:center;gap:5px;color:var(--fg2);font-size:11px;background:var(--bg2);user-select:none}
.bd-hd+.bd-hd,.bd-bd+.bd-hd{border-top:1px solid var(--border)}
.bd-hd.on{color:var(--accent)}.bd-hd .ar{font-size:8px;transition:transform .15s;color:#555;margin-right:2px}.bd-hd.on .ar{transform:rotate(90deg);color:var(--accent)}
.bd-hd .addbtn{margin-left:auto}
.bd-bd{display:none;padding:8px;border-top:1px solid var(--border)}.bd-bd.on{display:block}
.bd-bd textarea{width:100%;min-height:36px;max-height:40vh;padding:5px 8px;border:1px solid var(--border);border-radius:var(--r);background:var(--bg2);color:var(--fg);font-size:12px;font-family:Consolas,Monaco,monospace;resize:vertical;outline:none;overflow-y:auto}
.bd-bd textarea:focus{border-color:var(--accent)}
.req-star{color:var(--red);font-size:10px;font-weight:700;margin-right:2px;flex-shrink:0;line-height:1.2;align-self:center}
</style></head><body>
<div id="side">
<div class="shd">
<h3 id="dt"></h3>
<input id="q" placeholder="搜索接口..." oninput="render()">
<div class="sft">
<button class="on" onclick="sf('all',this)">全部</button>
<button onclick="sf('yes',this)">已测试</button>
<button onclick="sf('no',this)">未测试</button>
<button onclick="sf('pass',this)">通过</button>
<button onclick="sf('fail',this)">未通过</button>
</div>
</div>
<div id="tree"></div>
</div>
<div id="main">
<div id="bar">
<label>认证</label>
<select id="at" onchange="syncAuth()"><option value="none">无认证</option><option value="header">Header (Authorization)</option><option value="query">URL (?token=)</option><option value="cookie">Cookie</option></select>
<input id="tk" placeholder="Token(登录后返回的 32 位字符串)" oninput="syncAuth()">
</div>
<div id="ct"><div style="text-align:center;color:var(--fg2);padding:80px 20px">&#8592; 选择一个接口开始</div></div>
</div>
<script>
let D=null,F='all',C=null;
fetch('./api-spec.json').then(r=>r.json()).then(d=>{
D=d;document.getElementById('dt').textContent=d.title+' v'+d.version;document.title=d.title;render();
});
function epPass(e){if(!e.cases?.length)return false;return e.cases.every(x=>x.passed);}
function epFail(e){if(!e.cases?.length)return e.tested;return e.cases.some(x=>!x.passed);}
function render(){
if(!D)return;const q=document.getElementById('q').value.toLowerCase();const T={};
for(const e of D.endpoints){
if(F==='yes'&&!e.tested)continue;
if(F==='no'&&e.tested)continue;
if(F==='pass'&&!epPass(e))continue;
if(F==='fail'&&!epFail(e))continue;
if(q&&!e.summary.toLowerCase().includes(q)&&!e.path.toLowerCase().includes(q)&&!e.ctr.toLowerCase().includes(q))continue;
if(!T[e.project])T[e.project]={};if(!T[e.project][e.ctr])T[e.project][e.ctr]=[];T[e.project][e.ctr].push(e);
}
let h='';
for(const p of Object.keys(T).sort()){let ph='';
for(const c of Object.keys(T[p]).sort()){const es=T[p][c];let eh='';
for(const e of es){const m=e.method.toLowerCase();
let tg='';if(!e.tested)tg='<span class="tg tg-no">未测试</span>';
else if(e.cases?.length){const pc=e.cases.filter(x=>x.passed).length;tg=pc===e.cases.length?'<span class="tg tg-ok">'+pc+'/'+e.cases.length+'</span>':'<span class="tg tg-err">'+pc+'/'+e.cases.length+'</span>';}
const mn=e.path.split('/').pop();const lbl=e.tested&&e.summary!==mn?mn+' '+e.summary:e.summary;
eh+='<div class="ep'+(C?.path===e.path?' act':'')+'" data-p="'+e.path+'" onclick="go(this)"><span class="mt mt-'+m+'">'+e.method+'</span><span class="nm" title="'+e.path+'">'+lbl+'</span>'+tg+'</div>';
}
ph+='<div class="l2h" onclick="tog(this)"><span class="ar">&#9654;</span>'+c+'<span class="cn">'+es.length+'</span></div><div class="sub">'+eh+'</div>';
}
const totalEps=Object.values(T[p]).reduce((s,es)=>s+es.length,0);
h+='<div class="l1"><div class="l1h" onclick="tog(this)"><span class="ar o">&#9654;</span>'+p+'<span class="cn">'+totalEps+'</span></div><div class="sub o">'+ph+'</div></div>';
}
document.getElementById('tree').innerHTML=h||'<div style="padding:30px;color:#555;text-align:center">无匹配</div>';
}
function sf(f,b){F=f;document.querySelectorAll('.sft button').forEach(x=>x.classList.remove('on'));b.classList.add('on');render();}
function tog(e){e.querySelector('.ar').classList.toggle('o');e.nextElementSibling.classList.toggle('o');}
function go(el){C=D.endpoints.find(e=>e.path===el.dataset.p);if(!C)return;document.querySelectorAll('.ep').forEach(e=>e.classList.remove('act'));el.classList.add('act');show();}
function show(){
const e=C;if(!e)return;const m=e.method.toLowerCase();const cnt=e.cases?.length||0;
let h='<div class="ct-hd"><div class="ehd">';
h+='<span class="mt mt-'+m+'" style="font-size:13px;padding:3px 8px">'+e.method+'</span>';
h+='<span class="pa">'+esc(e.path)+'</span><span class="ds">'+esc(e.summary)+'</span>';
if(cnt)h+='<span class="case-cnt">'+cnt+' 用例</span>';
if(!e.tested)h+='<span class="nt">未测试</span>';
h+='</div></div>';
h+='<div class="split">';
h+='<div class="split-l">'+buildLeft(e)+'</div>';
h+='<div class="split-r">';
h+='<div class="rtabs"><div class="rtab on" id="rt0" onclick="rswitch(0)">用例结果</div><div class="rtab" id="rt1" onclick="rswitch(1)">发送结果</div></div>';
h+='<div class="rpn on" id="rp0"><div id="case-panel" style="padding:4px 0"></div></div>';
h+='<div class="rpn" id="rp1">'+respPanel()+'</div>';
h+='</div></div>';
document.getElementById('ct').innerHTML=h;
buildPfList(e);
let defIdx=-1;
if(cnt>0){for(let i=e.cases.length-1;i>=0;i--){if(e.cases[i].passed){defIdx=i;break;}}if(defIdx<0)defIdx=e.cases.length-1;}
pfSelect(defIdx>=0?defIdx:'');
}
function rswitch(i){
document.querySelectorAll('.rtab').forEach((t,j)=>t.classList.toggle('on',j===i));
document.querySelectorAll('.rpn').forEach((p,j)=>p.classList.toggle('on',j===i));
}
function caseCurl(e,c){
const base=window.location.origin;
let url=base+e.path;
const m=c.method||'POST';
if(c.query){const qs=Object.keys(c.query).filter(k=>c.query[k]!==undefined&&c.query[k]!=='').map(k=>encodeURIComponent(k)+'='+encodeURIComponent(c.query[k])).join('&');if(qs)url+='?'+qs;}
let cmd="curl -X "+m+" '"+url+"'";
if(c.json){cmd+="\\\n -H 'Content-Type: application/json'";cmd+="\\\n -d '"+JSON.stringify(c.json).replace(/'/g,"'\\''")+"'";}
else if(c.form){const fd=Object.keys(c.form).map(k=>"-F '"+k+"="+c.form[k]+"'").join("\\\n ");if(fd)cmd+="\\\n "+fd;}
return cmd;
}
function cases(e){
if(!e.cases?.length)return '<div style="color:#555;padding:20px">暂无测试用例</div>';
let h='';
for(let i=0;i<e.cases.length;i++){const c=e.cases[i];
h+='<div class="cs"><div class="csh" onclick="accordion(this)"><span class="ar'+(i===0?' o':'')+'">&#9654;</span><span class="dot '+(c.passed?'ok':'er')+'"></span>'+esc(c.name)+'<span class="ml">'+c.method+'</span></div>';
h+='<div class="csb'+(i===0?' o':'')+'"><div style="padding:8px 10px">';
if(c.note)h+='<div style="color:#aaa;font-size:12px;margin-bottom:8px;line-height:1.5">备注: '+esc(c.note)+'</div>';
const curlId='cc'+i;const respId='cr'+i;
h+='<div class="row-hd" style="margin-bottom:4px"><span class="sec-title">cURL</span><button class="cp-sm" onclick="event.stopPropagation();copyEl(\''+curlId+'\')">复制</button></div>';
h+='<div class="curl" id="'+curlId+'">'+esc(caseCurl(e,c))+'</div>';
h+='<div class="row-hd" style="margin:8px 0 4px"><span class="sec-title">响应</span><span class="'+(c.passed?'tg tg-ok':'tg tg-err')+'" style="margin-left:6px">'+(c.passed?'通过':'失败')+'</span><button class="cp-sm" onclick="event.stopPropagation();copyEl(\''+respId+'\')" style="margin-left:auto">复制</button></div>';
if(c.response)h+='<pre class="j" id="'+respId+'">'+fj(c.response)+'</pre>';
else h+='<span style="color:#555" id="'+respId+'">无响应</span>';
h+='</div></div></div>';
}
return h;
}
function accordion(el){
const bd=el.nextElementSibling;const wasOpen=bd.classList.contains('o');
const panel=el.closest('.rpn');
panel.querySelectorAll('.csb').forEach(b=>b.classList.remove('o'));
panel.querySelectorAll('.csh .ar').forEach(a=>a.classList.remove('o'));
if(!wasOpen){bd.classList.add('o');el.querySelector('.ar').classList.add('o');}
}
function respPanel(){
let r='';
r+='<div id="note-box" style="display:none;color:#aaa;font-size:12px;margin-bottom:8px;line-height:1.5"></div>';
r+='<div class="row-hd" style="margin-bottom:4px"><span class="sec-title">cURL</span><button class="cp-sm" onclick="copyEl(\'curl-box\')">复制</button></div>';
r+='<div class="curl" id="curl-box">发送请求后生成</div>';
r+='<div class="row-hd" style="margin:10px 0 4px"><span class="sec-title">响应 <span id="resp-st"></span></span><button class="cp-sm" onclick="copyEl(\'resp-body\')">复制</button></div>';
r+='<pre class="j" id="resp-body" style="min-height:60px;max-height:calc(100vh - 200px)">发送请求后显示</pre>';
return r;
}
function buildLeft(e){
const hasForm=e.cases?.some(c=>c.form)||e.params?.some(p=>p.in==='form');
const defBody=hasForm?'form':'json';
const tk=document.getElementById('tk').value||'';
const at=document.getElementById('at').value;
let l='';
l+='<div style="display:flex;align-items:center;gap:6px;margin-bottom:10px">';
l+='<span style="color:var(--fg2);font-size:11px;white-space:nowrap">测试用例选择</span>';
l+='<div class="pf-wrap"><button class="pf-btn" id="pf-btn" onclick="togglePfDd(event)"><span class="dot" id="pf-dot" style="display:none;flex-shrink:0"></span><span id="pf-label" style="flex:1;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg2)">选择用例...</span><span style="color:var(--fg2);font-size:10px;flex-shrink:0">▾</span></button><ul class="pf-dd" id="pf-dd"></ul></div>';
l+='<button class="sbtn" onclick="send()" id="sbtn">发送</button>';
l+='</div>';
let defH='<div class="kv"><input value="Content-Type"><input id="ct-val" value="'+(defBody==='json'?'application/json':'application/x-www-form-urlencoded')+'"><button onclick="this.parentElement.remove()">&#215;</button></div>';
if(tk&&at==='header')defH+='<div class="kv" data-auth="1"><input value="Authorization"><input value="'+esc(tk)+'"><button onclick="this.parentElement.remove()">&#215;</button></div>';
if(tk&&at==='cookie')defH+='<div class="kv" data-auth="1"><input value="Cookie"><input value="HOTIME='+esc(tk)+'"><button onclick="this.parentElement.remove()">&#215;</button></div>';
l+='<div class="sec" style="margin-top:0"><div class="row-hd"><span class="sec-title">请求头</span><button class="addbtn" onclick="addKV(\'h-rows\')">+ 添加</button></div><div id="h-rows">'+defH+'</div></div>';
let qDef='';
if(tk&&at==='query')qDef='<div class="kv" data-auth="1"><input value="token"><input value="'+esc(tk)+'"><button onclick="this.parentElement.remove()">&#215;</button></div>';
qDef+='<div class="kv"><input placeholder="key"><input placeholder="value"><button onclick="this.parentElement.remove()">&#215;</button></div>';
l+='<div class="sec"><div class="row-hd"><span class="sec-title">Query 参数</span><button class="addbtn" onclick="addKV(\'q-rows\')">+ 添加</button></div><div id="q-rows">'+qDef+'</div></div>';
l+='<div class="sec"><div class="row-hd"><span class="sec-title">文件上传</span><button class="addbtn" onclick="addFileRow()">+ 添加</button></div><div id="file-rows"><div class="kv"><input placeholder="字段名" value="file" style="max-width:120px"><input type="file"><button onclick="this.parentElement.remove()">&#215;</button></div></div></div>';
let jsonPH='';
if(e.params){
const jp=e.params.filter(p=>p.in==='json');
if(jp.length){const obj={};for(const p of jp)obj[(p.required?'*':'')+p.name]=p.example!==undefined?p.example:'';jsonPH=JSON.stringify(obj,null,2);}
}
l+='<div class="sec"><div class="sec-title" style="margin-bottom:4px">请求体</div><div class="bd-acc">';
l+='<div class="bd-hd'+(defBody==='form'?' on':'')+'" data-t="form" onclick="togBody(\'form\')"><span class="ar'+(defBody==='form'?' o':'')+'">&#9654;</span>表单 (Form)<button class="addbtn" onclick="event.stopPropagation();setAcc(\'form\');addKV(\'f-rows\')">+ 添加</button></div>';
l+='<div id="bt-form" class="bd-bd'+(defBody==='form'?' on':'')+'"><div id="f-rows"><div class="kv"><input placeholder="key"><input placeholder="value"><button onclick="this.parentElement.remove()">&#215;</button></div></div></div>';
l+='<div class="bd-hd'+(defBody==='json'?' on':'')+'" data-t="json" onclick="togBody(\'json\')"><span class="ar'+(defBody==='json'?' o':'')+'">&#9654;</span>JSON</div>';
l+='<div id="bt-json" class="bd-bd'+(defBody==='json'?' on':'')+'"><textarea id="xb" oninput="autoH(this)" placeholder="'+(jsonPH?esc(jsonPH):'{"key":"value"}')+'"></textarea></div>';
l+='</div></div>';
return l;
}
function addKV(id,k,v){
const row=document.createElement('div');row.className='kv';
row.innerHTML='<input placeholder="key" value="'+esc(k||'')+'"><input placeholder="value" value="'+esc(v||'')+'"><button onclick="this.parentElement.remove()">&#215;</button>';
document.getElementById(id).appendChild(row);
}
function addKVReq(id,k,v,req){
const row=document.createElement('div');row.className='kv';
const star=req?'<span class="req-star">*</span>':'';
row.innerHTML=star+'<input placeholder="key" value="'+esc(k||'')+'"><input placeholder="value" value="'+esc(v||'')+'"><button onclick="this.parentElement.remove()">&#215;</button>';
document.getElementById(id).appendChild(row);
}
function addFileRow(field){
const row=document.createElement('div');row.className='kv';
row.innerHTML='<input placeholder="字段名" value="'+esc(field||'file')+'" style="max-width:120px"><input type="file"><button onclick="this.parentElement.remove()">&#215;</button>';
document.getElementById('file-rows').appendChild(row);
}
function setAcc(t){
document.querySelectorAll('.bd-hd').forEach(h=>{h.classList.toggle('on',h.dataset.t===t);const a=h.querySelector('.ar');if(a)a.classList.toggle('o',h.dataset.t===t);});
document.getElementById('bt-json').classList.toggle('on',t==='json');
document.getElementById('bt-form').classList.toggle('on',t==='form');
const ctVal=document.getElementById('ct-val');
if(ctVal)ctVal.value=t==='json'?'application/json':'application/x-www-form-urlencoded';
}
function togBody(t){
const cur=document.getElementById(t==='json'?'bt-json':'bt-form').classList.contains('on');
setAcc(cur?(t==='json'?'form':'json'):t);
}
function autoH(el){el.style.height='36px';el.style.height=Math.min(el.scrollHeight,window.innerHeight*0.4)+'px';}
function syncAuth(){
const tk=document.getElementById('tk').value;const at=document.getElementById('at').value;
const hr=document.getElementById('h-rows');const qr=document.getElementById('q-rows');
if(!hr||!qr)return;
hr.querySelectorAll('[data-auth]').forEach(e=>e.remove());
qr.querySelectorAll('[data-auth]').forEach(e=>e.remove());
if(!tk)return;
function mkAuth(k,v,parent){const d=document.createElement('div');d.className='kv';d.dataset.auth='1';d.innerHTML='<input value="'+esc(k)+'"><input value="'+esc(v)+'"><button onclick="this.parentElement.remove()">&#215;</button>';parent.insertBefore(d,parent.querySelector('.kv:last-child'));}
if(at==='header')mkAuth('Authorization',tk,hr);
if(at==='cookie')mkAuth('Cookie','HOTIME='+tk,hr);
if(at==='query')mkAuth('token',tk,qr);
}
function buildPfList(e){
const dd=document.getElementById('pf-dd');if(!dd)return;
dd.innerHTML='';
if(e.cases){
for(let i=e.cases.length-1;i>=0;i--){
const c=e.cases[i];
const li=document.createElement('li');li.className='pf-opt';li.dataset.idx=String(i);
li.innerHTML='<span class="dot '+(c.passed?'ok':'er')+'"></span><span>'+esc(c.name)+'</span>';
li.onclick=ev=>{ev.stopPropagation();pfSelect(i);};
dd.appendChild(li);
}
}
const manual=document.createElement('li');manual.className='pf-opt';manual.dataset.idx='';
manual.innerHTML='<span style="color:var(--fg2);font-style:italic">-- 手动填写 --</span>';
manual.onclick=ev=>{ev.stopPropagation();pfSelect('');};
dd.appendChild(manual);
}
function togglePfDd(ev){ev.stopPropagation();const dd=document.getElementById('pf-dd');if(dd)dd.classList.toggle('on');}
document.addEventListener('click',()=>{document.getElementById('pf-dd')?.classList.remove('on');});
function pfSelect(i){
const e=C;if(!e)return;
const idxStr=String(i);
document.querySelectorAll('.pf-opt').forEach(el=>el.classList.toggle('sel',el.dataset.idx===idxStr));
const c=(i!==''&&e.cases?.[i])?e.cases[i]:null;
const dot=document.getElementById('pf-dot');
const lbl=document.getElementById('pf-label');
if(dot&&lbl){
if(c){dot.style.display='';dot.className='dot '+(c.passed?'ok':'er');lbl.style.color='var(--fg)';lbl.textContent=c.name;}
else{dot.style.display='none';lbl.style.color='var(--fg2)';lbl.textContent='-- 手动填写 --';}
}
document.getElementById('pf-dd')?.classList.remove('on');
updateCasePanel(e,i);
pfFill(i);
}
function updateCasePanel(e,i){
const panel=document.getElementById('case-panel');if(!panel)return;
if(i===''||!e?.cases?.[i]){panel.innerHTML='<div style="color:#555;padding:16px 10px">选择一个测试用例查看响应</div>';return;}
const c=e.cases[i];
let h='<div style="padding:8px 10px">';
if(c.note)h+='<div style="color:#aaa;font-size:12px;margin-bottom:8px;line-height:1.5">备注: '+esc(c.note)+'</div>';
h+='<div class="row-hd" style="margin-bottom:4px"><span class="sec-title">cURL</span><span class="'+(c.passed?'tg tg-ok':'tg tg-err')+'" style="margin-left:6px">'+(c.passed?'通过':'失败')+'</span><button class="cp-sm" onclick="copyEl(\'c-curl\')" style="margin-left:auto">复制</button></div>';
h+='<div class="curl" id="c-curl">'+esc(caseCurl(e,c))+'</div>';
h+='<div class="row-hd" style="margin:8px 0 4px"><span class="sec-title">响应</span><button class="cp-sm" onclick="copyEl(\'c-resp\')" style="margin-left:auto">复制</button></div>';
if(c.response)h+='<pre class="j" id="c-resp">'+fj(c.response)+'</pre>';
else h+='<span style="color:#555" id="c-resp">无响应</span>';
h+='</div>';
panel.innerHTML=h;
}
function pfFill(v){
const qr=document.getElementById('q-rows');
const fr=document.getElementById('f-rows');
if(qr)qr.innerHTML='';
if(v===''){
if(C?.params){
for(const p of C.params.filter(p=>p.in==='query'))addKVReq('q-rows',p.name,p.example!==undefined?String(p.example):'',p.required);
}
addKV('q-rows');
const el=document.getElementById('xb');if(el)el.value='';
if(fr){
fr.innerHTML='';
if(C?.params){for(const p of C.params.filter(p=>p.in==='form'))addKVReq('f-rows',p.name,p.example!==undefined?String(p.example):'',p.required);}
addKV('f-rows');
}
document.getElementById('file-rows').innerHTML='';addFileRow();
const nb0=document.getElementById('note-box');if(nb0){nb0.style.display='none';nb0.textContent='';}
syncAuth();return;
}
const i=parseInt(v);
if(isNaN(i)||i<0||!C?.cases?.[i])return;
const c=C.cases[i];
const nb=document.getElementById('note-box');
if(nb){if(c.note){nb.textContent='备注: '+c.note;nb.style.display='block';}else{nb.textContent='';nb.style.display='none';}}
if(c.query){
for(const[k,val]of Object.entries(c.query)){
const req=C?.params?.find(p=>p.name===k&&p.in==='query')?.required||false;
addKVReq('q-rows',k,String(val),req);
}
}
addKV('q-rows');
if(c.json){
setAcc('json');const el=document.getElementById('xb');
if(el){
const rq=new Set((C?.params||[]).filter(p=>p.required&&p.in==='json').map(p=>p.name));
let obj=c.json;
if(rq.size&&typeof obj==='object'&&obj!==null&&!Array.isArray(obj)){
const sorted={};
for(const k of Object.keys(obj).sort((a,b)=>(rq.has(b)?1:0)-(rq.has(a)?1:0)))sorted[k]=obj[k];
obj=sorted;
}
el.value=JSON.stringify(obj,null,2);autoH(el);
}
}else if(c.form){
setAcc('form');
if(fr){
fr.innerHTML='';
for(const[k,val]of Object.entries(c.form)){
const req=C?.params?.find(p=>p.name===k&&p.in==='form')?.required||false;
addKVReq('f-rows',k,String(val),req);
}
addKV('f-rows');
}
}else{
const el=document.getElementById('xb');if(el)el.value='';
if(fr){fr.innerHTML='';addKV('f-rows');}
}
document.getElementById('file-rows').innerHTML='';
if(c.hasFile&&c.fileField)addFileRow(c.fileField);
addFileRow();syncAuth();
}
function getKV(id){
const obj={};
document.querySelectorAll('#'+id+' .kv').forEach(r=>{const ins=r.querySelectorAll('input');const k=ins[0].value.trim(),v=ins[1]?.value;if(k)obj[k]=v||'';});
return Object.keys(obj).length?obj:null;
}
async function send(){
if(!C)return;const btn=document.getElementById('sbtn');btn.disabled=true;btn.textContent='请求中...';
let url=C.path;
const qp=getKV('q-rows');
if(qp){const ps=new URLSearchParams();for(const[k,v]of Object.entries(qp))ps.set(k,v);url+='?'+ps.toString();}
const token=document.getElementById('tk').value;
const authType=document.getElementById('at').value;
const opts={method:C.method,headers:{}};
if(authType==='cookie'){opts.credentials='same-origin';if(token)document.cookie='HOTIME='+token+';path=/';}
else{opts.credentials='omit';}
const ch=getKV('h-rows');if(ch)for(const[k,v]of Object.entries(ch)){
if(k.toLowerCase()==='authorization'&&token&&authType==='header')continue;
if(k.toLowerCase()==='cookie'&&token&&authType==='cookie')continue;
opts.headers[k]=v;
}
if(token&&authType==='header')opts.headers['Authorization']=token;
const fileEls=document.querySelectorAll('#file-rows .kv');
const files=[];fileEls.forEach(r=>{const fnEl=r.querySelector('input:not([type="file"])');const fi=r.querySelector('input[type="file"]');if(!fnEl||!fi)return;const fn=fnEl.value.trim();if(fn&&fi.files.length)files.push({field:fn,file:fi.files[0]});});
const isJson=document.getElementById('bt-json')?.classList.contains('on');
const bodyEl=document.getElementById('xb');const formKV=getKV('f-rows');let curlParts='';
if(files.length){
const fd=new FormData();for(const f of files)fd.append(f.field,f.file);
if(!isJson&&formKV)for(const[k,v]of Object.entries(formKV))fd.append(k,v);
opts.body=fd;delete opts.headers['Content-Type'];
for(const f of files)curlParts+=' -F "'+f.field+'=@'+f.file.name+'"';
if(!isJson&&formKV)for(const[k,v]of Object.entries(formKV))curlParts+=' -F "'+k+'='+v+'"';
}else if(isJson){
const b=bodyEl?.value?.trim();
if(b&&C.method!=='GET'){opts.body=b;curlParts=" -d '"+b+"'";}
}else if(formKV){
const fd=new FormData();for(const[k,v]of Object.entries(formKV))fd.append(k,v);opts.body=fd;delete opts.headers['Content-Type'];
for(const[k,v]of Object.entries(formKV))curlParts+=' -F "'+k+'='+v+'"';
}
let curl='curl -X '+C.method;
for(const[k,v]of Object.entries(opts.headers)){if(k&&v)curl+=" -H '"+k+": "+v+"'";}
if(token&&authType==='cookie')curl+=" --cookie 'HOTIME="+token+"'";
curl+=curlParts+' '+location.origin+url;
const curlBox=document.getElementById('curl-box');if(curlBox)curlBox.textContent=curl;
rswitch(1);
const t0=Date.now();
try{
const r=await fetch(url,opts);const ms=Date.now()-t0;const txt=await r.text();let pretty=txt;
try{pretty=JSON.stringify(JSON.parse(txt),null,2);}catch(e){}
const st=document.getElementById('resp-st');if(st)st.innerHTML='<span style="color:'+(r.ok?'var(--green)':'var(--red)')+'">HTTP '+r.status+'</span> <span style="color:#555">'+ms+'ms</span>';
const rb=document.getElementById('resp-body');if(rb)rb.textContent=pretty;
}catch(e){
const st=document.getElementById('resp-st');if(st)st.innerHTML='<span style="color:var(--red)">失败</span>';
const rb=document.getElementById('resp-body');if(rb)rb.textContent=e.message;
}
btn.disabled=false;btn.textContent='发送';
}
function copyEl(id){navigator.clipboard.writeText(document.getElementById(id).textContent).then(()=>{const b=event.target;b.textContent='已复制';setTimeout(()=>{b.textContent='复制'},1200);});}
function cpNext(btn){let el=btn.closest('h5').nextElementSibling;let t='';while(el&&el.tagName!=='H5'){if(el.tagName==='PRE')t+=(t?'\n':'')+el.textContent;el=el.nextElementSibling;}navigator.clipboard.writeText(t).then(()=>{btn.textContent='已复制';setTimeout(()=>{btn.textContent='复制'},1200);});}
function fj(o){return esc(JSON.stringify(o,null,2));}
function fjp(o,params){
if(!params||!params.length||typeof o!=='object'||o===null||Array.isArray(o))return fj(o);
const rq=new Set(params.filter(p=>p.required).map(p=>p.name));
const keys=Object.keys(o).sort((a,b)=>(rq.has(b)?1:0)-(rq.has(a)?1:0));
const lines=keys.map(k=>' "'+(rq.has(k)?'*':'')+k+'": '+JSON.stringify(o[k]));
return esc('{\n'+lines.join(',\n')+'\n}');
}
function kvList(o,params){
const rq=new Set((params||[]).filter(p=>p.required).map(p=>p.name));
let h='';
for(const[k,v]of Object.entries(o)){
const star=rq.has(k)?'<span class="req-star">*</span>':'';
h+='<div class="kv-ro">'+star+'<span class="kk">'+esc(k)+'</span><span class="vv">'+esc(String(v))+'</span></div>';
}
return h;
}
function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
</script></body></html>
+19
View File
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8"><title>API 文档中心</title>
<style>
:root{--bg:#1a1a2e;--bg2:#16213e;--bg3:#0f3460;--fg:#e0e0e0;--fg2:#888;--accent:#4fc3f7;--border:#2a2a4a}
*{box-sizing:border-box;margin:0;padding:0}
body{min-height:100vh;background:var(--bg);color:var(--fg);font:14px/1.6 -apple-system,sans-serif;display:flex;flex-direction:column;align-items:center;padding:60px 20px}
h1{font-size:24px;color:var(--accent);margin-bottom:8px}
.desc{color:var(--fg2);margin-bottom:40px;font-size:13px}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:16px;width:100%;max-width:900px}
.card{display:block;background:var(--bg2);border:1px solid var(--border);border-radius:8px;padding:24px 20px;text-decoration:none;color:var(--fg);transition:all .2s}
.card:hover{border-color:var(--accent);transform:translateY(-2px);box-shadow:0 4px 20px rgba(79,195,247,.15)}
.icon{font-size:20px;font-weight:700;color:var(--accent);margin-bottom:8px}
.title{font-size:15px;font-weight:600;margin-bottom:4px}
.sub{font-size:12px;color:var(--fg2)}
</style></head><body>
<h1>API 文档中心</h1>
<div class="desc">选择一个模块查看接口文档与调试控制台</div>
<div class="grid"><a class="card" href="app/index.html"><div class="icon">App</div><div class="title">My API</div><div class="sub">app/</div></a></div>
</body></html>
-848
View File
@@ -1,848 +0,0 @@
package main
import (
"database/sql"
"fmt"
"log"
// 实际使用时请替换为正确的导入路径
// "code.hoteas.com/golang/hotime/cache"
// "code.hoteas.com/golang/hotime/common"
// "code.hoteas.com/golang/hotime/db"
"code.hoteas.com/golang/hotime/cache"
"code.hoteas.com/golang/hotime/common"
"code.hoteas.com/golang/hotime/db"
_ "github.com/go-sql-driver/mysql"
"github.com/sirupsen/logrus"
)
// HoTimeDB使用示例代码集合 - 修正版
// 本文件包含了各种常见场景的完整示例代码,所有条件查询语法已修正
// 示例1: 基本初始化和配置
func Example1_BasicSetup() {
// 创建数据库实例
database := &db.HoTimeDB{
Prefix: "app_", // 设置表前缀
Mode: 2, // 开发模式,输出SQL日志
Type: "mysql", // 数据库类型
}
// 设置日志
logger := logrus.New()
database.Log = logger
// 设置连接函数
database.SetConnect(func(err ...*common.Error) (master, slave *sql.DB) {
// 主数据库连接
master, dbErr := sql.Open("mysql", "root:password@tcp(localhost:3306)/testdb?charset=utf8&parseTime=true")
if dbErr != nil {
log.Fatal("数据库连接失败:", dbErr)
}
// 从数据库连接(可选,用于读写分离)
slave = master // 这里使用同一个连接,实际项目中可以连接到从库
return master, slave
})
fmt.Println("数据库初始化完成")
}
// 示例2: 基本CRUD操作(修正版)
func Example2_BasicCRUD_Fixed(db *db.HoTimeDB) {
// 创建用户
fmt.Println("=== 创建用户 ===")
userId := db.Insert("user", common.Map{
"name": "张三",
"email": "zhangsan@example.com",
"age": 25,
"status": 1,
"balance": 1000.50,
"created_time[#]": "NOW()",
})
fmt.Printf("新用户ID: %d\n", userId)
// 查询用户(单条件,可以不用AND)
fmt.Println("\n=== 查询用户 ===")
user := db.Get("user", "*", common.Map{
"id": userId,
})
if user != nil {
fmt.Printf("用户信息: %+v\n", user)
}
// 更新用户(多条件必须用AND包装)
fmt.Println("\n=== 更新用户 ===")
affected := db.Update("user", common.Map{
"name": "李四",
"age": 26,
"updated_time[#]": "NOW()",
}, common.Map{
"AND": common.Map{
"id": userId,
"status": 1, // 确保只更新正常状态的用户
},
})
fmt.Printf("更新记录数: %d\n", affected)
// 软删除用户
fmt.Println("\n=== 软删除用户 ===")
affected = db.Update("user", common.Map{
"deleted_at[#]": "NOW()",
"status": 0,
}, common.Map{
"id": userId, // 单条件,不需要AND
})
fmt.Printf("软删除记录数: %d\n", affected)
}
// 示例3: 条件查询语法(修正版)
func Example3_ConditionQuery_Fixed(db *db.HoTimeDB) {
fmt.Println("=== 条件查询语法示例 ===")
// ✅ 正确:单个条件
users1 := db.Select("user", "*", common.Map{
"status": 1,
})
fmt.Printf("活跃用户: %d个\n", len(users1))
// ✅ 正确:多个条件用AND包装
users2 := db.Select("user", "*", common.Map{
"AND": common.Map{
"status": 1,
"age[>]": 18,
"age[<=]": 60,
},
})
fmt.Printf("活跃的成年用户: %d个\n", len(users2))
// ✅ 正确:OR条件
users3 := db.Select("user", "*", common.Map{
"OR": common.Map{
"level": "vip",
"balance[>]": 5000,
},
})
fmt.Printf("VIP或高余额用户: %d个\n", len(users3))
// ✅ 正确:条件 + 特殊参数
users4 := db.Select("user", "*", common.Map{
"AND": common.Map{
"status": 1,
"age[>=]": 18,
},
"ORDER": "created_time DESC",
"LIMIT": 10,
})
fmt.Printf("最近的活跃成年用户: %d个\n", len(users4))
// ✅ 正确:复杂嵌套条件
users5 := db.Select("user", "*", common.Map{
"AND": common.Map{
"status": 1,
"OR": common.Map{
"age[<]": 30,
"level": "vip",
},
},
"ORDER": []string{"level DESC", "created_time DESC"},
"LIMIT": []int{0, 20},
})
fmt.Printf("年轻或VIP的活跃用户: %d个\n", len(users5))
// ✅ 正确:模糊查询
users6 := db.Select("user", "*", common.Map{
"AND": common.Map{
"name[~]": "张", // 姓名包含"张"
"email[~!]": "gmail", // 邮箱以gmail开头
"status": 1,
},
})
fmt.Printf("姓张的gmail活跃用户: %d个\n", len(users6))
// ✅ 正确:范围查询
users7 := db.Select("user", "*", common.Map{
"AND": common.Map{
"age[<>]": []int{18, 35}, // 年龄在18-35之间
"balance[><]": []float64{0, 100}, // 余额不在0-100之间
"status": 1,
},
})
fmt.Printf("18-35岁且余额>100的活跃用户: %d个\n", len(users7))
// ✅ 正确:IN查询
users8 := db.Select("user", "*", common.Map{
"AND": common.Map{
"id": []int{1, 2, 3, 4, 5}, // ID在指定范围内
"status[!]": []int{0, -1}, // 状态不为0或-1
},
})
fmt.Printf("指定ID的活跃用户: %d个\n", len(users8))
}
// 示例4: 链式查询操作(正确版)
func Example4_ChainQuery_Fixed(db *db.HoTimeDB) {
fmt.Println("=== 链式查询示例 ===")
// 链式查询(链式语法允许单独的Where,然后用And添加更多条件)
users := db.Table("user").
Where("status", 1). // 链式中可以单独Where
And("age[>=]", 18). // 然后用And添加条件
And("age[<=]", 60). // 再添加条件
Or(common.Map{ // 或者用Or添加OR条件组
"level": "vip",
"balance[>]": 5000,
}).
Order("created_time DESC", "id ASC"). // 排序
Limit(0, 10). // 限制结果
Select("id,name,email,age,balance,level")
fmt.Printf("链式查询到 %d 个用户\n", len(users))
for i, user := range users {
fmt.Printf("用户%d: %s (年龄:%v, 余额:%v)\n",
i+1,
user.GetString("name"),
user.Get("age"),
user.Get("balance"))
}
// 链式统计查询
count := db.Table("user").
Where("status", 1).
And("age[>=]", 18).
Count()
fmt.Printf("符合条件的用户总数: %d\n", count)
}
// 示例5: JOIN查询操作(修正版)
func Example5_JoinQuery_Fixed(db *db.HoTimeDB) {
fmt.Println("=== JOIN查询示例 ===")
// 链式JOIN查询
orders := db.Table("order").
LeftJoin("user", "order.user_id = user.id").
LeftJoin("product", "order.product_id = product.id").
Where("order.status", "paid"). // 链式中单个条件可以直接Where
And("order.created_time[>]", "2023-01-01"). // 用And添加更多条件
Order("order.created_time DESC").
Select(`
order.id as order_id,
order.amount,
order.status,
order.created_time,
user.name as user_name,
user.email as user_email,
product.title as product_title,
product.price as product_price
`)
fmt.Printf("链式JOIN查询到 %d 个订单\n", len(orders))
for _, order := range orders {
fmt.Printf("订单ID:%v, 用户:%s, 商品:%s, 金额:%v\n",
order.Get("order_id"),
order.GetString("user_name"),
order.GetString("product_title"),
order.Get("amount"))
}
// 传统JOIN语法(多个条件必须用AND包装)
orders2 := db.Select("order",
common.Slice{
common.Map{"[>]user": "order.user_id = user.id"},
common.Map{"[>]product": "order.product_id = product.id"},
},
"order.*, user.name as user_name, product.title as product_title",
common.Map{
"AND": common.Map{
"order.status": "paid",
"order.created_time[>]": "2023-01-01",
},
})
fmt.Printf("传统JOIN语法查询到 %d 个订单\n", len(orders2))
}
// 示例6: 分页查询(修正版)
func Example6_PaginationQuery_Fixed(db *db.HoTimeDB) {
fmt.Println("=== 分页查询示例 ===")
page := 2
pageSize := 10
// 获取总数(单条件)
total := db.Count("user", common.Map{
"AND": common.Map{
"status": 1,
"deleted_at": nil,
},
})
// 分页数据(链式方式)
users := db.Table("user").
Where("status", 1).
And("deleted_at", nil).
Order("created_time DESC").
Page(page, pageSize).
Select("id,name,email,created_time")
// 使用传统方式的分页查询
users2 := db.Page(page, pageSize).PageSelect("user", "*", common.Map{
"AND": common.Map{
"status": 1,
"deleted_at": nil,
},
"ORDER": "created_time DESC",
})
// 计算分页信息
totalPages := (total + pageSize - 1) / pageSize
offset := (page - 1) * pageSize
fmt.Printf("总记录数: %d\n", total)
fmt.Printf("总页数: %d\n", totalPages)
fmt.Printf("当前页: %d\n", page)
fmt.Printf("每页大小: %d\n", pageSize)
fmt.Printf("偏移量: %d\n", offset)
fmt.Printf("链式查询当前页记录数: %d\n", len(users))
fmt.Printf("传统查询当前页记录数: %d\n", len(users2))
for i, user := range users {
fmt.Printf(" %d. %s (%s) - %v\n",
offset+i+1,
user.GetString("name"),
user.GetString("email"),
user.Get("created_time"))
}
}
// 示例7: 聚合函数查询(修正版)
func Example7_AggregateQuery_Fixed(db *db.HoTimeDB) {
fmt.Println("=== 聚合函数查询示例 ===")
// 基本统计
userCount := db.Count("user")
activeUserCount := db.Count("user", common.Map{"status": 1})
totalBalance := db.Sum("user", "balance", common.Map{"status": 1})
fmt.Printf("总用户数: %d\n", userCount)
fmt.Printf("活跃用户数: %d\n", activeUserCount)
fmt.Printf("活跃用户总余额: %.2f\n", totalBalance)
// 分组统计(正确语法)
stats := db.Select("user",
"level, COUNT(*) as user_count, AVG(age) as avg_age, SUM(balance) as total_balance",
common.Map{
"status": 1, // 单条件,不需要AND
"GROUP": "level",
"ORDER": "user_count DESC",
})
fmt.Println("\n按等级分组统计:")
for _, stat := range stats {
fmt.Printf("等级:%v, 用户数:%v, 平均年龄:%v, 总余额:%v\n",
stat.Get("level"),
stat.Get("user_count"),
stat.Get("avg_age"),
stat.Get("total_balance"))
}
// 关联统计(修正版)
orderStats := db.Select("order",
common.Slice{
common.Map{"[>]user": "order.user_id = user.id"},
},
"user.level, COUNT(order.id) as order_count, SUM(order.amount) as total_amount",
common.Map{
"AND": common.Map{
"order.status": "paid",
"order.created_time[>]": "2023-01-01",
},
"GROUP": "user.level",
"ORDER": "total_amount DESC",
})
fmt.Println("\n用户等级订单统计:")
for _, stat := range orderStats {
fmt.Printf("等级:%v, 订单数:%v, 总金额:%v\n",
stat.Get("level"),
stat.Get("order_count"),
stat.Get("total_amount"))
}
}
// 示例8: 事务处理(修正版)
func Example8_Transaction_Fixed(database *db.HoTimeDB) {
fmt.Println("=== 事务处理示例 ===")
// 模拟转账操作
fromUserId := int64(1)
toUserId := int64(2)
amount := 100.0
success := database.Action(func(tx db.HoTimeDB) bool {
// 检查转出账户余额(单条件)
fromUser := tx.Get("user", "balance", common.Map{"id": fromUserId})
if fromUser == nil {
fmt.Println("转出用户不存在")
return false
}
fromBalance := fromUser.GetFloat64("balance")
if fromBalance < amount {
fmt.Println("余额不足")
return false
}
// 扣减转出账户余额(多条件必须用AND)
affected1 := tx.Update("user", common.Map{
"balance[#]": fmt.Sprintf("balance - %.2f", amount),
"updated_time[#]": "NOW()",
}, common.Map{
"AND": common.Map{
"id": fromUserId,
"balance[>=]": amount, // 再次确保余额足够
},
})
if affected1 == 0 {
fmt.Println("扣减余额失败")
return false
}
// 增加转入账户余额(单条件)
affected2 := tx.Update("user", common.Map{
"balance[#]": fmt.Sprintf("balance + %.2f", amount),
"updated_time[#]": "NOW()",
}, common.Map{
"id": toUserId,
})
if affected2 == 0 {
fmt.Println("增加余额失败")
return false
}
// 记录转账日志
logId := tx.Insert("transfer_log", common.Map{
"from_user_id": fromUserId,
"to_user_id": toUserId,
"amount": amount,
"status": "success",
"created_time[#]": "NOW()",
})
if logId == 0 {
fmt.Println("记录日志失败")
return false
}
fmt.Printf("转账成功: 用户%d -> 用户%d, 金额:%.2f\n", fromUserId, toUserId, amount)
return true
})
if success {
fmt.Println("事务执行成功")
} else {
fmt.Println("事务回滚")
if database.LastErr.GetError() != nil {
fmt.Println("错误原因:", database.LastErr.GetError())
}
}
}
// 示例9: 缓存机制(修正版)
func Example9_CacheSystem_Fixed(database *db.HoTimeDB) {
fmt.Println("=== 缓存机制示例 ===")
// 设置缓存(实际项目中需要配置缓存参数)
database.HoTimeCache = &cache.HoTimeCache{}
// 第一次查询(会缓存结果)
fmt.Println("第一次查询(会缓存)...")
users1 := database.Select("user", "*", common.Map{
"status": 1, // 单条件
"LIMIT": 10,
})
fmt.Printf("查询到 %d 个用户\n", len(users1))
// 第二次相同查询(从缓存获取)
fmt.Println("第二次相同查询(从缓存获取)...")
users2 := database.Select("user", "*", common.Map{
"status": 1, // 单条件
"LIMIT": 10,
})
fmt.Printf("查询到 %d 个用户\n", len(users2))
// 更新操作会清除缓存
fmt.Println("执行更新操作(会清除缓存)...")
affected := database.Update("user", common.Map{
"updated_time[#]": "NOW()",
}, common.Map{
"id": 1, // 单条件
})
fmt.Printf("更新 %d 条记录\n", affected)
// 再次查询(重新从数据库获取并缓存)
fmt.Println("更新后再次查询(重新缓存)...")
users3 := database.Select("user", "*", common.Map{
"status": 1, // 单条件
"LIMIT": 10,
})
fmt.Printf("查询到 %d 个用户\n", len(users3))
}
// 示例10: 性能优化技巧(修正版)
func Example10_PerformanceOptimization_Fixed(database *db.HoTimeDB) {
fmt.Println("=== 性能优化技巧示例 ===")
// IN查询优化(连续数字自动转为BETWEEN)
fmt.Println("IN查询优化示例...")
users := database.Select("user", "*", common.Map{
"id": []int{1, 2, 3, 4, 5, 10, 11, 12, 13, 20}, // 单个IN条件,会被优化
})
fmt.Printf("查询到 %d 个用户\n", len(users))
fmt.Println("执行的SQL:", database.LastQuery)
// 批量插入(使用事务)
fmt.Println("\n批量插入示例...")
success := database.Action(func(tx db.HoTimeDB) bool {
for i := 1; i <= 100; i++ {
id := tx.Insert("user_batch", common.Map{
"name": fmt.Sprintf("批量用户%d", i),
"email": fmt.Sprintf("batch%d@example.com", i),
"status": 1,
"created_time[#]": "NOW()",
})
if id == 0 {
return false
}
// 每10个用户输出一次进度
if i%10 == 0 {
fmt.Printf("已插入 %d 个用户\n", i)
}
}
return true
})
if success {
fmt.Println("批量插入完成")
} else {
fmt.Println("批量插入失败")
}
// 索引友好的查询(修正版)
fmt.Println("\n索引友好的查询...")
recentUsers := database.Select("user", "*", common.Map{
"AND": common.Map{
"created_time[>]": "2023-01-01", // 假设created_time有索引
"status": 1, // 假设status有索引
},
"ORDER": "created_time DESC", // 利用索引排序
"LIMIT": 20,
})
fmt.Printf("查询到 %d 个近期用户\n", len(recentUsers))
}
// 完整的应用示例(修正版)
func CompleteExample_Fixed() {
fmt.Println("=== HoTimeDB完整应用示例(修正版) ===")
// 初始化数据库
database := &db.HoTimeDB{
Prefix: "app_",
Mode: 1, // 测试模式
Type: "mysql",
}
// 设置连接
database.SetConnect(func(err ...*common.Error) (master, slave *sql.DB) {
// 这里使用实际的数据库连接字符串
dsn := "root:password@tcp(localhost:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
master, dbErr := sql.Open("mysql", dsn)
if dbErr != nil {
log.Fatal("数据库连接失败:", dbErr)
}
return master, master
})
// 用户管理系统示例
fmt.Println("\n=== 用户管理系统 ===")
// 1. 创建用户
userId := database.Insert("user", common.Map{
"name": "示例用户",
"email": "example@test.com",
"password": "hashed_password",
"age": 28,
"status": 1,
"level": "normal",
"balance": 500.00,
"created_time[#]": "NOW()",
})
fmt.Printf("创建用户成功,ID: %d\n", userId)
// 2. 用户登录更新(多条件用AND)
database.Update("user", common.Map{
"last_login[#]": "NOW()",
"login_count[#]": "login_count + 1",
}, common.Map{
"AND": common.Map{
"id": userId,
"status": 1,
},
})
// 3. 创建订单
orderId := database.Insert("order", common.Map{
"user_id": userId,
"amount": 299.99,
"status": "pending",
"created_time[#]": "NOW()",
})
fmt.Printf("创建订单成功,ID: %d\n", orderId)
// 4. 订单支付(使用事务,修正版)
paymentSuccess := database.Action(func(tx db.HoTimeDB) bool {
// 更新订单状态(单条件)
affected1 := tx.Update("order", common.Map{
"status": "paid",
"paid_time[#]": "NOW()",
}, common.Map{
"id": orderId,
})
if affected1 == 0 {
return false
}
// 扣减用户余额(多条件用AND
affected2 := tx.Update("user", common.Map{
"balance[#]": "balance - 299.99",
}, common.Map{
"AND": common.Map{
"id": userId,
"balance[>=]": 299.99, // 确保余额足够
},
})
if affected2 == 0 {
fmt.Println("余额不足或用户不存在")
return false
}
// 记录支付日志
logId := tx.Insert("payment_log", common.Map{
"user_id": userId,
"order_id": orderId,
"amount": 299.99,
"type": "order_payment",
"status": "success",
"created_time[#]": "NOW()",
})
return logId > 0
})
if paymentSuccess {
fmt.Println("订单支付成功")
} else {
fmt.Println("订单支付失败")
}
// 5. 查询用户订单列表(链式查询)
userOrders := database.Table("order").
LeftJoin("user", "order.user_id = user.id").
Where("order.user_id", userId). // 链式中单个条件可以直接Where
Order("order.created_time DESC").
Select(`
order.id,
order.amount,
order.status,
order.created_time,
order.paid_time,
user.name as user_name
`)
fmt.Printf("\n用户订单列表 (%d个订单):\n", len(userOrders))
for _, order := range userOrders {
fmt.Printf(" 订单ID:%v, 金额:%v, 状态:%s, 创建时间:%v\n",
order.Get("id"),
order.Get("amount"),
order.GetString("status"),
order.Get("created_time"))
}
// 6. 生成统计报表(修正版)
stats := database.Select("order",
common.Slice{
common.Map{"[>]user": "order.user_id = user.id"},
},
`
DATE(order.created_time) as date,
COUNT(order.id) as order_count,
SUM(order.amount) as total_amount,
AVG(order.amount) as avg_amount
`,
common.Map{
"AND": common.Map{
"order.status": "paid",
"order.created_time[>]": "2023-01-01",
},
"GROUP": "DATE(order.created_time)",
"ORDER": "date DESC",
"LIMIT": 30,
})
fmt.Printf("\n最近30天订单统计:\n")
for _, stat := range stats {
fmt.Printf("日期:%v, 订单数:%v, 总金额:%v, 平均金额:%v\n",
stat.Get("date"),
stat.Get("order_count"),
stat.Get("total_amount"),
stat.Get("avg_amount"))
}
fmt.Println("\n示例执行完成!")
}
// 语法对比示例
func SyntaxComparison() {
fmt.Println("=== HoTimeDB语法对比 ===")
// 模拟数据库对象
var db *db.HoTimeDB
fmt.Println("❌ 错误语法示例(不支持):")
fmt.Println(`
// 这样写是错误的,多个条件不能直接放在根Map中
wrongUsers := db.Select("user", "*", common.Map{
"status": 1, // ❌ 错误
"age[>]": 18, // ❌ 错误
"ORDER": "id DESC",
})
`)
fmt.Println("✅ 正确语法示例:")
fmt.Println(`
// 单个条件可以直接写
correctUsers1 := db.Select("user", "*", common.Map{
"status": 1, // ✅ 正确,单个条件
})
// 多个条件必须用AND包装
correctUsers2 := db.Select("user", "*", common.Map{
"AND": common.Map{ // ✅ 正确,多个条件用AND包装
"status": 1,
"age[>]": 18,
},
"ORDER": "id DESC", // ✅ 正确,特殊参数与条件同级
})
// OR条件
correctUsers3 := db.Select("user", "*", common.Map{
"OR": common.Map{ // ✅ 正确,OR条件
"level": "vip",
"balance[>]": 1000,
},
})
// 嵌套条件
correctUsers4 := db.Select("user", "*", common.Map{
"AND": common.Map{ // ✅ 正确,嵌套条件
"status": 1,
"OR": common.Map{
"age[<]": 30,
"level": "vip",
},
},
"ORDER": "created_time DESC",
"LIMIT": 20,
})
`)
// 实际不执行查询,只是展示语法
_ = db
}
// InsertsExample 批量插入示例
func InsertsExample(database *db.HoTimeDB) {
fmt.Println("\n=== 批量插入示例 ===")
// 批量插入用户(使用 []Map 格式)
affected := database.Inserts("user", []common.Map{
{"name": "批量用户1", "email": "batch1@example.com", "age": 25, "status": 1},
{"name": "批量用户2", "email": "batch2@example.com", "age": 30, "status": 1},
{"name": "批量用户3", "email": "batch3@example.com", "age": 28, "status": 1},
})
fmt.Printf("批量插入了 %d 条用户记录\n", affected)
// 批量插入日志(使用 [#] 标记直接 SQL)
logAffected := database.Inserts("log", []common.Map{
{"user_id": 1, "action": "login", "ip": "192.168.1.1", "created_time[#]": "NOW()"},
{"user_id": 2, "action": "logout", "ip": "192.168.1.2", "created_time[#]": "NOW()"},
{"user_id": 3, "action": "view", "ip": "192.168.1.3", "created_time[#]": "NOW()"},
})
fmt.Printf("批量插入了 %d 条日志记录\n", logAffected)
}
// UpsertExample Upsert(插入或更新)示例
func UpsertExample(database *db.HoTimeDB) {
fmt.Println("\n=== Upsert示例 ===")
// Upsert 用户数据(使用 Slice 格式)
// 如果 id 存在则更新,不存在则插入
affected := database.Upsert("user",
common.Map{
"id": 1,
"name": "张三更新",
"email": "zhang_updated@example.com",
"age": 26,
},
common.Slice{"id"}, // 唯一键
common.Slice{"name", "email", "age"}, // 冲突时更新的字段
)
fmt.Printf("Upsert 影响了 %d 行\n", affected)
// Upsert 使用 [#] 直接 SQL
affected2 := database.Upsert("user_stats",
common.Map{
"user_id": 1,
"login_count[#]": "login_count + 1",
"last_login[#]": "NOW()",
},
common.Slice{"user_id"},
common.Slice{"login_count", "last_login"},
)
fmt.Printf("统计 Upsert 影响了 %d 行\n", affected2)
// 也支持可变参数形式
affected3 := database.Upsert("user",
common.Map{"id": 2, "name": "李四", "status": 1},
common.Slice{"id"},
"name", "status", // 可变参数
)
fmt.Printf("可变参数 Upsert 影响了 %d 行\n", affected3)
}
// 运行所有修正后的示例
func RunAllFixedExamples() {
fmt.Println("开始运行HoTimeDB所有修正后的示例...")
fmt.Println("注意:实际运行时需要确保数据库连接正确,并且相关表存在")
// 展示语法对比
SyntaxComparison()
fmt.Println("请根据实际环境配置数据库连接后运行相应示例")
fmt.Println("所有示例代码已修正完毕,语法正确!")
fmt.Println("")
fmt.Println("新增功能示例说明:")
fmt.Println(" - InsertsExample(db): 批量插入示例,使用 []Map 格式")
fmt.Println(" - UpsertExample(db): 插入或更新示例")
}
func main() {
// 运行语法对比示例
RunAllFixedExamples()
}
+1
View File
@@ -3,6 +3,7 @@ module code.hoteas.com/golang/hotime
go 1.16
require (
gitee.com/chunanyong/dm v1.8.22
github.com/360EntSecGroup-Skylar/excelize v1.4.1
github.com/garyburd/redigo v1.6.3
github.com/go-pay/gopay v1.5.78
+3
View File
@@ -1,3 +1,5 @@
gitee.com/chunanyong/dm v1.8.22 h1:H7fsrnUIvEA0jlDWew7vwELry1ff+tLMIu2Fk2cIBSg=
gitee.com/chunanyong/dm v1.8.22/go.mod h1:EPRJnuPFgbyOFgJ0TRYCTGzhq+ZT4wdyaj/GW/LLcNg=
github.com/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks=
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
@@ -92,6 +94,7 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+24
View File
@@ -27,6 +27,13 @@ var Config = Map{
"sqlite": Map{
"path": "config/data.db",
},
//"dm": Map{
// "host": "127.0.0.1",
// "name": "SYSDBA",
// "user": "SYSDBA",
// "password": "SYSDBA",
// "port": "5236",
//},
},
"cache": Map{
"memory": Map{
@@ -95,6 +102,23 @@ var ConfigNote = Map{
"sqlite": Map{
"path": "默认config/data.db,必须,数据库位置",
},
"dm": Map{
"注释": "达梦数据库配置,除prefix及主从数据库slave项,其他全部必须",
"host": "默认127.0.0.1,必须,数据库ip地址",
"name": "默认SYSDBA,必须,数据库schema名称",
"user": "默认SYSDBA,必须,数据库用户名",
"password": "默认SYSDBA,必须,数据库密码",
"port": "默认5236,必须,数据库端口",
"prefix": "默认空,非必须,数据表前缀",
"slave": Map{
"注释": "从数据库配置,dm里配置slave项即启用主从读写,减少数据库压力",
"host": "默认127.0.0.1,必须,数据库ip地址",
"name": "默认SYSDBA,必须,数据库schema名称",
"user": "默认SYSDBA,必须,数据库用户名",
"password": "默认SYSDBA,必须,数据库密码",
"port": "默认5236,必须,数据库端口",
},
},
},
"cache": Map{
"注释": "可配置memorydbredis,默认启用memory,默认优先级为memory>redis>db,memory与数据库缓存设置项一致,缓存数据填充会自动反方向反哺,加入memory缓存过期将自动从redis更新,但memory永远不会更新redis,如果是集群建议不要开启memory,配置即启用",
+43
View File
@@ -0,0 +1,43 @@
# Change Logs
*你可以在文件p.go中的发版标记里找到当前驱动的svn号
## svn 31633
修复了带ipv6地址的服务名无法连接数据库的问题
dm.DmError输出不再附加堆栈信息,如需要则调用dm.DmError.Stack()方法单独获取
服务器开启常量参数化优化时,go驱动出现一些报错问题
## svn 16752
支持在连接串上直接配置动态服务名,使用示例:
dm://user:password@GroupName?GroupName=(host1:port1,host2:port2,...)
## svn 16505
新增连接串属性driverReconnect,配合doSwitch=1或2使用,表示连接重连是否使用驱动自身的重连机制,否则在连接失效时返回sql标准错误driver.ErrBadConn,由go来处理重连
## svn 16258
重连逻辑修改,当连接失效时,返回driver.ErrBadConn而不是驱动自己管理重连,连接串参数doSwitch默认值改为1
驱动接口方法同步锁改到连接网络请求的总入口处,解决一些panic和数组越界问题
日志优化,去除遍历结果集时因io.EOF错误记录的日志
## svn 15619
驱动接口方法添加同步锁
驱动日志修改bug,可以记录SQL语句和参数值了
## svn 15357
发布方言包,支持gorm v1和v2框架,方言包位于达梦安装目录的drivers/go目录中,详细使用说明参考《DM8程序员手册》
## svn 15157
修复了字符大字段(Clob)中存在乱码字符时,读取结果会漏读一些字符的问题
修复了开启SSL后并发创建连接导致panic的问题
## svn 15035
修复了连接串属性doSwitch=1时,语句不会自动切换和重连的问题
修复了连接重置后,可能出现的空指针问题
## svn 14992
修复了连接串属性doSwitch=1时,连接不会自动切换和重连的问题
修复了连接串属性loginMode=默认值4时,备库可能会被优先连接的问题
## svn 14589
sql.Result.LastInsertId()函数能优先返回自增列的值,如果没有则返回数据库表内部的rowid
修复了开启事务时指定只读不生效的问题
+61
View File
@@ -0,0 +1,61 @@
# dm
### 介绍
```
go get gitee.com/chunanyong/dm
```
达梦数据库官方Go驱动,本项目和官方驱动版本同步,方便go mod 使用.
安装达梦数据库(版本>=8.1.1.126),安装目录下 drivers/go/dm-go-driver.zip
达梦官方文档:https://eco.dameng.com/docs/zh-cn/app-dev/go-go.html
资源下载:https://eco.dameng.com/download/
达梦官方Go驱动包:https://package.dameng.com/eco/adapter/resource/go/dm-go-driver.zip
达梦官方论坛(提交bug):https://eco.dameng.com/community/question
### zorm
Go轻量ORM https://gitee.com/chunanyong/zorm 原生支持达梦数据库
### DSN
dm://userName:password@127.0.0.1:5236?schema=DBName
用户名(userName)默认就是数据库的名称,达梦用户模式和数据库名称是对应的,也可以通过schema参数指定数据库
建议达梦使用UTF-8字符编码,不区分大小写,建表语句的字段名不要带""双引号
密码中包含特殊字符时,密码使用url.PathEscape进行Escape处理,并在DSN中加入&escapeProcess=true 参见:https://eco.dameng.com/community/question/d152258b30bd0fc030d15e6a4afd5fc2
### bug
- 达梦开启等保参数 COMM_ENCRYPT_NAME = AES128_ECB,导致连接异常
### 版本号
Go三段位版本号和达梦四段位版本号不兼容,统一使用1.达梦主版本号.发布的小版本号,具体查看标签的备注
* v1.8.22 来自 达梦8.1.4.170
* v1.8.21 来自 达梦8.1.4.116
* v1.8.20 来自 达梦8.1.4.80
* v1.8.19 来自 达梦8.1.4.48
* v1.8.18 来自 达梦8.1.4.6
* v1.8.17 来自 达梦8.1.4.6
* v1.8.16 来自 达梦8.1.3.162
* v1.8.15 来自 达梦8.1.3.140
* v1.8.14 来自 达梦8.1.3.100
* v1.8.13 来自 达梦8.1.3.62
* v1.8.12 来自 达梦8.1.3.12
* v1.8.11 来自 达梦8.1.2.192
* v1.8.10 来自 达梦8.1.2.174
* v1.8.9 来自 达梦8.1.2.162
* v1.8.8 来自 达梦8.1.2.138
* v1.8.7 来自 达梦8.1.2.128
* v1.8.6 来自 达梦8.1.2.114
* v1.8.5 来自 达梦8.1.2.94
* v1.8.4 来自 达梦8.1.2.84
* v1.8.3 来自 达梦8.1.2.38
* v1.8.2 来自 达梦8.1.2.18
* v1.8.1 来自 达梦8.1.1.190
* v1.8.0 来自 达梦8.1.1.126
+3
View File
@@ -0,0 +1,3 @@
#8.1.4.170
#2025.11.21
#43114
+863
View File
@@ -0,0 +1,863 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"bytes"
"context"
"crypto/tls"
"fmt"
"net"
"strconv"
"time"
"unicode/utf8"
"gitee.com/chunanyong/dm/security"
)
const (
Dm_build_412 = 8192
Dm_build_413 = 2 * time.Second
)
type dm_build_414 struct {
dm_build_415 net.Conn
dm_build_416 *tls.Conn
dm_build_417 *Dm_build_78
dm_build_418 *DmConnection
dm_build_419 security.Cipher
dm_build_420 bool
dm_build_421 bool
dm_build_422 *security.DhKey
dm_build_423 bool
dm_build_424 string
dm_build_425 bool
}
func dm_build_426(dm_build_427 context.Context, dm_build_428 *DmConnection) (*dm_build_414, error) {
var dm_build_429 net.Conn
var dm_build_430 error
dialsLock.RLock()
dm_build_431, dm_build_432 := dials[dm_build_428.dmConnector.dialName]
dialsLock.RUnlock()
if dm_build_432 {
dm_build_429, dm_build_430 = dm_build_431(dm_build_427, dm_build_428.dmConnector.host+":"+strconv.Itoa(int(dm_build_428.dmConnector.port)))
} else {
dm_build_429, dm_build_430 = dm_build_434(dm_build_428.dmConnector.host+":"+strconv.Itoa(int(dm_build_428.dmConnector.port)), time.Duration(dm_build_428.dmConnector.socketTimeout)*time.Second)
}
if dm_build_430 != nil {
return nil, dm_build_430
}
dm_build_433 := dm_build_414{}
dm_build_433.dm_build_415 = dm_build_429
dm_build_433.dm_build_417 = Dm_build_81(Dm_build_707)
dm_build_433.dm_build_418 = dm_build_428
dm_build_433.dm_build_420 = false
dm_build_433.dm_build_421 = false
dm_build_433.dm_build_423 = false
dm_build_433.dm_build_424 = ""
dm_build_433.dm_build_425 = false
dm_build_428.Access = &dm_build_433
return &dm_build_433, nil
}
func dm_build_434(dm_build_435 string, dm_build_436 time.Duration) (net.Conn, error) {
dm_build_437, dm_build_438 := net.DialTimeout("tcp", dm_build_435, dm_build_436)
if dm_build_438 != nil {
return &net.TCPConn{}, ECGO_COMMUNITION_ERROR.addDetail("\tdial address: " + dm_build_435).throw()
}
if tcpConn, ok := dm_build_437.(*net.TCPConn); ok {
tcpConn.SetKeepAlive(true)
tcpConn.SetKeepAlivePeriod(Dm_build_413)
tcpConn.SetNoDelay(true)
}
return dm_build_437, nil
}
func (dm_build_440 *dm_build_414) dm_build_439(dm_build_441 dm_build_828) bool {
var dm_build_442 = dm_build_440.dm_build_418.dmConnector.compress
if dm_build_441.dm_build_843() == Dm_build_735 || dm_build_442 == Dm_build_784 {
return false
}
if dm_build_442 == Dm_build_782 {
return true
} else if dm_build_442 == Dm_build_783 {
return !dm_build_440.dm_build_418.Local && dm_build_441.dm_build_841() > Dm_build_781
}
return false
}
func (dm_build_444 *dm_build_414) dm_build_443(dm_build_445 dm_build_828) bool {
var dm_build_446 = dm_build_444.dm_build_418.dmConnector.compress
if dm_build_445.dm_build_843() == Dm_build_735 || dm_build_446 == Dm_build_784 {
return false
}
if dm_build_446 == Dm_build_782 {
return true
} else if dm_build_446 == Dm_build_783 {
return dm_build_444.dm_build_417.Dm_build_345(Dm_build_743) == 1
}
return false
}
func (dm_build_448 *dm_build_414) dm_build_447(dm_build_449 dm_build_828) (err error) {
defer func() {
if p := recover(); p != nil {
if _, ok := p.(string); ok {
err = ECGO_COMMUNITION_ERROR.addDetail("\t" + p.(string)).throw()
} else {
err = fmt.Errorf("internal error: %v", p)
}
}
}()
dm_build_451 := dm_build_449.dm_build_841()
if dm_build_451 > 0 {
if dm_build_448.dm_build_439(dm_build_449) {
var retBytes, err = Compress(dm_build_448.dm_build_417, Dm_build_736, int(dm_build_451), int(dm_build_448.dm_build_418.dmConnector.compressID))
if err != nil {
return err
}
dm_build_448.dm_build_417.Dm_build_92(Dm_build_736)
dm_build_448.dm_build_417.Dm_build_133(dm_build_451)
dm_build_448.dm_build_417.Dm_build_161(retBytes)
dm_build_449.dm_build_842(int32(len(retBytes)) + ULINT_SIZE)
dm_build_448.dm_build_417.Dm_build_265(Dm_build_743, 1)
}
if dm_build_448.dm_build_421 {
dm_build_451 = dm_build_449.dm_build_841()
var retBytes = dm_build_448.dm_build_419.Encrypt(dm_build_448.dm_build_417.Dm_build_372(Dm_build_736, int(dm_build_451)), true)
dm_build_448.dm_build_417.Dm_build_92(Dm_build_736)
dm_build_448.dm_build_417.Dm_build_161(retBytes)
dm_build_449.dm_build_842(int32(len(retBytes)))
}
}
if dm_build_448.dm_build_417.Dm_build_90() > Dm_build_708 {
return ECGO_MSG_TOO_LONG.throw()
}
dm_build_449.dm_build_837()
if dm_build_448.dm_build_690(dm_build_449) {
if dm_build_448.dm_build_416 != nil {
dm_build_448.dm_build_417.Dm_build_95(0)
if _, err := dm_build_448.dm_build_417.Dm_build_114(dm_build_448.dm_build_416); err != nil {
return err
}
}
} else {
dm_build_448.dm_build_417.Dm_build_95(0)
if _, err := dm_build_448.dm_build_417.Dm_build_114(dm_build_448.dm_build_415); err != nil {
return err
}
}
return nil
}
func (dm_build_453 *dm_build_414) dm_build_452(dm_build_454 dm_build_828) (err error) {
defer func() {
if p := recover(); p != nil {
if _, ok := p.(string); ok {
err = ECGO_COMMUNITION_ERROR.addDetail("\t" + p.(string)).throw()
} else {
err = fmt.Errorf("internal error: %v", p)
}
}
}()
dm_build_456 := int32(0)
if dm_build_453.dm_build_690(dm_build_454) {
if dm_build_453.dm_build_416 != nil {
dm_build_453.dm_build_417.Dm_build_92(0)
if _, err := dm_build_453.dm_build_417.Dm_build_108(dm_build_453.dm_build_416, Dm_build_736); err != nil {
return err
}
dm_build_456 = dm_build_454.dm_build_841()
if dm_build_456 > 0 {
if _, err := dm_build_453.dm_build_417.Dm_build_108(dm_build_453.dm_build_416, int(dm_build_456)); err != nil {
return err
}
}
}
} else {
dm_build_453.dm_build_417.Dm_build_92(0)
if _, err := dm_build_453.dm_build_417.Dm_build_108(dm_build_453.dm_build_415, Dm_build_736); err != nil {
return err
}
dm_build_456 = dm_build_454.dm_build_841()
if dm_build_456 > 0 {
if _, err := dm_build_453.dm_build_417.Dm_build_108(dm_build_453.dm_build_415, int(dm_build_456)); err != nil {
return err
}
}
}
dm_build_454.dm_build_838()
dm_build_456 = dm_build_454.dm_build_841()
if dm_build_456 <= 0 {
return nil
}
if dm_build_453.dm_build_421 {
ebytes := dm_build_453.dm_build_417.Dm_build_372(Dm_build_736, int(dm_build_456))
bytes, err := dm_build_453.dm_build_419.Decrypt(ebytes, true)
if err != nil {
return err
}
dm_build_453.dm_build_417.Dm_build_92(Dm_build_736)
dm_build_453.dm_build_417.Dm_build_161(bytes)
dm_build_454.dm_build_842(int32(len(bytes)))
}
if dm_build_453.dm_build_443(dm_build_454) {
dm_build_456 = dm_build_454.dm_build_841()
cbytes := dm_build_453.dm_build_417.Dm_build_372(Dm_build_736+ULINT_SIZE, int(dm_build_456-ULINT_SIZE))
bytes, err := UnCompress(cbytes, int(dm_build_453.dm_build_418.dmConnector.compressID))
if err != nil {
return err
}
dm_build_453.dm_build_417.Dm_build_92(Dm_build_736)
dm_build_453.dm_build_417.Dm_build_161(bytes)
dm_build_454.dm_build_842(int32(len(bytes)))
}
return nil
}
func (dm_build_458 *dm_build_414) dm_build_457(dm_build_459 dm_build_828) (dm_build_460 interface{}, dm_build_461 error) {
if dm_build_458.dm_build_425 {
return nil, ECGO_CONNECTION_CLOSED.throw()
}
dm_build_462 := dm_build_458.dm_build_418
dm_build_462.mu.Lock()
defer dm_build_462.mu.Unlock()
dm_build_461 = dm_build_459.dm_build_832(dm_build_459)
if dm_build_461 != nil {
return nil, dm_build_461
}
dm_build_461 = dm_build_458.dm_build_447(dm_build_459)
if dm_build_461 != nil {
return nil, dm_build_461
}
dm_build_461 = dm_build_458.dm_build_452(dm_build_459)
if dm_build_461 != nil {
return nil, dm_build_461
}
return dm_build_459.dm_build_836(dm_build_459)
}
func (dm_build_464 *dm_build_414) dm_build_463() (*dm_build_1287, error) {
Dm_build_465 := dm_build_1293(dm_build_464)
_, dm_build_466 := dm_build_464.dm_build_457(Dm_build_465)
if dm_build_466 != nil {
return nil, dm_build_466
}
return Dm_build_465, nil
}
func (dm_build_468 *dm_build_414) dm_build_467() error {
dm_build_469 := dm_build_1152(dm_build_468)
_, dm_build_470 := dm_build_468.dm_build_457(dm_build_469)
if dm_build_470 != nil {
return dm_build_470
}
return nil
}
func (dm_build_472 *dm_build_414) dm_build_471() error {
var dm_build_473 *dm_build_1287
var err error
if dm_build_473, err = dm_build_472.dm_build_463(); err != nil {
return err
}
if dm_build_472.dm_build_418.sslEncrypt == 2 {
if err = dm_build_472.dm_build_686(false); err != nil {
return ECGO_INIT_SSL_FAILED.addDetail("\n" + err.Error()).throw()
}
} else if dm_build_472.dm_build_418.sslEncrypt == 1 {
if err = dm_build_472.dm_build_686(true); err != nil {
return ECGO_INIT_SSL_FAILED.addDetail("\n" + err.Error()).throw()
}
}
if dm_build_472.dm_build_421 || dm_build_472.dm_build_420 {
k, err := dm_build_472.dm_build_676()
if err != nil {
return err
}
sessionKey := security.ComputeSessionKey(k, dm_build_473.Dm_build_1291)
encryptType := dm_build_473.dm_build_1289
hashType := int(dm_build_473.Dm_build_1290)
if encryptType == -1 {
encryptType = security.DES_CFB
}
if hashType == -1 {
hashType = security.MD5
}
err = dm_build_472.dm_build_679(encryptType, sessionKey, dm_build_472.dm_build_418.dmConnector.cipherPath, hashType)
if err != nil {
return err
}
}
if err := dm_build_472.dm_build_467(); err != nil {
return err
}
return nil
}
func (dm_build_476 *dm_build_414) Dm_build_475(dm_build_477 *DmStatement) error {
dm_build_478 := dm_build_1317(dm_build_476, dm_build_477)
_, dm_build_479 := dm_build_476.dm_build_457(dm_build_478)
if dm_build_479 != nil {
return dm_build_479
}
return nil
}
func (dm_build_481 *dm_build_414) Dm_build_480(dm_build_482 int32) error {
dm_build_483 := dm_build_1327(dm_build_481, dm_build_482)
_, dm_build_484 := dm_build_481.dm_build_457(dm_build_483)
if dm_build_484 != nil {
return dm_build_484
}
return nil
}
func (dm_build_486 *dm_build_414) Dm_build_485(dm_build_487 *DmStatement, dm_build_488 bool, dm_build_489 int16) (*execRetInfo, error) {
dm_build_490 := dm_build_1193(dm_build_486, dm_build_487, dm_build_488, dm_build_489)
dm_build_491, dm_build_492 := dm_build_486.dm_build_457(dm_build_490)
if dm_build_492 != nil {
return nil, dm_build_492
}
return dm_build_491.(*execRetInfo), nil
}
func (dm_build_494 *dm_build_414) Dm_build_493(dm_build_495 *DmStatement, dm_build_496 int16) (*execRetInfo, error) {
return dm_build_494.Dm_build_485(dm_build_495, false, Dm_build_788)
}
func (dm_build_498 *dm_build_414) Dm_build_497(dm_build_499 *DmStatement, dm_build_500 []OptParameter) (*execRetInfo, error) {
dm_build_501, dm_build_502 := dm_build_498.dm_build_457(dm_build_931(dm_build_498, dm_build_499, dm_build_500))
if dm_build_502 != nil {
return nil, dm_build_502
}
return dm_build_501.(*execRetInfo), nil
}
func (dm_build_504 *dm_build_414) Dm_build_503(dm_build_505 *DmStatement, dm_build_506 int16) (*execRetInfo, error) {
return dm_build_504.Dm_build_485(dm_build_505, true, dm_build_506)
}
func (dm_build_508 *dm_build_414) Dm_build_507(dm_build_509 *DmStatement, dm_build_510 [][]interface{}) (*execRetInfo, error) {
dm_build_511 := dm_build_963(dm_build_508, dm_build_509, dm_build_510)
dm_build_512, dm_build_513 := dm_build_508.dm_build_457(dm_build_511)
if dm_build_513 != nil {
return nil, dm_build_513
}
return dm_build_512.(*execRetInfo), nil
}
func (dm_build_515 *dm_build_414) Dm_build_514(dm_build_516 *DmStatement, dm_build_517 [][]interface{}, dm_build_518 bool) (*execRetInfo, error) {
var dm_build_519, dm_build_520 = 0, 0
var dm_build_521 = len(dm_build_517)
var dm_build_522 [][]interface{}
var dm_build_523 = NewExceInfo()
dm_build_523.updateCounts = make([]int64, dm_build_521)
var dm_build_524 = false
for dm_build_519 < dm_build_521 {
for dm_build_520 = dm_build_519; dm_build_520 < dm_build_521; dm_build_520++ {
paramData := dm_build_517[dm_build_520]
bindData := make([]interface{}, dm_build_516.paramCount)
dm_build_524 = false
for icol := 0; icol < int(dm_build_516.paramCount); icol++ {
if dm_build_516.bindParams[icol].ioType == IO_TYPE_OUT {
continue
}
if dm_build_515.dm_build_659(bindData, paramData, icol) {
dm_build_524 = true
break
}
}
if dm_build_524 {
break
}
dm_build_522 = append(dm_build_522, bindData)
}
if dm_build_520 != dm_build_519 {
tmpExecInfo, err := dm_build_515.Dm_build_507(dm_build_516, dm_build_522)
if err != nil {
return nil, err
}
dm_build_522 = dm_build_522[0:0]
dm_build_523.union(tmpExecInfo, dm_build_519, dm_build_520-dm_build_519)
}
if dm_build_520 < dm_build_521 {
tmpExecInfo, err := dm_build_515.Dm_build_533(dm_build_516, dm_build_517[dm_build_520], dm_build_518)
if err != nil {
return nil, err
}
dm_build_518 = true
dm_build_523.union(tmpExecInfo, dm_build_520, 1)
}
dm_build_519 = dm_build_520 + 1
}
for _, i := range dm_build_523.updateCounts {
if i > 0 {
dm_build_523.updateCount += i
}
}
return dm_build_523, nil
}
func (dm_build_526 *dm_build_414) dm_build_525(dm_build_527 *DmStatement, dm_build_528 []parameter) error {
if !dm_build_527.prepared {
retInfo, err := dm_build_526.Dm_build_485(dm_build_527, false, Dm_build_788)
if err != nil {
return nil
}
dm_build_527.serverParams = retInfo.serverParams
dm_build_527.paramCount = int32(len(dm_build_527.serverParams))
dm_build_527.prepared = true
}
dm_build_529 := dm_build_1182(dm_build_526, dm_build_527, dm_build_527.bindParams)
dm_build_530, err := dm_build_526.dm_build_457(dm_build_529)
if err != nil {
return nil
}
retInfo := dm_build_530.(*execRetInfo)
if retInfo.serverParams != nil && len(retInfo.serverParams) > 0 {
dm_build_527.serverParams = retInfo.serverParams
dm_build_527.paramCount = int32(len(dm_build_527.serverParams))
}
dm_build_527.preExec = true
return nil
}
func (dm_build_534 *dm_build_414) Dm_build_533(dm_build_535 *DmStatement, dm_build_536 []interface{}, dm_build_537 bool) (*execRetInfo, error) {
var dm_build_538 = make([]interface{}, dm_build_535.paramCount)
for icol := 0; icol < int(dm_build_535.paramCount); icol++ {
if dm_build_535.bindParams[icol].ioType == IO_TYPE_OUT {
continue
}
if dm_build_534.dm_build_659(dm_build_538, dm_build_536, icol) {
if !dm_build_537 {
dm_build_534.dm_build_525(dm_build_535, dm_build_535.bindParams)
dm_build_537 = true
}
dm_build_534.dm_build_665(dm_build_535, dm_build_535.bindParams[icol], icol, dm_build_536[icol].(iOffRowBinder))
dm_build_538[icol] = ParamDataEnum_OFF_ROW
}
}
var dm_build_539 = make([][]interface{}, 1, 1)
dm_build_539[0] = dm_build_538
dm_build_540 := dm_build_963(dm_build_534, dm_build_535, dm_build_539)
dm_build_541, dm_build_542 := dm_build_534.dm_build_457(dm_build_540)
if dm_build_542 != nil {
return nil, dm_build_542
}
return dm_build_541.(*execRetInfo), nil
}
func (dm_build_544 *dm_build_414) Dm_build_543(dm_build_545 *DmStatement, dm_build_546 int16) (*execRetInfo, error) {
dm_build_547 := dm_build_1169(dm_build_544, dm_build_545, dm_build_546)
dm_build_548, dm_build_549 := dm_build_544.dm_build_457(dm_build_547)
if dm_build_549 != nil {
return nil, dm_build_549
}
return dm_build_548.(*execRetInfo), nil
}
func (dm_build_551 *dm_build_414) Dm_build_550(dm_build_552 *innerRows, dm_build_553 int64) (*execRetInfo, error) {
dm_build_554 := dm_build_1070(dm_build_551, dm_build_552, dm_build_553, INT64_MAX)
dm_build_555, dm_build_556 := dm_build_551.dm_build_457(dm_build_554)
if dm_build_556 != nil {
return nil, dm_build_556
}
return dm_build_555.(*execRetInfo), nil
}
func (dm_build_558 *dm_build_414) Commit() error {
dm_build_559 := dm_build_916(dm_build_558)
_, dm_build_560 := dm_build_558.dm_build_457(dm_build_559)
if dm_build_560 != nil {
return dm_build_560
}
return nil
}
func (dm_build_562 *dm_build_414) Rollback() error {
dm_build_563 := dm_build_1231(dm_build_562)
_, dm_build_564 := dm_build_562.dm_build_457(dm_build_563)
if dm_build_564 != nil {
return dm_build_564
}
return nil
}
func (dm_build_566 *dm_build_414) Dm_build_565(dm_build_567 *DmConnection) error {
dm_build_568 := dm_build_1236(dm_build_566, dm_build_567.IsoLevel)
_, dm_build_569 := dm_build_566.dm_build_457(dm_build_568)
if dm_build_569 != nil {
return dm_build_569
}
return nil
}
func (dm_build_571 *dm_build_414) Dm_build_570(dm_build_572 *DmStatement, dm_build_573 string) error {
dm_build_574 := dm_build_921(dm_build_571, dm_build_572, dm_build_573)
_, dm_build_575 := dm_build_571.dm_build_457(dm_build_574)
if dm_build_575 != nil {
return dm_build_575
}
return nil
}
func (dm_build_577 *dm_build_414) Dm_build_576(dm_build_578 []uint32) ([]int64, error) {
dm_build_579 := dm_build_1335(dm_build_577, dm_build_578)
dm_build_580, dm_build_581 := dm_build_577.dm_build_457(dm_build_579)
if dm_build_581 != nil {
return nil, dm_build_581
}
return dm_build_580.([]int64), nil
}
func (dm_build_583 *dm_build_414) Close() error {
if dm_build_583.dm_build_425 {
return nil
}
dm_build_584 := dm_build_583.dm_build_415.Close()
if dm_build_584 != nil {
return dm_build_584
}
dm_build_583.dm_build_418 = nil
dm_build_583.dm_build_425 = true
return nil
}
func (dm_build_586 *dm_build_414) dm_build_585(dm_build_587 *lob) (int64, error) {
dm_build_588 := dm_build_1103(dm_build_586, dm_build_587)
dm_build_589, dm_build_590 := dm_build_586.dm_build_457(dm_build_588)
if dm_build_590 != nil {
return 0, dm_build_590
}
return dm_build_589.(int64), nil
}
func (dm_build_592 *dm_build_414) dm_build_591(dm_build_593 *lob, dm_build_594 int32, dm_build_595 int32) (*lobRetInfo, error) {
dm_build_596 := dm_build_1088(dm_build_592, dm_build_593, int(dm_build_594), int(dm_build_595))
dm_build_597, dm_build_598 := dm_build_592.dm_build_457(dm_build_596)
if dm_build_598 != nil {
return nil, dm_build_598
}
return dm_build_597.(*lobRetInfo), nil
}
func (dm_build_600 *dm_build_414) dm_build_599(dm_build_601 *DmBlob, dm_build_602 int32, dm_build_603 int32) ([]byte, error) {
var dm_build_604 = make([]byte, dm_build_603)
var dm_build_605 int32 = 0
var dm_build_606 int32 = 0
var dm_build_607 *lobRetInfo
var dm_build_608 []byte
var dm_build_609 error
for dm_build_605 < dm_build_603 {
dm_build_606 = dm_build_603 - dm_build_605
if dm_build_606 > Dm_build_821 {
dm_build_606 = Dm_build_821
}
dm_build_607, dm_build_609 = dm_build_600.dm_build_591(&dm_build_601.lob, dm_build_602+dm_build_605, dm_build_606)
if dm_build_609 != nil {
return nil, dm_build_609
}
dm_build_608 = dm_build_607.data
if dm_build_608 == nil || len(dm_build_608) == 0 {
break
}
Dm_build_1346.Dm_build_1402(dm_build_604, int(dm_build_605), dm_build_608, 0, len(dm_build_608))
dm_build_605 += int32(len(dm_build_608))
if dm_build_601.readOver {
break
}
}
return dm_build_604, nil
}
func (dm_build_611 *dm_build_414) dm_build_610(dm_build_612 *DmClob, dm_build_613 int32, dm_build_614 int32) (string, error) {
var dm_build_615 bytes.Buffer
var dm_build_616 int32 = 0
var dm_build_617 int32 = 0
var dm_build_618 *lobRetInfo
var dm_build_619 []byte
var dm_build_620 string
var dm_build_621 error
for dm_build_616 < dm_build_614 {
dm_build_617 = dm_build_614 - dm_build_616
if dm_build_617 > Dm_build_821/2 {
dm_build_617 = Dm_build_821 / 2
}
dm_build_618, dm_build_621 = dm_build_611.dm_build_591(&dm_build_612.lob, dm_build_613+dm_build_616, dm_build_617)
if dm_build_621 != nil {
return "", dm_build_621
}
dm_build_619 = dm_build_618.data
if dm_build_619 == nil || len(dm_build_619) == 0 {
break
}
dm_build_620 = Dm_build_1346.Dm_build_1503(dm_build_619, 0, len(dm_build_619), dm_build_612.serverEncoding, dm_build_611.dm_build_418)
dm_build_615.WriteString(dm_build_620)
var strLen = dm_build_618.charLen
if strLen == -1 {
strLen = int64(utf8.RuneCountInString(dm_build_620))
}
dm_build_616 += int32(strLen)
if dm_build_612.readOver {
break
}
}
return dm_build_615.String(), nil
}
func (dm_build_623 *dm_build_414) dm_build_622(dm_build_624 *DmClob, dm_build_625 int, dm_build_626 string, dm_build_627 string) (int, error) {
var dm_build_628 = Dm_build_1346.Dm_build_1562(dm_build_626, dm_build_627, dm_build_623.dm_build_418)
var dm_build_629 = 0
var dm_build_630 = len(dm_build_628)
var dm_build_631 = 0
var dm_build_632 = 0
var dm_build_633 = 0
var dm_build_634 = dm_build_630/Dm_build_820 + 1
var dm_build_635 byte = 0
var dm_build_636 byte = 0x01
var dm_build_637 byte = 0x02
for i := 0; i < dm_build_634; i++ {
dm_build_635 = 0
if i == 0 {
dm_build_635 |= dm_build_636
}
if i == dm_build_634-1 {
dm_build_635 |= dm_build_637
}
dm_build_633 = dm_build_630 - dm_build_632
if dm_build_633 > Dm_build_820 {
dm_build_633 = Dm_build_820
}
setLobData := dm_build_1250(dm_build_623, &dm_build_624.lob, dm_build_635, dm_build_625, dm_build_628, dm_build_629, dm_build_633)
ret, err := dm_build_623.dm_build_457(setLobData)
if err != nil {
return 0, err
}
tmp := ret.(int32)
if err != nil {
return -1, err
}
if tmp <= 0 {
return dm_build_631, nil
} else {
dm_build_625 += int(tmp)
dm_build_631 += int(tmp)
dm_build_632 += dm_build_633
dm_build_629 += dm_build_633
}
}
return dm_build_631, nil
}
func (dm_build_639 *dm_build_414) dm_build_638(dm_build_640 *DmBlob, dm_build_641 int, dm_build_642 []byte) (int, error) {
var dm_build_643 = 0
var dm_build_644 = len(dm_build_642)
var dm_build_645 = 0
var dm_build_646 = 0
var dm_build_647 = 0
var dm_build_648 = dm_build_644/Dm_build_820 + 1
var dm_build_649 byte = 0
var dm_build_650 byte = 0x01
var dm_build_651 byte = 0x02
for i := 0; i < dm_build_648; i++ {
dm_build_649 = 0
if i == 0 {
dm_build_649 |= dm_build_650
}
if i == dm_build_648-1 {
dm_build_649 |= dm_build_651
}
dm_build_647 = dm_build_644 - dm_build_646
if dm_build_647 > Dm_build_820 {
dm_build_647 = Dm_build_820
}
setLobData := dm_build_1250(dm_build_639, &dm_build_640.lob, dm_build_649, dm_build_641, dm_build_642, dm_build_643, dm_build_647)
ret, err := dm_build_639.dm_build_457(setLobData)
if err != nil {
return 0, err
}
tmp := ret.(int32)
if tmp <= 0 {
return dm_build_645, nil
} else {
dm_build_641 += int(tmp)
dm_build_645 += int(tmp)
dm_build_646 += dm_build_647
dm_build_643 += dm_build_647
}
}
return dm_build_645, nil
}
func (dm_build_653 *dm_build_414) dm_build_652(dm_build_654 *lob, dm_build_655 int) (int64, error) {
dm_build_656 := dm_build_1114(dm_build_653, dm_build_654, dm_build_655)
dm_build_657, dm_build_658 := dm_build_653.dm_build_457(dm_build_656)
if dm_build_658 != nil {
return dm_build_654.length, dm_build_658
}
return dm_build_657.(int64), nil
}
func (dm_build_660 *dm_build_414) dm_build_659(dm_build_661 []interface{}, dm_build_662 []interface{}, dm_build_663 int) bool {
var dm_build_664 = false
dm_build_661[dm_build_663] = dm_build_662[dm_build_663]
if binder, ok := dm_build_662[dm_build_663].(iOffRowBinder); ok {
dm_build_664 = true
dm_build_661[dm_build_663] = make([]byte, 0)
var lob lob
if l, ok := binder.getObj().(DmBlob); ok {
lob = l.lob
} else if l, ok := binder.getObj().(DmClob); ok {
lob = l.lob
}
if &lob != nil && lob.canOptimized(dm_build_660.dm_build_418) {
dm_build_661[dm_build_663] = &lobCtl{lob.buildCtlData()}
dm_build_664 = false
}
} else {
dm_build_661[dm_build_663] = dm_build_662[dm_build_663]
}
return dm_build_664
}
func (dm_build_666 *dm_build_414) dm_build_665(dm_build_667 *DmStatement, dm_build_668 parameter, dm_build_669 int, dm_build_670 iOffRowBinder) error {
var dm_build_671 = Dm_build_4()
dm_build_670.read(dm_build_671)
var dm_build_672 = 0
for !dm_build_670.isReadOver() || dm_build_671.Dm_build_5() > 0 {
if !dm_build_670.isReadOver() && dm_build_671.Dm_build_5() < Dm_build_820 {
dm_build_670.read(dm_build_671)
}
if dm_build_671.Dm_build_5() > Dm_build_820 {
dm_build_672 = Dm_build_820
} else {
dm_build_672 = dm_build_671.Dm_build_5()
}
putData := dm_build_1221(dm_build_666, dm_build_667, int16(dm_build_669), dm_build_671, int32(dm_build_672))
_, err := dm_build_666.dm_build_457(putData)
if err != nil {
return err
}
}
return nil
}
func (dm_build_674 *dm_build_414) dm_build_673() ([]byte, error) {
var dm_build_675 error
if dm_build_674.dm_build_422 == nil {
if dm_build_674.dm_build_422, dm_build_675 = security.NewClientKeyPair(); dm_build_675 != nil {
return nil, dm_build_675
}
}
return security.Bn2Bytes(dm_build_674.dm_build_422.GetY(), security.DH_KEY_LENGTH), nil
}
func (dm_build_677 *dm_build_414) dm_build_676() (*security.DhKey, error) {
var dm_build_678 error
if dm_build_677.dm_build_422 == nil {
if dm_build_677.dm_build_422, dm_build_678 = security.NewClientKeyPair(); dm_build_678 != nil {
return nil, dm_build_678
}
}
return dm_build_677.dm_build_422, nil
}
func (dm_build_680 *dm_build_414) dm_build_679(dm_build_681 int, dm_build_682 []byte, dm_build_683 string, dm_build_684 int) (dm_build_685 error) {
if dm_build_681 > 0 && dm_build_681 < security.MIN_EXTERNAL_CIPHER_ID && dm_build_682 != nil {
dm_build_680.dm_build_419, dm_build_685 = security.NewSymmCipher(dm_build_681, dm_build_682)
} else if dm_build_681 >= security.MIN_EXTERNAL_CIPHER_ID {
if dm_build_680.dm_build_419, dm_build_685 = security.NewThirdPartCipher(dm_build_681, dm_build_682, dm_build_683, dm_build_684); dm_build_685 != nil {
dm_build_685 = THIRD_PART_CIPHER_INIT_FAILED.addDetailln(dm_build_685.Error()).throw()
}
}
return
}
func (dm_build_687 *dm_build_414) dm_build_686(dm_build_688 bool) (dm_build_689 error) {
if dm_build_687.dm_build_416, dm_build_689 = security.NewTLSFromTCP(dm_build_687.dm_build_415, dm_build_687.dm_build_418.dmConnector.sslCertPath, dm_build_687.dm_build_418.dmConnector.sslKeyPath, dm_build_687.dm_build_418.dmConnector.user); dm_build_689 != nil {
return
}
if !dm_build_688 {
dm_build_687.dm_build_416 = nil
}
return
}
func (dm_build_691 *dm_build_414) dm_build_690(dm_build_692 dm_build_828) bool {
return dm_build_692.dm_build_843() != Dm_build_735 && dm_build_691.dm_build_418.sslEncrypt == 1
}
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
type ArrayDescriptor struct {
m_typeDesc *TypeDescriptor
}
func newArrayDescriptor(fulName string, conn *DmConnection) (*ArrayDescriptor, error) {
ad := new(ArrayDescriptor)
if fulName == "" {
return nil, ECGO_INVALID_COMPLEX_TYPE_NAME.throw()
}
ad.m_typeDesc = newTypeDescriptorWithFulName(fulName, conn)
err := ad.m_typeDesc.parseDescByName()
if err != nil {
return nil, err
}
return ad, nil
}
func newArrayDescriptorByTypeDescriptor(desc *TypeDescriptor) *ArrayDescriptor {
ad := new(ArrayDescriptor)
ad.m_typeDesc = desc
return ad
}
func (ad *ArrayDescriptor) getMDesc() *TypeDescriptor {
return ad.m_typeDesc
}
func (ad *ArrayDescriptor) getItemDesc() *TypeDescriptor {
return ad.m_typeDesc.m_arrObj
}
func (ad *ArrayDescriptor) getLength() int {
return ad.m_typeDesc.m_length
}
+495
View File
@@ -0,0 +1,495 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"io"
"math"
)
type Dm_build_78 struct {
dm_build_79 []byte
dm_build_80 int
}
func Dm_build_81(dm_build_82 int) *Dm_build_78 {
return &Dm_build_78{make([]byte, 0, dm_build_82), 0}
}
func Dm_build_83(dm_build_84 []byte) *Dm_build_78 {
return &Dm_build_78{dm_build_84, 0}
}
func (dm_build_86 *Dm_build_78) dm_build_85(dm_build_87 int) *Dm_build_78 {
dm_build_88 := len(dm_build_86.dm_build_79)
dm_build_89 := cap(dm_build_86.dm_build_79)
if dm_build_88+dm_build_87 <= dm_build_89 {
dm_build_86.dm_build_79 = dm_build_86.dm_build_79[:dm_build_88+dm_build_87]
} else {
var calCap = int64(math.Max(float64(2*dm_build_89), float64(dm_build_87+dm_build_88)))
nbuf := make([]byte, dm_build_87+dm_build_88, calCap)
copy(nbuf, dm_build_86.dm_build_79)
dm_build_86.dm_build_79 = nbuf
}
return dm_build_86
}
func (dm_build_91 *Dm_build_78) Dm_build_90() int {
return len(dm_build_91.dm_build_79)
}
func (dm_build_93 *Dm_build_78) Dm_build_92(dm_build_94 int) *Dm_build_78 {
for i := dm_build_94; i < len(dm_build_93.dm_build_79); i++ {
dm_build_93.dm_build_79[i] = 0
}
dm_build_93.dm_build_79 = dm_build_93.dm_build_79[:dm_build_94]
return dm_build_93
}
func (dm_build_96 *Dm_build_78) Dm_build_95(dm_build_97 int) *Dm_build_78 {
dm_build_96.dm_build_80 = dm_build_97
return dm_build_96
}
func (dm_build_99 *Dm_build_78) Dm_build_98() int {
return dm_build_99.dm_build_80
}
func (dm_build_101 *Dm_build_78) Dm_build_100(dm_build_102 bool) int {
return len(dm_build_101.dm_build_79) - dm_build_101.dm_build_80
}
func (dm_build_104 *Dm_build_78) Dm_build_103(dm_build_105 int, dm_build_106 bool, dm_build_107 bool) *Dm_build_78 {
if dm_build_106 {
if dm_build_107 {
dm_build_104.dm_build_85(dm_build_105)
} else {
dm_build_104.dm_build_79 = dm_build_104.dm_build_79[:len(dm_build_104.dm_build_79)-dm_build_105]
}
} else {
if dm_build_107 {
dm_build_104.dm_build_80 += dm_build_105
} else {
dm_build_104.dm_build_80 -= dm_build_105
}
}
return dm_build_104
}
func (dm_build_109 *Dm_build_78) Dm_build_108(dm_build_110 io.Reader, dm_build_111 int) (int, error) {
dm_build_112 := len(dm_build_109.dm_build_79)
dm_build_109.dm_build_85(dm_build_111)
dm_build_113 := 0
for dm_build_111 > 0 {
n, err := dm_build_110.Read(dm_build_109.dm_build_79[dm_build_112+dm_build_113:])
if n > 0 && err == io.EOF {
dm_build_113 += n
dm_build_109.dm_build_79 = dm_build_109.dm_build_79[:dm_build_112+dm_build_113]
return dm_build_113, nil
} else if n > 0 && err == nil {
dm_build_111 -= n
dm_build_113 += n
} else if n == 0 && err != nil {
return -1, ECGO_COMMUNITION_ERROR.addDetailln(err.Error()).throw()
}
}
return dm_build_113, nil
}
func (dm_build_115 *Dm_build_78) Dm_build_114(dm_build_116 io.Writer) (*Dm_build_78, error) {
if _, err := dm_build_116.Write(dm_build_115.dm_build_79); err != nil {
return nil, ECGO_COMMUNITION_ERROR.addDetailln(err.Error()).throw()
}
return dm_build_115, nil
}
func (dm_build_118 *Dm_build_78) Dm_build_117(dm_build_119 bool) int {
dm_build_120 := len(dm_build_118.dm_build_79)
dm_build_118.dm_build_85(1)
if dm_build_119 {
return copy(dm_build_118.dm_build_79[dm_build_120:], []byte{1})
} else {
return copy(dm_build_118.dm_build_79[dm_build_120:], []byte{0})
}
}
func (dm_build_122 *Dm_build_78) Dm_build_121(dm_build_123 byte) int {
dm_build_124 := len(dm_build_122.dm_build_79)
dm_build_122.dm_build_85(1)
return copy(dm_build_122.dm_build_79[dm_build_124:], Dm_build_1346.Dm_build_1524(dm_build_123))
}
func (dm_build_126 *Dm_build_78) Dm_build_125(dm_build_127 int8) int {
dm_build_128 := len(dm_build_126.dm_build_79)
dm_build_126.dm_build_85(1)
return copy(dm_build_126.dm_build_79[dm_build_128:], Dm_build_1346.Dm_build_1527(dm_build_127))
}
func (dm_build_130 *Dm_build_78) Dm_build_129(dm_build_131 int16) int {
dm_build_132 := len(dm_build_130.dm_build_79)
dm_build_130.dm_build_85(2)
return copy(dm_build_130.dm_build_79[dm_build_132:], Dm_build_1346.Dm_build_1530(dm_build_131))
}
func (dm_build_134 *Dm_build_78) Dm_build_133(dm_build_135 int32) int {
dm_build_136 := len(dm_build_134.dm_build_79)
dm_build_134.dm_build_85(4)
return copy(dm_build_134.dm_build_79[dm_build_136:], Dm_build_1346.Dm_build_1533(dm_build_135))
}
func (dm_build_138 *Dm_build_78) Dm_build_137(dm_build_139 uint8) int {
dm_build_140 := len(dm_build_138.dm_build_79)
dm_build_138.dm_build_85(1)
return copy(dm_build_138.dm_build_79[dm_build_140:], Dm_build_1346.Dm_build_1545(dm_build_139))
}
func (dm_build_142 *Dm_build_78) Dm_build_141(dm_build_143 uint16) int {
dm_build_144 := len(dm_build_142.dm_build_79)
dm_build_142.dm_build_85(2)
return copy(dm_build_142.dm_build_79[dm_build_144:], Dm_build_1346.Dm_build_1548(dm_build_143))
}
func (dm_build_146 *Dm_build_78) Dm_build_145(dm_build_147 uint32) int {
dm_build_148 := len(dm_build_146.dm_build_79)
dm_build_146.dm_build_85(4)
return copy(dm_build_146.dm_build_79[dm_build_148:], Dm_build_1346.Dm_build_1551(dm_build_147))
}
func (dm_build_150 *Dm_build_78) Dm_build_149(dm_build_151 uint64) int {
dm_build_152 := len(dm_build_150.dm_build_79)
dm_build_150.dm_build_85(8)
return copy(dm_build_150.dm_build_79[dm_build_152:], Dm_build_1346.Dm_build_1554(dm_build_151))
}
func (dm_build_154 *Dm_build_78) Dm_build_153(dm_build_155 float32) int {
dm_build_156 := len(dm_build_154.dm_build_79)
dm_build_154.dm_build_85(4)
return copy(dm_build_154.dm_build_79[dm_build_156:], Dm_build_1346.Dm_build_1551(math.Float32bits(dm_build_155)))
}
func (dm_build_158 *Dm_build_78) Dm_build_157(dm_build_159 float64) int {
dm_build_160 := len(dm_build_158.dm_build_79)
dm_build_158.dm_build_85(8)
return copy(dm_build_158.dm_build_79[dm_build_160:], Dm_build_1346.Dm_build_1554(math.Float64bits(dm_build_159)))
}
func (dm_build_162 *Dm_build_78) Dm_build_161(dm_build_163 []byte) int {
dm_build_164 := len(dm_build_162.dm_build_79)
dm_build_162.dm_build_85(len(dm_build_163))
return copy(dm_build_162.dm_build_79[dm_build_164:], dm_build_163)
}
func (dm_build_166 *Dm_build_78) Dm_build_165(dm_build_167 []byte) int {
return dm_build_166.Dm_build_133(int32(len(dm_build_167))) + dm_build_166.Dm_build_161(dm_build_167)
}
func (dm_build_169 *Dm_build_78) Dm_build_168(dm_build_170 []byte) int {
return dm_build_169.Dm_build_137(uint8(len(dm_build_170))) + dm_build_169.Dm_build_161(dm_build_170)
}
func (dm_build_172 *Dm_build_78) Dm_build_171(dm_build_173 []byte) int {
return dm_build_172.Dm_build_141(uint16(len(dm_build_173))) + dm_build_172.Dm_build_161(dm_build_173)
}
func (dm_build_175 *Dm_build_78) Dm_build_174(dm_build_176 []byte) int {
return dm_build_175.Dm_build_161(dm_build_176) + dm_build_175.Dm_build_121(0)
}
func (dm_build_178 *Dm_build_78) Dm_build_177(dm_build_179 string, dm_build_180 string, dm_build_181 *DmConnection) int {
dm_build_182 := Dm_build_1346.Dm_build_1562(dm_build_179, dm_build_180, dm_build_181)
return dm_build_178.Dm_build_165(dm_build_182)
}
func (dm_build_184 *Dm_build_78) Dm_build_183(dm_build_185 string, dm_build_186 string, dm_build_187 *DmConnection) int {
dm_build_188 := Dm_build_1346.Dm_build_1562(dm_build_185, dm_build_186, dm_build_187)
return dm_build_184.Dm_build_168(dm_build_188)
}
func (dm_build_190 *Dm_build_78) Dm_build_189(dm_build_191 string, dm_build_192 string, dm_build_193 *DmConnection) int {
dm_build_194 := Dm_build_1346.Dm_build_1562(dm_build_191, dm_build_192, dm_build_193)
return dm_build_190.Dm_build_171(dm_build_194)
}
func (dm_build_196 *Dm_build_78) Dm_build_195(dm_build_197 string, dm_build_198 string, dm_build_199 *DmConnection) int {
dm_build_200 := Dm_build_1346.Dm_build_1562(dm_build_197, dm_build_198, dm_build_199)
return dm_build_196.Dm_build_174(dm_build_200)
}
func (dm_build_202 *Dm_build_78) Dm_build_201() byte {
dm_build_203 := Dm_build_1346.Dm_build_1439(dm_build_202.dm_build_79, dm_build_202.dm_build_80)
dm_build_202.dm_build_80++
return dm_build_203
}
func (dm_build_205 *Dm_build_78) Dm_build_204() int16 {
dm_build_206 := Dm_build_1346.Dm_build_1443(dm_build_205.dm_build_79, dm_build_205.dm_build_80)
dm_build_205.dm_build_80 += 2
return dm_build_206
}
func (dm_build_208 *Dm_build_78) Dm_build_207() int32 {
dm_build_209 := Dm_build_1346.Dm_build_1448(dm_build_208.dm_build_79, dm_build_208.dm_build_80)
dm_build_208.dm_build_80 += 4
return dm_build_209
}
func (dm_build_211 *Dm_build_78) Dm_build_210() int64 {
dm_build_212 := Dm_build_1346.Dm_build_1453(dm_build_211.dm_build_79, dm_build_211.dm_build_80)
dm_build_211.dm_build_80 += 8
return dm_build_212
}
func (dm_build_214 *Dm_build_78) Dm_build_213() float32 {
dm_build_215 := Dm_build_1346.Dm_build_1458(dm_build_214.dm_build_79, dm_build_214.dm_build_80)
dm_build_214.dm_build_80 += 4
return dm_build_215
}
func (dm_build_217 *Dm_build_78) Dm_build_216() float64 {
dm_build_218 := Dm_build_1346.Dm_build_1462(dm_build_217.dm_build_79, dm_build_217.dm_build_80)
dm_build_217.dm_build_80 += 8
return dm_build_218
}
func (dm_build_220 *Dm_build_78) Dm_build_219() uint8 {
dm_build_221 := Dm_build_1346.Dm_build_1466(dm_build_220.dm_build_79, dm_build_220.dm_build_80)
dm_build_220.dm_build_80 += 1
return dm_build_221
}
func (dm_build_223 *Dm_build_78) Dm_build_222() uint16 {
dm_build_224 := Dm_build_1346.Dm_build_1470(dm_build_223.dm_build_79, dm_build_223.dm_build_80)
dm_build_223.dm_build_80 += 2
return dm_build_224
}
func (dm_build_226 *Dm_build_78) Dm_build_225() uint32 {
dm_build_227 := Dm_build_1346.Dm_build_1475(dm_build_226.dm_build_79, dm_build_226.dm_build_80)
dm_build_226.dm_build_80 += 4
return dm_build_227
}
func (dm_build_229 *Dm_build_78) Dm_build_228(dm_build_230 int) []byte {
dm_build_231 := Dm_build_1346.Dm_build_1497(dm_build_229.dm_build_79, dm_build_229.dm_build_80, dm_build_230)
dm_build_229.dm_build_80 += dm_build_230
return dm_build_231
}
func (dm_build_233 *Dm_build_78) Dm_build_232() []byte {
return dm_build_233.Dm_build_228(int(dm_build_233.Dm_build_207()))
}
func (dm_build_235 *Dm_build_78) Dm_build_234() []byte {
return dm_build_235.Dm_build_228(int(dm_build_235.Dm_build_201()))
}
func (dm_build_237 *Dm_build_78) Dm_build_236() []byte {
return dm_build_237.Dm_build_228(int(dm_build_237.Dm_build_204()))
}
func (dm_build_239 *Dm_build_78) Dm_build_238(dm_build_240 int) []byte {
return dm_build_239.Dm_build_228(dm_build_240)
}
func (dm_build_242 *Dm_build_78) Dm_build_241() []byte {
dm_build_243 := 0
for dm_build_242.Dm_build_201() != 0 {
dm_build_243++
}
dm_build_242.Dm_build_103(dm_build_243, false, false)
return dm_build_242.Dm_build_228(dm_build_243)
}
func (dm_build_245 *Dm_build_78) Dm_build_244(dm_build_246 int, dm_build_247 string, dm_build_248 *DmConnection) string {
return Dm_build_1346.Dm_build_1598(dm_build_245.Dm_build_228(dm_build_246), dm_build_247, dm_build_248)
}
func (dm_build_250 *Dm_build_78) Dm_build_249(dm_build_251 string, dm_build_252 *DmConnection) string {
return Dm_build_1346.Dm_build_1598(dm_build_250.Dm_build_232(), dm_build_251, dm_build_252)
}
func (dm_build_254 *Dm_build_78) Dm_build_253(dm_build_255 string, dm_build_256 *DmConnection) string {
return Dm_build_1346.Dm_build_1598(dm_build_254.Dm_build_234(), dm_build_255, dm_build_256)
}
func (dm_build_258 *Dm_build_78) Dm_build_257(dm_build_259 string, dm_build_260 *DmConnection) string {
return Dm_build_1346.Dm_build_1598(dm_build_258.Dm_build_236(), dm_build_259, dm_build_260)
}
func (dm_build_262 *Dm_build_78) Dm_build_261(dm_build_263 string, dm_build_264 *DmConnection) string {
return Dm_build_1346.Dm_build_1598(dm_build_262.Dm_build_241(), dm_build_263, dm_build_264)
}
func (dm_build_266 *Dm_build_78) Dm_build_265(dm_build_267 int, dm_build_268 byte) int {
return dm_build_266.Dm_build_301(dm_build_267, Dm_build_1346.Dm_build_1524(dm_build_268))
}
func (dm_build_270 *Dm_build_78) Dm_build_269(dm_build_271 int, dm_build_272 int16) int {
return dm_build_270.Dm_build_301(dm_build_271, Dm_build_1346.Dm_build_1530(dm_build_272))
}
func (dm_build_274 *Dm_build_78) Dm_build_273(dm_build_275 int, dm_build_276 int32) int {
return dm_build_274.Dm_build_301(dm_build_275, Dm_build_1346.Dm_build_1533(dm_build_276))
}
func (dm_build_278 *Dm_build_78) Dm_build_277(dm_build_279 int, dm_build_280 int64) int {
return dm_build_278.Dm_build_301(dm_build_279, Dm_build_1346.Dm_build_1536(dm_build_280))
}
func (dm_build_282 *Dm_build_78) Dm_build_281(dm_build_283 int, dm_build_284 float32) int {
return dm_build_282.Dm_build_301(dm_build_283, Dm_build_1346.Dm_build_1539(dm_build_284))
}
func (dm_build_286 *Dm_build_78) Dm_build_285(dm_build_287 int, dm_build_288 float64) int {
return dm_build_286.Dm_build_301(dm_build_287, Dm_build_1346.Dm_build_1542(dm_build_288))
}
func (dm_build_290 *Dm_build_78) Dm_build_289(dm_build_291 int, dm_build_292 uint8) int {
return dm_build_290.Dm_build_301(dm_build_291, Dm_build_1346.Dm_build_1545(dm_build_292))
}
func (dm_build_294 *Dm_build_78) Dm_build_293(dm_build_295 int, dm_build_296 uint16) int {
return dm_build_294.Dm_build_301(dm_build_295, Dm_build_1346.Dm_build_1548(dm_build_296))
}
func (dm_build_298 *Dm_build_78) Dm_build_297(dm_build_299 int, dm_build_300 uint32) int {
return dm_build_298.Dm_build_301(dm_build_299, Dm_build_1346.Dm_build_1551(dm_build_300))
}
func (dm_build_302 *Dm_build_78) Dm_build_301(dm_build_303 int, dm_build_304 []byte) int {
return copy(dm_build_302.dm_build_79[dm_build_303:], dm_build_304)
}
func (dm_build_306 *Dm_build_78) Dm_build_305(dm_build_307 int, dm_build_308 []byte) int {
return dm_build_306.Dm_build_273(dm_build_307, int32(len(dm_build_308))) + dm_build_306.Dm_build_301(dm_build_307+4, dm_build_308)
}
func (dm_build_310 *Dm_build_78) Dm_build_309(dm_build_311 int, dm_build_312 []byte) int {
return dm_build_310.Dm_build_265(dm_build_311, byte(len(dm_build_312))) + dm_build_310.Dm_build_301(dm_build_311+1, dm_build_312)
}
func (dm_build_314 *Dm_build_78) Dm_build_313(dm_build_315 int, dm_build_316 []byte) int {
return dm_build_314.Dm_build_269(dm_build_315, int16(len(dm_build_316))) + dm_build_314.Dm_build_301(dm_build_315+2, dm_build_316)
}
func (dm_build_318 *Dm_build_78) Dm_build_317(dm_build_319 int, dm_build_320 []byte) int {
return dm_build_318.Dm_build_301(dm_build_319, dm_build_320) + dm_build_318.Dm_build_265(dm_build_319+len(dm_build_320), 0)
}
func (dm_build_322 *Dm_build_78) Dm_build_321(dm_build_323 int, dm_build_324 string, dm_build_325 string, dm_build_326 *DmConnection) int {
return dm_build_322.Dm_build_305(dm_build_323, Dm_build_1346.Dm_build_1562(dm_build_324, dm_build_325, dm_build_326))
}
func (dm_build_328 *Dm_build_78) Dm_build_327(dm_build_329 int, dm_build_330 string, dm_build_331 string, dm_build_332 *DmConnection) int {
return dm_build_328.Dm_build_309(dm_build_329, Dm_build_1346.Dm_build_1562(dm_build_330, dm_build_331, dm_build_332))
}
func (dm_build_334 *Dm_build_78) Dm_build_333(dm_build_335 int, dm_build_336 string, dm_build_337 string, dm_build_338 *DmConnection) int {
return dm_build_334.Dm_build_313(dm_build_335, Dm_build_1346.Dm_build_1562(dm_build_336, dm_build_337, dm_build_338))
}
func (dm_build_340 *Dm_build_78) Dm_build_339(dm_build_341 int, dm_build_342 string, dm_build_343 string, dm_build_344 *DmConnection) int {
return dm_build_340.Dm_build_317(dm_build_341, Dm_build_1346.Dm_build_1562(dm_build_342, dm_build_343, dm_build_344))
}
func (dm_build_346 *Dm_build_78) Dm_build_345(dm_build_347 int) byte {
return Dm_build_1346.Dm_build_1567(dm_build_346.Dm_build_372(dm_build_347, 1))
}
func (dm_build_349 *Dm_build_78) Dm_build_348(dm_build_350 int) int16 {
return Dm_build_1346.Dm_build_1570(dm_build_349.Dm_build_372(dm_build_350, 2))
}
func (dm_build_352 *Dm_build_78) Dm_build_351(dm_build_353 int) int32 {
return Dm_build_1346.Dm_build_1573(dm_build_352.Dm_build_372(dm_build_353, 4))
}
func (dm_build_355 *Dm_build_78) Dm_build_354(dm_build_356 int) int64 {
return Dm_build_1346.Dm_build_1576(dm_build_355.Dm_build_372(dm_build_356, 8))
}
func (dm_build_358 *Dm_build_78) Dm_build_357(dm_build_359 int) float32 {
return Dm_build_1346.Dm_build_1579(dm_build_358.Dm_build_372(dm_build_359, 4))
}
func (dm_build_361 *Dm_build_78) Dm_build_360(dm_build_362 int) float64 {
return Dm_build_1346.Dm_build_1582(dm_build_361.Dm_build_372(dm_build_362, 8))
}
func (dm_build_364 *Dm_build_78) Dm_build_363(dm_build_365 int) uint8 {
return Dm_build_1346.Dm_build_1585(dm_build_364.Dm_build_372(dm_build_365, 1))
}
func (dm_build_367 *Dm_build_78) Dm_build_366(dm_build_368 int) uint16 {
return Dm_build_1346.Dm_build_1588(dm_build_367.Dm_build_372(dm_build_368, 2))
}
func (dm_build_370 *Dm_build_78) Dm_build_369(dm_build_371 int) uint32 {
return Dm_build_1346.Dm_build_1591(dm_build_370.Dm_build_372(dm_build_371, 4))
}
func (dm_build_373 *Dm_build_78) Dm_build_372(dm_build_374 int, dm_build_375 int) []byte {
return dm_build_373.dm_build_79[dm_build_374 : dm_build_374+dm_build_375]
}
func (dm_build_377 *Dm_build_78) Dm_build_376(dm_build_378 int) []byte {
dm_build_379 := dm_build_377.Dm_build_351(dm_build_378)
return dm_build_377.Dm_build_372(dm_build_378+4, int(dm_build_379))
}
func (dm_build_381 *Dm_build_78) Dm_build_380(dm_build_382 int) []byte {
dm_build_383 := dm_build_381.Dm_build_345(dm_build_382)
return dm_build_381.Dm_build_372(dm_build_382+1, int(dm_build_383))
}
func (dm_build_385 *Dm_build_78) Dm_build_384(dm_build_386 int) []byte {
dm_build_387 := dm_build_385.Dm_build_348(dm_build_386)
return dm_build_385.Dm_build_372(dm_build_386+2, int(dm_build_387))
}
func (dm_build_389 *Dm_build_78) Dm_build_388(dm_build_390 int) []byte {
dm_build_391 := 0
for dm_build_389.Dm_build_345(dm_build_390) != 0 {
dm_build_390++
dm_build_391++
}
return dm_build_389.Dm_build_372(dm_build_390-dm_build_391, int(dm_build_391))
}
func (dm_build_393 *Dm_build_78) Dm_build_392(dm_build_394 int, dm_build_395 string, dm_build_396 *DmConnection) string {
return Dm_build_1346.Dm_build_1598(dm_build_393.Dm_build_376(dm_build_394), dm_build_395, dm_build_396)
}
func (dm_build_398 *Dm_build_78) Dm_build_397(dm_build_399 int, dm_build_400 string, dm_build_401 *DmConnection) string {
return Dm_build_1346.Dm_build_1598(dm_build_398.Dm_build_380(dm_build_399), dm_build_400, dm_build_401)
}
func (dm_build_403 *Dm_build_78) Dm_build_402(dm_build_404 int, dm_build_405 string, dm_build_406 *DmConnection) string {
return Dm_build_1346.Dm_build_1598(dm_build_403.Dm_build_384(dm_build_404), dm_build_405, dm_build_406)
}
func (dm_build_408 *Dm_build_78) Dm_build_407(dm_build_409 int, dm_build_410 string, dm_build_411 *DmConnection) string {
return Dm_build_1346.Dm_build_1598(dm_build_408.Dm_build_388(dm_build_409), dm_build_410, dm_build_411)
}
+206
View File
@@ -0,0 +1,206 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"container/list"
"io"
)
type Dm_build_0 struct {
dm_build_1 *list.List
dm_build_2 *dm_build_54
dm_build_3 int
}
func Dm_build_4() *Dm_build_0 {
return &Dm_build_0{
dm_build_1: list.New(),
dm_build_3: 0,
}
}
func (dm_build_6 *Dm_build_0) Dm_build_5() int {
return dm_build_6.dm_build_3
}
func (dm_build_8 *Dm_build_0) Dm_build_7(dm_build_9 *Dm_build_78, dm_build_10 int) int {
var dm_build_11 = 0
var dm_build_12 = 0
for dm_build_11 < dm_build_10 && dm_build_8.dm_build_2 != nil {
dm_build_12 = dm_build_8.dm_build_2.dm_build_62(dm_build_9, dm_build_10-dm_build_11)
if dm_build_8.dm_build_2.dm_build_57 == 0 {
dm_build_8.dm_build_44()
}
dm_build_11 += dm_build_12
dm_build_8.dm_build_3 -= dm_build_12
}
return dm_build_11
}
func (dm_build_14 *Dm_build_0) Dm_build_13(dm_build_15 []byte, dm_build_16 int, dm_build_17 int) int {
var dm_build_18 = 0
var dm_build_19 = 0
for dm_build_18 < dm_build_17 && dm_build_14.dm_build_2 != nil {
dm_build_19 = dm_build_14.dm_build_2.dm_build_66(dm_build_15, dm_build_16, dm_build_17-dm_build_18)
if dm_build_14.dm_build_2.dm_build_57 == 0 {
dm_build_14.dm_build_44()
}
dm_build_18 += dm_build_19
dm_build_14.dm_build_3 -= dm_build_19
dm_build_16 += dm_build_19
}
return dm_build_18
}
func (dm_build_21 *Dm_build_0) Dm_build_20(dm_build_22 io.Writer, dm_build_23 int) int {
var dm_build_24 = 0
var dm_build_25 = 0
for dm_build_24 < dm_build_23 && dm_build_21.dm_build_2 != nil {
dm_build_25 = dm_build_21.dm_build_2.dm_build_71(dm_build_22, dm_build_23-dm_build_24)
if dm_build_21.dm_build_2.dm_build_57 == 0 {
dm_build_21.dm_build_44()
}
dm_build_24 += dm_build_25
dm_build_21.dm_build_3 -= dm_build_25
}
return dm_build_24
}
func (dm_build_27 *Dm_build_0) Dm_build_26(dm_build_28 []byte, dm_build_29 int, dm_build_30 int) {
if dm_build_30 == 0 {
return
}
var dm_build_31 = dm_build_58(dm_build_28, dm_build_29, dm_build_30)
if dm_build_27.dm_build_2 == nil {
dm_build_27.dm_build_2 = dm_build_31
} else {
dm_build_27.dm_build_1.PushBack(dm_build_31)
}
dm_build_27.dm_build_3 += dm_build_30
}
func (dm_build_33 *Dm_build_0) dm_build_32(dm_build_34 int) byte {
var dm_build_35 = dm_build_34
var dm_build_36 = dm_build_33.dm_build_2
for dm_build_35 > 0 && dm_build_36 != nil {
if dm_build_36.dm_build_57 == 0 {
continue
}
if dm_build_35 > dm_build_36.dm_build_57-1 {
dm_build_35 -= dm_build_36.dm_build_57
dm_build_36 = dm_build_33.dm_build_1.Front().Value.(*dm_build_54)
} else {
break
}
}
return dm_build_36.dm_build_75(dm_build_35)
}
func (dm_build_38 *Dm_build_0) Dm_build_37(dm_build_39 *Dm_build_0) {
if dm_build_39.dm_build_3 == 0 {
return
}
var dm_build_40 = dm_build_39.dm_build_2
for dm_build_40 != nil {
dm_build_38.dm_build_41(dm_build_40)
dm_build_39.dm_build_44()
dm_build_40 = dm_build_39.dm_build_2
}
dm_build_39.dm_build_3 = 0
}
func (dm_build_42 *Dm_build_0) dm_build_41(dm_build_43 *dm_build_54) {
if dm_build_43.dm_build_57 == 0 {
return
}
if dm_build_42.dm_build_2 == nil {
dm_build_42.dm_build_2 = dm_build_43
} else {
dm_build_42.dm_build_1.PushBack(dm_build_43)
}
dm_build_42.dm_build_3 += dm_build_43.dm_build_57
}
func (dm_build_45 *Dm_build_0) dm_build_44() {
var dm_build_46 = dm_build_45.dm_build_1.Front()
if dm_build_46 == nil {
dm_build_45.dm_build_2 = nil
} else {
dm_build_45.dm_build_2 = dm_build_46.Value.(*dm_build_54)
dm_build_45.dm_build_1.Remove(dm_build_46)
}
}
func (dm_build_48 *Dm_build_0) Dm_build_47() []byte {
var dm_build_49 = make([]byte, dm_build_48.dm_build_3)
var dm_build_50 = dm_build_48.dm_build_2
var dm_build_51 = 0
var dm_build_52 = len(dm_build_49)
var dm_build_53 = 0
for dm_build_50 != nil {
if dm_build_50.dm_build_57 > 0 {
if dm_build_52 > dm_build_50.dm_build_57 {
dm_build_53 = dm_build_50.dm_build_57
} else {
dm_build_53 = dm_build_52
}
copy(dm_build_49[dm_build_51:dm_build_51+dm_build_53], dm_build_50.dm_build_55[dm_build_50.dm_build_56:dm_build_50.dm_build_56+dm_build_53])
dm_build_51 += dm_build_53
dm_build_52 -= dm_build_53
}
if dm_build_48.dm_build_1.Front() == nil {
dm_build_50 = nil
} else {
dm_build_50 = dm_build_48.dm_build_1.Front().Value.(*dm_build_54)
}
}
return dm_build_49
}
type dm_build_54 struct {
dm_build_55 []byte
dm_build_56 int
dm_build_57 int
}
func dm_build_58(dm_build_59 []byte, dm_build_60 int, dm_build_61 int) *dm_build_54 {
return &dm_build_54{
dm_build_59,
dm_build_60,
dm_build_61,
}
}
func (dm_build_63 *dm_build_54) dm_build_62(dm_build_64 *Dm_build_78, dm_build_65 int) int {
if dm_build_63.dm_build_57 <= dm_build_65 {
dm_build_65 = dm_build_63.dm_build_57
}
dm_build_64.Dm_build_161(dm_build_63.dm_build_55[dm_build_63.dm_build_56 : dm_build_63.dm_build_56+dm_build_65])
dm_build_63.dm_build_56 += dm_build_65
dm_build_63.dm_build_57 -= dm_build_65
return dm_build_65
}
func (dm_build_67 *dm_build_54) dm_build_66(dm_build_68 []byte, dm_build_69 int, dm_build_70 int) int {
if dm_build_67.dm_build_57 <= dm_build_70 {
dm_build_70 = dm_build_67.dm_build_57
}
copy(dm_build_68[dm_build_69:dm_build_69+dm_build_70], dm_build_67.dm_build_55[dm_build_67.dm_build_56:dm_build_67.dm_build_56+dm_build_70])
dm_build_67.dm_build_56 += dm_build_70
dm_build_67.dm_build_57 -= dm_build_70
return dm_build_70
}
func (dm_build_72 *dm_build_54) dm_build_71(dm_build_73 io.Writer, dm_build_74 int) int {
if dm_build_72.dm_build_57 <= dm_build_74 {
dm_build_74 = dm_build_72.dm_build_57
}
dm_build_73.Write(dm_build_72.dm_build_55[dm_build_72.dm_build_56 : dm_build_72.dm_build_56+dm_build_74])
dm_build_72.dm_build_56 += dm_build_74
dm_build_72.dm_build_57 -= dm_build_74
return dm_build_74
}
func (dm_build_76 *dm_build_54) dm_build_75(dm_build_77 int) byte {
return dm_build_76.dm_build_55[dm_build_76.dm_build_56+dm_build_77]
}
+522
View File
@@ -0,0 +1,522 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"bytes"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/ianaindex"
"golang.org/x/text/transform"
"io"
"io/ioutil"
"math"
)
type dm_build_1345 struct{}
var Dm_build_1346 = &dm_build_1345{}
func (Dm_build_1348 *dm_build_1345) Dm_build_1347(dm_build_1349 []byte, dm_build_1350 int, dm_build_1351 byte) int {
dm_build_1349[dm_build_1350] = dm_build_1351
return 1
}
func (Dm_build_1353 *dm_build_1345) Dm_build_1352(dm_build_1354 []byte, dm_build_1355 int, dm_build_1356 int8) int {
dm_build_1354[dm_build_1355] = byte(dm_build_1356)
return 1
}
func (Dm_build_1358 *dm_build_1345) Dm_build_1357(dm_build_1359 []byte, dm_build_1360 int, dm_build_1361 int16) int {
dm_build_1359[dm_build_1360] = byte(dm_build_1361)
dm_build_1360++
dm_build_1359[dm_build_1360] = byte(dm_build_1361 >> 8)
return 2
}
func (Dm_build_1363 *dm_build_1345) Dm_build_1362(dm_build_1364 []byte, dm_build_1365 int, dm_build_1366 int32) int {
dm_build_1364[dm_build_1365] = byte(dm_build_1366)
dm_build_1365++
dm_build_1364[dm_build_1365] = byte(dm_build_1366 >> 8)
dm_build_1365++
dm_build_1364[dm_build_1365] = byte(dm_build_1366 >> 16)
dm_build_1365++
dm_build_1364[dm_build_1365] = byte(dm_build_1366 >> 24)
dm_build_1365++
return 4
}
func (Dm_build_1368 *dm_build_1345) Dm_build_1367(dm_build_1369 []byte, dm_build_1370 int, dm_build_1371 int64) int {
dm_build_1369[dm_build_1370] = byte(dm_build_1371)
dm_build_1370++
dm_build_1369[dm_build_1370] = byte(dm_build_1371 >> 8)
dm_build_1370++
dm_build_1369[dm_build_1370] = byte(dm_build_1371 >> 16)
dm_build_1370++
dm_build_1369[dm_build_1370] = byte(dm_build_1371 >> 24)
dm_build_1370++
dm_build_1369[dm_build_1370] = byte(dm_build_1371 >> 32)
dm_build_1370++
dm_build_1369[dm_build_1370] = byte(dm_build_1371 >> 40)
dm_build_1370++
dm_build_1369[dm_build_1370] = byte(dm_build_1371 >> 48)
dm_build_1370++
dm_build_1369[dm_build_1370] = byte(dm_build_1371 >> 56)
return 8
}
func (Dm_build_1373 *dm_build_1345) Dm_build_1372(dm_build_1374 []byte, dm_build_1375 int, dm_build_1376 float32) int {
return Dm_build_1373.Dm_build_1392(dm_build_1374, dm_build_1375, math.Float32bits(dm_build_1376))
}
func (Dm_build_1378 *dm_build_1345) Dm_build_1377(dm_build_1379 []byte, dm_build_1380 int, dm_build_1381 float64) int {
return Dm_build_1378.Dm_build_1397(dm_build_1379, dm_build_1380, math.Float64bits(dm_build_1381))
}
func (Dm_build_1383 *dm_build_1345) Dm_build_1382(dm_build_1384 []byte, dm_build_1385 int, dm_build_1386 uint8) int {
dm_build_1384[dm_build_1385] = byte(dm_build_1386)
return 1
}
func (Dm_build_1388 *dm_build_1345) Dm_build_1387(dm_build_1389 []byte, dm_build_1390 int, dm_build_1391 uint16) int {
dm_build_1389[dm_build_1390] = byte(dm_build_1391)
dm_build_1390++
dm_build_1389[dm_build_1390] = byte(dm_build_1391 >> 8)
return 2
}
func (Dm_build_1393 *dm_build_1345) Dm_build_1392(dm_build_1394 []byte, dm_build_1395 int, dm_build_1396 uint32) int {
dm_build_1394[dm_build_1395] = byte(dm_build_1396)
dm_build_1395++
dm_build_1394[dm_build_1395] = byte(dm_build_1396 >> 8)
dm_build_1395++
dm_build_1394[dm_build_1395] = byte(dm_build_1396 >> 16)
dm_build_1395++
dm_build_1394[dm_build_1395] = byte(dm_build_1396 >> 24)
return 3
}
func (Dm_build_1398 *dm_build_1345) Dm_build_1397(dm_build_1399 []byte, dm_build_1400 int, dm_build_1401 uint64) int {
dm_build_1399[dm_build_1400] = byte(dm_build_1401)
dm_build_1400++
dm_build_1399[dm_build_1400] = byte(dm_build_1401 >> 8)
dm_build_1400++
dm_build_1399[dm_build_1400] = byte(dm_build_1401 >> 16)
dm_build_1400++
dm_build_1399[dm_build_1400] = byte(dm_build_1401 >> 24)
dm_build_1400++
dm_build_1399[dm_build_1400] = byte(dm_build_1401 >> 32)
dm_build_1400++
dm_build_1399[dm_build_1400] = byte(dm_build_1401 >> 40)
dm_build_1400++
dm_build_1399[dm_build_1400] = byte(dm_build_1401 >> 48)
dm_build_1400++
dm_build_1399[dm_build_1400] = byte(dm_build_1401 >> 56)
return 3
}
func (Dm_build_1403 *dm_build_1345) Dm_build_1402(dm_build_1404 []byte, dm_build_1405 int, dm_build_1406 []byte, dm_build_1407 int, dm_build_1408 int) int {
copy(dm_build_1404[dm_build_1405:dm_build_1405+dm_build_1408], dm_build_1406[dm_build_1407:dm_build_1407+dm_build_1408])
return dm_build_1408
}
func (Dm_build_1410 *dm_build_1345) Dm_build_1409(dm_build_1411 []byte, dm_build_1412 int, dm_build_1413 []byte, dm_build_1414 int, dm_build_1415 int) int {
dm_build_1412 += Dm_build_1410.Dm_build_1392(dm_build_1411, dm_build_1412, uint32(dm_build_1415))
return 4 + Dm_build_1410.Dm_build_1402(dm_build_1411, dm_build_1412, dm_build_1413, dm_build_1414, dm_build_1415)
}
func (Dm_build_1417 *dm_build_1345) Dm_build_1416(dm_build_1418 []byte, dm_build_1419 int, dm_build_1420 []byte, dm_build_1421 int, dm_build_1422 int) int {
dm_build_1419 += Dm_build_1417.Dm_build_1387(dm_build_1418, dm_build_1419, uint16(dm_build_1422))
return 2 + Dm_build_1417.Dm_build_1402(dm_build_1418, dm_build_1419, dm_build_1420, dm_build_1421, dm_build_1422)
}
func (Dm_build_1424 *dm_build_1345) Dm_build_1423(dm_build_1425 []byte, dm_build_1426 int, dm_build_1427 string, dm_build_1428 string, dm_build_1429 *DmConnection) int {
dm_build_1430 := Dm_build_1424.Dm_build_1562(dm_build_1427, dm_build_1428, dm_build_1429)
dm_build_1426 += Dm_build_1424.Dm_build_1392(dm_build_1425, dm_build_1426, uint32(len(dm_build_1430)))
return 4 + Dm_build_1424.Dm_build_1402(dm_build_1425, dm_build_1426, dm_build_1430, 0, len(dm_build_1430))
}
func (Dm_build_1432 *dm_build_1345) Dm_build_1431(dm_build_1433 []byte, dm_build_1434 int, dm_build_1435 string, dm_build_1436 string, dm_build_1437 *DmConnection) int {
dm_build_1438 := Dm_build_1432.Dm_build_1562(dm_build_1435, dm_build_1436, dm_build_1437)
dm_build_1434 += Dm_build_1432.Dm_build_1387(dm_build_1433, dm_build_1434, uint16(len(dm_build_1438)))
return 2 + Dm_build_1432.Dm_build_1402(dm_build_1433, dm_build_1434, dm_build_1438, 0, len(dm_build_1438))
}
func (Dm_build_1440 *dm_build_1345) Dm_build_1439(dm_build_1441 []byte, dm_build_1442 int) byte {
return dm_build_1441[dm_build_1442]
}
func (Dm_build_1444 *dm_build_1345) Dm_build_1443(dm_build_1445 []byte, dm_build_1446 int) int16 {
var dm_build_1447 int16
dm_build_1447 = int16(dm_build_1445[dm_build_1446] & 0xff)
dm_build_1446++
dm_build_1447 |= int16(dm_build_1445[dm_build_1446]&0xff) << 8
return dm_build_1447
}
func (Dm_build_1449 *dm_build_1345) Dm_build_1448(dm_build_1450 []byte, dm_build_1451 int) int32 {
var dm_build_1452 int32
dm_build_1452 = int32(dm_build_1450[dm_build_1451] & 0xff)
dm_build_1451++
dm_build_1452 |= int32(dm_build_1450[dm_build_1451]&0xff) << 8
dm_build_1451++
dm_build_1452 |= int32(dm_build_1450[dm_build_1451]&0xff) << 16
dm_build_1451++
dm_build_1452 |= int32(dm_build_1450[dm_build_1451]&0xff) << 24
return dm_build_1452
}
func (Dm_build_1454 *dm_build_1345) Dm_build_1453(dm_build_1455 []byte, dm_build_1456 int) int64 {
var dm_build_1457 int64
dm_build_1457 = int64(dm_build_1455[dm_build_1456] & 0xff)
dm_build_1456++
dm_build_1457 |= int64(dm_build_1455[dm_build_1456]&0xff) << 8
dm_build_1456++
dm_build_1457 |= int64(dm_build_1455[dm_build_1456]&0xff) << 16
dm_build_1456++
dm_build_1457 |= int64(dm_build_1455[dm_build_1456]&0xff) << 24
dm_build_1456++
dm_build_1457 |= int64(dm_build_1455[dm_build_1456]&0xff) << 32
dm_build_1456++
dm_build_1457 |= int64(dm_build_1455[dm_build_1456]&0xff) << 40
dm_build_1456++
dm_build_1457 |= int64(dm_build_1455[dm_build_1456]&0xff) << 48
dm_build_1456++
dm_build_1457 |= int64(dm_build_1455[dm_build_1456]&0xff) << 56
return dm_build_1457
}
func (Dm_build_1459 *dm_build_1345) Dm_build_1458(dm_build_1460 []byte, dm_build_1461 int) float32 {
return math.Float32frombits(Dm_build_1459.Dm_build_1475(dm_build_1460, dm_build_1461))
}
func (Dm_build_1463 *dm_build_1345) Dm_build_1462(dm_build_1464 []byte, dm_build_1465 int) float64 {
return math.Float64frombits(Dm_build_1463.Dm_build_1480(dm_build_1464, dm_build_1465))
}
func (Dm_build_1467 *dm_build_1345) Dm_build_1466(dm_build_1468 []byte, dm_build_1469 int) uint8 {
return uint8(dm_build_1468[dm_build_1469] & 0xff)
}
func (Dm_build_1471 *dm_build_1345) Dm_build_1470(dm_build_1472 []byte, dm_build_1473 int) uint16 {
var dm_build_1474 uint16
dm_build_1474 = uint16(dm_build_1472[dm_build_1473] & 0xff)
dm_build_1473++
dm_build_1474 |= uint16(dm_build_1472[dm_build_1473]&0xff) << 8
return dm_build_1474
}
func (Dm_build_1476 *dm_build_1345) Dm_build_1475(dm_build_1477 []byte, dm_build_1478 int) uint32 {
var dm_build_1479 uint32
dm_build_1479 = uint32(dm_build_1477[dm_build_1478] & 0xff)
dm_build_1478++
dm_build_1479 |= uint32(dm_build_1477[dm_build_1478]&0xff) << 8
dm_build_1478++
dm_build_1479 |= uint32(dm_build_1477[dm_build_1478]&0xff) << 16
dm_build_1478++
dm_build_1479 |= uint32(dm_build_1477[dm_build_1478]&0xff) << 24
return dm_build_1479
}
func (Dm_build_1481 *dm_build_1345) Dm_build_1480(dm_build_1482 []byte, dm_build_1483 int) uint64 {
var dm_build_1484 uint64
dm_build_1484 = uint64(dm_build_1482[dm_build_1483] & 0xff)
dm_build_1483++
dm_build_1484 |= uint64(dm_build_1482[dm_build_1483]&0xff) << 8
dm_build_1483++
dm_build_1484 |= uint64(dm_build_1482[dm_build_1483]&0xff) << 16
dm_build_1483++
dm_build_1484 |= uint64(dm_build_1482[dm_build_1483]&0xff) << 24
dm_build_1483++
dm_build_1484 |= uint64(dm_build_1482[dm_build_1483]&0xff) << 32
dm_build_1483++
dm_build_1484 |= uint64(dm_build_1482[dm_build_1483]&0xff) << 40
dm_build_1483++
dm_build_1484 |= uint64(dm_build_1482[dm_build_1483]&0xff) << 48
dm_build_1483++
dm_build_1484 |= uint64(dm_build_1482[dm_build_1483]&0xff) << 56
return dm_build_1484
}
func (Dm_build_1486 *dm_build_1345) Dm_build_1485(dm_build_1487 []byte, dm_build_1488 int) []byte {
dm_build_1489 := Dm_build_1486.Dm_build_1475(dm_build_1487, dm_build_1488)
dm_build_1490 := make([]byte, dm_build_1489)
copy(dm_build_1490[:int(dm_build_1489)], dm_build_1487[dm_build_1488+4:dm_build_1488+4+int(dm_build_1489)])
return dm_build_1490
}
func (Dm_build_1492 *dm_build_1345) Dm_build_1491(dm_build_1493 []byte, dm_build_1494 int) []byte {
dm_build_1495 := Dm_build_1492.Dm_build_1470(dm_build_1493, dm_build_1494)
dm_build_1496 := make([]byte, dm_build_1495)
copy(dm_build_1496[:int(dm_build_1495)], dm_build_1493[dm_build_1494+2:dm_build_1494+2+int(dm_build_1495)])
return dm_build_1496
}
func (Dm_build_1498 *dm_build_1345) Dm_build_1497(dm_build_1499 []byte, dm_build_1500 int, dm_build_1501 int) []byte {
dm_build_1502 := make([]byte, dm_build_1501)
copy(dm_build_1502[:dm_build_1501], dm_build_1499[dm_build_1500:dm_build_1500+dm_build_1501])
return dm_build_1502
}
func (Dm_build_1504 *dm_build_1345) Dm_build_1503(dm_build_1505 []byte, dm_build_1506 int, dm_build_1507 int, dm_build_1508 string, dm_build_1509 *DmConnection) string {
return Dm_build_1504.Dm_build_1598(dm_build_1505[dm_build_1506:dm_build_1506+dm_build_1507], dm_build_1508, dm_build_1509)
}
func (Dm_build_1511 *dm_build_1345) Dm_build_1510(dm_build_1512 []byte, dm_build_1513 int, dm_build_1514 string, dm_build_1515 *DmConnection) string {
dm_build_1516 := Dm_build_1511.Dm_build_1475(dm_build_1512, dm_build_1513)
dm_build_1513 += 4
return Dm_build_1511.Dm_build_1503(dm_build_1512, dm_build_1513, int(dm_build_1516), dm_build_1514, dm_build_1515)
}
func (Dm_build_1518 *dm_build_1345) Dm_build_1517(dm_build_1519 []byte, dm_build_1520 int, dm_build_1521 string, dm_build_1522 *DmConnection) string {
dm_build_1523 := Dm_build_1518.Dm_build_1470(dm_build_1519, dm_build_1520)
dm_build_1520 += 2
return Dm_build_1518.Dm_build_1503(dm_build_1519, dm_build_1520, int(dm_build_1523), dm_build_1521, dm_build_1522)
}
func (Dm_build_1525 *dm_build_1345) Dm_build_1524(dm_build_1526 byte) []byte {
return []byte{dm_build_1526}
}
func (Dm_build_1528 *dm_build_1345) Dm_build_1527(dm_build_1529 int8) []byte {
return []byte{byte(dm_build_1529)}
}
func (Dm_build_1531 *dm_build_1345) Dm_build_1530(dm_build_1532 int16) []byte {
return []byte{byte(dm_build_1532), byte(dm_build_1532 >> 8)}
}
func (Dm_build_1534 *dm_build_1345) Dm_build_1533(dm_build_1535 int32) []byte {
return []byte{byte(dm_build_1535), byte(dm_build_1535 >> 8), byte(dm_build_1535 >> 16), byte(dm_build_1535 >> 24)}
}
func (Dm_build_1537 *dm_build_1345) Dm_build_1536(dm_build_1538 int64) []byte {
return []byte{byte(dm_build_1538), byte(dm_build_1538 >> 8), byte(dm_build_1538 >> 16), byte(dm_build_1538 >> 24), byte(dm_build_1538 >> 32),
byte(dm_build_1538 >> 40), byte(dm_build_1538 >> 48), byte(dm_build_1538 >> 56)}
}
func (Dm_build_1540 *dm_build_1345) Dm_build_1539(dm_build_1541 float32) []byte {
return Dm_build_1540.Dm_build_1551(math.Float32bits(dm_build_1541))
}
func (Dm_build_1543 *dm_build_1345) Dm_build_1542(dm_build_1544 float64) []byte {
return Dm_build_1543.Dm_build_1554(math.Float64bits(dm_build_1544))
}
func (Dm_build_1546 *dm_build_1345) Dm_build_1545(dm_build_1547 uint8) []byte {
return []byte{byte(dm_build_1547)}
}
func (Dm_build_1549 *dm_build_1345) Dm_build_1548(dm_build_1550 uint16) []byte {
return []byte{byte(dm_build_1550), byte(dm_build_1550 >> 8)}
}
func (Dm_build_1552 *dm_build_1345) Dm_build_1551(dm_build_1553 uint32) []byte {
return []byte{byte(dm_build_1553), byte(dm_build_1553 >> 8), byte(dm_build_1553 >> 16), byte(dm_build_1553 >> 24)}
}
func (Dm_build_1555 *dm_build_1345) Dm_build_1554(dm_build_1556 uint64) []byte {
return []byte{byte(dm_build_1556), byte(dm_build_1556 >> 8), byte(dm_build_1556 >> 16), byte(dm_build_1556 >> 24), byte(dm_build_1556 >> 32), byte(dm_build_1556 >> 40), byte(dm_build_1556 >> 48), byte(dm_build_1556 >> 56)}
}
func (Dm_build_1558 *dm_build_1345) Dm_build_1557(dm_build_1559 []byte, dm_build_1560 string, dm_build_1561 *DmConnection) []byte {
if dm_build_1560 == "UTF-8" {
return dm_build_1559
}
if dm_build_1561 == nil {
if e := dm_build_1603(dm_build_1560); e != nil {
tmp, err := ioutil.ReadAll(
transform.NewReader(bytes.NewReader(dm_build_1559), e.NewEncoder()),
)
if err != nil {
panic("UTF8 To Charset error!")
}
return tmp
}
panic("Unsupported Charset!")
}
if dm_build_1561.encodeBuffer == nil {
dm_build_1561.encodeBuffer = bytes.NewBuffer(nil)
dm_build_1561.encode = dm_build_1603(dm_build_1561.getServerEncoding())
dm_build_1561.transformReaderDst = make([]byte, 4096)
dm_build_1561.transformReaderSrc = make([]byte, 4096)
}
if e := dm_build_1561.encode; e != nil {
dm_build_1561.encodeBuffer.Reset()
n, err := dm_build_1561.encodeBuffer.ReadFrom(
Dm_build_1617(bytes.NewReader(dm_build_1559), e.NewEncoder(), dm_build_1561.transformReaderDst, dm_build_1561.transformReaderSrc),
)
if err != nil {
panic("UTF8 To Charset error!")
}
var tmp = make([]byte, n)
if _, err = dm_build_1561.encodeBuffer.Read(tmp); err != nil {
panic("UTF8 To Charset error!")
}
return tmp
}
panic("Unsupported Charset!")
}
func (Dm_build_1563 *dm_build_1345) Dm_build_1562(dm_build_1564 string, dm_build_1565 string, dm_build_1566 *DmConnection) []byte {
return Dm_build_1563.Dm_build_1557([]byte(dm_build_1564), dm_build_1565, dm_build_1566)
}
func (Dm_build_1568 *dm_build_1345) Dm_build_1567(dm_build_1569 []byte) byte {
return Dm_build_1568.Dm_build_1439(dm_build_1569, 0)
}
func (Dm_build_1571 *dm_build_1345) Dm_build_1570(dm_build_1572 []byte) int16 {
return Dm_build_1571.Dm_build_1443(dm_build_1572, 0)
}
func (Dm_build_1574 *dm_build_1345) Dm_build_1573(dm_build_1575 []byte) int32 {
return Dm_build_1574.Dm_build_1448(dm_build_1575, 0)
}
func (Dm_build_1577 *dm_build_1345) Dm_build_1576(dm_build_1578 []byte) int64 {
return Dm_build_1577.Dm_build_1453(dm_build_1578, 0)
}
func (Dm_build_1580 *dm_build_1345) Dm_build_1579(dm_build_1581 []byte) float32 {
return Dm_build_1580.Dm_build_1458(dm_build_1581, 0)
}
func (Dm_build_1583 *dm_build_1345) Dm_build_1582(dm_build_1584 []byte) float64 {
return Dm_build_1583.Dm_build_1462(dm_build_1584, 0)
}
func (Dm_build_1586 *dm_build_1345) Dm_build_1585(dm_build_1587 []byte) uint8 {
return Dm_build_1586.Dm_build_1466(dm_build_1587, 0)
}
func (Dm_build_1589 *dm_build_1345) Dm_build_1588(dm_build_1590 []byte) uint16 {
return Dm_build_1589.Dm_build_1470(dm_build_1590, 0)
}
func (Dm_build_1592 *dm_build_1345) Dm_build_1591(dm_build_1593 []byte) uint32 {
return Dm_build_1592.Dm_build_1475(dm_build_1593, 0)
}
func (Dm_build_1595 *dm_build_1345) Dm_build_1594(dm_build_1596 []byte, dm_build_1597 string) []byte {
if dm_build_1597 == "UTF-8" {
return dm_build_1596
}
if e := dm_build_1603(dm_build_1597); e != nil {
tmp, err := ioutil.ReadAll(
transform.NewReader(bytes.NewReader(dm_build_1596), e.NewDecoder()),
)
if err != nil {
panic("Charset To UTF8 error!")
}
return tmp
}
panic("Unsupported Charset!")
}
func (Dm_build_1599 *dm_build_1345) Dm_build_1598(dm_build_1600 []byte, dm_build_1601 string, dm_build_1602 *DmConnection) string {
return string(Dm_build_1599.Dm_build_1594(dm_build_1600, dm_build_1601))
}
func dm_build_1603(dm_build_1604 string) encoding.Encoding {
if e, err := ianaindex.MIB.Encoding(dm_build_1604); err == nil && e != nil {
return e
}
return nil
}
type Dm_build_1605 struct {
dm_build_1606 io.Reader
dm_build_1607 transform.Transformer
dm_build_1608 error
dm_build_1609 []byte
dm_build_1610, dm_build_1611 int
dm_build_1612 []byte
dm_build_1613, dm_build_1614 int
dm_build_1615 bool
}
const dm_build_1616 = 4096
func Dm_build_1617(dm_build_1618 io.Reader, dm_build_1619 transform.Transformer, dm_build_1620 []byte, dm_build_1621 []byte) *Dm_build_1605 {
dm_build_1619.Reset()
return &Dm_build_1605{
dm_build_1606: dm_build_1618,
dm_build_1607: dm_build_1619,
dm_build_1609: dm_build_1620,
dm_build_1612: dm_build_1621,
}
}
func (dm_build_1623 *Dm_build_1605) Read(dm_build_1624 []byte) (int, error) {
dm_build_1625, dm_build_1626 := 0, error(nil)
for {
if dm_build_1623.dm_build_1610 != dm_build_1623.dm_build_1611 {
dm_build_1625 = copy(dm_build_1624, dm_build_1623.dm_build_1609[dm_build_1623.dm_build_1610:dm_build_1623.dm_build_1611])
dm_build_1623.dm_build_1610 += dm_build_1625
if dm_build_1623.dm_build_1610 == dm_build_1623.dm_build_1611 && dm_build_1623.dm_build_1615 {
return dm_build_1625, dm_build_1623.dm_build_1608
}
return dm_build_1625, nil
} else if dm_build_1623.dm_build_1615 {
return 0, dm_build_1623.dm_build_1608
}
if dm_build_1623.dm_build_1613 != dm_build_1623.dm_build_1614 || dm_build_1623.dm_build_1608 != nil {
dm_build_1623.dm_build_1610 = 0
dm_build_1623.dm_build_1611, dm_build_1625, dm_build_1626 = dm_build_1623.dm_build_1607.Transform(dm_build_1623.dm_build_1609, dm_build_1623.dm_build_1612[dm_build_1623.dm_build_1613:dm_build_1623.dm_build_1614], dm_build_1623.dm_build_1608 == io.EOF)
dm_build_1623.dm_build_1613 += dm_build_1625
switch {
case dm_build_1626 == nil:
if dm_build_1623.dm_build_1613 != dm_build_1623.dm_build_1614 {
dm_build_1623.dm_build_1608 = nil
}
dm_build_1623.dm_build_1615 = dm_build_1623.dm_build_1608 != nil
continue
case dm_build_1626 == transform.ErrShortDst && (dm_build_1623.dm_build_1611 != 0 || dm_build_1625 != 0):
continue
case dm_build_1626 == transform.ErrShortSrc && dm_build_1623.dm_build_1614-dm_build_1623.dm_build_1613 != len(dm_build_1623.dm_build_1612) && dm_build_1623.dm_build_1608 == nil:
default:
dm_build_1623.dm_build_1615 = true
if dm_build_1623.dm_build_1608 == nil || dm_build_1623.dm_build_1608 == io.EOF {
dm_build_1623.dm_build_1608 = dm_build_1626
}
continue
}
}
if dm_build_1623.dm_build_1613 != 0 {
dm_build_1623.dm_build_1613, dm_build_1623.dm_build_1614 = 0, copy(dm_build_1623.dm_build_1612, dm_build_1623.dm_build_1612[dm_build_1623.dm_build_1613:dm_build_1623.dm_build_1614])
}
dm_build_1625, dm_build_1623.dm_build_1608 = dm_build_1623.dm_build_1606.Read(dm_build_1623.dm_build_1612[dm_build_1623.dm_build_1614:])
dm_build_1623.dm_build_1614 += dm_build_1625
}
}
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"bytes"
"compress/zlib"
"github.com/golang/snappy"
)
func Compress(srcBuffer *Dm_build_78, offset int, length int, compressID int) ([]byte, error) {
if compressID == Dm_build_786 {
return snappy.Encode(nil, srcBuffer.Dm_build_372(offset, length)), nil
}
return GzlibCompress(srcBuffer, offset, length)
}
func UnCompress(srcBytes []byte, compressID int) ([]byte, error) {
if compressID == Dm_build_786 {
return snappy.Decode(nil, srcBytes)
}
return GzlibUncompress(srcBytes)
}
func GzlibCompress(srcBuffer *Dm_build_78, offset int, length int) ([]byte, error) {
var ret bytes.Buffer
var w = zlib.NewWriter(&ret)
w.Write(srcBuffer.Dm_build_372(offset, length))
w.Close()
return ret.Bytes(), nil
}
func GzlibUncompress(srcBytes []byte) ([]byte, error) {
var bytesBuf = new(bytes.Buffer)
r, err := zlib.NewReader(bytes.NewReader(srcBytes))
if err != nil {
return nil, err
}
defer r.Close()
_, err = bytesBuf.ReadFrom(r)
if err != nil {
return nil, err
}
return bytesBuf.Bytes(), nil
}
+2158
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
module gitee.com/chunanyong/dm
go 1.13
require (
github.com/golang/snappy v0.0.1
golang.org/x/text v0.3.2
)
+5
View File
@@ -0,0 +1,5 @@
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+923
View File
@@ -0,0 +1,923 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"math"
"strconv"
"strings"
"time"
"unicode"
)
func encodeByString(x string, column column, conn DmConnection) ([]byte, error) {
dt := make([]int, DT_LEN)
if _, err := toDTFromString(x, dt); err != nil {
return nil, err
}
return encode(dt, column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
}
func encodeByTime(x time.Time, column column, conn DmConnection) ([]byte, error) {
dt := toDTFromTime(x)
return encode(dt, column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
}
func toTimeFromString(str string, ltz int) time.Time {
dt := make([]int, DT_LEN)
toDTFromString(str, dt)
return toTimeFromDT(dt, ltz)
}
func toTimeFromDT(dt []int, ltz int) time.Time {
var year, month, day, hour, minute, second, nsec, tz int
year = dt[OFFSET_YEAR]
if dt[OFFSET_MONTH] > 0 {
month = dt[OFFSET_MONTH]
} else {
month = 1
}
if dt[OFFSET_DAY] > 0 {
day = dt[OFFSET_DAY]
} else {
day = 1
}
hour = dt[OFFSET_HOUR]
minute = dt[OFFSET_MINUTE]
second = dt[OFFSET_SECOND]
nsec = dt[OFFSET_NANOSECOND]
if dt[OFFSET_TIMEZONE] == INVALID_VALUE {
tz = ltz * 60
} else {
tz = dt[OFFSET_TIMEZONE] * 60
}
return time.Date(year, time.Month(month), day, hour, minute, second, nsec, time.FixedZone("", tz))
}
func decode(value []byte, isBdta bool, column column, ltz int, dtz int) []int {
var dt []int
if isBdta {
dt = dmdtDecodeBdta(value)
} else {
dt = dmdtDecodeFast(value)
}
if column.mask == MASK_LOCAL_DATETIME {
transformTZ(dt, dtz, ltz)
}
return dt
}
func dmdtDecodeFast(value []byte) []int {
dt := make([]int, DT_LEN)
dt[OFFSET_TIMEZONE] = INVALID_VALUE
dtype := 0
if len(value) == DATE_PREC {
dtype = DATE
} else if len(value) == TIME_PREC {
dtype = TIME
} else if len(value) == TIME_TZ_PREC {
dtype = TIME_TZ
} else if len(value) == DATETIME_PREC {
dtype = DATETIME
} else if len(value) == DATETIME2_PREC {
dtype = DATETIME2
} else if len(value) == DATETIME_TZ_PREC {
dtype = DATETIME_TZ
} else if len(value) == DATETIME2_TZ_PREC {
dtype = DATETIME2_TZ
}
if dtype == DATE {
dt[OFFSET_YEAR] = int(Dm_build_1346.Dm_build_1443(value, 0)) & 0x7FFF
if dt[OFFSET_YEAR] > 9999 {
dt[OFFSET_YEAR] = int(int16(dt[OFFSET_YEAR] | 0x8000))
}
dt[OFFSET_MONTH] = ((int(value[1]) >> 7) & 0x1) + ((int(value[2]) & 0x07) << 1)
dt[OFFSET_DAY] = ((int(value[2]) & 0xF8) >> 3) & 0x1f
} else if dtype == TIME {
dt[OFFSET_HOUR] = int(value[0]) & 0x1F
dt[OFFSET_MINUTE] = ((int(value[0]) >> 5) & 0x07) + ((int(value[1]) & 0x07) << 3)
dt[OFFSET_SECOND] = ((int(value[1]) >> 3) & 0x1f) + ((int(value[2]) & 0x01) << 5)
dt[OFFSET_NANOSECOND] = ((int(value[2]) >> 1) & 0x7f) + ((int(value[3]) & 0x00ff) << 7) + ((int(value[4]) & 0x1F) << 15)
dt[OFFSET_NANOSECOND] *= 1000
} else if dtype == TIME_TZ {
dt[OFFSET_HOUR] = int(value[0]) & 0x1F
dt[OFFSET_MINUTE] = ((int(value[0]) >> 5) & 0x07) + ((int(value[1]) & 0x07) << 3)
dt[OFFSET_SECOND] = ((int(value[1]) >> 3) & 0x1f) + ((int(value[2]) & 0x01) << 5)
dt[OFFSET_NANOSECOND] = ((int(value[2]) >> 1) & 0x7f) + ((int(value[3]) & 0x00ff) << 7) + ((int(value[4]) & 0x1F) << 15)
dt[OFFSET_NANOSECOND] *= 1000
dt[OFFSET_TIMEZONE] = int(Dm_build_1346.Dm_build_1443(value, 5))
} else if dtype == DATETIME {
dt[OFFSET_YEAR] = int(Dm_build_1346.Dm_build_1443(value, 0)) & 0x7FFF
if dt[OFFSET_YEAR] > 9999 {
dt[OFFSET_YEAR] = int(int16(dt[OFFSET_YEAR] | 0x8000))
}
dt[OFFSET_MONTH] = ((int(value[1]) >> 7) & 0x1) + ((int(value[2]) & 0x07) << 1)
dt[OFFSET_DAY] = ((int(value[2]) & 0xF8) >> 3) & 0x1f
dt[OFFSET_HOUR] = (int(value[3]) & 0x1F)
dt[OFFSET_MINUTE] = ((int(value[3]) >> 5) & 0x07) + ((int(value[4]) & 0x07) << 3)
dt[OFFSET_SECOND] = ((int(value[4]) >> 3) & 0x1f) + ((int(value[5]) & 0x01) << 5)
dt[OFFSET_NANOSECOND] = ((int(value[5]) >> 1) & 0x7f) + ((int(value[6]) & 0x00ff) << 7) + ((int(value[7]) & 0x1F) << 15)
dt[OFFSET_NANOSECOND] *= 1000
} else if dtype == DATETIME_TZ {
dt[OFFSET_YEAR] = int(Dm_build_1346.Dm_build_1443(value, 0)) & 0x7FFF
if dt[OFFSET_YEAR] > 9999 {
dt[OFFSET_YEAR] = int(int16(dt[OFFSET_YEAR] | 0x8000))
}
dt[OFFSET_MONTH] = ((int(value[1]) >> 7) & 0x1) + ((int(value[2]) & 0x07) << 1)
dt[OFFSET_DAY] = ((int(value[2]) & 0xF8) >> 3) & 0x1f
dt[OFFSET_HOUR] = (int(value[3]) & 0x1F)
dt[OFFSET_MINUTE] = ((int(value[3]) >> 5) & 0x07) + ((int(value[4]) & 0x07) << 3)
dt[OFFSET_SECOND] = ((int(value[4]) >> 3) & 0x1f) + ((int(value[5]) & 0x01) << 5)
dt[OFFSET_NANOSECOND] = ((int(value[5]) >> 1) & 0x7f) + ((int(value[6]) & 0x00ff) << 7) + ((int(value[7]) & 0x1F) << 15)
dt[OFFSET_NANOSECOND] *= 1000
dt[OFFSET_TIMEZONE] = int(Dm_build_1346.Dm_build_1443(value, len(value)-2))
} else if dtype == DATETIME2 {
dt[OFFSET_YEAR] = int(Dm_build_1346.Dm_build_1443(value, 0)) & 0x7FFF
if dt[OFFSET_YEAR] > 9999 {
dt[OFFSET_YEAR] = int(int16(dt[OFFSET_YEAR] | 0x8000))
}
dt[OFFSET_MONTH] = ((int(value[1]) >> 7) & 0x1) + ((int(value[2]) & 0x07) << 1)
dt[OFFSET_DAY] = ((int(value[2]) & 0xF8) >> 3) & 0x1f
dt[OFFSET_HOUR] = (int(value[3]) & 0x1F)
dt[OFFSET_MINUTE] = ((int(value[3]) >> 5) & 0x07) + ((int(value[4]) & 0x07) << 3)
dt[OFFSET_SECOND] = ((int(value[4]) >> 3) & 0x1f) + ((int(value[5]) & 0x01) << 5)
dt[OFFSET_NANOSECOND] = ((int(value[5]) >> 1) & 0x7f) + ((int(value[6]) & 0x00ff) << 7) + ((int(value[7]) & 0x00ff) << 15) + ((int(value[8]) & 0x7F) << 23)
} else if dtype == DATETIME2_TZ {
dt[OFFSET_YEAR] = int(Dm_build_1346.Dm_build_1443(value, 0)) & 0x7FFF
if dt[OFFSET_YEAR] > 9999 {
dt[OFFSET_YEAR] = int(int16(dt[OFFSET_YEAR] | 0x8000))
}
dt[OFFSET_MONTH] = ((int(value[1]) >> 7) & 0x1) + ((int(value[2]) & 0x07) << 1)
dt[OFFSET_DAY] = ((int(value[2]) & 0xF8) >> 3) & 0x1f
dt[OFFSET_HOUR] = (int(value[3]) & 0x1F)
dt[OFFSET_MINUTE] = ((int(value[3]) >> 5) & 0x07) + ((int(value[4]) & 0x07) << 3)
dt[OFFSET_SECOND] = ((int(value[4]) >> 3) & 0x1f) + ((int(value[5]) & 0x01) << 5)
dt[OFFSET_NANOSECOND] = ((int(value[5]) >> 1) & 0x7f) + ((int(value[6]) & 0x00ff) << 7) + ((int(value[7]) & 0x00ff) << 15) + ((int(value[8]) & 0x7F) << 23)
dt[OFFSET_TIMEZONE] = int(Dm_build_1346.Dm_build_1443(value, len(value)-2))
}
return dt
}
func dmdtDecodeBdta(value []byte) []int {
dt := make([]int, DT_LEN)
dt[OFFSET_YEAR] = int(Dm_build_1346.Dm_build_1443(value, 0))
dt[OFFSET_MONTH] = int(value[2] & 0xFF)
dt[OFFSET_DAY] = int(value[3] & 0xFF)
dt[OFFSET_HOUR] = int(value[4] & 0xFF)
dt[OFFSET_MINUTE] = int(value[5] & 0xFF)
dt[OFFSET_SECOND] = int(value[6] & 0xFF)
dt[OFFSET_NANOSECOND] = int((value[7] & 0xFF) + (value[8] << 8) + (value[9] << 16))
dt[OFFSET_TIMEZONE] = int(Dm_build_1346.Dm_build_1443(value, 10))
if len(value) > 12 {
dt[OFFSET_NANOSECOND] += int(value[12] << 24)
}
return dt
}
func dtToStringByOracleFormat(dt []int, oracleFormatPattern string, scale int32, language int) string {
return format(dt, oracleFormatPattern, scale, language)
}
func dtToString(dt []int, dtype int, scale int) string {
switch dtype {
case DATE:
return formatYear(dt[OFFSET_YEAR]) + "-" + format2(dt[OFFSET_MONTH]) + "-" + format2(dt[OFFSET_DAY])
case TIME:
if scale > 0 {
return format2(dt[OFFSET_HOUR]) + ":" + format2(dt[OFFSET_MINUTE]) + ":" + format2(dt[OFFSET_SECOND]) + "." + formatMilliSecond(dt[OFFSET_NANOSECOND], scale)
} else {
return format2(dt[OFFSET_HOUR]) + ":" + format2(dt[OFFSET_MINUTE]) + ":" + format2(dt[OFFSET_SECOND])
}
case TIME_TZ:
if scale > 0 {
return format2(dt[OFFSET_HOUR]) + ":" + format2(dt[OFFSET_MINUTE]) + ":" + format2(dt[OFFSET_SECOND]) + "." + formatMilliSecond(dt[OFFSET_NANOSECOND], scale) + " " + formatTZ(dt[OFFSET_TIMEZONE])
} else {
return format2(dt[OFFSET_HOUR]) + ":" + format2(dt[OFFSET_MINUTE]) + ":" + format2(dt[OFFSET_SECOND]) + " " + formatTZ(dt[OFFSET_TIMEZONE])
}
case DATETIME, DATETIME2:
if scale > 0 {
return formatYear(dt[OFFSET_YEAR]) + "-" + format2(dt[OFFSET_MONTH]) + "-" + format2(dt[OFFSET_DAY]) + " " + format2(dt[OFFSET_HOUR]) + ":" + format2(dt[OFFSET_MINUTE]) + ":" + format2(dt[OFFSET_SECOND]) + "." + formatMilliSecond(dt[OFFSET_NANOSECOND], scale)
} else {
return formatYear(dt[OFFSET_YEAR]) + "-" + format2(dt[OFFSET_MONTH]) + "-" + format2(dt[OFFSET_DAY]) + " " + format2(dt[OFFSET_HOUR]) + ":" + format2(dt[OFFSET_MINUTE]) + ":" + format2(dt[OFFSET_SECOND])
}
case DATETIME_TZ, DATETIME2_TZ:
if scale > 0 {
return formatYear(dt[OFFSET_YEAR]) + "-" + format2(dt[OFFSET_MONTH]) + "-" + format2(dt[OFFSET_DAY]) + " " + format2(dt[OFFSET_HOUR]) + ":" + format2(dt[OFFSET_MINUTE]) + ":" + format2(dt[OFFSET_SECOND]) + "." + formatMilliSecond(dt[OFFSET_NANOSECOND], scale) + " " + formatTZ(dt[OFFSET_TIMEZONE])
} else {
return formatYear(dt[OFFSET_YEAR]) + "-" + format2(dt[OFFSET_MONTH]) + "-" + format2(dt[OFFSET_DAY]) + " " + format2(dt[OFFSET_HOUR]) + ":" + format2(dt[OFFSET_MINUTE]) + ":" + format2(dt[OFFSET_SECOND]) + " " + formatTZ(dt[OFFSET_TIMEZONE])
}
}
return ""
}
func formatYear(value int) string {
if value >= 0 {
if value < 10 {
return "000" + strconv.FormatInt(int64(value), 10)
} else if value < 100 {
return "00" + strconv.FormatInt(int64(value), 10)
} else if value < 1000 {
return "0" + strconv.FormatInt(int64(value), 10)
} else {
return strconv.FormatInt(int64(value), 10)
}
} else {
if value > -10 {
return "-000" + strconv.FormatInt(int64(-value), 10)
} else if value > -100 {
return "-00" + strconv.FormatInt(int64(-value), 10)
} else if value > -1000 {
return "-0" + strconv.FormatInt(int64(-value), 10)
} else {
return strconv.FormatInt(int64(value), 10)
}
}
}
func format2(value int) string {
if value < 10 {
return "0" + strconv.FormatInt(int64(value), 10)
} else {
return strconv.FormatInt(int64(value), 10)
}
}
func formatMilliSecond(ms int, prec int) string {
var ret string
if ms < 10 {
ret = "00000000" + strconv.FormatInt(int64(ms), 10)
} else if ms < 100 {
ret = "0000000" + strconv.FormatInt(int64(ms), 10)
} else if ms < 1000 {
ret = "000000" + strconv.FormatInt(int64(ms), 10)
} else if ms < 10000 {
ret = "00000" + strconv.FormatInt(int64(ms), 10)
} else if ms < 100000 {
ret = "0000" + strconv.FormatInt(int64(ms), 10)
} else if ms < 1000000 {
ret = "000" + strconv.FormatInt(int64(ms), 10)
} else if ms < 10000000 {
ret = "00" + strconv.FormatInt(int64(ms), 10)
} else if ms < 100000000 {
ret = "0" + strconv.FormatInt(int64(ms), 10)
} else {
ret = strconv.FormatInt(int64(ms), 10)
}
if prec < NANOSECOND_DIGITS {
ret = ret[:prec]
}
return ret
}
func formatTZ(tz int) string {
tz_hour := int(math.Abs(float64(tz / 60)))
tz_min := int(math.Abs(float64(tz % 60)))
if tz >= 0 {
return "+" + format2(tz_hour) + ":" + format2(tz_min)
} else {
return "-" + format2(tz_hour) + ":" + format2(tz_min)
}
}
func toDTFromTime(x time.Time) []int {
hour, min, sec := x.Clock()
ts := make([]int, DT_LEN)
ts[OFFSET_YEAR] = x.Year()
ts[OFFSET_MONTH] = int(x.Month())
ts[OFFSET_DAY] = x.Day()
ts[OFFSET_HOUR] = hour
ts[OFFSET_MINUTE] = min
ts[OFFSET_SECOND] = sec
ts[OFFSET_NANOSECOND] = (int)(x.Nanosecond())
_, tz := x.Zone()
ts[OFFSET_TIMEZONE] = tz / 60
return ts
}
func toDTFromUnix(sec int64, nsec int64) []int {
return toDTFromTime(time.Unix(sec, nsec))
}
func toDTFromString(s string, dt []int) (dtype int, err error) {
defer func() {
if p := recover(); p != nil {
err = ECGO_INVALID_DATETIME_FORMAT.throw()
}
}()
date_s := ""
time_s := ""
nanos_s := ""
tz_s := ""
year := 0
month := 0
day := 0
hour := 0
minute := 0
second := 0
a_nanos := 0
firstDash := -1
secondDash := -1
firstColon := -1
secondColon := -1
period := -1
sign := 0
ownTz := INVALID_VALUE
dtype = -1
zeros := "000000000"
if s != "" && strings.TrimSpace(s) == "" {
return 0, ECGO_INVALID_DATETIME_FORMAT.throw()
}
s = strings.TrimSpace(s)
if strings.Index(s, "-") == 0 {
s = strings.TrimSpace(s[1:])
sign = 1
}
comps := strings.Split(s, " ")
switch len(comps) {
case 3:
date_s = comps[0]
time_s = comps[1]
tz_s = comps[2]
dtype = DATETIME_TZ
case 2:
if strings.Index(comps[0], ":") > 0 {
time_s = comps[0]
tz_s = comps[1]
dtype = TIME_TZ
} else {
date_s = comps[0]
time_s = comps[1]
dtype = DATETIME
}
case 1:
if strings.Index(comps[0], ":") > 0 {
time_s = comps[0]
dtype = TIME
} else {
date_s = comps[0]
dtype = DATE
}
default:
return 0, ECGO_INVALID_DATETIME_FORMAT.throw()
}
if date_s != "" {
firstDash = strings.Index(date_s, "-")
secondDash = strings.Index(date_s[firstDash+1:], "-")
if firstDash < 0 || secondDash < 0 {
firstDash = strings.Index(s, ".")
secondDash = strings.Index(date_s[firstDash+1:], ".")
}
if firstDash < 0 || secondDash < 0 {
firstDash = strings.Index(s, "/")
secondDash = strings.Index(date_s[firstDash+1:], "/")
}
if secondDash > 0 {
secondDash += firstDash + 1
}
if (firstDash > 0) && (secondDash > 0) && (secondDash < len(date_s)-1) {
if sign == 1 {
i, err := strconv.ParseInt(date_s[:firstDash], 10, 32)
if err != nil {
return 0, ECGO_INVALID_DATETIME_FORMAT.addDetailln(err.Error()).throw()
}
year = 0 - int(i) - 1900
} else {
i, err := strconv.ParseInt(date_s[:firstDash], 10, 32)
if err != nil {
return 0, ECGO_INVALID_DATETIME_FORMAT.addDetailln(err.Error()).throw()
}
year = int(i) - 1900
}
i, err := strconv.ParseInt(date_s[firstDash+1:secondDash], 10, 32)
if err != nil {
return 0, ECGO_INVALID_DATETIME_FORMAT.addDetailln(err.Error()).throw()
}
month = int(i) - 1
i, err = strconv.ParseInt(date_s[secondDash+1:], 10, 32)
if err != nil {
return 0, ECGO_INVALID_DATETIME_FORMAT.addDetailln(err.Error()).throw()
}
day = int(i)
if !checkDate(year+1900, month+1, day) {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
} else {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
}
if time_s != "" {
firstColon = strings.Index(time_s, ":")
secondColon = strings.Index(time_s[firstColon+1:], ":")
if secondColon > 0 {
secondColon += firstColon + 1
}
period = strings.Index(time_s[secondColon+1:], ".")
if period > 0 {
period += secondColon + 1
}
if (firstColon > 0) && (secondColon > 0) && (secondColon < len(time_s)-1) {
i, err := strconv.ParseInt(time_s[:firstColon], 10, 32)
if err != nil {
return 0, ECGO_INVALID_DATETIME_FORMAT.addDetailln(err.Error()).throw()
}
hour = int(i)
i, err = strconv.ParseInt(time_s[firstColon+1:secondColon], 10, 32)
if err != nil {
return 0, ECGO_INVALID_DATETIME_FORMAT.addDetailln(err.Error()).throw()
}
minute = int(i)
if period > 0 && period < len(time_s)-1 {
i, err = strconv.ParseInt(time_s[secondColon+1:period], 10, 32)
if err != nil {
return 0, ECGO_INVALID_DATETIME_FORMAT.addDetailln(err.Error()).throw()
}
second = int(i)
nanos_s = time_s[period+1:]
if len(nanos_s) > NANOSECOND_DIGITS {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
if !unicode.IsDigit(rune(nanos_s[0])) {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
nanos_s = nanos_s + zeros[0:NANOSECOND_DIGITS-len(nanos_s)]
i, err = strconv.ParseInt(nanos_s, 10, 32)
if err != nil {
return 0, ECGO_INVALID_DATETIME_FORMAT.addDetailln(err.Error()).throw()
}
a_nanos = int(i)
} else if period > 0 {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
} else {
i, err = strconv.ParseInt(time_s[secondColon+1:], 10, 32)
if err != nil {
return 0, ECGO_INVALID_DATETIME_FORMAT.addDetailln(err.Error()).throw()
}
second = int(i)
}
if hour >= 24 || hour < 0 || minute >= 60 || minute < 0 || second >= 60 || second < 0 {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
} else {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
}
if tz_s != "" {
neg := false
if strings.Index(tz_s, "-") == 0 {
neg = true
}
if strings.Index(tz_s, "-") == 0 || strings.Index(tz_s, "+") == 0 {
tz_s = strings.TrimSpace(tz_s[1:])
}
hm := strings.Split(tz_s, ":")
var tzh, tzm int16 = 0, 0
switch len(hm) {
case 2:
s, err := strconv.ParseInt(strings.TrimSpace(hm[0]), 10, 16)
if err != nil {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
tzh = int16(s)
s, err = strconv.ParseInt(strings.TrimSpace(hm[1]), 10, 16)
if err != nil {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
tzm = int16(s)
case 1:
s, err := strconv.ParseInt(strings.TrimSpace(hm[0]), 10, 16)
if err != nil {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
tzh = int16(s)
default:
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
ownTz = int(tzh*60 + tzm)
if ownTz < 0 {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
if neg {
ownTz *= -1
}
if ownTz <= -13*60 || ownTz > 14*60 {
return -1, ECGO_INVALID_DATETIME_FORMAT.throw()
}
}
dt[OFFSET_YEAR] = year + 1900
dt[OFFSET_MONTH] = month + 1
if day == 0 {
dt[OFFSET_DAY] = 1
} else {
dt[OFFSET_DAY] = day
}
dt[OFFSET_HOUR] = hour
dt[OFFSET_MINUTE] = minute
dt[OFFSET_SECOND] = second
dt[OFFSET_NANOSECOND] = a_nanos
dt[OFFSET_TIMEZONE] = int(ownTz)
return dtype, nil
}
func transformTZ(dt []int, defaultSrcTz int, destTz int) {
srcTz := defaultSrcTz
if srcTz != INVALID_VALUE && destTz != INVALID_VALUE && destTz != srcTz {
dt = addMinute(dt, destTz-srcTz)
dt[OFFSET_TIMEZONE] = destTz
}
}
func encode(dt []int, column column, lTz int, dTz int) ([]byte, error) {
if dt[OFFSET_TIMEZONE] != INVALID_VALUE {
transformTZ(dt, dt[OFFSET_TIMEZONE], lTz)
}
if column.mask == MASK_LOCAL_DATETIME {
transformTZ(dt, dt[OFFSET_TIMEZONE], dTz)
}
if dt[OFFSET_YEAR] < -4712 || dt[OFFSET_YEAR] > 9999 {
return nil, ECGO_DATETIME_OVERFLOW.throw()
}
year := dt[OFFSET_YEAR]
month := dt[OFFSET_MONTH]
day := dt[OFFSET_DAY]
hour := dt[OFFSET_HOUR]
min := dt[OFFSET_MINUTE]
sec := dt[OFFSET_SECOND]
msec := dt[OFFSET_NANOSECOND]
var tz int
if dt[OFFSET_TIMEZONE] == INVALID_VALUE {
tz = dTz
} else {
tz = dt[OFFSET_TIMEZONE]
}
var ret []byte
if column.colType == DATE {
ret = make([]byte, 3)
ret[0] = (byte)(year & 0xFF)
if year >= 0 {
ret[1] = (byte)((year >> 8) | ((month & 0x01) << 7))
} else {
ret[1] = (byte)((year >> 8) & (((month & 0x01) << 7) | 0x7f))
}
ret[2] = (byte)(((month & 0x0E) >> 1) | (day << 3))
} else if column.colType == DATETIME {
msec /= 1000
ret = make([]byte, 8)
ret[0] = (byte)(year & 0xFF)
if year >= 0 {
ret[1] = (byte)((year >> 8) | ((month & 0x01) << 7))
} else {
ret[1] = (byte)((year >> 8) & (((month & 0x01) << 7) | 0x7f))
}
ret[2] = (byte)(((month & 0x0E) >> 1) | (day << 3))
ret[3] = (byte)(hour | ((min & 0x07) << 5))
ret[4] = (byte)(((min & 0x38) >> 3) | ((sec & 0x1F) << 3))
ret[5] = (byte)(((sec & 0x20) >> 5) | ((msec & 0x7F) << 1))
ret[6] = (byte)((msec >> 7) & 0xFF)
ret[7] = (byte)((msec >> 15) & 0xFF)
} else if column.colType == DATETIME2 {
ret = make([]byte, 9)
ret[0] = (byte)(year & 0xFF)
if year >= 0 {
ret[1] = (byte)((year >> 8) | ((month & 0x01) << 7))
} else {
ret[1] = (byte)((year >> 8) & (((month & 0x01) << 7) | 0x7f))
}
ret[2] = (byte)(((month & 0x0E) >> 1) | (day << 3))
ret[3] = (byte)(hour | ((min & 0x07) << 5))
ret[4] = (byte)(((min & 0x38) >> 3) | ((sec & 0x1F) << 3))
ret[5] = (byte)(((sec & 0x20) >> 5) | ((msec & 0x7F) << 1))
ret[6] = (byte)((msec >> 7) & 0xFF)
ret[7] = (byte)((msec >> 15) & 0xFF)
ret[8] = (byte)((msec >> 23) & 0xFF)
} else if column.colType == DATETIME_TZ {
msec /= 1000
ret = make([]byte, 10)
ret[0] = (byte)(year & 0xFF)
if year >= 0 {
ret[1] = (byte)((year >> 8) | ((month & 0x01) << 7))
} else {
ret[1] = (byte)((year >> 8) & (((month & 0x01) << 7) | 0x7f))
}
ret[2] = (byte)(((month & 0x0E) >> 1) | (day << 3))
ret[3] = (byte)(hour | ((min & 0x07) << 5))
ret[4] = (byte)(((min & 0x38) >> 3) | ((sec & 0x1F) << 3))
ret[5] = (byte)(((sec & 0x20) >> 5) | ((msec & 0x7F) << 1))
ret[6] = (byte)((msec >> 7) & 0xFF)
ret[7] = (byte)((msec >> 15) & 0xFF)
Dm_build_1346.Dm_build_1357(ret, 8, int16(tz))
} else if column.colType == DATETIME2_TZ {
ret = make([]byte, 11)
ret[0] = (byte)(year & 0xFF)
if year >= 0 {
ret[1] = (byte)((year >> 8) | ((month & 0x01) << 7))
} else {
ret[1] = (byte)((year >> 8) & (((month & 0x01) << 7) | 0x7f))
}
ret[2] = (byte)(((month & 0x0E) >> 1) | (day << 3))
ret[3] = (byte)(hour | ((min & 0x07) << 5))
ret[4] = (byte)(((min & 0x38) >> 3) | ((sec & 0x1F) << 3))
ret[5] = (byte)(((sec & 0x20) >> 5) | ((msec & 0x7F) << 1))
ret[6] = (byte)((msec >> 7) & 0xFF)
ret[7] = (byte)((msec >> 15) & 0xFF)
ret[8] = (byte)((msec >> 23) & 0xFF)
Dm_build_1346.Dm_build_1357(ret, 8, int16(tz))
} else if column.colType == TIME {
msec /= 1000
ret = make([]byte, 5)
ret[0] = (byte)(hour | ((min & 0x07) << 5))
ret[1] = (byte)(((min & 0x38) >> 3) | ((sec & 0x1F) << 3))
ret[2] = (byte)(((sec & 0x20) >> 5) | ((msec & 0x7F) << 1))
ret[3] = (byte)((msec >> 7) & 0xFF)
ret[4] = (byte)((msec >> 15) & 0xFF)
} else if column.colType == TIME_TZ {
msec /= 1000
ret = make([]byte, 7)
ret[0] = (byte)(hour | ((min & 0x07) << 5))
ret[1] = (byte)(((min & 0x38) >> 3) | ((sec & 0x1F) << 3))
ret[2] = (byte)(((sec & 0x20) >> 5) | ((msec & 0x7F) << 1))
ret[3] = (byte)((msec >> 7) & 0xFF)
ret[4] = (byte)((msec >> 15) & 0xFF)
Dm_build_1346.Dm_build_1357(ret, 5, int16(tz))
}
return ret, nil
}
func toDate(x int64, column column, conn DmConnection) ([]byte, error) {
switch column.colType {
case DATETIME, DATETIME2:
if x > 2958463*24*60*60 {
return nil, ECGO_DATETIME_OVERFLOW.throw()
}
dt := toDTFromUnix(x-Seconds_1900_1970, 0)
return encode(dt, column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
case TIME:
dt := toDTFromUnix(x, 0)
return encode(dt, column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
case DATE:
if x > 2958463 {
return nil, ECGO_DATETIME_OVERFLOW.throw()
}
dt := toDTFromUnix(x*24*60*60-Seconds_1900_1970, 0)
if dt[OFFSET_YEAR] < -4712 || dt[OFFSET_YEAR] > 9999 {
return nil, ECGO_DATETIME_OVERFLOW.throw()
}
return encode(dt, column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
default:
return nil, ECGO_DATA_CONVERTION_ERROR.throw()
}
}
func checkDate(year int, month int, day int) bool {
if year > 9999 || year < -4712 || month > 12 || month < 1 {
return false
}
monthDays := getDaysOfMonth(year, month)
if day > monthDays || day < 1 {
return false
}
return true
}
func getDaysOfMonth(year int, month int) int {
switch month {
case 1, 3, 5, 7, 8, 10, 12:
return 31
case 4, 6, 9, 11:
return 30
case 2:
if isLeapYear(year) {
return 29
}
return 28
default:
return 0
}
}
func isLeapYear(year int) bool {
return (year%4 == 0 && year%100 != 0) || year%400 == 0
}
func addYear(dt []int, n int) []int {
dt[OFFSET_YEAR] += n
return dt
}
func addMonth(dt []int, n int) []int {
month := dt[OFFSET_MONTH] + n
addYearValue := month / 12
if month %= 12; month < 1 {
month += 12
addYearValue--
}
daysOfMonth := getDaysOfMonth(dt[OFFSET_YEAR], month)
if dt[OFFSET_DAY] > daysOfMonth {
dt[OFFSET_DAY] = daysOfMonth
}
dt[OFFSET_MONTH] = month
addYear(dt, addYearValue)
return dt
}
func addDay(dt []int, n int) []int {
tmp := dt[OFFSET_DAY] + n
monthDays := 0
monthDays = getDaysOfMonth(dt[OFFSET_YEAR], dt[OFFSET_MONTH])
for tmp > monthDays || tmp <= 0 {
if tmp > monthDays {
addMonth(dt, 1)
tmp -= monthDays
} else {
addMonth(dt, -1)
tmp += monthDays
}
}
dt[OFFSET_DAY] = tmp
return dt
}
func addHour(dt []int, n int) []int {
hour := dt[OFFSET_HOUR] + n
addDayValue := hour / 24
if hour %= 24; hour < 0 {
hour += 24
addDayValue--
}
dt[OFFSET_HOUR] = hour
addDay(dt, addDayValue)
return dt
}
func addMinute(dt []int, n int) []int {
minute := dt[OFFSET_MINUTE] + n
addHourValue := minute / 60
if minute %= 60; minute < 0 {
minute += 60
addHourValue--
}
dt[OFFSET_MINUTE] = minute
addHour(dt, addHourValue)
return dt
}
+917
View File
@@ -0,0 +1,917 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"strconv"
"time"
"gitee.com/chunanyong/dm/util"
)
var DB2G db2g
type db2g struct {
}
func (DB2G db2g) processVarchar2(bytes []byte, prec int) []byte {
rbytes := make([]byte, prec)
copy(rbytes[:len(bytes)], bytes[:])
for i := len(bytes); i < len(rbytes); i++ {
rbytes[i] = ' '
}
return rbytes
}
func (DB2G db2g) charToString(bytes []byte, column *column, conn *DmConnection) string {
if column.colType == VARCHAR2 {
bytes = DB2G.processVarchar2(bytes, int(column.prec))
} else if column.colType == CLOB {
clob := newClobFromDB(bytes, conn, column, true)
clobLen, _ := clob.GetLength()
clobStr, _ := clob.getSubString(1, int32(clobLen))
return clobStr
}
return Dm_build_1346.Dm_build_1598(bytes, conn.serverEncoding, conn)
}
func (DB2G db2g) charToFloat64(bytes []byte, column *column, conn *DmConnection) (float64, error) {
str := DB2G.charToString(bytes, column, conn)
val, err := strconv.ParseFloat(str, 64)
if err != nil {
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
return val, nil
}
func (DB2G db2g) charToDeciaml(bytes []byte, column *column, conn *DmConnection) (*DmDecimal, error) {
str := DB2G.charToString(bytes, column, conn)
return NewDecimalFromString(str)
}
func (DB2G db2g) BinaryToInt64(bytes []byte, column *column, conn *DmConnection) (int64, error) {
if column.colType == BLOB {
blob := newBlobFromDB(bytes, conn, column, true)
blobLen, err := blob.GetLength()
if err != nil {
return 0, err
}
bytes, err = blob.getBytes(1, int32(blobLen))
if err != nil {
return 0, err
}
}
var n, b int64 = 0, 0
startIndex := 0
var length int
if len(bytes) > 8 {
length = 8
for j := 0; j < len(bytes)-8; j++ {
if bytes[j] != 0 {
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
startIndex = len(bytes) - 8
length = 8
}
} else {
length = len(bytes)
}
for j := startIndex; j < startIndex+length; j++ {
b = int64(0xff & bytes[j])
n = b | (n << 8)
}
return n, nil
}
func (DB2G db2g) decToDecimal(bytes []byte, prec int, scale int, compatibleOracle bool) (*DmDecimal, error) {
if compatibleOracle {
prec = -1
scale = -1
}
return newDecimal(bytes, prec, scale)
}
func (DB2G db2g) toBytes(bytes []byte, column *column, conn *DmConnection) ([]byte, error) {
retBytes := Dm_build_1346.Dm_build_1497(bytes, 0, len(bytes))
switch column.colType {
case CLOB:
clob := newClobFromDB(retBytes, conn, column, true)
str, err := clob.getSubString(1, int32(clob.length))
if err != nil {
return nil, err
}
return Dm_build_1346.Dm_build_1562(str, conn.getServerEncoding(), conn), nil
case BLOB:
blob := newBlobFromDB(retBytes, conn, column, true)
bs, err := blob.getBytes(1, int32(blob.length))
if err != nil {
return nil, err
}
return bs, nil
}
return nil, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toString(bytes []byte, column *column, conn *DmConnection) string {
switch column.colType {
case CHAR, VARCHAR, VARCHAR2:
return DB2G.charToString(bytes, column, conn)
case BIT, BOOLEAN, TINYINT:
return strconv.FormatInt(int64(bytes[0]), 10)
case SMALLINT:
return strconv.FormatInt(int64(Dm_build_1346.Dm_build_1570(bytes)), 10)
case INT:
return strconv.FormatInt(int64(Dm_build_1346.Dm_build_1573(bytes)), 10)
case BIGINT:
return strconv.FormatInt(int64(Dm_build_1346.Dm_build_1576(bytes)), 10)
case REAL:
return strconv.FormatFloat(float64(Dm_build_1346.Dm_build_1579(bytes)), 'f', -1, 32)
case DOUBLE:
return strconv.FormatFloat(float64(Dm_build_1346.Dm_build_1582(bytes)), 'f', -1, 64)
case DECIMAL:
case BINARY, VARBINARY:
util.StringUtil.BytesToHexString(bytes, false)
case BLOB:
case CLOB:
case DATE:
dt := decode(bytes, column.isBdta, *column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
if conn.FormatDate != "" {
return dtToStringByOracleFormat(dt, conn.FormatDate, column.scale, int(conn.OracleDateLanguage))
}
case TIME:
dt := decode(bytes, column.isBdta, *column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
if conn.FormatTime != "" {
return dtToStringByOracleFormat(dt, conn.FormatTime, column.scale, int(conn.OracleDateLanguage))
}
case DATETIME, DATETIME2:
dt := decode(bytes, column.isBdta, *column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
if conn.FormatTimestamp != "" {
return dtToStringByOracleFormat(dt, conn.FormatTimestamp, column.scale, int(conn.OracleDateLanguage))
}
case TIME_TZ:
dt := decode(bytes, column.isBdta, *column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
if conn.FormatTimeTZ != "" {
return dtToStringByOracleFormat(dt, conn.FormatTimeTZ, column.scale, int(conn.OracleDateLanguage))
}
case DATETIME_TZ, DATETIME2_TZ:
dt := decode(bytes, column.isBdta, *column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
if conn.FormatTimestampTZ != "" {
return dtToStringByOracleFormat(dt, conn.FormatTimestampTZ, column.scale, int(conn.OracleDateLanguage))
}
case INTERVAL_DT:
return newDmIntervalDTByBytes(bytes).String()
case INTERVAL_YM:
return newDmIntervalYMByBytes(bytes).String()
case ARRAY:
case SARRAY:
case CLASS:
case PLTYPE_RECORD:
}
return ""
}
func (DB2G db2g) toBool(bytes []byte, column *column, conn *DmConnection) (bool, error) {
switch column.colType {
case BIT, BOOLEAN, TINYINT:
return bytes[0] != 0, nil
case SMALLINT:
return Dm_build_1346.Dm_build_1443(bytes, 0) != 0, nil
case INT:
return Dm_build_1346.Dm_build_1448(bytes, 0) != 0, nil
case BIGINT:
return Dm_build_1346.Dm_build_1453(bytes, 0) != 0, nil
case REAL:
return Dm_build_1346.Dm_build_1458(bytes, 0) != 0, nil
case DOUBLE:
return Dm_build_1346.Dm_build_1462(bytes, 0) != 0, nil
case DECIMAL:
case CHAR, VARCHAR, VARCHAR2, CLOB:
return G2DB.toBool(DB2G.charToString(bytes, column, conn))
}
return false, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toByte(bytes []byte, column *column, conn *DmConnection) (byte, error) {
switch column.colType {
case BIT, BOOLEAN, TINYINT:
if bytes == nil || len(bytes) == 0 {
return 0, nil
} else {
return bytes[0], nil
}
case SMALLINT:
tval := Dm_build_1346.Dm_build_1443(bytes, 0)
if tval < int16(BYTE_MIN) || tval > int16(BYTE_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return byte(tval), nil
case INT:
tval := Dm_build_1346.Dm_build_1448(bytes, 0)
if tval < int32(BYTE_MIN) || tval > int32(BYTE_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return byte(tval), nil
case BIGINT:
tval := Dm_build_1346.Dm_build_1453(bytes, 0)
if tval < int64(BYTE_MIN) || tval > int64(BYTE_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return byte(tval), nil
case REAL:
tval := Dm_build_1346.Dm_build_1458(bytes, 0)
if tval < float32(BYTE_MIN) || tval > float32(BYTE_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return byte(tval), nil
case DOUBLE:
tval := Dm_build_1346.Dm_build_1462(bytes, 0)
if tval < float64(BYTE_MIN) || tval > float64(BYTE_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return byte(tval), nil
case DECIMAL:
case CHAR, VARCHAR, VARCHAR2, CLOB:
tval, err := DB2G.charToFloat64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < float64(BYTE_MIN) || tval > float64(BYTE_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return byte(tval), nil
case BINARY, VARBINARY, BLOB:
{
tval, err := DB2G.BinaryToInt64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < int64(BYTE_MIN) || tval > int64(BYTE_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return byte(tval), nil
}
}
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toInt8(bytes []byte, column *column, conn *DmConnection) (int8, error) {
switch column.colType {
case BIT, BOOLEAN, TINYINT:
if bytes == nil || len(bytes) == 0 {
return 0, nil
}
return int8(bytes[0]), nil
case SMALLINT:
tval := Dm_build_1346.Dm_build_1443(bytes, 0)
if tval < int16(INT8_MIN) || tval < int16(INT8_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int8(tval), nil
case INT:
tval := Dm_build_1346.Dm_build_1448(bytes, 0)
if tval < int32(INT8_MIN) || tval > int32(INT8_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int8(tval), nil
case BIGINT:
tval := Dm_build_1346.Dm_build_1453(bytes, 0)
if tval < int64(INT8_MIN) || tval > int64(INT8_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int8(tval), nil
case REAL:
tval := Dm_build_1346.Dm_build_1458(bytes, 0)
if tval < float32(INT8_MIN) || tval > float32(INT8_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int8(tval), nil
case DOUBLE:
tval := Dm_build_1346.Dm_build_1462(bytes, 0)
if tval < float64(INT8_MIN) || tval > float64(INT8_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int8(tval), nil
case DECIMAL:
case CHAR, VARCHAR, VARCHAR2, CLOB:
tval, err := DB2G.charToFloat64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < float64(INT8_MIN) || tval > float64(INT8_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int8(tval), nil
case BINARY, VARBINARY, BLOB:
{
tval, err := DB2G.BinaryToInt64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < int64(INT8_MIN) || tval > int64(INT8_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int8(tval), nil
}
}
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toInt16(bytes []byte, column *column, conn *DmConnection) (int16, error) {
switch column.colType {
case BIT, BOOLEAN, TINYINT:
if bytes == nil || len(bytes) == 0 {
return 0, nil
}
return int16(bytes[0]), nil
case SMALLINT:
return Dm_build_1346.Dm_build_1443(bytes, 0), nil
case INT:
tval := Dm_build_1346.Dm_build_1448(bytes, 0)
if tval < int32(INT16_MIN) || tval > int32(INT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int16(tval), nil
case BIGINT:
tval := Dm_build_1346.Dm_build_1453(bytes, 0)
if tval < int64(INT16_MIN) || tval > int64(INT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int16(tval), nil
case REAL:
tval := Dm_build_1346.Dm_build_1458(bytes, 0)
if tval < float32(INT16_MIN) || tval > float32(INT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int16(tval), nil
case DOUBLE:
tval := Dm_build_1346.Dm_build_1462(bytes, 0)
if tval < float64(INT16_MIN) || tval > float64(INT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int16(tval), nil
case DECIMAL:
case CHAR, VARCHAR, VARCHAR2, CLOB:
tval, err := DB2G.charToFloat64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < float64(INT16_MIN) || tval > float64(INT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int16(tval), nil
case BINARY, VARBINARY, BLOB:
{
tval, err := DB2G.BinaryToInt64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < int64(INT16_MIN) || tval > int64(INT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int16(tval), nil
}
}
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toUInt16(bytes []byte, column *column, conn *DmConnection) (uint16, error) {
switch column.colType {
case BIT, BOOLEAN, TINYINT:
if bytes == nil || len(bytes) == 0 {
return 0, nil
}
return uint16(bytes[0]), nil
case SMALLINT:
return uint16(Dm_build_1346.Dm_build_1443(bytes, 0)), nil
case INT:
tval := Dm_build_1346.Dm_build_1448(bytes, 0)
if tval < int32(UINT16_MIN) || tval > int32(UINT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint16(tval), nil
case BIGINT:
tval := Dm_build_1346.Dm_build_1453(bytes, 0)
if tval < int64(UINT16_MIN) || tval > int64(UINT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint16(tval), nil
case REAL:
tval := Dm_build_1346.Dm_build_1458(bytes, 0)
if tval < float32(UINT16_MIN) || tval > float32(UINT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint16(tval), nil
case DOUBLE:
tval := Dm_build_1346.Dm_build_1462(bytes, 0)
if tval < float64(UINT16_MIN) || tval > float64(UINT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint16(tval), nil
case DECIMAL:
case CHAR, VARCHAR, VARCHAR2, CLOB:
tval, err := DB2G.charToFloat64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < float64(UINT16_MIN) || tval > float64(UINT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint16(tval), nil
case BINARY, VARBINARY, BLOB:
{
tval, err := DB2G.BinaryToInt64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < int64(UINT16_MIN) || tval > int64(UINT16_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint16(tval), nil
}
}
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toInt32(bytes []byte, column *column, conn *DmConnection) (int32, error) {
switch column.colType {
case BIT, BOOLEAN, TINYINT:
if bytes == nil || len(bytes) == 0 {
return 0, nil
}
return int32(bytes[0]), nil
case SMALLINT:
return int32(Dm_build_1346.Dm_build_1443(bytes, 0)), nil
case INT:
return Dm_build_1346.Dm_build_1448(bytes, 0), nil
case BIGINT:
tval := Dm_build_1346.Dm_build_1453(bytes, 0)
if tval < int64(INT32_MIN) || tval > int64(INT32_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int32(tval), nil
case REAL:
tval := Dm_build_1346.Dm_build_1458(bytes, 0)
if tval < float32(INT32_MIN) || tval > float32(INT32_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int32(tval), nil
case DOUBLE:
tval := Dm_build_1346.Dm_build_1462(bytes, 0)
if tval < float64(INT32_MIN) || tval > float64(INT32_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int32(tval), nil
case DECIMAL:
case CHAR, VARCHAR, VARCHAR2, CLOB:
tval, err := DB2G.charToFloat64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < float64(INT32_MIN) || tval > float64(INT32_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int32(tval), nil
case BINARY, VARBINARY, BLOB:
{
tval, err := DB2G.BinaryToInt64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < int64(INT32_MIN) || tval > int64(INT32_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int32(tval), nil
}
}
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toUInt32(bytes []byte, column *column, conn *DmConnection) (uint32, error) {
switch column.colType {
case BIT, BOOLEAN, TINYINT:
if bytes == nil || len(bytes) == 0 {
return 0, nil
}
return uint32(bytes[0]), nil
case SMALLINT:
return uint32(Dm_build_1346.Dm_build_1443(bytes, 0)), nil
case INT:
return uint32(Dm_build_1346.Dm_build_1448(bytes, 0)), nil
case BIGINT:
tval := Dm_build_1346.Dm_build_1453(bytes, 0)
if tval < int64(UINT32_MIN) || tval > int64(UINT32_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint32(tval), nil
case REAL:
tval := Dm_build_1346.Dm_build_1458(bytes, 0)
if tval < float32(UINT32_MIN) || tval > float32(UINT32_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint32(tval), nil
case DOUBLE:
tval := Dm_build_1346.Dm_build_1462(bytes, 0)
if tval < float64(UINT32_MIN) || tval > float64(UINT32_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint32(tval), nil
case DECIMAL:
case CHAR, VARCHAR, VARCHAR2, CLOB:
tval, err := DB2G.charToFloat64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < float64(UINT32_MIN) || tval > float64(UINT32_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint32(tval), nil
case BINARY, VARBINARY, BLOB:
{
tval, err := DB2G.BinaryToInt64(bytes, column, conn)
if err != nil {
return 0, err
}
if tval < int64(UINT32_MIN) || tval > int64(UINT32_MAX) {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint32(tval), nil
}
}
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toInt64(bytes []byte, column *column, conn *DmConnection) (int64, error) {
switch column.colType {
case BOOLEAN, BIT, TINYINT:
if bytes == nil || len(bytes) == 0 {
return int64(0), nil
} else {
return int64(bytes[0]), nil
}
case SMALLINT:
return int64(Dm_build_1346.Dm_build_1570(bytes)), nil
case INT:
return int64(Dm_build_1346.Dm_build_1573(bytes)), nil
case BIGINT:
return int64(Dm_build_1346.Dm_build_1576(bytes)), nil
case REAL:
return int64(Dm_build_1346.Dm_build_1579(bytes)), nil
case DOUBLE:
return int64(Dm_build_1346.Dm_build_1582(bytes)), nil
case CHAR, VARCHAR2, VARCHAR, CLOB:
tval, err := DB2G.charToFloat64(bytes, column, conn)
if err != nil {
return 0, err
}
if int64(tval) < INT64_MIN || int64(tval) > INT64_MAX {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return int64(tval), nil
case BINARY, VARBINARY, BLOB:
tval, err := DB2G.BinaryToInt64(bytes, column, conn)
if err != nil {
return 0, err
}
return tval, nil
}
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toUInt64(bytes []byte, column *column, conn *DmConnection) (uint64, error) {
switch column.colType {
case BOOLEAN, BIT, TINYINT:
if bytes == nil || len(bytes) == 0 {
return uint64(0), nil
} else {
return uint64(bytes[0]), nil
}
case SMALLINT:
return uint64(Dm_build_1346.Dm_build_1570(bytes)), nil
case INT:
return uint64(Dm_build_1346.Dm_build_1573(bytes)), nil
case BIGINT:
return uint64(Dm_build_1346.Dm_build_1576(bytes)), nil
case REAL:
return uint64(Dm_build_1346.Dm_build_1579(bytes)), nil
case DOUBLE:
return uint64(Dm_build_1346.Dm_build_1582(bytes)), nil
case CHAR, VARCHAR2, VARCHAR, CLOB:
tval, err := DB2G.charToFloat64(bytes, column, conn)
if err != nil {
return 0, err
}
if uint64(tval) < UINT64_MIN || uint64(tval) > UINT64_MAX {
return 0, ECGO_DATA_OVERFLOW.throw()
}
return uint64(tval), nil
case BINARY, VARBINARY, BLOB:
tval, err := DB2G.BinaryToInt64(bytes, column, conn)
if err != nil {
return 0, err
}
return uint64(tval), nil
}
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toInt(bytes []byte, column *column, conn *DmConnection) (int, error) {
if strconv.IntSize == 32 {
tmp, err := DB2G.toInt32(bytes, column, conn)
return int(tmp), err
} else {
tmp, err := DB2G.toInt64(bytes, column, conn)
return int(tmp), err
}
}
func (DB2G db2g) toUInt(bytes []byte, column *column, conn *DmConnection) (uint, error) {
if strconv.IntSize == 32 {
tmp, err := DB2G.toUInt32(bytes, column, conn)
return uint(tmp), err
} else {
tmp, err := DB2G.toUInt64(bytes, column, conn)
return uint(tmp), err
}
}
func (DB2G db2g) toFloat32(bytes []byte, column *column, conn *DmConnection) (float32, error) {
switch column.colType {
case BIT, BOOLEAN, TINYINT:
if bytes == nil || len(bytes) == 0 {
return 0, nil
}
return float32(bytes[0]), nil
case SMALLINT:
return float32(Dm_build_1346.Dm_build_1443(bytes, 0)), nil
case INT:
return float32(Dm_build_1346.Dm_build_1448(bytes, 0)), nil
case BIGINT:
return float32(Dm_build_1346.Dm_build_1453(bytes, 0)), nil
case REAL:
return Dm_build_1346.Dm_build_1458(bytes, 0), nil
case DOUBLE:
dval := Dm_build_1346.Dm_build_1462(bytes, 0)
return float32(dval), nil
case DECIMAL:
dval, err := DB2G.decToDecimal(bytes, int(column.prec), int(column.scale), conn.CompatibleOracle())
if err != nil {
return 0, err
}
return float32(dval.ToFloat64()), nil
case CHAR, VARCHAR2, VARCHAR, CLOB:
dval, err := DB2G.charToDeciaml(bytes, column, conn)
if err != nil {
return 0, err
}
return float32(dval.ToFloat64()), nil
}
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toFloat64(bytes []byte, column *column, conn *DmConnection) (float64, error) {
switch column.colType {
case BIT, BOOLEAN, TINYINT:
if bytes == nil || len(bytes) == 0 {
return 0, nil
}
return float64(bytes[0]), nil
case SMALLINT:
return float64(Dm_build_1346.Dm_build_1443(bytes, 0)), nil
case INT:
return float64(Dm_build_1346.Dm_build_1448(bytes, 0)), nil
case BIGINT:
return float64(Dm_build_1346.Dm_build_1453(bytes, 0)), nil
case REAL:
return float64(Dm_build_1346.Dm_build_1458(bytes, 0)), nil
case DOUBLE:
return Dm_build_1346.Dm_build_1462(bytes, 0), nil
case DECIMAL:
dval, err := DB2G.decToDecimal(bytes, int(column.prec), int(column.scale), conn.CompatibleOracle())
if err != nil {
return 0, err
}
return dval.ToFloat64(), nil
case CHAR, VARCHAR2, VARCHAR, CLOB:
dval, err := DB2G.charToDeciaml(bytes, column, conn)
if err != nil {
return 0, err
}
return dval.ToFloat64(), nil
}
return 0, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toDmBlob(value []byte, column *column, conn *DmConnection) *DmBlob {
switch column.colType {
case BLOB:
return newBlobFromDB(value, conn, column, conn.lobFetchAll())
default:
return newBlobOfLocal(value, conn)
}
}
func (DB2G db2g) toDmClob(value []byte, conn *DmConnection, column *column) *DmClob {
switch column.colType {
case CLOB:
return newClobFromDB(value, conn, column, conn.lobFetchAll())
default:
return newClobOfLocal(DB2G.toString(value, column, conn), conn)
}
}
func (DB2G db2g) toDmDecimal(value []byte, column *column, conn *DmConnection) (*DmDecimal, error) {
switch column.colType {
case BIT, BOOLEAN, TINYINT:
if value == nil || len(value) == 0 {
return NewDecimalFromInt64(0)
} else {
return NewDecimalFromInt64(int64(value[0]))
}
case SMALLINT:
return NewDecimalFromInt64(int64(Dm_build_1346.Dm_build_1443(value, 0)))
case INT:
return NewDecimalFromInt64(int64(Dm_build_1346.Dm_build_1448(value, 0)))
case BIGINT:
return NewDecimalFromInt64(Dm_build_1346.Dm_build_1453(value, 0))
case REAL:
return NewDecimalFromFloat64(float64(Dm_build_1346.Dm_build_1458(value, 0)))
case DOUBLE:
return NewDecimalFromFloat64(Dm_build_1346.Dm_build_1462(value, 0))
case DECIMAL:
return decodeDecimal(value, int(column.prec), int(column.scale))
case CHAR, VARCHAR, VARCHAR2, CLOB:
return DB2G.charToDeciaml(value, column, conn)
}
return nil, ECGO_DATA_CONVERTION_ERROR
}
func (DB2G db2g) toTime(bytes []byte, column *column, conn *DmConnection) (time.Time, error) {
switch column.colType {
case DATE, TIME, TIME_TZ, DATETIME_TZ, DATETIME, DATETIME2_TZ, DATETIME2:
dt := decode(bytes, column.isBdta, *column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
return toTimeFromDT(dt, int(conn.dmConnector.localTimezone)), nil
case CHAR, VARCHAR2, VARCHAR, CLOB:
return toTimeFromString(DB2G.charToString(bytes, column, conn), int(conn.dmConnector.localTimezone)), nil
}
return time.Now(), ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toObject(bytes []byte, column *column, conn *DmConnection) (interface{}, error) {
switch column.colType {
case BIT, BOOLEAN:
return bytes[0] != 0, nil
case TINYINT:
return Dm_build_1346.Dm_build_1439(bytes, 0), nil
case SMALLINT:
return Dm_build_1346.Dm_build_1443(bytes, 0), nil
case INT:
return Dm_build_1346.Dm_build_1448(bytes, 0), nil
case BIGINT:
return Dm_build_1346.Dm_build_1453(bytes, 0), nil
case DECIMAL:
return DB2G.decToDecimal(bytes, int(column.prec), int(column.scale), conn.CompatibleOracle())
case REAL:
return Dm_build_1346.Dm_build_1458(bytes, 0), nil
case DOUBLE:
return Dm_build_1346.Dm_build_1462(bytes, 0), nil
case DATE, TIME, DATETIME, TIME_TZ, DATETIME_TZ, DATETIME2, DATETIME2_TZ:
dt := decode(bytes, column.isBdta, *column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
return toTimeFromDT(dt, int(conn.dmConnector.localTimezone)), nil
case BINARY, VARBINARY:
return bytes, nil
case BLOB:
blob := newBlobFromDB(bytes, conn, column, conn.lobFetchAll())
if util.StringUtil.EqualsIgnoreCase(column.typeName, "LONGVARBINARY") {
l, err := blob.GetLength()
if err != nil {
return nil, err
}
return blob.getBytes(1, int32(l))
} else {
return blob, nil
}
case CHAR, VARCHAR, VARCHAR2:
val := DB2G.charToString(bytes, column, conn)
if column.mask == MASK_BFILE {
}
return val, nil
case CLOB:
clob := newClobFromDB(bytes, conn, column, conn.lobFetchAll())
if util.StringUtil.EqualsIgnoreCase(column.typeName, "LONGVARCHAR") {
l, err := clob.GetLength()
if err != nil {
return nil, err
}
return clob.getSubString(1, int32(l))
} else {
return clob, nil
}
case INTERVAL_YM:
return newDmIntervalYMByBytes(bytes), nil
case INTERVAL_DT:
return newDmIntervalDTByBytes(bytes), nil
case ARRAY:
return TypeDataSV.bytesToArray(bytes, nil, column.typeDescriptor)
case SARRAY:
return TypeDataSV.bytesToSArray(bytes, nil, column.typeDescriptor)
case CLASS:
case PLTYPE_RECORD:
default:
return nil, ECGO_DATA_CONVERTION_ERROR.throw()
}
return nil, ECGO_DATA_CONVERTION_ERROR.throw()
}
func (DB2G db2g) toComplexType(bytes []byte, column *column, conn *DmConnection) (interface{}, error) {
switch column.colType {
case BLOB:
if !isComplexType(int(column.colType), int(column.scale)) {
return nil, ECGO_DATA_CONVERTION_ERROR.throw()
}
blob := newBlobFromDB(bytes, conn, column, true)
return TypeDataSV.objBlobToObj(blob, column.typeDescriptor)
case ARRAY:
return TypeDataSV.bytesToArray(bytes, nil, column.typeDescriptor)
case SARRAY:
return TypeDataSV.bytesToSArray(bytes, nil, column.typeDescriptor)
case CLASS:
return TypeDataSV.bytesToObj(bytes, nil, column.typeDescriptor)
case PLTYPE_RECORD:
return nil, ECGO_DATA_CONVERTION_ERROR.throw()
default:
return nil, ECGO_DATA_CONVERTION_ERROR.throw()
}
}
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package i18n
import (
"encoding/json"
"golang.org/x/text/language"
"golang.org/x/text/message"
)
type msg struct {
Id string `json:"id"`
Translation string `json:"translation,omitempty"`
}
type i18n struct {
Language string `json:"language"`
Messages []msg `json:"messages"`
}
func InitConfig(jsonStr string) {
var i18n i18n
json.Unmarshal([]byte(jsonStr), &i18n)
msaArry := i18n.Messages
tag := language.MustParse(i18n.Language)
for _, e := range msaArry {
message.SetString(tag, e.Id, e.Translation)
}
}
func Get(key string, locale int) string {
var p *message.Printer
switch locale {
case 0:
p = message.NewPrinter(language.SimplifiedChinese)
case 1:
p = message.NewPrinter(language.AmericanEnglish)
case 2:
p = message.NewPrinter(language.TraditionalChinese)
}
return p.Sprintf(key)
}
+248
View File
@@ -0,0 +1,248 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package i18n
const Messages_en_US = `{
"language": "en-US",
"messages": [
{
"id": "error.dsn.invalidSchema",
"translation": "DSN must start with dm://"
},
{
"id": "error.dsn.invalidFormat",
"translation": "DSN is invalid"
},
{
"id": "error.unsupported.scan",
"translation": "Unsupported scan type"
},
{
"id": "error.invalidParameterNumber",
"translation": "Invalid parameter number"
},
{
"id": "error.initThirdPartCipherFailed",
"translation": "Init third part cipher failed"
},
{
"id": "error.connectionSwitchFailed",
"translation": "Connection switch failed"
},
{
"id": "error.connectionSwitched",
"translation": "Connection has been switched"
},
{
"id": "error.invalidServerMode",
"translation": "Invalid server mode"
},
{
"id": "error.osauthError",
"translation": "At the same time using the specifed user login and OS authentication login, please determine a way."
},
{
"id": "error.notQuerySQL",
"translation": "The SQL is not a query SQL"
},
{
"id": "error.notExecSQL",
"translation": "The SQL is not a execute SQL"
},
{
"id": "error.invalidTranIsolation",
"translation": "invalid Transaltion Isolation"
},
{
"id": "errorCommitInAutoCommitMode",
"translation": "Can't commit in Auto commit status"
},
{
"id": "errorCommitInAutoCommitMode",
"translation": "Can't rollback in Auto commit status"
},
{
"id": "errorStatementHandleClosed",
"translation": "Statement handle is closed"
},
{
"id": "errorResultSetColsed",
"translation": "Resultset is closed"
},
{
"id": "error.communicationError",
"translation": "Communication error"
},
{
"id": "error.msgCheckError",
"translation": "Message check error"
},
{
"id": "error.unkownNetWork",
"translation": "Unkown net work"
},
{
"id": "error.serverVersion",
"translation": "Server version is too low"
},
{
"id": "error.usernameTooLong",
"translation": "Username is too long."
},
{
"id": "error.passwordTooLong",
"translation": "Password to login is too long."
},
{
"id": "error.dataTooLong",
"translation": "The data is too large to support."
},
{
"id": "error.invalidColumnType",
"translation": "Invalid column type"
},
{
"id": "error.dataConvertionError",
"translation": "Data convertion error"
},
{
"id": "error.invalidConn",
"translation": "Invalid connection"
},
{
"id": "error.invalidHex",
"translation": "Invalid Hex Number."
},
{
"id": "error.invalidBFile",
"translation": "Invalid BFile format string."
},
{
"id": "error.dataOverflow",
"translation": "Digital overflow"
},
{
"id": "error.invalidDateTimeFormat",
"translation": "Invalid datetime type format"
},
{
"id": "error.datetimeOverflow",
"translation": "Digital overflow"
},
{
"id": "error.invalidTimeInterval",
"translation": "Invalid time interval type value"
},
{
"id": "error.unsupportedInparamType",
"translation": "Unsupported input parameter type"
},
{
"id": "error.unsupportedOutparamType",
"translation": "Unsupported output parameter type"
},
{
"id": "error.unsupportedType",
"translation": "Not support this type"
},
{
"id": "error.invalidObjBlob",
"translation": "invalid Object Blob Data."
},
{
"id": "error.structMemNotMatch",
"translation": "Members are not matched in Record or Class"
},
{
"id": "error.invalidComplexTypeName",
"translation": "Invalid descriptor name."
},
{
"id": "error.invalidParamterValue",
"translation": "Invalid parameter value"
},
{
"id": "error.invalidArrayLen",
"translation": "the length of static array is bigger than the one when defined."
},
{
"id": "error.invalidSequenceNumber",
"translation": "Invalid sequence no"
},
{
"id": "error.resultsetInReadOnlyStatus",
"translation": "Resultset in readonly status"
},
{
"id": "error.SSLInitFailed",
"translation": "Failed to initialize SSL"
},
{
"id": "error.LobDataHasFreed",
"translation": "Lob Data has been freed"
},
{
"id": "error.fatalError",
"translation": "Fatal error"
},
{
"id": "error.invalidLenOrOffset",
"translation": "Invalid length or offset"
},
{
"id": "error.intervalValueOverflow",
"translation": "interval type value overflow"
},
{
"id": "error.invalidCipher",
"translation": "Invalid cipher type"
},
{
"id": "error.storeInNilPointer",
"translation": "Can't store value into a nil pointer"
},
{
"id": "error.batchError",
"translation": "Error in executing with batch"
},
{
"id": "warning.bpWithErr",
"translation": "Warning:Partial failure on execute with batch"
},
{
"id": "error.invalidSqlType",
"translation": "Invalid sql type"
},
{
"id": "error.invalidDateTimeValue",
"translation": "Invalid datetime value"
},
{
"id": "error.msgTooLong",
"translation": "Message too long, limit 512M"
},
{
"id": "error.isNull",
"translation": "Data is NULL"
},
{
"id": "error.ParamCountLimit",
"translation": "Parameter count limit is 65536."
},
{
"id": "error.unbindedParameter",
"translation": "Unbound parameter"
},
{
"id": "error.stringCut",
"translation": "The string is cut"
},
{
"id": "error.connectionClosedOrNotBuild",
"translation": "Connection is colsed or not build"
}
]
}`
+248
View File
@@ -0,0 +1,248 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package i18n
const Messages_zh_CN = `{
"language": "zh-Hans",
"messages": [
{
"id": "error.dsn.invalidSchema",
"translation": "DSN串必须以dm://开头"
},
{
"id": "error.dsn.invalidFormat",
"translation": "DSN串格式不正确"
},
{
"id": "error.unsupported.scan",
"translation": "Scan类型转换出错"
},
{
"id": "error.invalidParameterNumber",
"translation": "参数个数不匹配"
},
{
"id": "error.initThirdPartCipherFailed",
"translation": "第三方加密初始化失败"
},
{
"id": "error.connectionSwitchFailed",
"translation": "连接重置失败"
},
{
"id": "error.connectionSwitched",
"translation": "连接已重置"
},
{
"id": "error.invalidServerMode",
"translation": "服务器模式不匹配"
},
{
"id": "error.osauthError",
"translation": "同时使用了指定用户登录和OS认证登录, 请确定一种方式."
},
{
"id": "error.notQuerySQL",
"translation": "非查询SQL语句"
},
{
"id": "error.notExecSQL",
"translation": "非执行SQL语句"
},
{
"id": "error.invalidTranIsolation",
"translation": "非法的事务隔离级"
},
{
"id": "errorCommitInAutoCommitMode",
"translation": "自动提交模式下不能手动提交"
},
{
"id": "errorRollbackInAutoCommitMode",
"translation": "自动提交模式下不能手动回滚"
},
{
"id": "errorStatementHandleClosed",
"translation": "语句已经关闭"
},
{
"id": "errorResultSetColsed",
"translation": "结果集已经关闭"
},
{
"id": "error.communicationError",
"translation": "网络通信异常"
},
{
"id": "error.msgCheckError",
"translation": "消息校验异常"
},
{
"id": "error.unkownNetWork",
"translation": "未知的网络"
},
{
"id": "error.serverVersion",
"translation": "服务器版本太低"
},
{
"id": "error.usernameTooLong",
"translation": "用户名超长"
},
{
"id": "error.passwordTooLong",
"translation": "密码超长"
},
{
"id": "error.dataTooLong",
"translation": "数据大小已超过可支持范围"
},
{
"id": "error.invalidColumnType",
"translation": "无效的列类型"
},
{
"id": "error.dataConvertionError",
"translation": "类型转换异常"
},
{
"id": "error.invalidConn",
"translation": "连接失效"
},
{
"id": "error.invalidHex",
"translation": "无效的十六进制数字"
},
{
"id": "error.invalidBFile",
"translation": "无效的BFile格式串"
},
{
"id": "error.dataOverflow",
"translation": "数字溢出"
},
{
"id": "error.invalidDateTimeFormat",
"translation": "错误的日期时间类型格式"
},
{
"id": "error.datetimeOverflow",
"translation": "数字溢出"
},
{
"id": "error.invalidTimeInterval",
"translation": "错误的时间间隔类型数据"
},
{
"id": "error.unsupportedInparamType",
"translation": "输入参数类型不支持"
},
{
"id": "error.unsupportedOutparamType",
"translation": "输出参数类型不支持"
},
{
"id": "error.unsupportedType",
"translation": "不支持该数据类型"
},
{
"id": "error.invalidObjBlob",
"translation": "无效的对象BLOB数据"
},
{
"id": "error.structMemNotMatch",
"translation": "记录或类数据成员不匹配"
},
{
"id": "error.invalidComplexTypeName",
"translation": "无效的类型描述名称"
},
{
"id": "error.invalidParamterValue",
"translation": "无效的参数值"
},
{
"id": "error.invalidArrayLen",
"translation": "静态数组长度大于定义时长度"
},
{
"id": "error.invalidSequenceNumber",
"translation": "无效的列序号"
},
{
"id": "error.resultsetInReadOnlyStatus",
"translation": "结果集处于只读状态"
},
{
"id": "error.SSLInitFailed",
"translation": "初始化SSL环境失败"
},
{
"id": "error.LobDataHasFreed",
"translation": "LOB数据已经被释放"
},
{
"id": "error.fatalError",
"translation": "致命错误"
},
{
"id": "error.invalidLenOrOffset",
"translation": "长度或偏移错误"
},
{
"id": "error.intervalValueOverflow",
"translation": "时间间隔类型数据溢出"
},
{
"id": "error.invalidCipher",
"translation": "不支持的加密类型"
},
{
"id": "error.storeInNilPointer",
"translation": "无法将数据存入空指针"
},
{
"id": "error.batchError",
"translation": "批量执行出错"
},
{
"id": "warning.bpWithErr",
"translation": "警告:批量执行部分行产生错误"
},
{
"id": "error.invalidSqlType",
"translation": "非法的SQL语句类型"
},
{
"id": "error.invalidDateTimeValue",
"translation": "无效的日期时间类型值"
},
{
"id": "error.msgTooLong",
"translation": "消息长度超出限制512M"
},
{
"id": "error.isNull",
"translation": "数据为NULL"
},
{
"id": "error.ParamCountLimit",
"translation": "参数个数超过最大值65536."
},
{
"id": "error.unbindedParameter",
"translation": "有参数未绑定"
},
{
"id": "error.stringCut",
"translation": "字符串截断"
},
{
"id": "error.connectionClosedOrNotBuild",
"translation": "连接尚未建立或已经关闭"
}
]
}`
+248
View File
@@ -0,0 +1,248 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package i18n
const Messages_zh_HK = `{
"language": "zh-Hant",
"messages": [
{
"id": "error.dsn.invalidSchema",
"translation": "DSN串必須以dm://開頭"
},
{
"id": "error.dsn.invalidFormat",
"translation": "DSN串格式不正確"
},
{
"id": "error.unsupported.scan",
"translation": "Scan類型轉換出錯"
},
{
"id": "error.invalidParameterNumber",
"translation": "參數個數不匹配"
},
{
"id": "error.initThirdPartCipherFailed",
"translation": "第三方加密初始化失敗"
},
{
"id": "error.connectionSwitchFailed",
"translation": "連接重置失敗"
},
{
"id": "error.connectionSwitched",
"translation": "連接已重置"
},
{
"id": "error.invalidServerMode",
"translation": "服務器模式不匹配"
},
{
"id": "error.osauthError",
"translation": "同時使用了指定用戶登錄和OS認證登錄, 請確定一種方式."
},
{
"id": "error.notQuerySQL",
"translation": "非查詢SQL語句"
},
{
"id": "error.notExecSQL",
"translation": "非執行SQL語句"
},
{
"id": "error.invalidTranIsolation",
"translation": "非法的事務隔離級"
},
{
"id": "errorCommitInAutoCommitMode",
"translation": "自動提交模式下不能手動提交"
},
{
"id": "errorRollbackInAutoCommitMode",
"translation": "自動提交模式下不能手動回滾"
},
{
"id": "errorStatementHandleClosed",
"translation": "語句已經關閉"
},
{
"id": "errorResultSetColsed",
"translation": "結果集已經關閉"
},
{
"id": "error.communicationError",
"translation": "網絡通信異常"
},
{
"id": "error.msgCheckError",
"translation": "消息校驗異常"
},
{
"id": "error.unkownNetWork",
"translation": "未知的網絡"
},
{
"id": "error.serverVersion",
"translation": "服務器版本太低"
},
{
"id": "error.usernameTooLong",
"translation": "用戶名超長"
},
{
"id": "error.passwordTooLong",
"translation": "密碼超長"
},
{
"id": "error.dataTooLong",
"translation": "數據大小已超過可支持範圍"
},
{
"id": "error.invalidColumnType",
"translation": "無效的列類型"
},
{
"id": "error.dataConvertionError",
"translation": "類型轉換異常"
},
{
"id": "error.invalidConn",
"translation": "連接失效"
},
{
"id": "error.invalidHex",
"translation": "無效的十六進制数字"
},
{
"id": "error.invalidBFile",
"translation": "無效的BFile格式串"
},
{
"id": "error.dataOverflow",
"translation": "数字溢出"
},
{
"id": "error.invalidDateTimeFormat",
"translation": "錯誤的日期時間類型格式"
},
{
"id": "error.datetimeOverflow",
"translation": "数字溢出"
},
{
"id": "error.invalidTimeInterval",
"translation": "錯誤的時間間隔類型數據"
},
{
"id": "error.unsupportedInparamType",
"translation": "輸入參數類型不支持"
},
{
"id": "error.unsupportedOutparamType",
"translation": "輸出參數類型不支持"
},
{
"id": "error.unsupportedType",
"translation": "不支持該數據類型"
},
{
"id": "error.invalidObjBlob",
"translation": "無效的對象BLOB數據"
},
{
"id": "error.structMemNotMatch",
"translation": "記錄或類數據成員不匹配"
},
{
"id": "error.invalidComplexTypeName",
"translation": "無效的類型描述名稱"
},
{
"id": "error.invalidParamterValue",
"translation": "無效的參數值"
},
{
"id": "error.invalidArrayLen",
"translation": "靜態數組長度大於定義時長度"
},
{
"id": "error.invalidSequenceNumber",
"translation": "無效的列序號"
},
{
"id": "error.resultsetInReadOnlyStatus",
"translation": "結果集處於只讀狀態"
},
{
"id": "error.SSLInitFailed",
"translation": "初始化SSL環境失敗"
},
{
"id": "error.LobDataHasFreed",
"translation": "LOB數據已經被釋放"
},
{
"id": "error.fatalError",
"translation": "致命錯誤"
},
{
"id": "error.invalidLenOrOffset",
"translation": "長度或偏移錯誤"
},
{
"id": "error.intervalValueOverflow",
"translation": "時間間隔類型數據溢出"
},
{
"id": "error.invalidCipher",
"translation": "不支持的加密類型"
},
{
"id": "error.storeInNilPointer",
"translation": "無法將數據存入空指針"
},
{
"id": "error.batchError",
"translation": "批量執行出錯"
},
{
"id": "warning.bpWithErr",
"translation": "警告:批量執行部分行產生錯誤"
},
{
"id": "error.invalidSqlType",
"translation": "非法的SQL語句類型"
},
{
"id": "error.invalidDateTimeValue",
"translation": "無效的日期時間類型值"
},
{
"id": "error.msgTooLong",
"translation": "消息長度超出限制512M"
},
{
"id": "error.isNull",
"translation": "數據為NULL"
},
{
"id": "error.ParamCountLimit",
"translation": "參數個數超過最大值65536."
},
{
"id": "error.unbindedParameter",
"translation": "有參數未綁定"
},
{
"id": "error.stringCut",
"translation": "字符串截斷"
},
{
"id": "error.connectionClosedOrNotBuild",
"translation": "連接尚未建立或已經關閉"
}
]
}`
+270
View File
@@ -0,0 +1,270 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import "database/sql/driver"
type DmArray struct {
TypeData
m_arrDesc *ArrayDescriptor // 数组的描述信息
m_arrData []TypeData // 数组中各行数据值
m_objArray interface{} // 从服务端获取的
m_itemCount int // 本次获取的行数
m_itemSize int // 数组中一个数组项的大小,单位bytes
m_objCount int // 一个数组项中存在对象类型的个数(class、动态数组)
m_strCount int // 一个数组项中存在字符串类型的个数
m_objStrOffs []int // 对象在前,字符串在后
typeName string
elements []interface{}
// Valid为false代表DmArray数据在数据库中为NULL
Valid bool
}
func (da *DmArray) init() *DmArray {
da.initTypeData()
da.m_itemCount = 0
da.m_itemSize = 0
da.m_objCount = 0
da.m_strCount = 0
da.m_objStrOffs = nil
da.m_dumyData = nil
da.m_offset = 0
da.m_objArray = nil
da.Valid = true
return da
}
// 数据库自定义数组Array构造函数,typeName为库中定义的数组类型名称,elements为该数组类型的每个值
//
// 例如,自定义数组类型语句为:create or replace type myArray is array int[];
//
// 则绑入绑出的go对象为: val := dm.NewDmArray("myArray", []interface{} {123, 456})
func NewDmArray(typeName string, elements []interface{}) *DmArray {
da := new(DmArray)
da.typeName = typeName
da.elements = elements
da.Valid = true
return da
}
func (da *DmArray) create(dc *DmConnection) (*DmArray, error) {
desc, err := newArrayDescriptor(da.typeName, dc)
if err != nil {
return nil, err
}
return da.createByArrayDescriptor(desc, dc)
}
func (da *DmArray) createByArrayDescriptor(arrDesc *ArrayDescriptor, conn *DmConnection) (*DmArray, error) {
if nil == arrDesc {
return nil, ECGO_INVALID_PARAMETER_VALUE.throw()
}
da.init()
da.m_arrDesc = arrDesc
if nil == da.elements {
da.m_arrData = make([]TypeData, 0)
} else {
// 若为静态数组,判断给定数组长度是否超过静态数组的上限
if arrDesc.getMDesc() == nil || (arrDesc.getMDesc().getDType() == SARRAY && len(da.elements) > arrDesc.getMDesc().getStaticArrayLength()) {
return nil, ECGO_INVALID_ARRAY_LEN.throw()
}
var err error
da.m_arrData, err = TypeDataSV.toArray(da.elements, da.m_arrDesc.getMDesc())
if err != nil {
return nil, err
}
}
da.m_itemCount = len(da.m_arrData)
return da, nil
}
func newDmArrayByTypeData(atData []TypeData, desc *TypeDescriptor) *DmArray {
da := new(DmArray)
da.init()
da.m_arrDesc = newArrayDescriptorByTypeDescriptor(desc)
da.m_arrData = atData
return da
}
func (da *DmArray) checkIndex(index int64) error {
if index < 0 || index > int64(len(da.m_arrData)-1) {
return ECGO_INVALID_LENGTH_OR_OFFSET.throw()
}
return nil
}
func (da *DmArray) checkIndexAndCount(index int64, count int) error {
err := da.checkIndex(index)
if err != nil {
return err
}
if count <= 0 || index+int64(count) > int64(len(da.m_arrData)) {
return ECGO_INVALID_LENGTH_OR_OFFSET.throw()
}
return nil
}
// 获取Array对象在数据库中的类型名称
func (da *DmArray) GetBaseTypeName() (string, error) {
if err := da.checkValid(); err != nil {
return "", err
}
return da.m_arrDesc.m_typeDesc.getFulName()
}
// 获取Array对象的go数组对象
func (da *DmArray) GetArray() (interface{}, error) {
if da.m_arrData == nil || len(da.m_arrData) <= 0 {
return nil, nil;
}
return TypeDataSV.toJavaArray(da, 0, len(da.m_arrData), da.m_arrDesc.getItemDesc().getDType())
}
// 获取Array对象的指定偏移和执行长度go数据对象 index从0开始
func (da *DmArray) GetObjArray(index int64, count int) (interface{}, error) {
var err error
if err = da.checkValid(); err != nil {
return nil, err
}
if err = da.checkIndexAndCount(index, count); err != nil {
return nil, err
}
return TypeDataSV.toJavaArray(da, index, count, da.m_arrDesc.getItemDesc().getDType())
}
func (da *DmArray) GetIntArray(index int64, count int) ([]int, error) {
var err error
if err = da.checkValid(); err != nil {
return nil, err
}
if err = da.checkIndexAndCount(index, count); err != nil {
return nil, err
}
tmp, err := TypeDataSV.toNumericArray(da, index, count, ARRAY_TYPE_INTEGER)
if err != nil {
return nil, err
}
return tmp.([]int), nil
}
func (da *DmArray) GetInt16Array(index int64, count int) ([]int16, error) {
var err error
if err = da.checkValid(); err != nil {
return nil, err
}
if err = da.checkIndexAndCount(index, count); err != nil {
return nil, err
}
tmp, err := TypeDataSV.toNumericArray(da, index, count, ARRAY_TYPE_SHORT)
if err != nil {
return nil, err
}
return tmp.([]int16), nil
}
func (da *DmArray) GetInt64Array(index int64, count int) ([]int64, error) {
var err error
if err = da.checkValid(); err != nil {
return nil, err
}
if err = da.checkIndexAndCount(index, count); err != nil {
return nil, err
}
tmp, err := TypeDataSV.toNumericArray(da, index, count, ARRAY_TYPE_LONG)
if err != nil {
return nil, err
}
return tmp.([]int64), nil
}
func (da *DmArray) GetFloatArray(index int64, count int) ([]float32, error) {
var err error
if err = da.checkValid(); err != nil {
return nil, err
}
if err = da.checkIndexAndCount(index, count); err != nil {
return nil, err
}
tmp, err := TypeDataSV.toNumericArray(da, index, count, ARRAY_TYPE_FLOAT)
if err != nil {
return nil, err
}
return tmp.([]float32), nil
}
func (da *DmArray) GetDoubleArray(index int64, count int) ([]float64, error) {
var err error
if err = da.checkValid(); err != nil {
return nil, err
}
if err = da.checkIndexAndCount(index, count); err != nil {
return nil, err
}
tmp, err := TypeDataSV.toNumericArray(da, index, count, ARRAY_TYPE_DOUBLE)
if err != nil {
return nil, err
}
return tmp.([]float64), nil
}
func (dest *DmArray) Scan(src interface{}) error {
if dest == nil {
return ECGO_STORE_IN_NIL_POINTER.throw()
}
switch src := src.(type) {
case nil:
*dest = *new(DmArray)
// 将Valid标志置false表示数据库中该列为NULL
(*dest).Valid = false
return nil
case *DmArray:
*dest = *src
return nil
default:
return UNSUPPORTED_SCAN.throw()
}
}
func (array DmArray) Value() (driver.Value, error) {
if !array.Valid {
return nil, nil
}
return array, nil
}
func (array *DmArray) checkValid() error {
if !array.Valid {
return ECGO_IS_NULL.throw()
}
return nil
}
+297
View File
@@ -0,0 +1,297 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"database/sql/driver"
"io"
)
type DmBlob struct {
lob
data []byte
offset int64
}
func newDmBlob() *DmBlob {
return &DmBlob{
lob: lob{
inRow: true,
groupId: -1,
fileId: -1,
pageNo: -1,
readOver: false,
local: true,
updateable: true,
length: -1,
compatibleOracle: false,
fetchAll: false,
freed: false,
modify: false,
Valid: true,
},
offset: 1,
}
}
func newBlobFromDB(value []byte, conn *DmConnection, column *column, fetchAll bool) *DmBlob {
var blob = newDmBlob()
blob.connection = conn
blob.lobFlag = LOB_FLAG_BYTE
blob.compatibleOracle = conn.CompatibleOracle()
blob.local = false
blob.updateable = !column.readonly
blob.tabId = column.lobTabId
blob.colId = column.lobColId
blob.inRow = Dm_build_1346.Dm_build_1439(value, NBLOB_HEAD_IN_ROW_FLAG) == LOB_IN_ROW
blob.blobId = Dm_build_1346.Dm_build_1453(value, NBLOB_HEAD_BLOBID)
if !blob.inRow {
blob.groupId = Dm_build_1346.Dm_build_1443(value, NBLOB_HEAD_OUTROW_GROUPID)
blob.fileId = Dm_build_1346.Dm_build_1443(value, NBLOB_HEAD_OUTROW_FILEID)
blob.pageNo = Dm_build_1346.Dm_build_1448(value, NBLOB_HEAD_OUTROW_PAGENO)
}
if conn.NewLobFlag {
blob.tabId = Dm_build_1346.Dm_build_1448(value, NBLOB_EX_HEAD_TABLE_ID)
blob.colId = Dm_build_1346.Dm_build_1443(value, NBLOB_EX_HEAD_COL_ID)
blob.rowId = Dm_build_1346.Dm_build_1453(value, NBLOB_EX_HEAD_ROW_ID)
blob.exGroupId = Dm_build_1346.Dm_build_1443(value, NBLOB_EX_HEAD_FPA_GRPID)
blob.exFileId = Dm_build_1346.Dm_build_1443(value, NBLOB_EX_HEAD_FPA_FILEID)
blob.exPageNo = Dm_build_1346.Dm_build_1448(value, NBLOB_EX_HEAD_FPA_PAGENO)
}
blob.resetCurrentInfo()
blob.length = blob.getLengthFromHead(value)
if blob.inRow {
blob.data = make([]byte, blob.length)
if conn.NewLobFlag {
Dm_build_1346.Dm_build_1402(blob.data, 0, value, NBLOB_EX_HEAD_SIZE, len(blob.data))
} else {
Dm_build_1346.Dm_build_1402(blob.data, 0, value, NBLOB_INROW_HEAD_SIZE, len(blob.data))
}
} else if fetchAll {
blob.loadAllData()
}
return blob
}
func newBlobOfLocal(value []byte, conn *DmConnection) *DmBlob {
var blob = newDmBlob()
blob.connection = conn
blob.lobFlag = LOB_FLAG_BYTE
blob.data = value
blob.length = int64(len(blob.data))
return blob
}
func NewBlob(value []byte) *DmBlob {
var blob = newDmBlob()
blob.lobFlag = LOB_FLAG_BYTE
blob.data = value
blob.length = int64(len(blob.data))
return blob
}
func (blob *DmBlob) Read(dest []byte) (n int, err error) {
if err = blob.checkValid(); err != nil {
return
}
result, err := blob.getBytes(blob.offset, int32(len(dest)))
if err != nil {
return 0, err
}
blob.offset += int64(len(result))
copy(dest, result)
if len(result) == 0 {
return 0, io.EOF
}
return len(result), nil
}
func (blob *DmBlob) ReadAt(pos int, dest []byte) (n int, err error) {
if err = blob.checkValid(); err != nil {
return
}
result, err := blob.getBytes(int64(pos), int32(len(dest)))
if err != nil {
return 0, err
}
if len(result) == 0 {
return 0, io.EOF
}
copy(dest[0:len(result)], result)
return len(result), nil
}
func (blob *DmBlob) Write(pos int, src []byte) (n int, err error) {
if err = blob.checkValid(); err != nil {
return
}
if err = blob.checkFreed(); err != nil {
return
}
if pos < 1 {
err = ECGO_INVALID_LENGTH_OR_OFFSET.throw()
return
}
if !blob.updateable {
err = ECGO_RESULTSET_IS_READ_ONLY.throw()
return
}
pos -= 1
if blob.local || blob.fetchAll {
if int64(pos) > blob.length {
err = ECGO_INVALID_LENGTH_OR_OFFSET.throw()
return
}
blob.setLocalData(pos, src)
n = len(src)
} else {
if err = blob.connection.checkClosed(); err != nil {
return -1, err
}
var writeLen, err = blob.connection.Access.dm_build_638(blob, pos, src)
if err != nil {
return -1, err
}
if blob.groupId == -1 {
blob.setLocalData(pos, src)
} else {
blob.inRow = false
blob.length = -1
}
n = writeLen
}
blob.modify = true
return
}
func (blob *DmBlob) Truncate(length int64) error {
var err error
if err = blob.checkValid(); err != nil {
return err
}
if err = blob.checkFreed(); err != nil {
return err
}
if length < 0 {
return ECGO_INVALID_LENGTH_OR_OFFSET.throw()
}
if !blob.updateable {
return ECGO_RESULTSET_IS_READ_ONLY.throw()
}
if blob.local || blob.fetchAll {
if length > int64(len(blob.data)) {
return ECGO_INVALID_LENGTH_OR_OFFSET.throw()
}
if length == int64(len(blob.data)) {
return nil
}
tmp := make([]byte, length)
Dm_build_1346.Dm_build_1402(tmp, 0, blob.data, 0, len(tmp))
blob.data = tmp
blob.length = int64(len(tmp))
} else {
if err = blob.connection.checkClosed(); err != nil {
return err
}
blob.length, err = blob.connection.Access.dm_build_652(&blob.lob, int(length))
if err != nil {
return err
}
if blob.groupId == -1 {
tmp := make([]byte, blob.length)
Dm_build_1346.Dm_build_1402(tmp, 0, blob.data, 0, int(blob.length))
blob.data = tmp
}
}
blob.modify = true
return nil
}
func (dest *DmBlob) Scan(src interface{}) error {
if dest == nil {
return ECGO_STORE_IN_NIL_POINTER.throw()
}
switch src := src.(type) {
case nil:
*dest = *new(DmBlob)
(*dest).Valid = false
return nil
case []byte:
*dest = *NewBlob(src)
return nil
case *DmBlob:
*dest = *src
return nil
default:
return UNSUPPORTED_SCAN.throw()
}
}
func (blob DmBlob) Value() (driver.Value, error) {
if !blob.Valid {
return nil, nil
}
return blob, nil
}
func (blob *DmBlob) getBytes(pos int64, length int32) ([]byte, error) {
var err error
var leaveLength int64
if err = blob.checkFreed(); err != nil {
return nil, err
}
if pos < 1 || length < 0 {
return nil, ECGO_INVALID_LENGTH_OR_OFFSET.throw()
}
pos = pos - 1
if leaveLength, err = blob.GetLength(); err != nil {
return nil, err
}
leaveLength -= pos
if leaveLength < 0 {
return nil, ECGO_INVALID_LENGTH_OR_OFFSET.throw()
}
if int64(length) > leaveLength {
length = int32(leaveLength)
}
if blob.local || blob.inRow || blob.fetchAll {
return blob.data[pos : pos+int64(length)], nil
} else {
return blob.connection.Access.dm_build_599(blob, int32(pos), length)
}
}
func (blob *DmBlob) loadAllData() {
blob.checkFreed()
if blob.local || blob.inRow || blob.fetchAll {
return
}
len, _ := blob.GetLength()
blob.data, _ = blob.getBytes(1, int32(len))
blob.fetchAll = true
}
func (blob *DmBlob) setLocalData(pos int, p []byte) {
if pos+len(p) >= int(blob.length) {
var tmp = make([]byte, pos+len(p))
Dm_build_1346.Dm_build_1402(tmp, 0, blob.data, 0, pos)
Dm_build_1346.Dm_build_1402(tmp, pos, p, 0, len(p))
blob.data = tmp
} else {
Dm_build_1346.Dm_build_1402(blob.data, pos, p, 0, len(p))
}
blob.length = int64(len(blob.data))
}
func (d *DmBlob) GormDataType() string {
return "BLOB"
}
+279
View File
@@ -0,0 +1,279 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"database/sql/driver"
"io"
)
type DmClob struct {
lob
data []rune
serverEncoding string
}
func newDmClob() *DmClob {
return &DmClob{
lob: lob{
inRow: true,
groupId: -1,
fileId: -1,
pageNo: -1,
readOver: false,
local: true,
updateable: true,
length: -1,
compatibleOracle: false,
fetchAll: false,
freed: false,
modify: false,
Valid: true,
},
}
}
func newClobFromDB(value []byte, conn *DmConnection, column *column, fetchAll bool) *DmClob {
var clob = newDmClob()
clob.connection = conn
clob.lobFlag = LOB_FLAG_CHAR
clob.compatibleOracle = conn.CompatibleOracle()
clob.local = false
clob.updateable = !column.readonly
clob.tabId = column.lobTabId
clob.colId = column.lobColId
clob.inRow = Dm_build_1346.Dm_build_1439(value, NBLOB_HEAD_IN_ROW_FLAG) == LOB_IN_ROW
clob.blobId = Dm_build_1346.Dm_build_1453(value, NBLOB_HEAD_BLOBID)
if !clob.inRow {
clob.groupId = Dm_build_1346.Dm_build_1443(value, NBLOB_HEAD_OUTROW_GROUPID)
clob.fileId = Dm_build_1346.Dm_build_1443(value, NBLOB_HEAD_OUTROW_FILEID)
clob.pageNo = Dm_build_1346.Dm_build_1448(value, NBLOB_HEAD_OUTROW_PAGENO)
}
if conn.NewLobFlag {
clob.tabId = Dm_build_1346.Dm_build_1448(value, NBLOB_EX_HEAD_TABLE_ID)
clob.colId = Dm_build_1346.Dm_build_1443(value, NBLOB_EX_HEAD_COL_ID)
clob.rowId = Dm_build_1346.Dm_build_1453(value, NBLOB_EX_HEAD_ROW_ID)
clob.exGroupId = Dm_build_1346.Dm_build_1443(value, NBLOB_EX_HEAD_FPA_GRPID)
clob.exFileId = Dm_build_1346.Dm_build_1443(value, NBLOB_EX_HEAD_FPA_FILEID)
clob.exPageNo = Dm_build_1346.Dm_build_1448(value, NBLOB_EX_HEAD_FPA_PAGENO)
}
clob.resetCurrentInfo()
clob.serverEncoding = conn.getServerEncoding()
if clob.inRow {
if conn.NewLobFlag {
clob.data = []rune(Dm_build_1346.Dm_build_1503(value, NBLOB_EX_HEAD_SIZE, int(clob.getLengthFromHead(value)), clob.serverEncoding, conn))
} else {
clob.data = []rune(Dm_build_1346.Dm_build_1503(value, NBLOB_INROW_HEAD_SIZE, int(clob.getLengthFromHead(value)), clob.serverEncoding, conn))
}
clob.length = int64(len(clob.data))
} else if fetchAll {
clob.loadAllData()
}
return clob
}
func newClobOfLocal(value string, conn *DmConnection) *DmClob {
var clob = newDmClob()
clob.connection = conn
clob.lobFlag = LOB_FLAG_CHAR
clob.data = []rune(value)
clob.length = int64(len(clob.data))
return clob
}
func NewClob(value string) *DmClob {
var clob = newDmClob()
clob.lobFlag = LOB_FLAG_CHAR
clob.data = []rune(value)
clob.length = int64(len(clob.data))
return clob
}
func (clob *DmClob) ReadString(pos int, length int) (result string, err error) {
if err = clob.checkValid(); err != nil {
return
}
result, err = clob.getSubString(int64(pos), int32(length))
if err != nil {
return
}
if len(result) == 0 {
err = io.EOF
return
}
return
}
func (clob *DmClob) WriteString(pos int, s string) (n int, err error) {
if err = clob.checkValid(); err != nil {
return
}
if err = clob.checkFreed(); err != nil {
return
}
if pos < 1 {
err = ECGO_INVALID_LENGTH_OR_OFFSET.throw()
return
}
if !clob.updateable {
err = ECGO_RESULTSET_IS_READ_ONLY.throw()
return
}
pos -= 1
if clob.local || clob.fetchAll {
if int64(pos) > clob.length {
err = ECGO_INVALID_LENGTH_OR_OFFSET.throw()
return
}
clob.setLocalData(pos, s)
n = len(s)
} else {
if err = clob.connection.checkClosed(); err != nil {
return -1, err
}
var writeLen, err = clob.connection.Access.dm_build_622(clob, pos, s, clob.serverEncoding)
if err != nil {
return -1, err
}
if clob.groupId == -1 {
clob.setLocalData(pos, s)
} else {
clob.inRow = false
clob.length = -1
}
n = writeLen
}
clob.modify = true
return
}
func (clob *DmClob) Truncate(length int64) error {
var err error
if err = clob.checkValid(); err != nil {
return err
}
if err = clob.checkFreed(); err != nil {
return err
}
if length < 0 {
return ECGO_INVALID_LENGTH_OR_OFFSET.throw()
}
if !clob.updateable {
return ECGO_RESULTSET_IS_READ_ONLY.throw()
}
if clob.local || clob.fetchAll {
if length > int64(len(clob.data)) {
return ECGO_INVALID_LENGTH_OR_OFFSET.throw()
}
if length == int64(len(clob.data)) {
return nil
}
clob.data = clob.data[0:length]
clob.length = int64(len(clob.data))
} else {
if err = clob.connection.checkClosed(); err != nil {
return err
}
clob.length, err = clob.connection.Access.dm_build_652(&clob.lob, int(length))
if err != nil {
return err
}
if clob.groupId == -1 {
clob.data = clob.data[0:clob.length]
}
}
clob.modify = true
return nil
}
func (dest *DmClob) Scan(src interface{}) error {
if dest == nil {
return ECGO_STORE_IN_NIL_POINTER.throw()
}
switch src := src.(type) {
case nil:
*dest = *new(DmClob)
(*dest).Valid = false
return nil
case string:
*dest = *NewClob(src)
return nil
case *DmClob:
*dest = *src
return nil
default:
return UNSUPPORTED_SCAN.throw()
}
}
func (clob DmClob) Value() (driver.Value, error) {
if !clob.Valid {
return nil, nil
}
return clob, nil
}
func (clob *DmClob) getSubString(pos int64, len int32) (string, error) {
var err error
var leaveLength int64
if err = clob.checkFreed(); err != nil {
return "", err
}
if pos < 1 || len < 0 {
return "", ECGO_INVALID_LENGTH_OR_OFFSET.throw()
}
pos = pos - 1
if leaveLength, err = clob.GetLength(); err != nil {
return "", err
}
if pos > leaveLength {
pos = leaveLength
}
leaveLength -= pos
if leaveLength < 0 {
return "", ECGO_INVALID_LENGTH_OR_OFFSET.throw()
}
if int64(len) > leaveLength {
len = int32(leaveLength)
}
if clob.local || clob.inRow || clob.fetchAll {
if pos > clob.length {
return "", ECGO_INVALID_LENGTH_OR_OFFSET.throw()
}
return string(clob.data[pos : pos+int64(len)]), nil
} else {
return clob.connection.Access.dm_build_610(clob, int32(pos), len)
}
}
func (clob *DmClob) loadAllData() {
clob.checkFreed()
if clob.local || clob.inRow || clob.fetchAll {
return
}
len, _ := clob.GetLength()
s, _ := clob.getSubString(1, int32(len))
clob.data = []rune(s)
clob.fetchAll = true
}
func (clob *DmClob) setLocalData(pos int, str string) {
if pos+len(str) >= int(clob.length) {
clob.data = []rune(string(clob.data[0:pos]) + str)
} else {
clob.data = []rune(string(clob.data[0:pos]) + str + string(clob.data[pos+len(str):len(clob.data)]))
}
clob.length = int64(len(clob.data))
}
func (d *DmClob) GormDataType() string {
return "CLOB"
}
+874
View File
@@ -0,0 +1,874 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"bytes"
"context"
"database/sql"
"database/sql/driver"
"fmt"
"sync"
"sync/atomic"
"gitee.com/chunanyong/dm/parser"
"gitee.com/chunanyong/dm/util"
"golang.org/x/text/encoding"
)
type DmConnection struct {
filterable
mu sync.Mutex
dmConnector *DmConnector
Access *dm_build_414
stmtMap map[int32]*DmStatement
lastExecInfo *execRetInfo
lexer *parser.Lexer
encode encoding.Encoding
encodeBuffer *bytes.Buffer
transformReaderDst []byte
transformReaderSrc []byte
serverEncoding string
GlobalServerSeries int
ServerVersion string
Malini2 bool
Execute2 bool
LobEmptyCompOrcl bool
IsoLevel int32
ReadOnly bool
NewLobFlag bool
sslEncrypt int
MaxRowSize int32
DDLAutoCommit bool
BackSlashFlag bool
SvrStat int32
SvrMode int32
ConstParaOpt bool
DbTimezone int16
LifeTimeRemainder int16
InstanceName string
Schema string
LastLoginIP string
LastLoginTime string
FailedAttempts int32
LoginWarningID int32
GraceTimeRemainder int32
Guid string
DbName string
StandbyHost string
StandbyPort int32
StandbyCount int32
SessionID int64
OracleDateLanguage byte
FormatDate string
FormatTimestamp string
FormatTimestampTZ string
FormatTime string
FormatTimeTZ string
Local bool
MsgVersion int32
TrxStatus int32
dscControl bool
trxFinish bool
autoCommit bool
isBatch bool
watching bool
watcher chan<- context.Context
closech chan struct{}
finished chan<- struct{}
canceled atomicError
closed atomicBool
}
func (conn *DmConnection) setTrxFinish(status int32) {
switch status & Dm_build_825 {
case Dm_build_822, Dm_build_823, Dm_build_824:
conn.trxFinish = true
default:
conn.trxFinish = false
}
}
func (dmConn *DmConnection) init() {
dmConn.stmtMap = make(map[int32]*DmStatement)
dmConn.DbTimezone = 0
dmConn.GlobalServerSeries = 0
dmConn.MaxRowSize = 0
dmConn.LobEmptyCompOrcl = false
dmConn.ReadOnly = false
dmConn.DDLAutoCommit = false
dmConn.ConstParaOpt = false
dmConn.IsoLevel = -1
dmConn.Malini2 = true
dmConn.NewLobFlag = true
dmConn.Execute2 = true
dmConn.serverEncoding = ENCODING_GB18030
dmConn.TrxStatus = Dm_build_773
dmConn.setTrxFinish(dmConn.TrxStatus)
dmConn.OracleDateLanguage = byte(Locale)
dmConn.lastExecInfo = NewExceInfo()
dmConn.MsgVersion = Dm_build_706
dmConn.idGenerator = dmConnIDGenerator
}
func (dmConn *DmConnection) reset() {
dmConn.DbTimezone = 0
dmConn.GlobalServerSeries = 0
dmConn.MaxRowSize = 0
dmConn.LobEmptyCompOrcl = false
dmConn.ReadOnly = false
dmConn.DDLAutoCommit = false
dmConn.ConstParaOpt = false
dmConn.IsoLevel = -1
dmConn.Malini2 = true
dmConn.NewLobFlag = true
dmConn.Execute2 = true
dmConn.serverEncoding = ENCODING_GB18030
dmConn.TrxStatus = Dm_build_773
dmConn.setTrxFinish(dmConn.TrxStatus)
}
func (dc *DmConnection) checkClosed() error {
if dc.closed.IsSet() {
return driver.ErrBadConn
}
return nil
}
func (dc *DmConnection) executeInner(query string, execType int16) (interface{}, error) {
stmt, err := NewDmStmt(dc, query)
if err != nil {
return nil, err
}
if execType == Dm_build_790 {
defer stmt.close()
}
stmt.innerUsed = true
var escapeSql = query
if stmt.dmConn.dmConnector.escapeProcess {
escapeSql, err = stmt.dmConn.escape(escapeSql, stmt.dmConn.dmConnector.keyWords)
if err != nil {
stmt.close()
return nil, err
}
}
stmt.nativeSql = escapeSql
var optParamList []OptParameter
if stmt.dmConn.ConstParaOpt {
optParamList = make([]OptParameter, 0)
stmt.nativeSql, optParamList, err = stmt.dmConn.execOpt(stmt.nativeSql, optParamList, stmt.dmConn.getServerEncoding(), stmt.dmConn.BackSlashFlag)
}
if execType == Dm_build_789 && dc.dmConnector.enRsCache {
rpv, err := rp.get(stmt, query)
if err != nil {
return nil, err
}
if rpv != nil {
stmt.execInfo = rpv.execInfo
dc.lastExecInfo = rpv.execInfo
return newDmRows(rpv.getResultSet(stmt)), nil
}
}
var info *execRetInfo
if optParamList != nil && len(optParamList) > 0 {
info, err = dc.Access.Dm_build_497(stmt, optParamList)
if err != nil {
stmt.nativeSql = escapeSql
info, err = dc.Access.Dm_build_503(stmt, execType)
}
} else {
info, err = dc.Access.Dm_build_503(stmt, execType)
}
if err != nil {
stmt.close()
return nil, err
}
dc.lastExecInfo = info
if execType == Dm_build_789 && info.hasResultSet {
return newDmRows(newInnerRows(0, stmt, info)), nil
} else {
return newDmResult(stmt, info), nil
}
}
func g2dbIsoLevel(isoLevel int32) int32 {
switch isoLevel {
case 1:
return Dm_build_777
case 2:
return Dm_build_778
case 4:
return Dm_build_779
case 6:
return Dm_build_780
default:
return -1
}
}
func (dc *DmConnection) Begin() (driver.Tx, error) {
if len(dc.filterChain.filters) == 0 {
return dc.begin()
} else {
return dc.filterChain.reset().DmConnectionBegin(dc)
}
}
func (dc *DmConnection) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if len(dc.filterChain.filters) == 0 {
return dc.beginTx(ctx, opts)
}
return dc.filterChain.reset().DmConnectionBeginTx(dc, ctx, opts)
}
func (dc *DmConnection) Commit() error {
if len(dc.filterChain.filters) == 0 {
return dc.commit()
} else {
return dc.filterChain.reset().DmConnectionCommit(dc)
}
}
func (dc *DmConnection) Rollback() error {
if len(dc.filterChain.filters) == 0 {
return dc.rollback()
} else {
return dc.filterChain.reset().DmConnectionRollback(dc)
}
}
func (dc *DmConnection) Close() error {
if len(dc.filterChain.filters) == 0 {
return dc.close()
} else {
return dc.filterChain.reset().DmConnectionClose(dc)
}
}
func (dc *DmConnection) Ping(ctx context.Context) error {
if len(dc.filterChain.filters) == 0 {
return dc.ping(ctx)
} else {
return dc.filterChain.reset().DmConnectionPing(dc, ctx)
}
}
func (dc *DmConnection) Exec(query string, args []driver.Value) (driver.Result, error) {
if len(dc.filterChain.filters) == 0 {
return dc.exec(query, args)
}
return dc.filterChain.reset().DmConnectionExec(dc, query, args)
}
func (dc *DmConnection) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
if len(dc.filterChain.filters) == 0 {
return dc.execContext(ctx, query, args)
}
return dc.filterChain.reset().DmConnectionExecContext(dc, ctx, query, args)
}
func (dc *DmConnection) Query(query string, args []driver.Value) (driver.Rows, error) {
if len(dc.filterChain.filters) == 0 {
return dc.query(query, args)
}
return dc.filterChain.reset().DmConnectionQuery(dc, query, args)
}
func (dc *DmConnection) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
if len(dc.filterChain.filters) == 0 {
return dc.queryContext(ctx, query, args)
}
return dc.filterChain.reset().DmConnectionQueryContext(dc, ctx, query, args)
}
func (dc *DmConnection) Prepare(query string) (driver.Stmt, error) {
if len(dc.filterChain.filters) == 0 {
return dc.prepare(query)
}
return dc.filterChain.reset().DmConnectionPrepare(dc, query)
}
func (dc *DmConnection) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
if len(dc.filterChain.filters) == 0 {
return dc.prepareContext(ctx, query)
}
return dc.filterChain.reset().DmConnectionPrepareContext(dc, ctx, query)
}
func (dc *DmConnection) ResetSession(ctx context.Context) error {
if len(dc.filterChain.filters) == 0 {
return dc.resetSession(ctx)
}
if err := dc.filterChain.reset().DmConnectionResetSession(dc, ctx); err != nil {
return driver.ErrBadConn
} else {
return nil
}
}
func (dc *DmConnection) CheckNamedValue(nv *driver.NamedValue) error {
if len(dc.filterChain.filters) == 0 {
return dc.checkNamedValue(nv)
}
return dc.filterChain.reset().DmConnectionCheckNamedValue(dc, nv)
}
func (dc *DmConnection) begin() (*DmConnection, error) {
return dc.beginTx(context.Background(), driver.TxOptions{driver.IsolationLevel(sql.LevelDefault), false})
}
func (dc *DmConnection) beginTx(ctx context.Context, opts driver.TxOptions) (*DmConnection, error) {
if err := dc.watchCancel(ctx); err != nil {
return nil, err
}
defer dc.finish()
err := dc.checkClosed()
if err != nil {
return nil, err
}
dc.autoCommit = false
if dc.ReadOnly != opts.ReadOnly {
dc.ReadOnly = opts.ReadOnly
var readonly = 0
if opts.ReadOnly {
readonly = 1
}
dc.exec(fmt.Sprintf("SP_SET_SESSION_READONLY(%d)", readonly), nil)
}
if dc.IsoLevel != int32(opts.Isolation) {
switch sql.IsolationLevel(opts.Isolation) {
case sql.LevelDefault:
dc.IsoLevel = int32(sql.LevelReadCommitted)
case sql.LevelReadUncommitted, sql.LevelReadCommitted, sql.LevelSerializable:
dc.IsoLevel = int32(opts.Isolation)
case sql.LevelRepeatableRead:
if dc.CompatibleMysql() {
dc.IsoLevel = int32(sql.LevelReadCommitted)
} else {
return nil, ECGO_INVALID_TRAN_ISOLATION.throw()
}
default:
return nil, ECGO_INVALID_TRAN_ISOLATION.throw()
}
err = dc.Access.Dm_build_565(dc)
if err != nil {
return nil, err
}
}
return dc, nil
}
func (dc *DmConnection) commit() error {
err := dc.checkClosed()
if err != nil {
return err
}
defer func() {
dc.autoCommit = dc.dmConnector.autoCommit
if dc.ReadOnly {
dc.exec("SP_SET_SESSION_READONLY(0)", nil)
}
}()
if !dc.autoCommit || !dc.trxFinish {
err = dc.Access.Commit()
if err != nil {
return err
}
dc.trxFinish = true
return nil
} else if !dc.dmConnector.alwayseAllowCommit {
return ECGO_COMMIT_IN_AUTOCOMMIT_MODE.throw()
}
return nil
}
func (dc *DmConnection) rollback() error {
err := dc.checkClosed()
if err != nil {
return err
}
defer func() {
dc.autoCommit = dc.dmConnector.autoCommit
if dc.ReadOnly {
dc.exec("SP_SET_SESSION_READONLY(0)", nil)
}
}()
if !dc.autoCommit {
err = dc.Access.Rollback()
if err != nil {
return err
}
dc.trxFinish = true
return nil
} else if !dc.dmConnector.alwayseAllowCommit {
return ECGO_ROLLBACK_IN_AUTOCOMMIT_MODE.throw()
}
return nil
}
func (dc *DmConnection) reconnect() error {
err := dc.Access.Close()
if err != nil {
return err
}
for _, stmt := range dc.stmtMap {
for id, rs := range stmt.rsMap {
rs.Close()
delete(stmt.rsMap, id)
}
}
var newConn *DmConnection
if dc.dmConnector.group != nil {
if newConn, err = dc.dmConnector.group.connect(dc.dmConnector); err != nil {
return err
}
} else {
newConn, err = dc.dmConnector.connect(context.Background())
}
oldMap := dc.stmtMap
newConn.mu = dc.mu
newConn.filterable = dc.filterable
*dc = *newConn
for _, stmt := range oldMap {
if stmt.closed {
continue
}
err = dc.Access.Dm_build_475(stmt)
if err != nil {
stmt.free()
continue
}
if stmt.prepared || stmt.paramCount > 0 {
if err = stmt.prepare(); err != nil {
continue
}
}
dc.stmtMap[stmt.id] = stmt
}
return nil
}
func (dc *DmConnection) cleanup() {
dc.close()
}
func (dc *DmConnection) close() error {
if !dc.closed.TrySet(true) {
return nil
}
util.AbsorbPanic(func() {
close(dc.closech)
})
if dc.Access == nil {
return nil
}
dc.rollback()
for _, stmt := range dc.stmtMap {
stmt.free()
}
dc.Access.Close()
return nil
}
func (dc *DmConnection) ping(ctx context.Context) error {
if err := dc.watchCancel(ctx); err != nil {
return err
}
defer dc.finish()
rows, err := dc.query("select 1", nil)
if err != nil {
return err
}
return rows.close()
}
func (dc *DmConnection) exec(query string, args []driver.Value) (*DmResult, error) {
err := dc.checkClosed()
if err != nil {
return nil, err
}
if args != nil && len(args) > 0 {
stmt, err := dc.prepare(query)
if err != nil {
return nil, err
}
defer stmt.close()
dc.lastExecInfo = stmt.execInfo
return stmt.exec(args)
} else {
r1, err := dc.executeInner(query, Dm_build_790)
if err != nil {
return nil, err
}
if r2, ok := r1.(*DmResult); ok {
return r2, nil
} else {
return nil, ECGO_NOT_EXEC_SQL.throw()
}
}
}
func (dc *DmConnection) execContext(ctx context.Context, query string, args []driver.NamedValue) (*DmResult, error) {
if err := dc.watchCancel(ctx); err != nil {
return nil, err
}
defer dc.finish()
err := dc.checkClosed()
if err != nil {
return nil, err
}
if args != nil && len(args) > 0 {
stmt, err := dc.prepare(query)
if err != nil {
return nil, err
}
defer stmt.close()
dc.lastExecInfo = stmt.execInfo
dargs, err := namedValueToValue(stmt, args)
if err != nil {
return nil, err
}
return stmt.exec(dargs)
} else {
r1, err := dc.executeInner(query, Dm_build_790)
if err != nil {
return nil, err
}
if r2, ok := r1.(*DmResult); ok {
return r2, nil
} else {
return nil, ECGO_NOT_EXEC_SQL.throw()
}
}
}
func (dc *DmConnection) query(query string, args []driver.Value) (*DmRows, error) {
err := dc.checkClosed()
if err != nil {
return nil, err
}
if args != nil && len(args) > 0 {
stmt, err := dc.prepare(query)
if err != nil {
return nil, err
}
dc.lastExecInfo = stmt.execInfo
stmt.innerUsed = true
return stmt.query(args)
} else {
r1, err := dc.executeInner(query, Dm_build_789)
if err != nil {
return nil, err
}
if r2, ok := r1.(*DmRows); ok {
return r2, nil
} else {
return nil, ECGO_NOT_QUERY_SQL.throw()
}
}
}
func (dc *DmConnection) queryContext(ctx context.Context, query string, args []driver.NamedValue) (*DmRows, error) {
if err := dc.watchCancel(ctx); err != nil {
return nil, err
}
defer dc.finish()
err := dc.checkClosed()
if err != nil {
return nil, err
}
if args != nil && len(args) > 0 {
stmt, err := dc.prepare(query)
if err != nil {
return nil, err
}
dc.lastExecInfo = stmt.execInfo
stmt.innerUsed = true
dargs, err := namedValueToValue(stmt, args)
if err != nil {
return nil, err
}
return stmt.query(dargs)
} else {
r1, err := dc.executeInner(query, Dm_build_789)
if err != nil {
return nil, err
}
if r2, ok := r1.(*DmRows); ok {
return r2, nil
} else {
return nil, ECGO_NOT_QUERY_SQL.throw()
}
}
}
func (dc *DmConnection) prepare(query string) (stmt *DmStatement, err error) {
if err = dc.checkClosed(); err != nil {
return
}
if stmt, err = NewDmStmt(dc, query); err != nil {
return
}
if err = stmt.prepare(); err != nil {
stmt.close()
stmt = nil
return
}
return
}
func (dc *DmConnection) prepareContext(ctx context.Context, query string) (*DmStatement, error) {
if err := dc.watchCancel(ctx); err != nil {
return nil, err
}
defer dc.finish()
return dc.prepare(query)
}
func (dc *DmConnection) resetSession(ctx context.Context) error {
if err := dc.watchCancel(ctx); err != nil {
return err
}
defer dc.finish()
err := dc.checkClosed()
if err != nil {
return err
}
return nil
}
func (dc *DmConnection) checkNamedValue(nv *driver.NamedValue) error {
var err error
var cvt = converter{dc, false}
nv.Value, err = cvt.ConvertValue(nv.Value)
dc.isBatch = cvt.isBatch
return err
}
func (dc *DmConnection) driverQuery(query string) (*DmStatement, *DmRows, error) {
stmt, err := NewDmStmt(dc, query)
if err != nil {
return nil, nil, err
}
stmt.innerUsed = true
stmt.innerExec = true
info, err := dc.Access.Dm_build_503(stmt, Dm_build_789)
if err != nil {
return nil, nil, err
}
dc.lastExecInfo = info
stmt.innerExec = false
return stmt, newDmRows(newInnerRows(0, stmt, info)), nil
}
func (dc *DmConnection) getIndexOnEPGroup() int32 {
if dc.dmConnector.group == nil || dc.dmConnector.group.epList == nil {
return -1
}
for i := 0; i < len(dc.dmConnector.group.epList); i++ {
ep := dc.dmConnector.group.epList[i]
if dc.dmConnector.host == ep.host && dc.dmConnector.port == ep.port {
return int32(i)
}
}
return -1
}
func (dc *DmConnection) getServerEncoding() string {
if dc.dmConnector.charCode != "" {
return dc.dmConnector.charCode
}
return dc.serverEncoding
}
func (dc *DmConnection) lobFetchAll() bool {
return dc.dmConnector.lobMode == 2
}
func (conn *DmConnection) CompatibleOracle() bool {
return conn.dmConnector.compatibleMode == COMPATIBLE_MODE_ORACLE
}
func (conn *DmConnection) CompatibleMysql() bool {
return conn.dmConnector.compatibleMode == COMPATIBLE_MODE_MYSQL
}
func (conn *DmConnection) cancel(err error) {
conn.canceled.Set(err)
conn.close()
}
func (conn *DmConnection) finish() {
if !conn.watching || conn.finished == nil {
return
}
select {
case conn.finished <- struct{}{}:
conn.watching = false
case <-conn.closech:
}
}
func (conn *DmConnection) startWatcher() {
watcher := make(chan context.Context, 1)
conn.watcher = watcher
finished := make(chan struct{})
conn.finished = finished
go func() {
for {
var ctx context.Context
select {
case ctx = <-watcher:
case <-conn.closech:
return
}
select {
case <-ctx.Done():
conn.cancel(ctx.Err())
case <-finished:
case <-conn.closech:
return
}
}
}()
}
func (conn *DmConnection) watchCancel(ctx context.Context) error {
if conn.watching {
conn.cleanup()
return nil
}
if err := ctx.Err(); err != nil {
return err
}
if ctx.Done() == nil {
return nil
}
if conn.watcher == nil {
return nil
}
conn.watching = true
conn.watcher <- ctx
return nil
}
type noCopy struct{}
func (*noCopy) Lock() {}
type atomicBool struct {
_noCopy noCopy
value uint32
}
func (ab *atomicBool) IsSet() bool {
return atomic.LoadUint32(&ab.value) > 0
}
func (ab *atomicBool) Set(value bool) {
if value {
atomic.StoreUint32(&ab.value, 1)
} else {
atomic.StoreUint32(&ab.value, 0)
}
}
func (ab *atomicBool) TrySet(value bool) bool {
if value {
return atomic.SwapUint32(&ab.value, 1) == 0
}
return atomic.SwapUint32(&ab.value, 0) > 0
}
type atomicError struct {
_noCopy noCopy
value atomic.Value
}
func (ae *atomicError) Set(value error) {
ae.value.Store(value)
}
func (ae *atomicError) Value() error {
if v := ae.value.Load(); v != nil {
return v.(error)
}
return nil
}
+985
View File
@@ -0,0 +1,985 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"bytes"
"context"
"database/sql/driver"
"net"
"net/url"
"os"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
"time"
"gitee.com/chunanyong/dm/util"
)
const (
TimeZoneKey = "timeZone"
EnRsCacheKey = "enRsCache"
RsCacheSizeKey = "rsCacheSize"
RsRefreshFreqKey = "rsRefreshFreq"
LoginPrimary = "loginPrimary"
LoginModeKey = "loginMode"
LoginStatusKey = "loginStatus"
LoginDscCtrlKey = "loginDscCtrl"
SwitchTimesKey = "switchTimes"
SwitchIntervalKey = "switchInterval"
EpSelectorKey = "epSelector"
PrimaryKey = "primaryKey"
KeywordsKey = "keywords"
CompressKey = "compress"
CompressIdKey = "compressId"
LoginEncryptKey = "loginEncrypt"
CommunicationEncryptKey = "communicationEncrypt"
DirectKey = "direct"
Dec2DoubleKey = "dec2double"
RwSeparateKey = "rwSeparate"
RwPercentKey = "rwPercent"
RwAutoDistributeKey = "rwAutoDistribute"
CompatibleModeKey = "compatibleMode"
CompatibleOraKey = "comOra"
CipherPathKey = "cipherPath"
DoSwitchKey = "doSwitch"
DriverReconnectKey = "driverReconnect"
ClusterKey = "cluster"
LanguageKey = "language"
DbAliveCheckFreqKey = "dbAliveCheckFreq"
RwStandbyRecoverTimeKey = "rwStandbyRecoverTime"
LogLevelKey = "logLevel"
LogDirKey = "logDir"
LogBufferPoolSizeKey = "logBufferPoolSize"
LogBufferSizeKey = "logBufferSize"
LogFlusherQueueSizeKey = "logFlusherQueueSize"
LogFlushFreqKey = "logFlushFreq"
StatEnableKey = "statEnable"
StatDirKey = "statDir"
StatFlushFreqKey = "statFlushFreq"
StatHighFreqSqlCountKey = "statHighFreqSqlCount"
StatSlowSqlCountKey = "statSlowSqlCount"
StatSqlMaxCountKey = "statSqlMaxCount"
StatSqlRemoveModeKey = "statSqlRemoveMode"
AddressRemapKey = "addressRemap"
UserRemapKey = "userRemap"
ConnectTimeoutKey = "connectTimeout"
LoginCertificateKey = "loginCertificate"
UrlKey = "url"
HostKey = "host"
PortKey = "port"
UserKey = "user"
PasswordKey = "password"
DialNameKey = "dialName"
RwStandbyKey = "rwStandby"
IsCompressKey = "isCompress"
RwHAKey = "rwHA"
RwIgnoreSqlKey = "rwIgnoreSql"
AppNameKey = "appName"
OsNameKey = "osName"
MppLocalKey = "mppLocal"
SocketTimeoutKey = "socketTimeout"
SessionTimeoutKey = "sessionTimeout"
ContinueBatchOnErrorKey = "continueBatchOnError"
BatchAllowMaxErrorsKey = "batchAllowMaxErrors"
EscapeProcessKey = "escapeProcess"
AutoCommitKey = "autoCommit"
MaxRowsKey = "maxRows"
RowPrefetchKey = "rowPrefetch"
BufPrefetchKey = "bufPrefetch"
LobModeKey = "LobMode"
StmtPoolSizeKey = "StmtPoolSize"
AlwayseAllowCommitKey = "AlwayseAllowCommit"
BatchTypeKey = "batchType"
BatchNotOnCallKey = "batchNotOnCall"
IsBdtaRSKey = "isBdtaRS"
ClobAsStringKey = "clobAsString"
SslCertPathKey = "sslCertPath"
SslKeyPathKey = "sslKeyPath"
SslFilesPathKey = "sslFilesPath"
KerberosLoginConfPathKey = "kerberosLoginConfPath"
UKeyNameKey = "uKeyName"
UKeyPinKey = "uKeyPin"
ColumnNameUpperCaseKey = "columnNameUpperCase"
ColumnNameCaseKey = "columnNameCase"
DatabaseProductNameKey = "databaseProductName"
OsAuthTypeKey = "osAuthType"
SchemaKey = "schema"
CatalogKey = "catalog"
ServerOptionKey = "serverOption"
ClobToBytesKey = "clobToBytes"
DO_SWITCH_OFF int32 = 0
DO_SWITCH_WHEN_CONN_ERROR int32 = 1
DO_SWITCH_WHEN_EP_RECOVER int32 = 2
CLUSTER_TYPE_NORMAL int32 = 0
CLUSTER_TYPE_RW int32 = 1
CLUSTER_TYPE_DW int32 = 2
CLUSTER_TYPE_DSC int32 = 3
CLUSTER_TYPE_MPP int32 = 4
EP_STATUS_OK int32 = 1
EP_STATUS_ERROR int32 = 2
LOGIN_MODE_PRIMARY_FIRST int32 = 0
LOGIN_MODE_PRIMARY_ONLY int32 = 1
LOGIN_MODE_STANDBY_ONLY int32 = 2
LOGIN_MODE_STANDBY_FIRST int32 = 3
LOGIN_MODE_NORMAL_FIRST int32 = 4
SERVER_MODE_NORMAL int32 = 0
SERVER_MODE_PRIMARY int32 = 1
SERVER_MODE_STANDBY int32 = 2
SERVER_STATUS_MOUNT int32 = 3
SERVER_STATUS_OPEN int32 = 4
SERVER_STATUS_SUSPEND int32 = 5
COMPATIBLE_MODE_ORACLE int = 1
COMPATIBLE_MODE_MYSQL int = 2
LANGUAGE_CN int = 0
LANGUAGE_EN int = 1
LANGUAGE_CNT_HK = 2
COLUMN_NAME_NATURAL_CASE = 0
COLUMN_NAME_UPPER_CASE = 1
COLUMN_NAME_LOWER_CASE = 2
RW_SEPARATE_OFF int32 = 0
RW_SEPARATE_CLIENT int32 = 1
RW_SEPARATE_EP_GROUP int32 = 2
RW_SEPARATE_DB int32 = 3
RW_SEPARATE_DB_APPLY_WAIT int32 = 4
RW_SEPARATE_USER_DEFINED int32 = 5
compressDef = Dm_build_784
compressIDDef = Dm_build_785
charCodeDef = ""
enRsCacheDef = false
rsCacheSizeDef = 20
rsRefreshFreqDef = 10
loginModeDef = LOGIN_MODE_NORMAL_FIRST
loginStatusDef = 0
loginEncryptDef = true
loginCertificateDef = ""
dec2DoubleDef = false
rwHADef = false
rwStandbyDef = false
rwSeparateDef = RW_SEPARATE_OFF
rwPercentDef = 25
rwAutoDistributeDef = true
rwStandbyRecoverTimeDef = 1000
cipherPathDef = ""
urlDef = ""
userDef = "SYSDBA"
passwordDef = "SYSDBA"
hostDef = "localhost"
portDef = DEFAULT_PORT
appNameDef = ""
mppLocalDef = false
socketTimeoutDef = 0
connectTimeoutDef = 5000
sessionTimeoutDef = 0
osAuthTypeDef = Dm_build_767
continueBatchOnErrorDef = false
escapeProcessDef = false
autoCommitDef = true
maxRowsDef = 0
rowPrefetchDef = Dm_build_768
bufPrefetchDef = 0
lobModeDef = 1
stmtPoolMaxSizeDef = 15
alwayseAllowCommitDef = true
isBdtaRSDef = false
kerberosLoginConfPathDef = ""
uKeyNameDef = ""
uKeyPinDef = ""
databaseProductNameDef = ""
caseSensitiveDef = true
compatibleModeDef = 0
)
type DmConnector struct {
filterable
mu sync.Mutex
dmDriver *DmDriver
compress int
compressID int8
newClientType bool
charCode string
enRsCache bool
rsCacheSize int
rsRefreshFreq int
loginMode int32
loginStatus int
loginDscCtrl bool
switchTimes int32
switchInterval int
epSelector int32
keyWords []string
loginEncrypt bool
loginCertificate string
dec2Double bool
rwHA bool
rwStandby bool
rwSeparate int32
rwPercent int32
rwAutoDistribute bool
rwStandbyRecoverTime int
rwIgnoreSql bool
doSwitch int32
driverReconnect bool
cluster int32
cipherPath string
url string
user string
password string
dialName string
host string
group *epGroup
port int32
appName string
osName string
mppLocal bool
socketTimeout int
connectTimeout int
sessionTimeout int
osAuthType byte
continueBatchOnError bool
batchAllowMaxErrors int32
escapeProcess bool
autoCommit bool
maxRows int
rowPrefetch int
bufPrefetch int
lobMode int
stmtPoolMaxSize int
alwayseAllowCommit bool
batchType int
batchNotOnCall bool
isBdtaRS bool
sslCertPath string
sslKeyPath string
sslFilesPath string
kerberosLoginConfPath string
uKeyName string
uKeyPin string
svcConfPath string
columnNameCase int
caseSensitive bool
compatibleMode int
localTimezone int16
schema string
catalog string
logLevel int
logDir string
logFlushFreq int
logFlushQueueSize int
logBufferSize int
statEnable bool
statDir string
statFlushFreq int
statSlowSqlCount int
statHighFreqSqlCount int
statSqlMaxCount int
statSqlRemoveMode int
serverOption []string
clobToBytes bool
}
func (c *DmConnector) init() *DmConnector {
c.compress = compressDef
c.compressID = compressIDDef
c.charCode = charCodeDef
c.enRsCache = enRsCacheDef
c.rsCacheSize = rsCacheSizeDef
c.rsRefreshFreq = rsRefreshFreqDef
c.loginMode = loginModeDef
c.loginStatus = loginStatusDef
c.loginDscCtrl = false
c.switchTimes = 1
c.switchInterval = 200
c.epSelector = 0
c.keyWords = nil
c.loginEncrypt = loginEncryptDef
c.loginCertificate = loginCertificateDef
c.dec2Double = dec2DoubleDef
c.rwHA = rwHADef
c.rwStandby = rwStandbyDef
c.rwSeparate = rwSeparateDef
c.rwPercent = rwPercentDef
c.rwAutoDistribute = rwAutoDistributeDef
c.rwStandbyRecoverTime = rwStandbyRecoverTimeDef
c.rwIgnoreSql = false
c.doSwitch = DO_SWITCH_WHEN_CONN_ERROR
c.driverReconnect = false
c.cluster = CLUSTER_TYPE_NORMAL
c.cipherPath = cipherPathDef
c.url = urlDef
c.user = userDef
c.password = passwordDef
c.host = hostDef
c.port = portDef
c.appName = appNameDef
c.osName = runtime.GOOS
c.mppLocal = mppLocalDef
c.socketTimeout = socketTimeoutDef
c.connectTimeout = connectTimeoutDef
c.sessionTimeout = sessionTimeoutDef
c.osAuthType = osAuthTypeDef
c.continueBatchOnError = continueBatchOnErrorDef
c.batchAllowMaxErrors = 0
c.escapeProcess = escapeProcessDef
c.autoCommit = autoCommitDef
c.maxRows = maxRowsDef
c.rowPrefetch = rowPrefetchDef
c.bufPrefetch = bufPrefetchDef
c.lobMode = lobModeDef
c.stmtPoolMaxSize = stmtPoolMaxSizeDef
c.alwayseAllowCommit = alwayseAllowCommitDef
c.batchType = 1
c.batchNotOnCall = false
c.isBdtaRS = isBdtaRSDef
c.kerberosLoginConfPath = kerberosLoginConfPathDef
c.uKeyName = uKeyNameDef
c.uKeyPin = uKeyPinDef
c.columnNameCase = COLUMN_NAME_NATURAL_CASE
c.caseSensitive = caseSensitiveDef
c.compatibleMode = compatibleModeDef
_, tzs := time.Now().Zone()
c.localTimezone = int16(tzs / 60)
c.idGenerator = dmConntorIDGenerator
c.logDir = LogDirDef
c.logFlushFreq = LogFlushFreqDef
c.logFlushQueueSize = LogFlushQueueSizeDef
c.logBufferSize = LogBufferSizeDef
c.statEnable = StatEnableDef
c.statDir = StatDirDef
c.statFlushFreq = StatFlushFreqDef
c.statSlowSqlCount = StatSlowSqlCountDef
c.statHighFreqSqlCount = StatHighFreqSqlCountDef
c.statSqlMaxCount = StatSqlMaxCountDef
c.statSqlRemoveMode = StatSqlRemoveModeDef
return c
}
func (c *DmConnector) setAttributes(props *Properties) error {
if props == nil || props.Len() == 0 {
return nil
}
c.url = props.GetTrimString(UrlKey, c.url)
c.host = props.GetTrimString(HostKey, c.host)
c.port = int32(props.GetInt(PortKey, int(c.port), 0, 65535))
c.user = props.GetString(UserKey, c.user)
c.password = props.GetString(PasswordKey, c.password)
c.dialName = props.GetString(DialNameKey, "")
c.rwStandby = props.GetBool(RwStandbyKey, c.rwStandby)
if b := props.GetBool(IsCompressKey, false); b {
c.compress = Dm_build_783
}
c.compress = props.GetInt(CompressKey, c.compress, 0, 2)
c.compressID = int8(props.GetInt(CompressIdKey, int(c.compressID), 0, 1))
c.enRsCache = props.GetBool(EnRsCacheKey, c.enRsCache)
c.localTimezone = int16(props.GetInt(TimeZoneKey, int(c.localTimezone), -779, 840))
c.rsCacheSize = props.GetInt(RsCacheSizeKey, c.rsCacheSize, 0, int(INT32_MAX))
c.rsRefreshFreq = props.GetInt(RsRefreshFreqKey, c.rsRefreshFreq, 0, int(INT32_MAX))
c.loginMode = int32(props.GetInt(LoginModeKey, int(c.loginMode), 0, 4))
c.loginStatus = props.GetInt(LoginStatusKey, c.loginStatus, 0, int(INT32_MAX))
c.loginDscCtrl = props.GetBool(LoginDscCtrlKey, c.loginDscCtrl)
c.switchTimes = int32(props.GetInt(SwitchTimesKey, int(c.switchTimes), 0, int(INT32_MAX)))
c.switchInterval = props.GetInt(SwitchIntervalKey, c.switchInterval, 0, int(INT32_MAX))
c.epSelector = int32(props.GetInt(EpSelectorKey, int(c.epSelector), 0, 1))
c.loginEncrypt = props.GetBool(LoginEncryptKey, c.loginEncrypt)
c.loginCertificate = props.GetTrimString(LoginCertificateKey, c.loginCertificate)
c.dec2Double = props.GetBool(Dec2DoubleKey, c.dec2Double)
parseLanguage(props.GetString(LanguageKey, ""))
c.rwSeparate = int32(props.GetInt(RwSeparateKey, int(c.rwSeparate), 0, 4))
c.rwAutoDistribute = props.GetBool(RwAutoDistributeKey, c.rwAutoDistribute)
c.rwPercent = int32(props.GetInt(RwPercentKey, int(c.rwPercent), 0, 100))
c.rwHA = props.GetBool(RwHAKey, c.rwHA)
c.rwStandbyRecoverTime = props.GetInt(RwStandbyRecoverTimeKey, c.rwStandbyRecoverTime, 0, int(INT32_MAX))
c.rwIgnoreSql = props.GetBool(RwIgnoreSqlKey, c.rwIgnoreSql)
c.doSwitch = int32(props.GetInt(DoSwitchKey, int(c.doSwitch), 0, 2))
c.driverReconnect = props.GetBool(DriverReconnectKey, c.driverReconnect)
c.parseCluster(props)
c.cipherPath = props.GetTrimString(CipherPathKey, c.cipherPath)
if props.GetBool(CompatibleOraKey, false) {
c.compatibleMode = int(COMPATIBLE_MODE_ORACLE)
}
c.parseCompatibleMode(props)
c.keyWords = props.GetStringArray(KeywordsKey, c.keyWords)
c.appName = props.GetTrimString(AppNameKey, c.appName)
c.osName = props.GetTrimString(OsNameKey, c.osName)
c.mppLocal = props.GetBool(MppLocalKey, c.mppLocal)
c.socketTimeout = props.GetInt(SocketTimeoutKey, c.socketTimeout, 0, int(INT32_MAX))
c.connectTimeout = props.GetInt(ConnectTimeoutKey, c.connectTimeout, 0, int(INT32_MAX))
c.sessionTimeout = props.GetInt(SessionTimeoutKey, c.sessionTimeout, 0, int(INT32_MAX))
err := c.parseOsAuthType(props)
if err != nil {
return err
}
c.continueBatchOnError = props.GetBool(ContinueBatchOnErrorKey, c.continueBatchOnError)
c.batchAllowMaxErrors = int32(props.GetInt(BatchAllowMaxErrorsKey, int(c.batchAllowMaxErrors), 0, int(INT32_MAX)))
c.escapeProcess = props.GetBool(EscapeProcessKey, c.escapeProcess)
c.autoCommit = props.GetBool(AutoCommitKey, c.autoCommit)
c.maxRows = props.GetInt(MaxRowsKey, c.maxRows, 0, int(INT32_MAX))
c.rowPrefetch = props.GetInt(RowPrefetchKey, c.rowPrefetch, 0, int(INT32_MAX))
c.bufPrefetch = props.GetInt(BufPrefetchKey, c.bufPrefetch, int(Dm_build_769), int(Dm_build_770))
c.lobMode = props.GetInt(LobModeKey, c.lobMode, 1, 2)
c.stmtPoolMaxSize = props.GetInt(StmtPoolSizeKey, c.stmtPoolMaxSize, 0, int(INT32_MAX))
c.alwayseAllowCommit = props.GetBool(AlwayseAllowCommitKey, c.alwayseAllowCommit)
c.batchType = props.GetInt(BatchTypeKey, c.batchType, 1, 2)
c.batchNotOnCall = props.GetBool(BatchNotOnCallKey, c.batchNotOnCall)
c.isBdtaRS = props.GetBool(IsBdtaRSKey, c.isBdtaRS)
c.sslFilesPath = props.GetTrimString(SslFilesPathKey, c.sslFilesPath)
c.sslCertPath = props.GetTrimString(SslCertPathKey, c.sslCertPath)
if c.sslCertPath == "" && c.sslFilesPath != "" {
c.sslCertPath = filepath.Join(c.sslFilesPath, "client-cert.pem")
}
c.sslKeyPath = props.GetTrimString(SslKeyPathKey, c.sslKeyPath)
if c.sslKeyPath == "" && c.sslFilesPath != "" {
c.sslKeyPath = filepath.Join(c.sslKeyPath, "client-key.pem")
}
c.kerberosLoginConfPath = props.GetTrimString(KerberosLoginConfPathKey, c.kerberosLoginConfPath)
c.uKeyName = props.GetTrimString(UKeyNameKey, c.uKeyName)
c.uKeyPin = props.GetTrimString(UKeyPinKey, c.uKeyPin)
c.svcConfPath = props.GetString("confPath", "")
if props.GetBool(ColumnNameUpperCaseKey, false) {
c.columnNameCase = COLUMN_NAME_UPPER_CASE
}
v := props.GetTrimString(ColumnNameCaseKey, "")
if util.StringUtil.EqualsIgnoreCase(v, "upper") {
c.columnNameCase = COLUMN_NAME_UPPER_CASE
} else if util.StringUtil.EqualsIgnoreCase(v, "lower") {
c.columnNameCase = COLUMN_NAME_LOWER_CASE
}
c.schema = props.GetTrimString(SchemaKey, c.schema)
c.catalog = props.GetTrimString(CatalogKey, c.catalog)
c.logLevel = ParseLogLevel(props)
LogLevel = c.logLevel
c.logDir = util.StringUtil.FormatDir(props.GetTrimString(LogDirKey, LogDirDef))
LogDir = c.logDir
c.logBufferSize = props.GetInt(LogBufferSizeKey, LogBufferSizeDef, 1, int(INT32_MAX))
LogBufferSize = c.logBufferSize
c.logFlushFreq = props.GetInt(LogFlushFreqKey, LogFlushFreqDef, 1, int(INT32_MAX))
LogFlushFreq = c.logFlushFreq
c.logFlushQueueSize = props.GetInt(LogFlusherQueueSizeKey, LogFlushQueueSizeDef, 1, int(INT32_MAX))
LogFlushQueueSize = c.logFlushQueueSize
c.statEnable = props.GetBool(StatEnableKey, StatEnableDef)
StatEnable = c.statEnable
c.statDir = util.StringUtil.FormatDir(props.GetTrimString(StatDirKey, StatDirDef))
StatDir = c.statDir
c.statFlushFreq = props.GetInt(StatFlushFreqKey, StatFlushFreqDef, 1, int(INT32_MAX))
StatFlushFreq = c.statFlushFreq
c.statHighFreqSqlCount = props.GetInt(StatHighFreqSqlCountKey, StatHighFreqSqlCountDef, 0, 1000)
StatHighFreqSqlCount = c.statHighFreqSqlCount
c.statSlowSqlCount = props.GetInt(StatSlowSqlCountKey, StatSlowSqlCountDef, 0, 1000)
StatSlowSqlCount = c.statSlowSqlCount
c.statSqlMaxCount = props.GetInt(StatSqlMaxCountKey, StatSqlMaxCountDef, 0, 100000)
StatSqlMaxCount = c.statSqlMaxCount
c.parseStatSqlRemoveMode(props)
c.parseServerOption(props)
c.clobToBytes = props.GetBool(ClobToBytesKey, ClobToBytesDef)
return nil
}
func (c *DmConnector) parseServerOption(props *Properties) {
value := props.GetString(ServerOptionKey, "")
if len(value) == 0 {
return
}
if strings.HasPrefix(value, "{") && strings.HasSuffix(value, "}") {
value = strings.TrimPrefix(value, "{")
value = strings.TrimSuffix(value, "}")
}
c.serverOption = strings.Split(value, ",")
}
func (c *DmConnector) parseOsAuthType(props *Properties) error {
value := props.GetString(OsAuthTypeKey, "")
if value != "" && !util.StringUtil.IsDigit(value) {
if util.StringUtil.EqualsIgnoreCase(value, "ON") {
c.osAuthType = Dm_build_767
} else if util.StringUtil.EqualsIgnoreCase(value, "SYSDBA") {
c.osAuthType = Dm_build_763
} else if util.StringUtil.EqualsIgnoreCase(value, "SYSAUDITOR") {
c.osAuthType = Dm_build_765
} else if util.StringUtil.EqualsIgnoreCase(value, "SYSSSO") {
c.osAuthType = Dm_build_764
} else if util.StringUtil.EqualsIgnoreCase(value, "AUTO") {
c.osAuthType = Dm_build_766
} else if util.StringUtil.EqualsIgnoreCase(value, "OFF") {
c.osAuthType = Dm_build_762
}
} else {
c.osAuthType = byte(props.GetInt(OsAuthTypeKey, int(c.osAuthType), 0, 4))
}
if c.user == "" && c.osAuthType == Dm_build_762 {
c.user = "SYSDBA"
} else if c.osAuthType != Dm_build_762 && c.user != "" {
return ECGO_OSAUTH_ERROR.throw()
} else if c.osAuthType != Dm_build_762 {
c.user = os.Getenv("user")
c.password = ""
}
return nil
}
func (c *DmConnector) parseCompatibleMode(props *Properties) {
value := props.GetString(CompatibleModeKey, "")
if value != "" && !util.StringUtil.IsDigit(value) {
if util.StringUtil.EqualsIgnoreCase(value, "oracle") {
c.compatibleMode = COMPATIBLE_MODE_ORACLE
} else if util.StringUtil.EqualsIgnoreCase(value, "mysql") {
c.compatibleMode = COMPATIBLE_MODE_MYSQL
}
} else {
c.compatibleMode = props.GetInt(CompatibleModeKey, c.compatibleMode, 0, 2)
}
}
func (c *DmConnector) parseStatSqlRemoveMode(props *Properties) {
value := props.GetString(StatSqlRemoveModeKey, "")
if value != "" && !util.StringUtil.IsDigit(value) {
if util.StringUtil.EqualsIgnoreCase("oldest", value) || util.StringUtil.EqualsIgnoreCase("eldest", value) {
c.statSqlRemoveMode = STAT_SQL_REMOVE_OLDEST
} else if util.StringUtil.EqualsIgnoreCase("latest", value) {
c.statSqlRemoveMode = STAT_SQL_REMOVE_LATEST
}
} else {
c.statSqlRemoveMode = props.GetInt(StatSqlRemoveModeKey, StatSqlRemoveModeDef, 1, 2)
}
}
func (c *DmConnector) parseCluster(props *Properties) {
value := props.GetTrimString(ClusterKey, "")
if util.StringUtil.EqualsIgnoreCase(value, "DSC") {
c.cluster = CLUSTER_TYPE_DSC
} else if util.StringUtil.EqualsIgnoreCase(value, "RW") {
c.cluster = CLUSTER_TYPE_RW
} else if util.StringUtil.EqualsIgnoreCase(value, "DW") {
c.cluster = CLUSTER_TYPE_DW
} else if util.StringUtil.EqualsIgnoreCase(value, "MPP") {
c.cluster = CLUSTER_TYPE_MPP
} else {
c.cluster = CLUSTER_TYPE_NORMAL
}
}
func (c *DmConnector) parseDSN(dsn string) (*Properties, string, string, error) {
var dsnProps = NewProperties()
if strings.Index(dsn, "dm://") != 0 {
return nil, "", "", DSN_INVALID_SCHEMA
}
dsn = dsn[5:]
urlString := dsn
queryIndex := strings.LastIndex(dsn, "?")
if queryIndex > 0 {
urlString = dsn[:queryIndex]
var queryString = dsn[queryIndex+1:]
for _, kvString := range strings.Split(queryString, "&") {
kv := strings.SplitN(kvString, "=", 2)
if kv != nil && len(kv) > 1 {
dsnProps.Set(kv[0], kv[1])
}
}
}
hostString := urlString
atIndex := strings.LastIndex(urlString, "@")
if atIndex == -1 {
return nil, "", "", DSN_INVALID_FORMAT
} else {
var userString = urlString[:atIndex]
hostString = urlString[atIndex+1:]
kv := strings.SplitN(userString, ":", 2)
if kv != nil && len(kv) > 1 {
c.user = kv[0]
c.password = kv[1]
}
}
if catalogIndex := strings.LastIndex(hostString, "/"); catalogIndex > 0 {
return dsnProps, hostString[0:catalogIndex], hostString[catalogIndex+1:], nil
}
return dsnProps, hostString, "", nil
}
func (c *DmConnector) BuildDSN() string {
var buf bytes.Buffer
buf.WriteString("dm://")
if len(c.user) > 0 {
buf.WriteString(url.QueryEscape(c.user))
if len(c.password) > 0 {
buf.WriteByte(':')
buf.WriteString(url.QueryEscape(c.password))
}
buf.WriteByte('@')
}
if len(c.host) > 0 {
buf.WriteString(c.host)
if c.port > 0 {
buf.WriteByte(':')
buf.WriteString(strconv.Itoa(int(c.port)))
}
}
hasParam := false
if c.connectTimeout > 0 {
if hasParam {
buf.WriteString("&timeout=")
} else {
buf.WriteString("?timeout=")
hasParam = true
}
buf.WriteString(strconv.Itoa(c.connectTimeout))
}
return buf.String()
}
func (c *DmConnector) mergeConfigs(dsn string) error {
props, host, catalog, err := c.parseDSN(dsn)
if err != nil {
return err
}
driverInit(props.GetString("svcConfPath", ""))
addressRemapStr := props.GetTrimString(AddressRemapKey, "")
userRemapStr := props.GetTrimString(UserRemapKey, "")
if addressRemapStr == "" {
addressRemapStr = GlobalProperties.GetTrimString(AddressRemapKey, "")
}
if userRemapStr == "" {
userRemapStr = GlobalProperties.GetTrimString(UserRemapKey, "")
}
host = c.remap(host, addressRemapStr)
c.user = c.remap(c.user, userRemapStr)
if a := props.GetTrimString(host, ""); a != "" {
if strings.HasPrefix(a, "(") && strings.HasSuffix(a, ")") {
a = strings.TrimSpace(a[1 : len(a)-1])
}
c.group = parseServerName(host, a)
if c.group != nil {
c.group.props = NewProperties()
c.group.props.SetProperties(GlobalProperties)
}
} else if group, ok := ServerGroupMap.Load(strings.ToLower(host)); ok {
c.group = group.(*epGroup)
} else {
host, port, err := net.SplitHostPort(host)
if err == nil {
ip := net.ParseIP(host)
var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
if ip != nil && len(ip) == net.IPv6len && !bytes.Equal(ip[0:12], v4InV6Prefix) {
host = "[" + host + "]"
}
}
c.host = host
tmpPort, err := strconv.Atoi(port)
if err != nil {
c.port = portDef
} else {
c.port = int32(tmpPort)
}
if len(catalog) > 0 {
c.schema = catalog
}
c.group = newEPGroup(c.host+":"+strconv.Itoa(int(c.port)), []*ep{newEP(c.host, c.port)})
}
props.SetDiffProperties(c.group.props)
props.SetDiffProperties(GlobalProperties)
if c.rwSeparate = int32(props.GetInt(RwSeparateKey, 0, 0, 5)); c.rwSeparate > RW_SEPARATE_OFF {
props.SetIfNotExist(LoginModeKey, strconv.Itoa(int(LOGIN_MODE_PRIMARY_ONLY)))
props.SetIfNotExist(LoginStatusKey, strconv.Itoa(int(SERVER_STATUS_OPEN)))
props.SetIfNotExist(DoSwitchKey, "true")
}
if err = c.setAttributes(props); err != nil {
return err
}
return nil
}
func (c *DmConnector) remap(origin string, cfgStr string) string {
if cfgStr == "" || origin == "" {
return origin
}
maps := regexp.MustCompile("\\(.*?,.*?\\)").FindAllString(cfgStr, -1)
for _, kvStr := range maps {
kv := strings.Split(strings.TrimSpace(kvStr[1:len(kvStr)-1]), ",")
if util.StringUtil.Equals(strings.TrimSpace(kv[0]), origin) {
return strings.TrimSpace(kv[1])
}
}
return origin
}
func (c *DmConnector) Connect(ctx context.Context) (driver.Conn, error) {
c.mu.Lock()
defer c.mu.Unlock()
return c.filterChain.reset().DmConnectorConnect(c, ctx)
}
func (c *DmConnector) Driver() driver.Driver {
c.mu.Lock()
defer c.mu.Unlock()
return c.filterChain.reset().DmConnectorDriver(c)
}
func (c *DmConnector) connect(ctx context.Context) (*DmConnection, error) {
if c.group != nil && len(c.group.epList) > 0 {
return c.group.connect(c)
} else {
return c.connectSingle(ctx)
}
}
func (c *DmConnector) driver() *DmDriver {
return c.dmDriver
}
func (c *DmConnector) connectSingle(ctx context.Context) (*DmConnection, error) {
var err error
var dc = &DmConnection{
closech: make(chan struct{}),
dmConnector: c,
autoCommit: c.autoCommit,
}
dc.createFilterChain(c, nil)
dc.objId = -1
dc.init()
dc.Access, err = dm_build_426(ctx, dc)
if err != nil {
return nil, err
}
dc.startWatcher()
if err = dc.watchCancel(ctx); err != nil {
return nil, err
}
defer dc.finish()
if err = dc.Access.dm_build_471(); err != nil {
if !dc.closed.IsSet() {
close(dc.closech)
if dc.Access != nil {
dc.Access.Close()
}
dc.closed.Set(true)
}
return nil, err
}
if c.schema != "" {
_, err = dc.exec("set schema \""+util.StringUtil.ProcessDoubleQuoteOfName(c.schema)+"\"", nil)
if err != nil {
dc.cleanup()
return nil, err
}
}
return dc, nil
}
+456
View File
@@ -0,0 +1,456 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"database/sql/driver"
"math/big"
"reflect"
"strconv"
"strings"
)
const (
XDEC_MAX_PREC int = 40
XDEC_SIZE = 21
FLAG_ZERO int = 0x80
FLAG_POSITIVE int = 0xC1
FLAG_NEGTIVE int = 0x3E
POSITIVE_EXP_MAX = 0xff - FLAG_POSITIVE
EXP_MAX int = 0xFF - 1 - FLAG_POSITIVE
EXP_MIN int = FLAG_NEGTIVE + 1 - 0x7F
NUM_POSITIVE int = 1
NUM_NEGTIVE int = 101
)
type DmDecimal struct {
sign int
weight int
prec int
scale int
digits string
Valid bool
}
func NewDecimalFromInt64(x int64) (*DmDecimal, error) {
return NewDecimalFromBigInt(big.NewInt(x))
}
func (d DmDecimal) ToInt64() int64 {
return d.ToBigInt().Int64()
}
func NewDecimalFromFloat64(x float64) (*DmDecimal, error) {
return NewDecimalFromBigFloat(big.NewFloat(x))
}
func (d DmDecimal) ToFloat64() float64 {
f, _ := d.ToBigFloat().Float64()
return f
}
func NewDecimalFromBigInt(bigInt *big.Int) (*DmDecimal, error) {
return newDecimal(bigInt, len(bigInt.String()), 0)
}
func (d DmDecimal) ToBigInt() *big.Int {
if d.isZero() {
return big.NewInt(0)
}
var digits = d.digits
if d.sign < 0 {
digits = "-" + digits
}
i1, ok := new(big.Int).SetString(digits, 10)
if !ok {
return nil
}
if d.weight > 0 {
i2, ok := new(big.Int).SetString("1"+strings.Repeat("0", d.weight), 10)
if !ok {
return nil
}
i1.Mul(i1, i2)
} else if d.weight < 0 {
i2, ok := new(big.Int).SetString("1"+strings.Repeat("0", -d.weight), 10)
if !ok {
return nil
}
i1.Quo(i1, i2)
}
return i1
}
func NewDecimalFromBigFloat(bigFloat *big.Float) (*DmDecimal, error) {
return newDecimal(bigFloat, int(bigFloat.Prec()), int(bigFloat.Prec()))
}
func (d DmDecimal) ToBigFloat() *big.Float {
if d.isZero() {
return big.NewFloat(0.0)
}
var digits = d.digits
if d.sign < 0 {
digits = "-" + digits
}
f1, ok := new(big.Float).SetString(digits)
if !ok {
return nil
}
if d.weight > 0 {
f2, ok := new(big.Float).SetString("1" + strings.Repeat("0", d.weight))
if !ok {
return nil
}
f1.Mul(f1, f2)
} else if d.weight < 0 {
f2, ok := new(big.Float).SetString("1" + strings.Repeat("0", -d.weight))
if !ok {
return nil
}
f1.Quo(f1, f2)
}
return f1
}
func NewDecimalFromString(s string) (*DmDecimal, error) {
num, ok := new(big.Float).SetString(strings.TrimSpace(s))
if !ok {
return nil, ECGO_DATA_CONVERTION_ERROR.throw()
}
return NewDecimalFromBigFloat(num)
}
func (d DmDecimal) String() string {
if d.isZero() {
return "0"
}
digitsStr := d.digits
if d.weight > 0 {
digitsStr = digitsStr + strings.Repeat("0", d.weight)
} else if d.weight < 0 {
if len(digitsStr) < -d.weight {
digitsStr = strings.Repeat("0", -d.weight-len(digitsStr)+1) + digitsStr
}
indexOfDot := len(digitsStr) + d.weight
digitsStr = digitsStr[:indexOfDot] + "." + digitsStr[indexOfDot:]
}
if digitsStr[0] == '0' && digitsStr[1] != '.' {
digitsStr = digitsStr[1:]
}
if digitsStr[len(digitsStr)-1] == '0' && strings.IndexRune(digitsStr, '.') >= 0 {
digitsStr = digitsStr[0 : len(digitsStr)-1]
}
if d.sign < 0 {
digitsStr = "-" + digitsStr
}
return digitsStr
}
func (d DmDecimal) Sign() int {
return d.sign
}
func (dest *DmDecimal) Scan(src interface{}) error {
if dest == nil {
return ECGO_STORE_IN_NIL_POINTER.throw()
}
switch src := src.(type) {
case nil:
*dest = *new(DmDecimal)
(*dest).Valid = false
return nil
case int, int8, int16, int32, int64:
d, err := NewDecimalFromInt64(reflect.ValueOf(src).Int())
if err != nil {
return err
}
*dest = *d
return nil
case uint, uint8, uint16, uint32, uint64:
d, err := NewDecimalFromBigInt(new(big.Int).SetUint64(reflect.ValueOf(src).Uint()))
if err != nil {
return err
}
*dest = *d
return nil
case float32, float64:
d, err := NewDecimalFromFloat64(reflect.ValueOf(src).Float())
if err != nil {
return err
}
*dest = *d
return nil
case string:
d, err := NewDecimalFromString(src)
if err != nil {
return err
}
*dest = *d
return nil
case *DmDecimal:
*dest = *src
return nil
default:
return UNSUPPORTED_SCAN
}
}
func (d DmDecimal) Value() (driver.Value, error) {
if !d.Valid {
return nil, nil
}
return d, nil
}
func newDecimal(dec interface{}, prec int, scale int) (*DmDecimal, error) {
d := &DmDecimal{
prec: prec,
scale: scale,
Valid: true,
}
if isFloat(DECIMAL, scale) {
d.prec = getFloatPrec(prec)
d.scale = -1
}
switch de := dec.(type) {
case *big.Int:
d.sign = de.Sign()
if d.isZero() {
return d, nil
}
str := de.String()
if d.sign < 0 {
str = str[1:]
}
if err := checkPrec(len(str), prec); err != nil {
return d, err
}
i := 0
istart := len(str) - 1
for i = istart; i > 0; i-- {
if str[i] != '0' {
break
}
}
str = str[:i+1]
d.weight += istart - i
if isOdd(d.weight) {
str += "0"
d.weight -= 1
}
if isOdd(len(str)) {
str = "0" + str
}
d.digits = str
case *big.Float:
d.sign = de.Sign()
if d.isZero() {
return d, nil
}
str := de.Text('f', -1)
if d.sign < 0 {
str = str[1:]
}
pointIndex := strings.IndexByte(str, '.')
i, istart, length := 0, 0, len(str)
if pointIndex != -1 {
if str[0] == '0' {
istart = 2
for i = istart; i < length; i++ {
if str[i] != '0' {
break
}
}
str = str[i:]
d.weight -= i - istart + len(str)
} else {
str = str[:pointIndex] + str[pointIndex+1:]
d.weight -= length - pointIndex - 1
}
}
length = len(str)
istart = length - 1
for i = istart; i > 0; i-- {
if str[i] != '0' {
break
}
}
str = str[:i+1] + str[length:]
d.weight += istart - i
if isOdd(d.weight) {
str += "0"
d.weight -= 1
}
if isOdd(len(str)) {
str = "0" + str
}
d.digits = str
case []byte:
return decodeDecimal(de, prec, scale)
}
return d, nil
}
func (d DmDecimal) encodeDecimal() ([]byte, error) {
if d.isZero() {
return []byte{byte(FLAG_ZERO)}, nil
}
exp := (d.weight+len(d.digits))/2 - 1
var realExpMax int
if d.sign == NUM_POSITIVE {
realExpMax = POSITIVE_EXP_MAX
} else {
realExpMax = EXP_MAX
}
if exp > realExpMax || exp < EXP_MIN {
return nil, ECGO_DATA_TOO_LONG.throw()
}
validLen := len(d.digits)/2 + 1
if d.sign < 0 && validLen >= XDEC_SIZE {
validLen = XDEC_SIZE - 1
} else if validLen > XDEC_SIZE {
validLen = XDEC_SIZE
}
retLen := validLen
if d.sign < 0 {
retLen = validLen + 1
}
retBytes := make([]byte, retLen)
if d.sign > 0 {
retBytes[0] = byte(exp + FLAG_POSITIVE)
} else {
retBytes[0] = byte(FLAG_NEGTIVE - exp)
}
ibytes := 1
for ichar := 0; ibytes < validLen; {
digit1, err := strconv.Atoi(string(d.digits[ichar]))
if err != nil {
return nil, err
}
ichar++
digit2, err := strconv.Atoi(string(d.digits[ichar]))
ichar++
if err != nil {
return nil, err
}
digit := digit1*10 + digit2
if d.sign > 0 {
retBytes[ibytes] = byte(digit + NUM_POSITIVE)
} else {
retBytes[ibytes] = byte(NUM_NEGTIVE - digit)
}
ibytes++
}
if d.sign < 0 && ibytes < retLen {
retBytes[ibytes] = 0x66
ibytes++
}
if ibytes < retLen {
retBytes[ibytes] = 0x00
}
return retBytes, nil
}
func decodeDecimal(values []byte, prec int, scale int) (*DmDecimal, error) {
var decimal = &DmDecimal{
prec: prec,
scale: scale,
sign: 0,
weight: 0,
Valid: true,
}
if values == nil || len(values) == 0 || len(values) > XDEC_SIZE {
return nil, ECGO_FATAL_ERROR.throw()
}
if values[0] == byte(FLAG_ZERO) || len(values) == 1 {
return decimal, nil
}
if values[0]&byte(FLAG_ZERO) != 0 {
decimal.sign = 1
} else {
decimal.sign = -1
}
var flag = int(Dm_build_1346.Dm_build_1466(values, 0))
var exp int
if decimal.sign > 0 {
exp = flag - FLAG_POSITIVE
} else {
exp = FLAG_NEGTIVE - flag
}
var digit = 0
var sf = ""
for ival := 1; ival < len(values); ival++ {
if decimal.sign > 0 {
digit = int(values[ival]) - NUM_POSITIVE
} else {
digit = NUM_NEGTIVE - int(values[ival])
}
if digit < 0 || digit > 99 {
break
}
if digit < 10 {
sf += "0"
}
sf += strconv.Itoa(digit)
}
decimal.digits = sf
decimal.weight = exp*2 - (len(decimal.digits) - 2)
return decimal, nil
}
func (d DmDecimal) isZero() bool {
return d.sign == 0
}
func checkPrec(len int, prec int) error {
if prec > 0 && len > prec || len > XDEC_MAX_PREC {
return ECGO_DATA_TOO_LONG.throw()
}
return nil
}
func isOdd(val int) bool {
return val%2 != 0
}
func (d *DmDecimal) checkValid() error {
if !d.Valid {
return ECGO_IS_NULL.throw()
}
return nil
}
func (d *DmDecimal) GormDataType() string {
return "DECIMAL"
}
+119
View File
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"context"
"database/sql"
"database/sql/driver"
"net"
"sync"
"gitee.com/chunanyong/dm/i18n"
)
// 发版标记
var version = "8.1.4.170"
var build_date = "2025.11.21"
var svn = "43114"
var globalDmDriver = newDmDriver()
func init() {
sql.Register("dm", globalDmDriver)
i18n.InitConfig(i18n.Messages_zh_CN)
i18n.InitConfig(i18n.Messages_en_US)
i18n.InitConfig(i18n.Messages_zh_HK)
}
func driverInit(svcConfPath string) {
load(svcConfPath)
if GlobalProperties != nil && GlobalProperties.Len() > 0 {
setDriverAttributes(GlobalProperties)
}
globalDmDriver.createFilterChain(nil, GlobalProperties)
}
type DmDriver struct {
filterable
mu sync.Mutex
//readPropMutex sync.Mutex
}
func newDmDriver() *DmDriver {
d := new(DmDriver)
d.idGenerator = dmDriverIDGenerator
return d
}
// 支持自定义连接网络地址,返回标准net.Conn对象,相关数据库操作的消息包都将发送到该对象
type DialFunc func(addr string) (net.Conn, error)
// 支持自定义连接网络地址,返回标准net.Conn对象,相关数据库操作的消息包都将发送到该对象
type DialContextFunc func(ctx context.Context, addr string) (net.Conn, error)
var (
dialsLock sync.RWMutex
dials map[string]DialContextFunc
)
// 注册自定义连接方法
func RegisterDial(dialName string, dial DialFunc) {
RegisterDialContext(dialName, func(_ context.Context, addr string) (net.Conn, error) {
return dial(addr)
})
}
// 注册自定义连接方法
func RegisterDialContext(dialName string, dial DialContextFunc) {
dialsLock.Lock()
defer dialsLock.Unlock()
if dials == nil {
dials = make(map[string]DialContextFunc)
}
dials[dialName] = dial
}
/*************************************************************
** PUBLIC METHODS AND FUNCTIONS
*************************************************************/
func (d *DmDriver) Open(dsn string) (driver.Conn, error) {
d.mu.Lock()
defer d.mu.Unlock()
return d.open(dsn)
}
func (d *DmDriver) OpenConnector(dsn string) (driver.Connector, error) {
d.mu.Lock()
defer d.mu.Unlock()
return d.openConnector(dsn)
}
func (d *DmDriver) open(dsn string) (*DmConnection, error) {
c, err := d.openConnector(dsn)
if err != nil {
return nil, err
}
return c.connect(context.Background())
}
func (d *DmDriver) openConnector(dsn string) (*DmConnector, error) {
connector := new(DmConnector).init()
connector.url = dsn
connector.dmDriver = d
//d.readPropMutex.Lock()
err := connector.mergeConfigs(dsn)
//d.readPropMutex.Unlock()
if err != nil {
return nil, err
}
connector.createFilterChain(connector, nil)
return connector, nil
}
func GetDriverVersion() string {
return version
}
+46
View File
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package parser
import "strconv"
const (
MAX_DEC_LEN = 38
)
const (
NORMAL int = iota
INT
DOUBLE
DECIMAL
STRING
HEX_INT
WHITESPACE_OR_COMMENT
NULL
)
type LVal struct {
Value string
Tp int
Position int
}
func newLValNoParams() *LVal {
return new(LVal).reset()
}
func newLVal(value string, tp int) *LVal {
return &LVal{Value: value, Tp: tp}
}
func (l *LVal) reset() *LVal {
l.Value = ""
l.Tp = NORMAL
return l
}
func (l *LVal) String() string {
return strconv.Itoa(l.Tp) + ":" + l.Value
}
+1206
View File
File diff suppressed because it is too large Load Diff
+1455
View File
File diff suppressed because it is too large Load Diff
+494
View File
@@ -0,0 +1,494 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"database/sql/driver"
"math"
"strconv"
"strings"
"gitee.com/chunanyong/dm/util"
)
const (
QUA_Y = 0
QUA_YM = 1
QUA_MO = 2
)
type DmIntervalYM struct {
leadScale int
isLeadScaleSet bool
_type byte
years int
months int
scaleForSvr int
Valid bool
}
func newDmIntervalYM() *DmIntervalYM {
return &DmIntervalYM{
Valid: true,
}
}
func NewDmIntervalYMByString(str string) (ym *DmIntervalYM, err error) {
defer func() {
if p := recover(); p != nil {
err = ECGO_INVALID_TIME_INTERVAL.throw()
}
}()
ym = newDmIntervalYM()
ym.isLeadScaleSet = false
if err = ym.parseIntervYMString(strings.TrimSpace(str)); err != nil {
return nil, err
}
return ym, nil
}
func newDmIntervalYMByBytes(bytes []byte) *DmIntervalYM {
ym := newDmIntervalYM()
ym.scaleForSvr = int(Dm_build_1346.Dm_build_1448(bytes, 8))
ym.leadScale = (ym.scaleForSvr >> 4) & 0x0000000F
ym._type = bytes[9]
switch ym._type {
case QUA_Y:
ym.years = int(Dm_build_1346.Dm_build_1448(bytes, 0))
case QUA_YM:
ym.years = int(Dm_build_1346.Dm_build_1448(bytes, 0))
ym.months = int(Dm_build_1346.Dm_build_1448(bytes, 4))
case QUA_MO:
ym.months = int(Dm_build_1346.Dm_build_1448(bytes, 4))
}
return ym
}
func (ym *DmIntervalYM) GetYear() int {
return ym.years
}
func (ym *DmIntervalYM) GetMonth() int {
return ym.months
}
func (ym *DmIntervalYM) GetYMType() byte {
return ym._type
}
func (ym *DmIntervalYM) String() string {
if !ym.Valid {
return ""
}
str := "INTERVAL "
var year, month string
var l int
var destLen int
switch ym._type {
case QUA_Y:
year = strconv.FormatInt(int64(math.Abs(float64(ym.years))), 10)
if ym.years < 0 {
str += "-"
}
if ym.leadScale > len(year) {
l = len(year)
destLen = ym.leadScale
for destLen > l {
year = "0" + year
destLen--
}
}
str += "'" + year + "' YEAR(" + strconv.FormatInt(int64(ym.leadScale), 10) + ")"
case QUA_YM:
year = strconv.FormatInt(int64(math.Abs(float64(ym.years))), 10)
month = strconv.FormatInt(int64(math.Abs(float64(ym.months))), 10)
if ym.years < 0 || ym.months < 0 {
str += "-"
}
if ym.leadScale > len(year) {
l = len(year)
destLen = ym.leadScale
for destLen > l {
year = "0" + year
destLen--
}
}
if len(month) < 2 {
month = "0" + month
}
str += "'" + year + "-" + month + "' YEAR(" + strconv.FormatInt(int64(ym.leadScale), 10) + ") TO MONTH"
case QUA_MO:
month = strconv.FormatInt(int64(math.Abs(float64(ym.months))), 10)
if ym.months < 0 {
str += "-"
}
if ym.leadScale > len(month) {
l = len(month)
destLen = ym.leadScale
for destLen > l {
month = "0" + month
destLen--
}
}
str += "'" + month + "' MONTH(" + strconv.FormatInt(int64(ym.leadScale), 10) + ")"
}
return str
}
func (dest *DmIntervalYM) Scan(src interface{}) error {
if dest == nil {
return ECGO_STORE_IN_NIL_POINTER.throw()
}
switch src := src.(type) {
case nil:
*dest = *new(DmIntervalYM)
(*dest).Valid = false
return nil
case *DmIntervalYM:
*dest = *src
return nil
case string:
ret, err := NewDmIntervalYMByString(src)
if err != nil {
return err
}
*dest = *ret
return nil
default:
return UNSUPPORTED_SCAN
}
}
func (ym DmIntervalYM) Value() (driver.Value, error) {
if !ym.Valid {
return nil, nil
}
return ym, nil
}
func (ym *DmIntervalYM) parseIntervYMString(str string) error {
str = strings.ToUpper(str)
ret := strings.Split(str, " ")
l := len(ret)
if l < 3 || !util.StringUtil.EqualsIgnoreCase(ret[0], "INTERVAL") || !(strings.HasPrefix(ret[2], "YEAR") || strings.HasPrefix(ret[2], "MONTH")) {
return ECGO_INVALID_TIME_INTERVAL.throw()
}
ym._type = QUA_YM
yearId := strings.Index(str, "YEAR")
monthId := strings.Index(str, "MONTH")
toId := strings.Index(str, "TO")
var err error
if toId == -1 {
if yearId != -1 && monthId == -1 {
ym._type = QUA_Y
ym.leadScale, err = ym.getLeadPrec(str, yearId)
if err != nil {
return err
}
} else if monthId != -1 && yearId == -1 {
ym._type = QUA_MO
ym.leadScale, err = ym.getLeadPrec(str, monthId)
if err != nil {
return err
}
} else {
return ECGO_INVALID_TIME_INTERVAL.throw()
}
} else {
if yearId == -1 || monthId == -1 {
return ECGO_INVALID_TIME_INTERVAL.throw()
}
ym._type = QUA_YM
ym.leadScale, err = ym.getLeadPrec(str, yearId)
if err != nil {
return err
}
}
ym.scaleForSvr = (int(ym._type) << 8) + (ym.leadScale << 4)
timeVals, err := ym.getTimeValue(ret[1], int(ym._type))
if err != nil {
return err
}
ym.years = timeVals[0]
ym.months = timeVals[1]
return ym.checkScale(ym.leadScale)
}
func (ym *DmIntervalYM) getLeadPrec(str string, startIndex int) (int, error) {
if ym.isLeadScaleSet {
return ym.leadScale, nil
}
leftBtId := strings.Index(str[startIndex:], "(")
rightBtId := strings.Index(str[startIndex:], ")")
leadPrec := 0
if rightBtId == -1 && leftBtId == -1 {
leftBtId += startIndex
rightBtId += startIndex
l := strings.Index(str, "'")
var r int
var dataStr string
if l != -1 {
r = strings.Index(str[l+1:], "'")
if r != -1 {
r += l + 1
}
} else {
r = -1
}
if r != -1 {
dataStr = strings.TrimSpace(str[l+1 : r])
} else {
dataStr = ""
}
if dataStr != "" {
sign := dataStr[0]
if sign == '+' || sign == '-' {
dataStr = strings.TrimSpace(dataStr[1:])
}
end := strings.Index(dataStr, "-")
if end != -1 {
dataStr = dataStr[:end]
}
leadPrec = len(dataStr)
} else {
leadPrec = 2
}
} else if rightBtId != -1 && leftBtId != -1 && rightBtId > leftBtId+1 {
leftBtId += startIndex
rightBtId += startIndex
strPrec := strings.TrimSpace(str[leftBtId+1 : rightBtId])
temp, err := strconv.ParseInt(strPrec, 10, 32)
if err != nil {
return 0, err
}
leadPrec = int(temp)
} else {
return 0, ECGO_INVALID_TIME_INTERVAL.throw()
}
return leadPrec, nil
}
func (ym *DmIntervalYM) checkScale(prec int) error {
switch ym._type {
case QUA_Y:
if prec < len(strconv.FormatInt(int64(math.Abs(float64(ym.years))), 10)) {
return ECGO_INVALID_TIME_INTERVAL.throw()
}
case QUA_YM:
if prec < len(strconv.FormatInt(int64(math.Abs(float64(ym.years))), 10)) {
return ECGO_INVALID_TIME_INTERVAL.throw()
}
if int64(math.Abs(float64(ym.months))) > 11 {
return ECGO_INVALID_TIME_INTERVAL.throw()
}
case QUA_MO:
if prec < len(strconv.FormatInt(int64(math.Abs(float64(ym.months))), 10)) {
return ECGO_INVALID_TIME_INTERVAL.throw()
}
}
return nil
}
func (ym *DmIntervalYM) getTimeValue(subStr string, _type int) ([]int, error) {
hasQuate := false
if subStr[0] == '\'' && subStr[len(subStr)-1] == '\'' {
hasQuate = true
subStr = strings.TrimSpace(subStr[1 : len(subStr)-1])
}
negative := false
if strings.Index(subStr, "-") == 0 {
negative = true
subStr = subStr[1:]
} else if strings.Index(subStr, "+") == 0 {
negative = false
subStr = subStr[1:]
}
if subStr[0] == '\'' && subStr[len(subStr)-1] == '\'' {
hasQuate = true
subStr = strings.TrimSpace(subStr[1 : len(subStr)-1])
}
if !hasQuate {
return nil, ECGO_INVALID_TIME_INTERVAL.throw()
}
lastSignIndex := strings.LastIndex(subStr, "-")
list := make([]string, 2)
if lastSignIndex == -1 || lastSignIndex == 0 {
list[0] = subStr
list[1] = ""
} else {
list[0] = subStr[0:lastSignIndex]
list[1] = subStr[lastSignIndex+1:]
}
var yearVal, monthVal int64
var err error
if ym._type == QUA_YM {
yearVal, err = strconv.ParseInt(list[0], 10, 32)
if err != nil {
return nil, err
}
if util.StringUtil.EqualsIgnoreCase(list[1], "") {
monthVal = 0
} else {
monthVal, err = strconv.ParseInt(list[1], 10, 32)
if err != nil {
return nil, err
}
}
if negative {
yearVal *= -1
monthVal *= -1
}
if yearVal > int64(math.Pow10(ym.leadScale))-1 || yearVal < 1-int64(math.Pow10(ym.leadScale)) {
return nil, ECGO_INVALID_TIME_INTERVAL.throw()
}
} else if ym._type == QUA_Y {
yearVal, err = strconv.ParseInt(list[0], 10, 32)
if err != nil {
return nil, err
}
monthVal = 0
if negative {
yearVal *= -1
}
if yearVal > int64(math.Pow10(ym.leadScale))-1 || yearVal < 1-int64(math.Pow10(ym.leadScale)) {
return nil, ECGO_INVALID_TIME_INTERVAL.throw()
}
} else {
yearVal = 0
monthVal, err = strconv.ParseInt(list[0], 10, 32)
if err != nil {
return nil, err
}
if negative {
monthVal *= -1
}
if monthVal > int64(math.Pow10(ym.leadScale))-1 || monthVal < 1-int64(math.Pow10(ym.leadScale)) {
return nil, ECGO_INVALID_TIME_INTERVAL.throw()
}
}
ret := make([]int, 2)
ret[0] = int(yearVal)
ret[1] = int(monthVal)
return ret, nil
}
func (ym *DmIntervalYM) encode(scale int) ([]byte, error) {
if scale == 0 {
scale = ym.scaleForSvr
}
year, month := ym.years, ym.months
if err := ym.checkScale(ym.leadScale); err != nil {
return nil, err
}
if scale != ym.scaleForSvr {
convertYM, err := ym.convertTo(scale)
if err != nil {
return nil, err
}
year = convertYM.years
month = convertYM.months
} else {
if err := ym.checkScale(ym.leadScale); err != nil {
return nil, err
}
}
bytes := make([]byte, 12)
Dm_build_1346.Dm_build_1362(bytes, 0, int32(year))
Dm_build_1346.Dm_build_1362(bytes, 4, int32(month))
Dm_build_1346.Dm_build_1362(bytes, 8, int32(scale))
return bytes, nil
}
func (ym *DmIntervalYM) convertTo(scale int) (*DmIntervalYM, error) {
destType := (scale & 0x0000FF00) >> 8
leadPrec := (scale >> 4) & 0x0000000F
totalMonths := ym.years*12 + ym.months
year := 0
month := 0
switch destType {
case QUA_Y:
year = totalMonths / 12
if totalMonths%12 >= 6 {
year++
} else if totalMonths%12 <= -6 {
year--
}
if leadPrec < len(strconv.Itoa(int(math.Abs(float64(year))))) {
return nil, ECGO_INVALID_TIME_INTERVAL.throw()
}
case QUA_YM:
year = totalMonths / 12
month = totalMonths % 12
if leadPrec < len(strconv.Itoa(int(math.Abs(float64(year))))) {
return nil, ECGO_INVALID_TIME_INTERVAL.throw()
}
case QUA_MO:
month = totalMonths
if leadPrec < len(strconv.Itoa(int(math.Abs(float64(month))))) {
return nil, ECGO_INVALID_TIME_INTERVAL.throw()
}
}
return &DmIntervalYM{
_type: byte(destType),
years: year,
months: month,
scaleForSvr: scale,
leadScale: (scale >> 4) & 0x0000000F,
Valid: true,
}, nil
}
func (ym *DmIntervalYM) checkValid() error {
if !ym.Valid {
return ECGO_IS_NULL.throw()
}
return nil
}
func (d *DmIntervalYM) GormDataType() string {
return "INTERVAL YEAR TO MONTH"
}
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import "strings"
type DmResult struct {
filterable
dmStmt *DmStatement
affectedRows int64
insertId int64
}
func newDmResult(bs *DmStatement, execInfo *execRetInfo) *DmResult {
result := DmResult{}
result.resetFilterable(&bs.filterable)
result.dmStmt = bs
result.affectedRows = execInfo.updateCount
if execInfo.lastInsertId == 0 && execInfo.hasResultSet && strings.Index(execInfo.nativeSQL, "/*DMGORM-UPSERT*/") == 0 {
if len(execInfo.rsDatas) > 0 && len(execInfo.rsDatas[0]) > 0 {
result.insertId = Dm_build_1346.Dm_build_1576(execInfo.rsDatas[0][1])
} else {
result.insertId = 0
}
} else {
result.insertId = execInfo.lastInsertId
}
result.idGenerator = dmResultIDGenerator
return &result
}
func (r *DmResult) LastInsertId() (int64, error) {
if len(r.filterChain.filters) == 0 {
return r.lastInsertId()
}
return r.filterChain.reset().DmResultLastInsertId(r)
}
func (r *DmResult) RowsAffected() (int64, error) {
if len(r.filterChain.filters) == 0 {
return r.rowsAffected()
}
return r.filterChain.reset().DmResultRowsAffected(r)
}
func (result *DmResult) lastInsertId() (int64, error) {
return result.insertId, nil
}
func (result *DmResult) rowsAffected() (int64, error) {
return result.affectedRows, nil
}
+30
View File
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
// This is a mirror of golang.org/x/crypto/internal/subtle.
package security
import "unsafe"
// AnyOverlap reports whether x and y share memory at any (not necessarily
// corresponding) index. The memory beyond the slice length is ignored.
func AnyOverlap(x, y []byte) bool {
return len(x) > 0 && len(y) > 0 &&
uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) &&
uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1]))
}
// InexactOverlap reports whether x and y share memory at any non-corresponding
// index. The memory beyond the slice length is ignored. Note that x and y can
// have different lengths and still not have any inexact overlap.
//
// InexactOverlap can be used to implement the requirements of the crypto/cipher
// AEAD, Block, BlockMode and Stream interfaces.
func InexactOverlap(x, y []byte) bool {
if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
return false
}
return AnyOverlap(x, y)
}
+11
View File
@@ -0,0 +1,11 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package security
type Cipher interface {
Encrypt(plaintext []byte, genDigest bool) []byte
Decrypt(ciphertext []byte, checkDigest bool) ([]byte, error)
}
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package security
import (
"crypto/rand"
"errors"
"io"
"math/big"
)
type dhGroup struct {
p *big.Int
g *big.Int
}
func newDhGroup(prime, generator *big.Int) *dhGroup {
return &dhGroup{
p: prime,
g: generator,
}
}
func (dg *dhGroup) P() *big.Int {
p := new(big.Int)
p.Set(dg.p)
return p
}
func (dg *dhGroup) G() *big.Int {
g := new(big.Int)
g.Set(dg.g)
return g
}
// 生成本地公私钥
func (dg *dhGroup) GeneratePrivateKey(randReader io.Reader) (key *DhKey, err error) {
if randReader == nil {
randReader = rand.Reader
}
// 0 < x < p
x, err := rand.Int(randReader, dg.p)
if err != nil {
return
}
zero := big.NewInt(0)
for x.Cmp(zero) == 0 {
x, err = rand.Int(randReader, dg.p)
if err != nil {
return
}
}
key = new(DhKey)
key.x = x
// y = g ^ x mod p
key.y = new(big.Int).Exp(dg.g, x, dg.p)
key.group = dg
return
}
func (dg *dhGroup) ComputeKey(pubkey *DhKey, privkey *DhKey) (kye *DhKey, err error) {
if dg.p == nil {
err = errors.New("DH: invalid group")
return
}
if pubkey.y == nil {
err = errors.New("DH: invalid public key")
return
}
if pubkey.y.Sign() <= 0 || pubkey.y.Cmp(dg.p) >= 0 {
err = errors.New("DH parameter out of bounds")
return
}
if privkey.x == nil {
err = errors.New("DH: invalid private key")
return
}
k := new(big.Int).Exp(pubkey.y, privkey.x, dg.p)
key := new(DhKey)
key.y = k
key.group = dg
return
}
+64
View File
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package security
import "math/big"
type DhKey struct {
x *big.Int
y *big.Int
group *dhGroup
}
func newPublicKey(s []byte) *DhKey {
key := new(DhKey)
key.y = new(big.Int).SetBytes(s)
return key
}
func (dk *DhKey) GetX() *big.Int {
x := new(big.Int)
x.Set(dk.x)
return x
}
func (dk *DhKey) GetY() *big.Int {
y := new(big.Int)
y.Set(dk.y)
return y
}
func (dk *DhKey) GetYBytes() []byte {
if dk.y == nil {
return nil
}
if dk.group != nil {
blen := (dk.group.p.BitLen() + 7) / 8
ret := make([]byte, blen)
copyWithLeftPad(ret, dk.y.Bytes())
return ret
}
return dk.y.Bytes()
}
func (dk *DhKey) GetYString() string {
if dk.y == nil {
return ""
}
return dk.y.String()
}
func (dk *DhKey) IsPrivateKey() bool {
return dk.x != nil
}
func copyWithLeftPad(dest, src []byte) {
numPaddingBytes := len(dest) - len(src)
for i := 0; i < numPaddingBytes; i++ {
dest[i] = 0
}
copy(dest[:numPaddingBytes], src)
}
+69
View File
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
// go官方没有实现ecb加密模式
package security
import (
"crypto/cipher"
)
type ecb struct {
b cipher.Block
blockSize int
}
func newECB(b cipher.Block) *ecb {
return &ecb{
b: b,
blockSize: b.BlockSize(),
}
}
type ecbEncrypter ecb
func NewECBEncrypter(b cipher.Block) cipher.BlockMode {
return (*ecbEncrypter)(newECB(b))
}
func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
if len(src)%x.blockSize != 0 {
panic("dm/security: input not full blocks")
}
if len(dst) < len(src) {
panic("dm/security: output smaller than input")
}
if InexactOverlap(dst[:len(src)], src) {
panic("dm/security: invalid buffer overlap")
}
for bs, be := 0, x.blockSize; bs < len(src); bs, be = bs+x.blockSize, be+x.blockSize {
x.b.Encrypt(dst[bs:be], src[bs:be])
}
}
type ecbDecrypter ecb
func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
return (*ecbDecrypter)(newECB(b))
}
func (x *ecbDecrypter) BlockSize() int { return x.blockSize }
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
if len(src)%x.blockSize != 0 {
panic("dm/security: input not full blocks")
}
if len(dst) < len(src) {
panic("dm/security: output smaller than input")
}
if InexactOverlap(dst[:len(src)], src) {
panic("dm/security: invalid buffer overlap")
}
for bs, be := 0, x.blockSize; bs < len(src); bs, be = bs+x.blockSize, be+x.blockSize {
x.b.Decrypt(dst[bs:be], src[bs:be])
}
}
+114
View File
@@ -0,0 +1,114 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package security
import (
"math/big"
)
const (
DH_KEY_LENGTH int = 64
/* 低7位用于保存分组加密算法中的工作模式 */
WORK_MODE_MASK int = 0x007f
ECB_MODE int = 0x1
CBC_MODE int = 0x2
CFB_MODE int = 0x4
OFB_MODE int = 0x8
/* 高位保存加密算法 */
ALGO_MASK int = 0xff80
DES int = 0x0080
DES3 int = 0x0100
AES128 int = 0x0200
AES192 int = 0x0400
AES256 int = 0x0800
RC4 int = 0x1000
MD5 int = 0x1100
// 用户名密码加密算法
DES_CFB int = 132
// 消息加密摘要长度
MD5_DIGEST_SIZE int = 16
MIN_EXTERNAL_CIPHER_ID int = 5000
)
var dhParaP = "C009D877BAF5FAF416B7F778E6115DCB90D65217DCC2F08A9DFCB5A192C593EBAB02929266B8DBFC2021039FDBD4B7FDE2B996E00008F57AE6EFB4ED3F17B6D3"
var dhParaG = "5"
var defaultIV = []byte{0x20, 0x21, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x20}
var p *big.Int
var g *big.Int
func NewClientKeyPair() (key *DhKey, err error) {
p, _ = new(big.Int).SetString(dhParaP, 16)
g, _ = new(big.Int).SetString(dhParaG, 16)
dhGroup := newDhGroup(p, g)
key, err = dhGroup.GeneratePrivateKey(nil)
if err != nil {
return nil, err
}
return key, nil
}
func ComputeSessionKey(clientPrivKey *DhKey, serverPubKey []byte) []byte {
serverKeyX := bytes2Bn(serverPubKey)
clientPrivKeyX := clientPrivKey.GetX()
sessionKeyBN := serverKeyX.Exp(serverKeyX, clientPrivKeyX, p)
return Bn2Bytes(sessionKeyBN, 0)
}
func bytes2Bn(bnBytesSrc []byte) *big.Int {
if bnBytesSrc == nil {
return nil
}
if bnBytesSrc[0] == 0 {
return new(big.Int).SetBytes(bnBytesSrc)
}
validBytesCount := len(bnBytesSrc) + 1
bnBytesTo := make([]byte, validBytesCount)
bnBytesTo[0] = 0
copy(bnBytesTo[1:validBytesCount], bnBytesSrc)
return new(big.Int).SetBytes(bnBytesTo)
}
func Bn2Bytes(bn *big.Int, bnLen int) []byte {
var bnBytesSrc, bnBytesTemp, bnBytesTo []byte
var leading_zero_count int
validBytesCount := 0
if bn == nil {
return nil
}
bnBytesSrc = bn.Bytes()
// 去除首位0
if bnBytesSrc[0] != 0 {
bnBytesTemp = bnBytesSrc
validBytesCount = len(bnBytesTemp)
} else {
validBytesCount = len(bnBytesSrc) - 1
bnBytesTemp = make([]byte, validBytesCount)
copy(bnBytesTemp, bnBytesSrc[1:validBytesCount+1])
}
if bnLen == 0 {
leading_zero_count = 0
} else {
leading_zero_count = bnLen - validBytesCount
}
// 如果位数不足DH_KEY_LENGTH则在前面补0
if leading_zero_count > 0 {
bnBytesTo = make([]byte, DH_KEY_LENGTH)
i := 0
for i = 0; i < leading_zero_count; i++ {
bnBytesTo[i] = 0
}
copy(bnBytesTo[i:i+validBytesCount], bnBytesTemp)
} else {
bnBytesTo = bnBytesTemp
}
return bnBytesTo
}
+211
View File
@@ -0,0 +1,211 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package security
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/md5"
"crypto/rc4"
"errors"
"reflect"
)
type SymmCipher struct {
encryptCipher interface{} //cipher.BlockMode | cipher.Stream
decryptCipher interface{} //cipher.BlockMode | cipher.Stream
key []byte
block cipher.Block // 分组加密算法
algorithmType int
workMode int
needPadding bool
}
func NewSymmCipher(algorithmID int, key []byte) (SymmCipher, error) {
var sc SymmCipher
var err error
sc.key = key
sc.algorithmType = algorithmID & ALGO_MASK
sc.workMode = algorithmID & WORK_MODE_MASK
switch sc.algorithmType {
case AES128:
if sc.block, err = aes.NewCipher(key[:16]); err != nil {
return sc, err
}
case AES192:
if sc.block, err = aes.NewCipher(key[:24]); err != nil {
return sc, err
}
case AES256:
if sc.block, err = aes.NewCipher(key[:32]); err != nil {
return sc, err
}
case DES:
if sc.block, err = des.NewCipher(key[:8]); err != nil {
return sc, err
}
case DES3:
var tripleDESKey []byte
tripleDESKey = append(tripleDESKey, key[:16]...)
tripleDESKey = append(tripleDESKey, key[:8]...)
if sc.block, err = des.NewTripleDESCipher(tripleDESKey); err != nil {
return sc, err
}
case RC4:
if sc.encryptCipher, err = rc4.NewCipher(key[:16]); err != nil {
return sc, err
}
if sc.decryptCipher, err = rc4.NewCipher(key[:16]); err != nil {
return sc, err
}
return sc, nil
default:
return sc, errors.New("invalidCipher")
}
blockSize := sc.block.BlockSize()
if sc.encryptCipher, err = sc.getEncrypter(sc.workMode, sc.block, defaultIV[:blockSize]); err != nil {
return sc, err
}
if sc.decryptCipher, err = sc.getDecrypter(sc.workMode, sc.block, defaultIV[:blockSize]); err != nil {
return sc, err
}
return sc, nil
}
func (sc SymmCipher) Encrypt(plaintext []byte, genDigest bool) []byte {
// 执行过加密后,IV值变了,需要重新初始化encryptCipher对象(因为没有类似resetIV的方法)
if sc.algorithmType != RC4 {
sc.encryptCipher, _ = sc.getEncrypter(sc.workMode, sc.block, defaultIV[:sc.block.BlockSize()])
} else {
sc.encryptCipher, _ = rc4.NewCipher(sc.key[:16])
}
// 填充
var paddingtext = make([]byte, len(plaintext))
copy(paddingtext, plaintext)
if sc.needPadding {
paddingtext = pkcs5Padding(paddingtext)
}
ret := make([]byte, len(paddingtext))
if v, ok := sc.encryptCipher.(cipher.Stream); ok {
v.XORKeyStream(ret, paddingtext)
} else if v, ok := sc.encryptCipher.(cipher.BlockMode); ok {
v.CryptBlocks(ret, paddingtext)
}
// md5摘要
if genDigest {
digest := md5.Sum(plaintext)
encrypt := ret
ret = make([]byte, len(encrypt)+len(digest))
copy(ret[:len(encrypt)], encrypt)
copy(ret[len(encrypt):], digest[:])
}
return ret
}
func (sc SymmCipher) Decrypt(ciphertext []byte, checkDigest bool) ([]byte, error) {
// 执行过解密后,IV值变了,需要重新初始化decryptCipher对象(因为没有类似resetIV的方法)
if sc.algorithmType != RC4 {
sc.decryptCipher, _ = sc.getDecrypter(sc.workMode, sc.block, defaultIV[:sc.block.BlockSize()])
} else {
sc.decryptCipher, _ = rc4.NewCipher(sc.key[:16])
}
var ret []byte
if checkDigest {
var digest = ciphertext[len(ciphertext)-MD5_DIGEST_SIZE:]
ret = ciphertext[:len(ciphertext)-MD5_DIGEST_SIZE]
ret = sc.decrypt(ret)
var msgDigest = md5.Sum(ret)
if !reflect.DeepEqual(msgDigest[:], digest) {
return nil, errors.New("Decrypt failed/Digest not match\n")
}
} else {
ret = sc.decrypt(ciphertext)
}
return ret, nil
}
func (sc SymmCipher) decrypt(ciphertext []byte) []byte {
ret := make([]byte, len(ciphertext))
if v, ok := sc.decryptCipher.(cipher.Stream); ok {
v.XORKeyStream(ret, ciphertext)
} else if v, ok := sc.decryptCipher.(cipher.BlockMode); ok {
v.CryptBlocks(ret, ciphertext)
}
// 去除填充
if sc.needPadding {
ret = pkcs5UnPadding(ret)
}
return ret
}
func (sc *SymmCipher) getEncrypter(workMode int, block cipher.Block, iv []byte) (ret interface{}, err error) {
switch workMode {
case ECB_MODE:
ret = NewECBEncrypter(block)
sc.needPadding = true
case CBC_MODE:
ret = cipher.NewCBCEncrypter(block, iv)
sc.needPadding = true
case CFB_MODE:
ret = cipher.NewCFBEncrypter(block, iv)
sc.needPadding = false
case OFB_MODE:
ret = cipher.NewOFB(block, iv)
sc.needPadding = false
default:
err = errors.New("invalidCipherMode")
}
return
}
func (sc *SymmCipher) getDecrypter(workMode int, block cipher.Block, iv []byte) (ret interface{}, err error) {
switch workMode {
case ECB_MODE:
ret = NewECBDecrypter(block)
sc.needPadding = true
case CBC_MODE:
ret = cipher.NewCBCDecrypter(block, iv)
sc.needPadding = true
case CFB_MODE:
ret = cipher.NewCFBDecrypter(block, iv)
sc.needPadding = false
case OFB_MODE:
ret = cipher.NewOFB(block, iv)
sc.needPadding = false
default:
err = errors.New("invalidCipherMode")
}
return
}
// 补码
func pkcs77Padding(ciphertext []byte, blocksize int) []byte {
padding := blocksize - len(ciphertext)%blocksize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
// 去码
func pkcs7UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:length-unpadding]
}
// 补码
func pkcs5Padding(ciphertext []byte) []byte {
return pkcs77Padding(ciphertext, 8)
}
// 去码
func pkcs5UnPadding(ciphertext []byte) []byte {
return pkcs7UnPadding(ciphertext)
}
+142
View File
@@ -0,0 +1,142 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package security
import (
"crypto/md5"
"errors"
"fmt"
"reflect"
"unsafe"
)
type ThirdPartCipher struct {
encryptType int // 外部加密算法id
encryptName string // 外部加密算法名称
hashType int
key []byte
cipherCount int // 外部加密算法个数
//innerId int // 外部加密算法内部id
blockSize int // 分组块大小
khSize int // key/hash大小
}
func NewThirdPartCipher(encryptType int, key []byte, cipherPath string, hashType int) (ThirdPartCipher, error) {
var tpc = ThirdPartCipher{
encryptType: encryptType,
key: key,
hashType: hashType,
cipherCount: -1,
}
var err error
err = initThirdPartCipher(cipherPath)
if err != nil {
return tpc, err
}
tpc.getCount()
if err = tpc.getInfo(); err != nil {
return tpc, err
}
return tpc, nil
}
func (tpc *ThirdPartCipher) getCount() int {
if tpc.cipherCount == -1 {
tpc.cipherCount = cipherGetCount()
}
return tpc.cipherCount
}
func (tpc *ThirdPartCipher) getInfo() error {
var cipher_id, ty, blk_size, kh_size int
//var strptr, _ = syscall.UTF16PtrFromString(tpc.encryptName)
var strptr *uint16 = new(uint16)
for i := 1; i <= tpc.getCount(); i++ {
cipherGetInfo(uintptr(i), uintptr(unsafe.Pointer(&cipher_id)), uintptr(unsafe.Pointer(&strptr)),
uintptr(unsafe.Pointer(&ty)), uintptr(unsafe.Pointer(&blk_size)), uintptr(unsafe.Pointer(&kh_size)))
if tpc.encryptType == cipher_id {
tpc.blockSize = blk_size
tpc.khSize = kh_size
tpc.encryptName = string(uintptr2bytes(uintptr(unsafe.Pointer(strptr))))
return nil
}
}
return fmt.Errorf("ThirdPartyCipher: cipher id:%d not found", tpc.encryptType)
}
func (tpc ThirdPartCipher) Encrypt(plaintext []byte, genDigest bool) []byte {
var tmp_para uintptr
cipherEncryptInit(uintptr(tpc.encryptType), uintptr(unsafe.Pointer(&tpc.key[0])), uintptr(len(tpc.key)), tmp_para)
ciphertextLen := cipherGetCipherTextSize(uintptr(tpc.encryptType), tmp_para, uintptr(len(plaintext)))
ciphertext := make([]byte, ciphertextLen)
ret := cipherEncrypt(uintptr(tpc.encryptType), tmp_para, uintptr(unsafe.Pointer(&plaintext[0])), uintptr(len(plaintext)),
uintptr(unsafe.Pointer(&ciphertext[0])), uintptr(len(ciphertext)))
ciphertext = ciphertext[:ret]
cipherClean(uintptr(tpc.encryptType), tmp_para)
// md5摘要
if genDigest {
digest := md5.Sum(plaintext)
encrypt := ciphertext
ciphertext = make([]byte, len(encrypt)+len(digest))
copy(ciphertext[:len(encrypt)], encrypt)
copy(ciphertext[len(encrypt):], digest[:])
}
return ciphertext
}
func (tpc ThirdPartCipher) Decrypt(ciphertext []byte, checkDigest bool) ([]byte, error) {
var ret []byte
if checkDigest {
var digest = ciphertext[len(ciphertext)-MD5_DIGEST_SIZE:]
ret = ciphertext[:len(ciphertext)-MD5_DIGEST_SIZE]
ret = tpc.decrypt(ret)
var msgDigest = md5.Sum(ret)
if !reflect.DeepEqual(msgDigest[:], digest) {
return nil, errors.New("Decrypt failed/Digest not match\n")
}
} else {
ret = tpc.decrypt(ciphertext)
}
return ret, nil
}
func (tpc ThirdPartCipher) decrypt(ciphertext []byte) []byte {
var tmp_para uintptr
cipherDecryptInit(uintptr(tpc.encryptType), uintptr(unsafe.Pointer(&tpc.key[0])), uintptr(len(tpc.key)), tmp_para)
plaintext := make([]byte, len(ciphertext))
ret := cipherDecrypt(uintptr(tpc.encryptType), tmp_para, uintptr(unsafe.Pointer(&ciphertext[0])), uintptr(len(ciphertext)),
uintptr(unsafe.Pointer(&plaintext[0])), uintptr(len(plaintext)))
plaintext = plaintext[:ret]
cipherClean(uintptr(tpc.encryptType), tmp_para)
return plaintext
}
func addBufSize(buf []byte, newCap int) []byte {
newBuf := make([]byte, newCap)
copy(newBuf, buf)
return newBuf
}
func uintptr2bytes(p uintptr) []byte {
buf := make([]byte, 64)
i := 0
for b := (*byte)(unsafe.Pointer(p)); *b != 0; i++ {
if i > cap(buf) {
buf = addBufSize(buf, i*2)
}
buf[i] = *b
// byte占1字节
p++
b = (*byte)(unsafe.Pointer(p))
}
return buf[:i]
}
+96
View File
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package security
import "plugin"
var (
dmCipherEncryptSo *plugin.Plugin
cipherGetCountProc plugin.Symbol
cipherGetInfoProc plugin.Symbol
cipherEncryptInitProc plugin.Symbol
cipherGetCipherTextSizeProc plugin.Symbol
cipherEncryptProc plugin.Symbol
cipherCleanupProc plugin.Symbol
cipherDecryptInitProc plugin.Symbol
cipherDecryptProc plugin.Symbol
)
func initThirdPartCipher(cipherPath string) (err error) {
if dmCipherEncryptSo, err = plugin.Open(cipherPath); err != nil {
return err
}
if cipherGetCountProc, err = dmCipherEncryptSo.Lookup("cipher_get_count"); err != nil {
return err
}
if cipherGetInfoProc, err = dmCipherEncryptSo.Lookup("cipher_get_info"); err != nil {
return err
}
if cipherEncryptInitProc, err = dmCipherEncryptSo.Lookup("cipher_encrypt_init"); err != nil {
return err
}
if cipherGetCipherTextSizeProc, err = dmCipherEncryptSo.Lookup("cipher_get_cipher_text_size"); err != nil {
return err
}
if cipherEncryptProc, err = dmCipherEncryptSo.Lookup("cipher_encrypt"); err != nil {
return err
}
if cipherCleanupProc, err = dmCipherEncryptSo.Lookup("cipher_cleanup"); err != nil {
return err
}
if cipherDecryptInitProc, err = dmCipherEncryptSo.Lookup("cipher_decrypt_init"); err != nil {
return err
}
if cipherDecryptProc, err = dmCipherEncryptSo.Lookup("cipher_decrypt"); err != nil {
return err
}
return nil
}
func cipherGetCount() int {
ret := cipherGetCountProc.(func() interface{})()
return ret.(int)
}
func cipherGetInfo(seqno, cipherId, cipherName, _type, blkSize, khSIze uintptr) {
ret := cipherGetInfoProc.(func(uintptr, uintptr, uintptr, uintptr, uintptr, uintptr) interface{})(seqno, cipherId, cipherName, _type, blkSize, khSIze)
if ret.(int) == 0 {
panic("ThirdPartyCipher: call cipher_get_info failed")
}
}
func cipherEncryptInit(cipherId, key, keySize, cipherPara uintptr) {
ret := cipherEncryptInitProc.(func(uintptr, uintptr, uintptr, uintptr) interface{})(cipherId, key, keySize, cipherPara)
if ret.(int) == 0 {
panic("ThirdPartyCipher: call cipher_encrypt_init failed")
}
}
func cipherGetCipherTextSize(cipherId, cipherPara, plainTextSize uintptr) uintptr {
ciphertextLen := cipherGetCipherTextSizeProc.(func(uintptr, uintptr, uintptr) interface{})(cipherId, cipherPara, plainTextSize)
return ciphertextLen.(uintptr)
}
func cipherEncrypt(cipherId, cipherPara, plainText, plainTextSize, cipherText, cipherTextBufSize uintptr) uintptr {
ret := cipherEncryptProc.(func(uintptr, uintptr, uintptr, uintptr, uintptr, uintptr) interface{})(cipherId, cipherPara, plainText, plainTextSize, cipherText, cipherTextBufSize)
return ret.(uintptr)
}
func cipherClean(cipherId, cipherPara uintptr) {
cipherEncryptProc.(func(uintptr, uintptr))(cipherId, cipherPara)
}
func cipherDecryptInit(cipherId, key, keySize, cipherPara uintptr) {
ret := cipherDecryptInitProc.(func(uintptr, uintptr, uintptr, uintptr) interface{})(cipherId, key, keySize, cipherPara)
if ret.(int) == 0 {
panic("ThirdPartyCipher: call cipher_decrypt_init failed")
}
}
func cipherDecrypt(cipherId, cipherPara, cipherText, cipherTextSize, plainText, plainTextBufSize uintptr) uintptr {
ret := cipherDecryptProc.(func(uintptr, uintptr, uintptr, uintptr, uintptr, uintptr) interface{})(cipherId, cipherPara, cipherText, cipherTextSize, plainText, plainTextBufSize)
return ret.(uintptr)
}
+96
View File
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package security
import "plugin"
var (
dmCipherEncryptSo *plugin.Plugin
cipherGetCountProc plugin.Symbol
cipherGetInfoProc plugin.Symbol
cipherEncryptInitProc plugin.Symbol
cipherGetCipherTextSizeProc plugin.Symbol
cipherEncryptProc plugin.Symbol
cipherCleanupProc plugin.Symbol
cipherDecryptInitProc plugin.Symbol
cipherDecryptProc plugin.Symbol
)
func initThirdPartCipher(cipherPath string) (err error) {
if dmCipherEncryptSo, err = plugin.Open(cipherPath); err != nil {
return err
}
if cipherGetCountProc, err = dmCipherEncryptSo.Lookup("cipher_get_count"); err != nil {
return err
}
if cipherGetInfoProc, err = dmCipherEncryptSo.Lookup("cipher_get_info"); err != nil {
return err
}
if cipherEncryptInitProc, err = dmCipherEncryptSo.Lookup("cipher_encrypt_init"); err != nil {
return err
}
if cipherGetCipherTextSizeProc, err = dmCipherEncryptSo.Lookup("cipher_get_cipher_text_size"); err != nil {
return err
}
if cipherEncryptProc, err = dmCipherEncryptSo.Lookup("cipher_encrypt"); err != nil {
return err
}
if cipherCleanupProc, err = dmCipherEncryptSo.Lookup("cipher_cleanup"); err != nil {
return err
}
if cipherDecryptInitProc, err = dmCipherEncryptSo.Lookup("cipher_decrypt_init"); err != nil {
return err
}
if cipherDecryptProc, err = dmCipherEncryptSo.Lookup("cipher_decrypt"); err != nil {
return err
}
return nil
}
func cipherGetCount() int {
ret := cipherGetCountProc.(func() interface{})()
return ret.(int)
}
func cipherGetInfo(seqno, cipherId, cipherName, _type, blkSize, khSIze uintptr) {
ret := cipherGetInfoProc.(func(uintptr, uintptr, uintptr, uintptr, uintptr, uintptr) interface{})(seqno, cipherId, cipherName, _type, blkSize, khSIze)
if ret.(int) == 0 {
panic("ThirdPartyCipher: call cipher_get_info failed")
}
}
func cipherEncryptInit(cipherId, key, keySize, cipherPara uintptr) {
ret := cipherEncryptInitProc.(func(uintptr, uintptr, uintptr, uintptr) interface{})(cipherId, key, keySize, cipherPara)
if ret.(int) == 0 {
panic("ThirdPartyCipher: call cipher_encrypt_init failed")
}
}
func cipherGetCipherTextSize(cipherId, cipherPara, plainTextSize uintptr) uintptr {
ciphertextLen := cipherGetCipherTextSizeProc.(func(uintptr, uintptr, uintptr) interface{})(cipherId, cipherPara, plainTextSize)
return ciphertextLen.(uintptr)
}
func cipherEncrypt(cipherId, cipherPara, plainText, plainTextSize, cipherText, cipherTextBufSize uintptr) uintptr {
ret := cipherEncryptProc.(func(uintptr, uintptr, uintptr, uintptr, uintptr, uintptr) interface{})(cipherId, cipherPara, plainText, plainTextSize, cipherText, cipherTextBufSize)
return ret.(uintptr)
}
func cipherClean(cipherId, cipherPara uintptr) {
cipherEncryptProc.(func(uintptr, uintptr))(cipherId, cipherPara)
}
func cipherDecryptInit(cipherId, key, keySize, cipherPara uintptr) {
ret := cipherDecryptInitProc.(func(uintptr, uintptr, uintptr, uintptr) interface{})(cipherId, key, keySize, cipherPara)
if ret.(int) == 0 {
panic("ThirdPartyCipher: call cipher_decrypt_init failed")
}
}
func cipherDecrypt(cipherId, cipherPara, cipherText, cipherTextSize, plainText, plainTextBufSize uintptr) uintptr {
ret := cipherDecryptProc.(func(uintptr, uintptr, uintptr, uintptr, uintptr, uintptr) interface{})(cipherId, cipherPara, cipherText, cipherTextSize, plainText, plainTextBufSize)
return ret.(uintptr)
}
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package security
import (
"syscall"
)
var (
dmCipherEncryptDLL *syscall.LazyDLL
cipherGetCountProc *syscall.LazyProc
cipherGetInfoProc *syscall.LazyProc
cipherEncryptInitProc *syscall.LazyProc
cipherGetCipherTextSizeProc *syscall.LazyProc
cipherEncryptProc *syscall.LazyProc
cipherCleanupProc *syscall.LazyProc
cipherDecryptInitProc *syscall.LazyProc
cipherDecryptProc *syscall.LazyProc
)
func initThirdPartCipher(cipherPath string) error {
dmCipherEncryptDLL = syscall.NewLazyDLL(cipherPath)
if err := dmCipherEncryptDLL.Load(); err != nil {
return err
}
cipherGetCountProc = dmCipherEncryptDLL.NewProc("cipher_get_count")
cipherGetInfoProc = dmCipherEncryptDLL.NewProc("cipher_get_info")
cipherEncryptInitProc = dmCipherEncryptDLL.NewProc("cipher_encrypt_init")
cipherGetCipherTextSizeProc = dmCipherEncryptDLL.NewProc("cipher_get_cipher_text_size")
cipherEncryptProc = dmCipherEncryptDLL.NewProc("cipher_encrypt")
cipherCleanupProc = dmCipherEncryptDLL.NewProc("cipher_cleanup")
cipherDecryptInitProc = dmCipherEncryptDLL.NewProc("cipher_decrypt_init")
cipherDecryptProc = dmCipherEncryptDLL.NewProc("cipher_decrypt")
return nil
}
func cipherGetCount() int {
ret, _, _ := cipherGetCountProc.Call()
return int(ret)
}
func cipherGetInfo(seqno, cipherId, cipherName, _type, blkSize, khSIze uintptr) {
ret, _, _ := cipherGetInfoProc.Call(seqno, cipherId, cipherName, _type, blkSize, khSIze)
if ret == 0 {
panic("ThirdPartyCipher: call cipher_get_info failed")
}
}
func cipherEncryptInit(cipherId, key, keySize, cipherPara uintptr) {
ret, _, _ := cipherEncryptInitProc.Call(cipherId, key, keySize, cipherPara)
if ret == 0 {
panic("ThirdPartyCipher: call cipher_encrypt_init failed")
}
}
func cipherGetCipherTextSize(cipherId, cipherPara, plainTextSize uintptr) uintptr {
ciphertextLen, _, _ := cipherGetCipherTextSizeProc.Call(cipherId, cipherPara, plainTextSize)
return ciphertextLen
}
func cipherEncrypt(cipherId, cipherPara, plainText, plainTextSize, cipherText, cipherTextBufSize uintptr) uintptr {
ret, _, _ := cipherEncryptProc.Call(cipherId, cipherPara, plainText, plainTextSize, cipherText, cipherTextBufSize)
return ret
}
func cipherClean(cipherId, cipherPara uintptr) {
_, _, _ = cipherCleanupProc.Call(cipherId, cipherPara)
}
func cipherDecryptInit(cipherId, key, keySize, cipherPara uintptr) {
ret, _, _ := cipherDecryptInitProc.Call(cipherId, key, keySize, cipherPara)
if ret == 0 {
panic("ThirdPartyCipher: call cipher_decrypt_init failed")
}
}
func cipherDecrypt(cipherId, cipherPara, cipherText, cipherTextSize, plainText, plainTextBufSize uintptr) uintptr {
ret, _, _ := cipherDecryptProc.Call(cipherId, cipherPara, cipherText, cipherTextSize, plainText, plainTextBufSize)
return ret
}
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package security
import (
"crypto/tls"
"errors"
"net"
"sync"
)
//var dmHome = flag.String("DM_HOME", "", "Where DMDB installed")
var flagLock = sync.Mutex{}
func NewTLSFromTCP(conn net.Conn, sslCertPath string, sslKeyPath string, user string) (*tls.Conn, error) {
if sslCertPath == "" && sslKeyPath == "" {
// 用户必须手动指定ssl文件和签名(.cert文件)
return nil, errors.New("sslCertPath and sslKeyPath can not be empty!")
}
cer, err := tls.LoadX509KeyPair(sslCertPath, sslKeyPath)
if err != nil {
return nil, err
}
conf := &tls.Config{
InsecureSkipVerify: true,
Certificates: []tls.Certificate{cer},
}
tlsConn := tls.Client(conn, conf)
if err := tlsConn.Handshake(); err != nil {
return nil, err
}
return tlsConn, nil
}
+522
View File
@@ -0,0 +1,522 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"database/sql/driver"
"io"
"reflect"
"strings"
)
type DmRows struct {
filterable
CurrentRows *innerRows
finish func()
}
func (r *DmRows) Columns() []string {
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
return nil
}
if len(r.filterChain.filters) == 0 {
return r.columns()
}
return r.filterChain.reset().DmRowsColumns(r)
}
func (r *DmRows) Close() error {
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
return err
}
if len(r.filterChain.filters) == 0 {
return r.close()
}
return r.filterChain.reset().DmRowsClose(r)
}
func (r *DmRows) Next(dest []driver.Value) error {
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
return err
}
if len(r.filterChain.filters) == 0 {
return r.next(dest)
}
return r.filterChain.reset().DmRowsNext(r, dest)
}
func (r *DmRows) HasNextResultSet() bool {
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
return false
}
if len(r.filterChain.filters) == 0 {
return r.hasNextResultSet()
}
return r.filterChain.reset().DmRowsHasNextResultSet(r)
}
func (r *DmRows) NextResultSet() error {
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
return err
}
if len(r.filterChain.filters) == 0 {
return r.nextResultSet()
}
return r.filterChain.reset().DmRowsNextResultSet(r)
}
func (r *DmRows) ColumnTypeScanType(index int) reflect.Type {
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
return nil
}
if len(r.filterChain.filters) == 0 {
return r.columnTypeScanType(index)
}
return r.filterChain.reset().DmRowsColumnTypeScanType(r, index)
}
func (r *DmRows) ColumnTypeDatabaseTypeName(index int) string {
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
return ""
}
if len(r.filterChain.filters) == 0 {
return r.columnTypeDatabaseTypeName(index)
}
return r.filterChain.reset().DmRowsColumnTypeDatabaseTypeName(r, index)
}
func (r *DmRows) ColumnTypeLength(index int) (length int64, ok bool) {
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
return -1, false
}
if len(r.filterChain.filters) == 0 {
return r.columnTypeLength(index)
}
return r.filterChain.reset().DmRowsColumnTypeLength(r, index)
}
func (r *DmRows) ColumnTypeNullable(index int) (nullable, ok bool) {
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
return false, false
}
if len(r.filterChain.filters) == 0 {
return r.columnTypeNullable(index)
}
return r.filterChain.reset().DmRowsColumnTypeNullable(r, index)
}
func (r *DmRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) {
if err := r.CurrentRows.dmStmt.checkClosed(); err != nil {
return -1, -1, false
}
if len(r.filterChain.filters) == 0 {
return r.columnTypePrecisionScale(index)
}
return r.filterChain.reset().DmRowsColumnTypePrecisionScale(r, index)
}
func (dest *DmRows) Scan(src interface{}) error {
switch src := src.(type) {
case nil:
*dest = *new(DmRows)
return nil
case *DmRows:
*dest = *src
return nil
default:
return UNSUPPORTED_SCAN
}
}
func (rows *DmRows) columns() []string {
return rows.CurrentRows.Columns()
}
func (rows *DmRows) close() error {
if f := rows.finish; f != nil {
f()
rows.finish = nil
}
return rows.CurrentRows.Close()
}
func (rows *DmRows) next(dest []driver.Value) error {
return rows.CurrentRows.Next(dest)
}
func (rows *DmRows) hasNextResultSet() bool {
return rows.CurrentRows.HasNextResultSet()
}
func (rows *DmRows) nextResultSet() error {
return rows.CurrentRows.NextResultSet()
}
func (rows *DmRows) columnTypeScanType(index int) reflect.Type {
return rows.CurrentRows.ColumnTypeScanType(index)
}
func (rows *DmRows) columnTypeDatabaseTypeName(index int) string {
return rows.CurrentRows.ColumnTypeDatabaseTypeName(index)
}
func (rows *DmRows) columnTypeLength(index int) (length int64, ok bool) {
return rows.CurrentRows.ColumnTypeLength(index)
}
func (rows *DmRows) columnTypeNullable(index int) (nullable, ok bool) {
return rows.CurrentRows.ColumnTypeNullable(index)
}
func (rows *DmRows) columnTypePrecisionScale(index int) (precision, scale int64, ok bool) {
return rows.CurrentRows.ColumnTypePrecisionScale(index)
}
type innerRows struct {
dmStmt *DmStatement
id int16
columns []column
datas [][][]byte
datasOffset int
datasStartPos int64
currentPos int64
totalRowCount int64
fetchSize int
sizeOfRow int
isBdta bool
nextExecInfo *execRetInfo
next *innerRows
dmRows *DmRows
closed bool
}
func (innerRows *innerRows) checkClosed() error {
if innerRows.closed {
return ECGO_RESULTSET_CLOSED.throw()
}
return nil
}
func (innerRows *innerRows) Columns() []string {
if err := innerRows.checkClosed(); err != nil {
return nil
}
columnNames := make([]string, len(innerRows.columns))
nameCase := innerRows.dmStmt.dmConn.dmConnector.columnNameCase
for i, column := range innerRows.columns {
if nameCase == COLUMN_NAME_NATURAL_CASE {
columnNames[i] = column.name
} else if nameCase == COLUMN_NAME_UPPER_CASE {
columnNames[i] = strings.ToUpper(column.name)
} else if nameCase == COLUMN_NAME_LOWER_CASE {
columnNames[i] = strings.ToLower(column.name)
} else {
columnNames[i] = column.name
}
}
return columnNames
}
func (innerRows *innerRows) Close() error {
if innerRows.closed {
return nil
}
innerRows.closed = true
if innerRows.dmStmt.innerUsed {
innerRows.dmStmt.close()
} else {
delete(innerRows.dmStmt.rsMap, innerRows.id)
}
innerRows.dmStmt = nil
return nil
}
func (innerRows *innerRows) Next(dest []driver.Value) error {
err := innerRows.checkClosed()
if err != nil {
return err
}
if innerRows.totalRowCount == 0 || innerRows.currentPos >= innerRows.totalRowCount {
return io.EOF
}
if innerRows.currentPos+1 == innerRows.totalRowCount {
innerRows.currentPos++
innerRows.datasOffset++
return io.EOF
}
if innerRows.currentPos+1 < innerRows.datasStartPos || innerRows.currentPos+1 >= innerRows.datasStartPos+int64(len(innerRows.datas)) {
if innerRows.fetchData(innerRows.currentPos + 1) {
innerRows.currentPos++
err := innerRows.getRowData(dest)
if err != nil {
return err
}
} else {
innerRows.currentPos++
innerRows.datasOffset++
return io.EOF
}
} else {
innerRows.currentPos++
innerRows.datasOffset++
err := innerRows.getRowData(dest)
if err != nil {
return err
}
}
return nil
}
func (innerRows *innerRows) HasNextResultSet() bool {
err := innerRows.checkClosed()
if err != nil {
return false
}
if innerRows.nextExecInfo != nil {
return innerRows.nextExecInfo.hasResultSet
}
innerRows.nextExecInfo, err = innerRows.dmStmt.dmConn.Access.Dm_build_543(innerRows.dmStmt, 0)
if err != nil {
return false
}
if innerRows.nextExecInfo.hasResultSet {
innerRows.next = newInnerRows(innerRows.id+1, innerRows.dmStmt, innerRows.nextExecInfo)
return true
}
return false
}
func (innerRows *innerRows) NextResultSet() error {
err := innerRows.checkClosed()
if err != nil {
return err
}
if innerRows.nextExecInfo == nil {
innerRows.HasNextResultSet()
}
if innerRows.next == nil {
return io.EOF
}
innerRows.next.dmRows = innerRows.dmRows
innerRows.dmRows.CurrentRows = innerRows.next
return nil
}
func (innerRows *innerRows) ColumnTypeScanType(index int) reflect.Type {
if err := innerRows.checkClosed(); err != nil {
return nil
}
if column := innerRows.checkIndex(index); column != nil {
return column.ScanType()
}
return nil
}
func (innerRows *innerRows) ColumnTypeDatabaseTypeName(index int) string {
if err := innerRows.checkClosed(); err != nil {
return ""
}
if column := innerRows.checkIndex(index); column != nil {
return column.typeName
}
return ""
}
func (innerRows *innerRows) ColumnTypeLength(index int) (length int64, ok bool) {
if err := innerRows.checkClosed(); err != nil {
return 0, false
}
if column := innerRows.checkIndex(index); column != nil {
return column.Length()
}
return 0, false
}
func (innerRows *innerRows) ColumnTypeNullable(index int) (nullable, ok bool) {
if err := innerRows.checkClosed(); err != nil {
return false, false
}
if column := innerRows.checkIndex(index); column != nil {
return column.nullable, true
}
return false, false
}
func (innerRows *innerRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) {
if err := innerRows.checkClosed(); err != nil {
return 0, 0, false
}
if column := innerRows.checkIndex(index); column != nil {
return column.PrecisionScale()
}
return 0, 0, false
}
func newDmRows(currentRows *innerRows) *DmRows {
dr := new(DmRows)
dr.resetFilterable(&currentRows.dmStmt.filterable)
dr.CurrentRows = currentRows
dr.idGenerator = dmRowsIDGenerator
currentRows.dmRows = dr
return dr
}
func newInnerRows(id int16, stmt *DmStatement, execInfo *execRetInfo) *innerRows {
rows := new(innerRows)
rows.id = id
rows.dmStmt = stmt
rows.columns = stmt.columns
rows.datas = execInfo.rsDatas
rows.totalRowCount = execInfo.updateCount
rows.isBdta = execInfo.rsBdta
rows.fetchSize = stmt.fetchSize
if len(execInfo.rsDatas) == 0 {
rows.sizeOfRow = 0
} else {
rows.sizeOfRow = execInfo.rsSizeof / len(execInfo.rsDatas)
}
rows.currentPos = -1
rows.datasOffset = -1
rows.datasStartPos = 0
rows.nextExecInfo = nil
rows.next = nil
if rows.dmStmt.rsMap != nil {
rows.dmStmt.rsMap[rows.id] = rows
}
if stmt.dmConn.dmConnector.enRsCache && execInfo.rsCacheOffset > 0 &&
int64(len(execInfo.rsDatas)) == execInfo.updateCount {
rp.put(stmt, stmt.nativeSql, execInfo)
}
return rows
}
func newLocalInnerRows(stmt *DmStatement, columns []column, rsDatas [][][]byte) *innerRows {
rows := new(innerRows)
rows.id = 0
rows.dmStmt = stmt
rows.fetchSize = stmt.fetchSize
if columns == nil {
rows.columns = make([]column, 0)
} else {
rows.columns = columns
}
if rsDatas == nil {
rows.datas = make([][][]byte, 0)
rows.totalRowCount = 0
} else {
rows.datas = rsDatas
rows.totalRowCount = int64(len(rsDatas))
}
rows.isBdta = false
return rows
}
func (innerRows *innerRows) checkIndex(index int) *column {
if index < 0 || index > len(innerRows.columns)-1 {
return nil
}
return &innerRows.columns[index]
}
func (innerRows *innerRows) fetchData(startPos int64) bool {
execInfo, err := innerRows.dmStmt.dmConn.Access.Dm_build_550(innerRows, startPos)
if err != nil {
return false
}
innerRows.totalRowCount = execInfo.updateCount
if execInfo.rsDatas != nil {
innerRows.datas = execInfo.rsDatas
innerRows.datasStartPos = startPos
innerRows.datasOffset = 0
return true
}
return false
}
func (innerRows *innerRows) getRowData(dest []driver.Value) (err error) {
for i, column := range innerRows.columns {
if i <= len(dest)-1 {
if column.colType == CURSOR {
var tmpExecInfo *execRetInfo
tmpExecInfo, err = innerRows.dmStmt.dmConn.Access.Dm_build_543(innerRows.dmStmt, 1)
if err != nil {
return err
}
if tmpExecInfo.hasResultSet {
dest[i] = newDmRows(newInnerRows(innerRows.id+1, innerRows.dmStmt, tmpExecInfo))
} else {
dest[i] = nil
}
continue
}
dest[i], err = column.getColumnData(innerRows.datas[innerRows.datasOffset][i+1], innerRows.dmStmt.dmConn)
innerRows.columns[i].isBdta = innerRows.isBdta
if err != nil {
return err
}
} else {
return nil
}
}
return nil
}
func (innerRows *innerRows) getRowCount() int64 {
innerRows.checkClosed()
if innerRows.totalRowCount == INT64_MAX {
return -1
}
return innerRows.totalRowCount
}
+1590
View File
File diff suppressed because it is too large Load Diff
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package util
func Split(s string, sep string) []string {
var foot = make([]int, len(s)) // 足够的元素个数
var count, sLen, sepLen = 0, len(s), len(sep)
for i := 0; i < sLen; i++ {
// 处理 s == “-9999-1" && seperators == "-"情况
if i == 0 && sLen >= sepLen {
if s[0:sepLen] == sep {
i += sepLen - 1
continue
}
}
for j := 0; j < sepLen; j++ {
if s[i] == sep[j] {
foot[count] = i
count++
break
}
}
}
var ret = make([]string, count+1)
if count == 0 {
ret[0] = s
return ret
}
ret[0] = s[0:foot[0]]
for i := 1; i < count; i++ {
ret[i] = s[foot[i-1]+1 : foot[i]]
}
ret[count] = s[foot[count-1]+1:]
return ret
}
+75
View File
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package util
import (
"go/build"
"os"
"runtime"
"strings"
)
const (
PathSeparator = string(os.PathSeparator)
PathListSeparator = string(os.PathListSeparator)
)
var (
goRoot = build.Default.GOROOT
goPath = build.Default.GOPATH //获取实际编译时的GOPATH值
)
type fileUtil struct {
}
var FileUtil = &fileUtil{}
func (fileUtil *fileUtil) Exists(path string) bool {
if _, err := os.Stat(path); !os.IsNotExist(err) {
return true
}
return false
}
func (fileUtil *fileUtil) Search(relativePath string) (path string) {
if strings.Contains(runtime.GOOS, "windows") {
relativePath = strings.ReplaceAll(relativePath, "/", "\\")
}
if fileUtil.Exists(goPath) {
for _, s := range strings.Split(goPath, PathListSeparator) {
path = s + PathSeparator + "src" + PathSeparator + relativePath
if fileUtil.Exists(path) {
return path
}
}
}
if fileUtil.Exists(goPath) {
for _, s := range strings.Split(goPath, PathListSeparator) {
path = s + PathSeparator + "pkg" + PathSeparator + relativePath
if fileUtil.Exists(path) {
return path
}
}
}
//if workDir, _ := os.Getwd(); fileUtil.Exists(workDir) {
// path = workDir + PathSeparator + "src" + PathSeparator + relativePath
// if fileUtil.Exists(path) {
// return path
// }
//}
//if fileUtil.Exists(goRoot) {
// path = goRoot + PathSeparator + "src" + PathSeparator + relativePath
// if fileUtil.Exists(path) {
// return path
// }
//}
return ""
}
+57
View File
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package util
const (
LINE_SEPARATOR = "\n"
)
// 执行f并忽略panic
func AbsorbPanic(f func()){
defer func() {
if p := recover(); p != nil {
// TODO do something
}
}()
f()
}
func SliceEquals(src []byte, dest []byte) bool {
if len(src) != len(dest) {
return false
}
for i, _ := range src {
if src[i] != dest[i] {
return false
}
}
return true
}
// 获取两个数的最大公约数,由调用者确保m、n>=0;如果m或n为0,返回1
func GCD(m int32, n int32) int32 {
if m == 0 || n == 0 {
return 1
}
r := m % n
m = n
n = r
if r == 0 {
return m
} else {
return GCD(m, n)
}
}
// 返回切片中所有数的累加值
func Sum(arr []int32) int32 {
var sum int32 = 0
for _, i := range arr {
sum += i
}
return sum
}
+264
View File
@@ -0,0 +1,264 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package util
import (
"bytes"
"runtime"
"strings"
"time"
"unicode"
)
type stringutil struct{}
var StringUtil = &stringutil{}
/*----------------------------------------------------*/
func (StringUtil *stringutil) LineSeparator() string {
var lineSeparator string
if strings.Contains(runtime.GOOS, "windows") {
lineSeparator = "\r\n"
} else if strings.Contains(runtime.GOOS, "mac") {
lineSeparator = "\r"
} else {
lineSeparator = "\n"
}
return lineSeparator
}
func (StringUtil *stringutil) Equals(str1 string, str2 string) bool {
return str1 == str2
}
func (StringUtil *stringutil) EqualsIgnoreCase(str1 string, str2 string) bool {
return strings.ToUpper(str1) == strings.ToUpper(str2)
}
func (StringUtil *stringutil) StartsWith(s string, subStr string) bool {
return strings.Index(s, subStr) == 0
}
func (StringUtil *stringutil) StartWithIgnoreCase(s string, subStr string) bool {
return strings.HasPrefix(strings.ToLower(s), strings.ToLower(subStr))
}
func (StringUtil *stringutil) EndsWith(s string, subStr string) bool {
return strings.LastIndex(s, subStr) == len(s)-1
}
func (StringUtil *stringutil) IsDigit(str string) bool {
if str == "" {
return false
}
sz := len(str)
for i := 0; i < sz; i++ {
if unicode.IsDigit(rune(str[i])) {
continue
} else {
return false
}
}
return true
}
func (StringUtil *stringutil) FormatDir(dir string) string {
dir = strings.TrimSpace(dir)
if dir != "" {
if !StringUtil.EndsWith(dir, PathSeparator) {
dir += PathSeparator
}
}
return dir
}
func (StringUtil *stringutil) HexStringToBytes(s string) []byte {
str := s
bs := make([]byte, 0)
flag := false
if strings.Index(str, "0x") == 0 || strings.Index(str, "0X") == 0 {
str = str[2:]
}
if len(str) == 0 {
return bs
}
var bsChr []byte
l := len(str)
if l%2 == 0 {
bsChr = []byte(str)
} else {
l += 1
bsChr = make([]byte, l)
bsChr[0] = '0'
for i := 0; i < l-1; i++ {
bsChr[i+1] = str[i]
}
}
bs = make([]byte, l/2)
pos := 0
for i := 0; i < len(bsChr); i += 2 {
bt := convertHex(bsChr[i])
bt2 := convertHex(bsChr[i+1])
if int(bt) == 0xff || int(bt2) == 0xff {
flag = true
break
}
bs[pos] = byte(bt*16 + bt2)
pos++
}
if flag {
bs = ([]byte)(str)
}
return bs
}
func convertHex(chr byte) byte {
if chr >= '0' && chr <= '9' {
return chr - '0'
} else if chr >= 'a' && chr <= 'f' {
return chr - 'a' + 10
} else if chr >= 'A' && chr <= 'F' {
return chr - 'A' + 10
} else {
return 0xff
}
}
func (StringUtil *stringutil) BytesToHexString(bs []byte, pre bool) string {
if bs == nil {
return ""
}
if len(bs) == 0 {
return ""
}
hexDigits := "0123456789ABCDEF"
ret := new(strings.Builder)
for _, b := range bs {
ret.WriteByte(hexDigits[0x0F&(b>>4)])
ret.WriteByte(hexDigits[0x0F&b])
}
if pre {
return "0x" + ret.String()
}
return ret.String()
}
func (StringUtil *stringutil) ProcessSingleQuoteOfName(name string) string {
return StringUtil.processQuoteOfName(name, "'")
}
func (StringUtil *stringutil) ProcessDoubleQuoteOfName(name string) string {
return StringUtil.processQuoteOfName(name, "\"")
}
func (StringUtil *stringutil) processQuoteOfName(name string, quote string) string {
if quote == "" || name == "" {
return name
}
temp := name
result := bytes.NewBufferString("")
index := -1
quetoLength := len(quote)
index = strings.Index(temp, quote)
for index != -1 {
result.WriteString(temp[:index+quetoLength])
result.WriteString(quote)
temp = temp[index+quetoLength:]
index = strings.Index(temp, quote)
}
result.WriteString(temp)
return result.String()
}
func (StringUtil *stringutil) FormatTime() string {
return time.Now().Format("2006-01-02 15:04:05")
}
func (StringUtil *stringutil) SubstringBetween(str string, open string, close string) string {
if str == "" {
return ""
}
iopen := -1
if open != "" {
iopen = strings.Index(str, open)
}
iclose := -1
if close != "" {
iclose = strings.LastIndex(str, close)
}
if iopen == -1 && iclose == -1 {
return ""
} else if iopen == -1 {
return str[0:iclose]
} else if iclose == -1 {
return str[iopen:]
} else {
return str[iopen:iclose]
}
}
//bug656976 在常量参数化时,将\+任意字符的两个字符解析成一个转义后字符,如:
// 字符串参数'a\nb'解析成字符串参数'a换行b'
func (StringUtil *stringutil) Translate(s string) string {
if !strings.ContainsRune(s, '\\') {
return s
}
reader := strings.NewReader(s)
trans := bytes.NewBufferString("")
for {
curRune, _, err := reader.ReadRune()
if err != nil {
break
}
if curRune != '\\' {
trans.WriteRune(curRune)
} else {
//转义规则参考mysql,'\'作为转义符必须消除,不管是否作为有真正含义的特殊字符,如\x也需要变化为x
nextRune, _, err := reader.ReadRune()
if err != nil {
break
}
switch nextRune {
case 'b':
trans.WriteRune('\b')
break
case 'f':
trans.WriteRune('\f')
break
case 'n':
trans.WriteRune('\n')
break
case 'r':
trans.WriteRune('\r')
break
case 't':
trans.WriteRune('\t')
break
default:
trans.WriteRune(nextRune)
break
}
}
}
return trans.String()
}
+142
View File
@@ -0,0 +1,142 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import "database/sql/driver"
type DmStruct struct {
TypeData
m_strctDesc *StructDescriptor // 结构体的描述信息
m_attribs []TypeData // 各属性值
m_objCount int // 一个数组项中存在对象类型的个数(class、动态数组)
m_strCount int // 一个数组项中存在字符串类型的个数
typeName string
elements []interface{}
// Valid为false代表DmArray数据在数据库中为NULL
Valid bool
}
// 数据库自定义类型Struct构造函数,typeName为库中定义的类型名称,elements为该类型每个字段的值
//
// 例如,自定义类型语句为:create or replace type myType as object (a1 int, a2 varchar);
//
// 则绑入绑出的go对象为: val := dm.NewDmStruct("myType", []interface{} {123, "abc"})
func NewDmStruct(typeName string, elements []interface{}) *DmStruct {
ds := new(DmStruct)
ds.typeName = typeName
ds.elements = elements
ds.Valid = true
return ds
}
func (ds *DmStruct) create(dc *DmConnection) (*DmStruct, error) {
desc, err := newStructDescriptor(ds.typeName, dc)
if err != nil {
return nil, err
}
return ds.createByStructDescriptor(desc, dc)
}
func newDmStructByTypeData(atData []TypeData, desc *TypeDescriptor) *DmStruct {
ds := new(DmStruct)
ds.Valid = true
ds.initTypeData()
ds.m_strctDesc = newStructDescriptorByTypeDescriptor(desc)
ds.m_attribs = atData
return ds
}
func (dest *DmStruct) Scan(src interface{}) error {
if dest == nil {
return ECGO_STORE_IN_NIL_POINTER.throw()
}
switch src := src.(type) {
case nil:
*dest = *new(DmStruct)
// 将Valid标志置false表示数据库中该列为NULL
(*dest).Valid = false
return nil
case *DmStruct:
*dest = *src
return nil
default:
return UNSUPPORTED_SCAN.throw()
}
}
func (dt DmStruct) Value() (driver.Value, error) {
if !dt.Valid {
return nil, nil
}
return dt, nil
}
func (ds *DmStruct) getAttribsTypeData() []TypeData {
return ds.m_attribs
}
func (ds *DmStruct) createByStructDescriptor(desc *StructDescriptor, conn *DmConnection) (*DmStruct, error) {
ds.initTypeData()
if nil == desc {
return nil, ECGO_INVALID_PARAMETER_VALUE.throw()
}
ds.m_strctDesc = desc
if nil == ds.elements {
ds.m_attribs = make([]TypeData, desc.getSize())
} else {
if desc.getSize() != len(ds.elements) && desc.getObjId() != 4 {
return nil, ECGO_STRUCT_MEM_NOT_MATCH.throw()
}
var err error
ds.m_attribs, err = TypeDataSV.toStruct(ds.elements, ds.m_strctDesc.m_typeDesc)
if err != nil {
return nil, err
}
}
return ds, nil
}
// 获取Struct对象在数据库中的类型名称
func (ds *DmStruct) GetSQLTypeName() (string, error) {
return ds.m_strctDesc.m_typeDesc.getFulName()
}
// 获取Struct对象中的各个字段的值
func (ds *DmStruct) GetAttributes() ([]interface{}, error) {
return TypeDataSV.toJavaArrayByDmStruct(ds)
}
func (ds *DmStruct) checkCol(col int) error {
if col < 1 || col > len(ds.m_attribs) {
return ECGO_INVALID_SEQUENCE_NUMBER.throw()
}
return nil
}
// 获取指定索引的成员变量值,以TypeData的形式给出,col 1 based
func (ds *DmStruct) getAttrValue(col int) (*TypeData, error) {
err := ds.checkCol(col)
if err != nil {
return nil, err
}
return &ds.m_attribs[col-1], nil
}
func (ds *DmStruct) checkValid() error {
if !ds.Valid {
return ECGO_IS_NULL.throw()
}
return nil
}
+233
View File
@@ -0,0 +1,233 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"database/sql/driver"
"strings"
"time"
)
const (
Seconds_1900_1970 = 2209017600
OFFSET_YEAR = 0
OFFSET_MONTH = 1
OFFSET_DAY = 2
OFFSET_HOUR = 3
OFFSET_MINUTE = 4
OFFSET_SECOND = 5
OFFSET_NANOSECOND = 6
OFFSET_TIMEZONE = 7
DT_LEN = 8
INVALID_VALUE = int(INT32_MIN)
NANOSECOND_DIGITS = 9
NANOSECOND_POW = 1000000000
)
type DmTimestamp struct {
dt []int
dtype int
scale int
oracleFormatPattern string
oracleDateLanguage int
// Valid为false代表DmArray数据在数据库中为NULL
Valid bool
}
func newDmTimestampFromDt(dt []int, dtype int, scale int) *DmTimestamp {
dmts := new(DmTimestamp)
dmts.Valid = true
dmts.dt = dt
dmts.dtype = dtype
dmts.scale = scale
return dmts
}
func newDmTimestampFromBytes(bytes []byte, column column, conn *DmConnection) *DmTimestamp {
dmts := new(DmTimestamp)
dmts.Valid = true
dmts.dt = decode(bytes, column.isBdta, column, int(conn.dmConnector.localTimezone), int(conn.DbTimezone))
if isLocalTimeZone(int(column.colType), int(column.scale)) {
dmts.scale = getLocalTimeZoneScale(int(column.colType), int(column.scale))
} else {
dmts.scale = int(column.scale)
}
dmts.dtype = int(column.colType)
dmts.scale = int(column.scale)
dmts.oracleDateLanguage = int(conn.OracleDateLanguage)
switch column.colType {
case DATE:
dmts.oracleFormatPattern = conn.FormatDate
case TIME:
dmts.oracleFormatPattern = conn.FormatTime
case TIME_TZ:
dmts.oracleFormatPattern = conn.FormatTimeTZ
case DATETIME, DATETIME2:
dmts.oracleFormatPattern = conn.FormatTimestamp
case DATETIME_TZ, DATETIME2_TZ:
dmts.oracleFormatPattern = conn.FormatTimestampTZ
}
return dmts
}
func NewDmTimestampFromString(str string) (*DmTimestamp, error) {
dt := make([]int, DT_LEN)
dtype, err := toDTFromString(strings.TrimSpace(str), dt)
if err != nil {
return nil, err
}
if dtype == DATE {
return newDmTimestampFromDt(dt, dtype, 0), nil
}
return newDmTimestampFromDt(dt, dtype, 6), nil
}
func NewDmTimestampFromTime(time time.Time) *DmTimestamp {
dt := toDTFromTime(time)
return newDmTimestampFromDt(dt, DATETIME, 6)
}
func (dmTimestamp *DmTimestamp) ToTime() time.Time {
_, tzs := time.Now().Zone()
return toTimeFromDT(dmTimestamp.dt, tzs / 60)
}
// 获取年月日时分秒毫秒时区
func (dmTimestamp *DmTimestamp) GetDt() []int {
return dmTimestamp.dt
}
func (dmTimestamp *DmTimestamp) CompareTo(ts DmTimestamp) int {
if dmTimestamp.ToTime().Equal(ts.ToTime()) {
return 0
} else if dmTimestamp.ToTime().Before(ts.ToTime()) {
return -1
} else {
return 1
}
}
func (dmTimestamp *DmTimestamp) String() string {
if dmTimestamp.oracleFormatPattern != "" {
return dtToStringByOracleFormat(dmTimestamp.dt, dmTimestamp.oracleFormatPattern, int32(dmTimestamp.scale), dmTimestamp.oracleDateLanguage)
}
return dtToString(dmTimestamp.dt, dmTimestamp.dtype, dmTimestamp.scale)
}
func (dest *DmTimestamp) Scan(src interface{}) error {
if dest == nil {
return ECGO_STORE_IN_NIL_POINTER.throw()
}
switch src := src.(type) {
case nil:
*dest = *new(DmTimestamp)
// 将Valid标志置false表示数据库中该列为NULL
(*dest).Valid = false
return nil
case *DmTimestamp:
*dest = *src
return nil
case time.Time:
ret := NewDmTimestampFromTime(src)
*dest = *ret
return nil
case string:
ret, err := NewDmTimestampFromString(src)
if err != nil {
return err
}
*dest = *ret
return nil
default:
return UNSUPPORTED_SCAN.throw()
}
}
func (dmTimestamp DmTimestamp) Value() (driver.Value, error) {
if !dmTimestamp.Valid {
return nil, nil
}
return dmTimestamp, nil
}
//func (dmTimestamp *DmTimestamp) toBytes() ([]byte, error) {
// return encode(dmTimestamp.dt, dmTimestamp.dtype, dmTimestamp.scale, dmTimestamp.dt[OFFSET_TIMEZONE])
//}
/**
* 获取当前对象的年月日时分秒,如果原来没有decode会先decode;
*/
func (dmTimestamp *DmTimestamp) getDt() []int {
return dmTimestamp.dt
}
func (dmTimestamp *DmTimestamp) getTime() int64 {
_, tzs := time.Now().Zone()
sec := toTimeFromDT(dmTimestamp.dt, tzs / 60).Unix()
return sec + int64(dmTimestamp.dt[OFFSET_NANOSECOND])
}
func (dmTimestamp *DmTimestamp) setTime(time int64) {
timeInMillis := (time / 1000) * 1000
nanos := (int64)((time % 1000) * 1000000)
if nanos < 0 {
nanos = 1000000000 + nanos
timeInMillis = (((time / 1000) - 1) * 1000)
}
dmTimestamp.dt = toDTFromUnix(timeInMillis, nanos)
}
func (dmTimestamp *DmTimestamp) setTimezone(tz int) error {
// DM中合法的时区取值范围为-12:59至+14:00
if tz <= -13*60 || tz > 14*60 {
return ECGO_INVALID_DATETIME_FORMAT.throw()
}
dmTimestamp.dt[OFFSET_TIMEZONE] = tz
return nil
}
func (dmTimestamp *DmTimestamp) getNano() int64 {
return int64(dmTimestamp.dt[OFFSET_NANOSECOND] * 1000)
}
func (dmTimestamp *DmTimestamp) setNano(nano int64) {
dmTimestamp.dt[OFFSET_NANOSECOND] = (int)(nano / 1000)
}
func (dmTimestamp *DmTimestamp) string() string {
if dmTimestamp.oracleFormatPattern != "" {
return dtToStringByOracleFormat(dmTimestamp.dt, dmTimestamp.oracleFormatPattern, int32(dmTimestamp.scale), dmTimestamp.oracleDateLanguage)
}
return dtToString(dmTimestamp.dt, dmTimestamp.dtype, dmTimestamp.scale)
}
func (dmTimestamp *DmTimestamp) checkValid() error {
if !dmTimestamp.Valid {
return ECGO_IS_NULL.throw()
}
return nil
}
/* for gorm v2 */
func (d *DmTimestamp) GormDataType() string {
return "TIMESTAMP"
}
+204
View File
@@ -0,0 +1,204 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"context"
"strconv"
"strings"
"sync"
"time"
)
const (
STATUS_VALID_TIME = 20 * time.Second // ms
// sort 值
SORT_SERVER_MODE_INVALID = -1 // 不允许连接的模式
SORT_SERVER_NOT_ALIVE = -2 // 站点无法连接
SORT_UNKNOWN = INT32_MAX // 站点还未连接过,模式未知
SORT_NORMAL = 30
SORT_PRIMARY = 20
SORT_STANDBY = 10
// OPEN>MOUNT>SUSPEND
SORT_OPEN = 3
SORT_MOUNT = 2
SORT_SUSPEND = 1
)
type ep struct {
host string
port int32
alive bool
statusRefreshTs int64 // 状态更新的时间点
serverMode int32
serverStatus int32
dscControl bool
sort int32
epSeqno int32
epStatus int32
lock sync.Mutex
}
func newEP(host string, port int32) *ep {
ep := new(ep)
ep.host = host
ep.port = port
ep.serverMode = -1
ep.serverStatus = -1
ep.sort = SORT_UNKNOWN
return ep
}
func (ep *ep) getSort(checkTime bool) int32 {
if checkTime {
if time.Now().UnixNano()-ep.statusRefreshTs < int64(STATUS_VALID_TIME) {
return ep.sort
} else {
return SORT_UNKNOWN
}
}
return ep.sort
}
func (ep *ep) calcSort(loginMode int32) int32 {
var sort int32 = 0
switch loginMode {
case LOGIN_MODE_PRIMARY_FIRST:
{
// 主机优先:PRIMARY>NORMAL>STANDBY
switch ep.serverMode {
case SERVER_MODE_NORMAL:
sort += SORT_NORMAL * 10
case SERVER_MODE_PRIMARY:
sort += SORT_PRIMARY * 100
case SERVER_MODE_STANDBY:
sort += SORT_STANDBY
}
}
case LOGIN_MODE_STANDBY_FIRST:
{
// STANDBY优先: STANDBY>PRIMARY>NORMAL
switch ep.serverMode {
case SERVER_MODE_NORMAL:
sort += SORT_NORMAL
case SERVER_MODE_PRIMARY:
sort += SORT_PRIMARY * 10
case SERVER_MODE_STANDBY:
sort += SORT_STANDBY * 100
}
}
case LOGIN_MODE_NORMAL_FIRST:
{
// NORMAL优先: NORMAL>PRIMARY>STANDBY
switch ep.serverMode {
case SERVER_MODE_STANDBY:
sort += SORT_STANDBY
case SERVER_MODE_PRIMARY:
sort += SORT_PRIMARY * 10
case SERVER_MODE_NORMAL:
sort += SORT_NORMAL * 100
}
}
case LOGIN_MODE_PRIMARY_ONLY:
if ep.serverMode != SERVER_MODE_PRIMARY {
return SORT_SERVER_MODE_INVALID
}
sort += SORT_PRIMARY
case LOGIN_MODE_STANDBY_ONLY:
if ep.serverMode != SERVER_MODE_STANDBY {
return SORT_SERVER_MODE_INVALID
}
sort += SORT_STANDBY
}
switch ep.serverStatus {
case SERVER_STATUS_MOUNT:
sort += SORT_MOUNT
case SERVER_STATUS_OPEN:
sort += SORT_OPEN
case SERVER_STATUS_SUSPEND:
sort += SORT_SUSPEND
}
return sort
}
func (ep *ep) refreshStatus(alive bool, conn *DmConnection) {
ep.lock.Lock()
defer ep.lock.Unlock()
ep.alive = alive
ep.statusRefreshTs = time.Now().UnixNano()
if alive {
ep.serverMode = conn.SvrMode
ep.serverStatus = conn.SvrStat
ep.dscControl = conn.dscControl
ep.sort = ep.calcSort(int32(conn.dmConnector.loginMode))
} else {
ep.serverMode = -1
ep.serverStatus = -1
ep.dscControl = false
ep.sort = SORT_SERVER_NOT_ALIVE
}
}
func (ep *ep) connect(connector *DmConnector) (*DmConnection, error) {
connector.host = ep.host
connector.port = ep.port
conn, err := connector.connectSingle(context.Background())
if err != nil {
ep.refreshStatus(false, conn)
return nil, err
}
ep.refreshStatus(true, conn)
return conn, nil
}
func (ep *ep) getServerStatusDesc(serverStatus int32) string {
ret := ""
switch ep.serverStatus {
case SERVER_STATUS_OPEN:
ret = "OPEN"
case SERVER_STATUS_MOUNT:
ret = "MOUNT"
case SERVER_STATUS_SUSPEND:
ret = "SUSPEND"
default:
ret = "UNKNOWN"
}
return ret
}
func (ep *ep) getServerModeDesc(serverMode int32) string {
ret := ""
switch ep.serverMode {
case SERVER_MODE_NORMAL:
ret = "NORMAL"
case SERVER_MODE_PRIMARY:
ret = "PRIMARY"
case SERVER_MODE_STANDBY:
ret = "STANDBY"
default:
ret = "UNKNOWN"
}
return ret
}
func (ep *ep) String() string {
dscControl := ")"
if ep.dscControl {
dscControl = ", DSC CONTROL)"
}
return strings.TrimSpace(ep.host) + ":" + strconv.Itoa(int(ep.port)) +
" (" + ep.getServerModeDesc(ep.serverMode) + ", " + ep.getServerStatusDesc(ep.serverStatus) + dscControl
}
+148
View File
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"bytes"
"math/rand"
"sync"
"time"
"gitee.com/chunanyong/dm/util"
)
/**
* dm_svc.conf中配置的服务名对应的一组实例, 以及相关属性和状态信息
*
* 需求:
* 1. 连接均匀分布在各个节点上
* 2. loginModeloginStatus匹配
* 3. 连接异常节点比较耗时,在DB列表中包含异常节点时异常连接尽量靠后,减少对建连接速度的影响
*
*
* DB 连接顺序:
* 1. well distribution,每次连接都从列表的下一个节点开始
* 2. 用DB sort值按从大到小排序,sort为一个四位数XXXX,个位--serverStatus,十位--serverMode,共 有三种模式,最优先的 *100, 次优先的*10
*/
type epGroup struct {
name string
epList []*ep
props *Properties
epStartPos int32 // wellDistribute 起始位置
lock sync.Mutex
}
func newEPGroup(name string, serverList []*ep) *epGroup {
g := new(epGroup)
g.name = name
g.epList = serverList
if serverList == nil || len(serverList) == 0 {
g.epStartPos = -1
} else {
// 保证进程间均衡,起始位置采用随机值
g.epStartPos = rand.Int31n(int32(len(serverList))) - 1
}
return g
}
func (g *epGroup) connect(connector *DmConnector) (*DmConnection, error) {
var dbSelector = g.getEPSelector(connector)
var ex error = nil
// 如果配置了loginMode的主、备等优先策略,而未找到最高优先级的节点时持续循环switchtimes次,如果最终还是没有找到最高优先级则选择次优先级的
// 如果只有一个节点,无需switchTimes+1;多个节点时保证switchTimes轮尝试,最后一轮决定用哪个节点(由于节点已经按照模式优先级排序,最后一轮理论上就是连第一个节点)
var cycleCount int32
if len(g.epList) == 1 {
cycleCount = connector.switchTimes
} else {
cycleCount = connector.switchTimes + 1
}
for i := int32(0); i < cycleCount; i++ {
// 循环了一遍,如果没有符合要求的, 重新排序, 再尝试连接
conn, err := g.traverseServerList(connector, dbSelector, i == 0, i == cycleCount-1)
if err != nil {
ex = err
time.Sleep(time.Duration(connector.switchInterval) * time.Millisecond)
continue
}
return conn, nil
}
return nil, ex
}
func (g *epGroup) getEPSelector(connector *DmConnector) *epSelector {
if connector.epSelector == TYPE_HEAD_FIRST {
return newEPSelector(g.epList)
} else {
serverCount := int32(len(g.epList))
sortEPs := make([]*ep, serverCount)
g.lock.Lock()
defer g.lock.Unlock()
g.epStartPos = (g.epStartPos + 1) % serverCount
for i := int32(0); i < serverCount; i++ {
sortEPs[i] = g.epList[(i+g.epStartPos)%serverCount]
}
return newEPSelector(sortEPs)
}
}
/**
* 从指定编号开始,遍历一遍服务名中的ip列表,只连接指定类型(主机或备机)的ip
* @param servers
* @param checkTime
*
* @exception
* DBError.ECJDBC_INVALID_SERVER_MODE 有站点的模式不匹配
* DBError.ECJDBC_COMMUNITION_ERROR 所有站点都连不上
*/
func (g *epGroup) traverseServerList(connector *DmConnector, epSelector *epSelector, first bool, last bool) (*DmConnection, error) {
epList := epSelector.sortDBList(first)
errorMsg := bytes.NewBufferString("")
var ex error = nil // 第一个错误
for _, server := range epList {
conn, err := server.connect(connector)
if err != nil {
if ex == nil {
ex = err
}
errorMsg.WriteString("[")
errorMsg.WriteString(server.String())
errorMsg.WriteString("]")
errorMsg.WriteString(err.Error())
errorMsg.WriteString(util.StringUtil.LineSeparator())
continue
}
valid, err := epSelector.checkServerMode(conn, last)
if err != nil {
if ex == nil {
ex = err
}
errorMsg.WriteString("[")
errorMsg.WriteString(server.String())
errorMsg.WriteString("]")
errorMsg.WriteString(err.Error())
errorMsg.WriteString(util.StringUtil.LineSeparator())
continue
}
if !valid {
conn.close()
err = ECGO_INVALID_SERVER_MODE.throw()
if ex == nil {
ex = err
}
errorMsg.WriteString("[")
errorMsg.WriteString(server.String())
errorMsg.WriteString("]")
errorMsg.WriteString(err.Error())
errorMsg.WriteString(util.StringUtil.LineSeparator())
continue
}
return conn, nil
}
if ex != nil {
return nil, ex
}
return nil, ECGO_COMMUNITION_ERROR.addDetail(errorMsg.String()).throw()
}
+65
View File
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import "sort"
const (
TYPE_WELL_DISTRIBUTE = 0
TYPE_HEAD_FIRST = 1
)
type epSelector struct {
dbs []*ep
}
func newEPSelector(dbs []*ep) *epSelector {
return &epSelector{dbs}
}
func (s *epSelector) sortDBList(first bool) []*ep {
if !first {
// 按sort从大到小排序,相同sort值顺序不变
sort.Slice(s.dbs, func(i, j int) bool {
return s.dbs[i].getSort(first) > s.dbs[j].getSort(first)
})
}
return s.dbs
}
func (s *epSelector) checkServerMode(conn *DmConnection, last bool) (bool, error) {
// 只连dsc control节点
if conn.dmConnector.loginDscCtrl && !conn.dscControl {
conn.close()
return false, ECGO_INVALID_SERVER_MODE.throw()
}
// 模式不匹配, 这里使用的是连接之前的sort,连接之后server的状态可能发生改变sort也可能改变
if conn.dmConnector.loginStatus > 0 && int(conn.SvrStat) != conn.dmConnector.loginStatus {
conn.close()
return false, ECGO_INVALID_SERVER_MODE.throw()
}
if last {
switch conn.dmConnector.loginMode {
case LOGIN_MODE_PRIMARY_ONLY:
return conn.SvrMode == SERVER_MODE_PRIMARY, nil
case LOGIN_MODE_STANDBY_ONLY:
return conn.SvrMode == SERVER_MODE_STANDBY, nil
default:
return true, nil
}
}
switch conn.dmConnector.loginMode {
case LOGIN_MODE_NORMAL_FIRST:
return conn.SvrMode == SERVER_MODE_NORMAL, nil
case LOGIN_MODE_PRIMARY_FIRST, LOGIN_MODE_PRIMARY_ONLY:
return conn.SvrMode == SERVER_MODE_PRIMARY, nil
case LOGIN_MODE_STANDBY_FIRST, LOGIN_MODE_STANDBY_ONLY:
return conn.SvrMode == SERVER_MODE_STANDBY, nil
default:
break
}
return false, nil
}
+143
View File
@@ -0,0 +1,143 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"bytes"
"fmt"
"runtime"
"gitee.com/chunanyong/dm/i18n"
)
// 驱动级错误
var (
DSN_INVALID_SCHEMA = newDmError(9001, "error.dsn.invalidSchema")
UNSUPPORTED_SCAN = newDmError(9002, "error.unsupported.scan")
INVALID_PARAMETER_NUMBER = newDmError(9003, "error.invalidParameterNumber")
THIRD_PART_CIPHER_INIT_FAILED = newDmError(9004, "error.initThirdPartCipherFailed")
ECGO_NOT_QUERY_SQL = newDmError(9005, "error.notQuerySQL")
ECGO_NOT_EXEC_SQL = newDmError(9006, "error.notExecSQL")
ECGO_UNKOWN_NETWORK = newDmError(9007, "error.unkownNetWork")
ECGO_INVALID_CONN = newDmError(9008, "error.invalidConn")
ECGO_UNSUPPORTED_INPARAM_TYPE = newDmError(9009, "error.unsupportedInparamType")
ECGO_UNSUPPORTED_OUTPARAM_TYPE = newDmError(9010, "error.unsupportedOutparamType")
ECGO_STORE_IN_NIL_POINTER = newDmError(9011, "error.storeInNilPointer")
ECGO_IS_NULL = newDmError(9012, "error.isNull")
DSN_INVALID_FORMAT = newDmError(9001, "error.dsn.invalidFormat")
)
var (
ECGO_CONNECTION_SWITCH_FAILED = newDmError(20001, "error.connectionSwitchFailed")
ECGO_CONNECTION_SWITCHED = newDmError(20000, "error.connectionSwitched")
ECGO_COMMUNITION_ERROR = newDmError(6001, "error.communicationError")
ECGO_MSG_CHECK_ERROR = newDmError(6002, "error.msgCheckError")
ECGO_INVALID_TIME_INTERVAL = newDmError(6005, "error.invalidTimeInterval")
ECGO_UNSUPPORTED_TYPE = newDmError(6006, "error.unsupportedType")
ECGO_DATA_CONVERTION_ERROR = newDmError(6007, "error.dataConvertionError")
ECGO_INVALID_SQL_TYPE = newDmError(6009, "error.invalidSqlType")
ECGO_INVALID_DATETIME_FORMAT = newDmError(6015, "error.invalidDateTimeFormat")
ECGO_INVALID_COLUMN_TYPE = newDmError(6016, "error.invalidColumnType")
ECGO_RESULTSET_IS_READ_ONLY = newDmError(6029, "error.resultsetInReadOnlyStatus")
ECGO_INVALID_SEQUENCE_NUMBER = newDmError(6032, "error.invalidSequenceNumber")
ECGO_RESULTSET_CLOSED = newDmError(6034, "errorResultSetColsed")
ECGO_STATEMENT_HANDLE_CLOSED = newDmError(6035, "errorStatementHandleClosed")
ECGO_INVALID_PARAMETER_VALUE = newDmError(6036, "error.invalidParamterValue")
ECGO_INVALID_TRAN_ISOLATION = newDmError(6038, "error.invalidTranIsolation")
ECGO_COMMIT_IN_AUTOCOMMIT_MODE = newDmError(6039, "errorCommitInAutoCommitMode")
ECGO_ROLLBACK_IN_AUTOCOMMIT_MODE = newDmError(6040, "errorRollbackInAutoCommitMode")
ECGO_UNBINDED_PARAMETER = newDmError(6054, "error.unbindedParameter")
ECGO_PARAM_COUNT_LIMIT = newDmError(6056, "error.ParamCountLimit")
ECGO_INVALID_LENGTH_OR_OFFSET = newDmError(6057, "error.invalidLenOrOffset")
ECGO_CONNECTION_CLOSED = newDmError(6060, "error.error.connectionClosedOrNotBuild")
ECGO_INTERVAL_OVERFLOW = newDmError(6066, "error.intervalValueOverflow")
ECGO_STRING_CUT = newDmError(6067, "error.stringCut")
ECGO_INVALID_HEX = newDmError(6068, "error.invalidHex")
ECGO_INVALID_CIPHER = newDmError(6069, "error.invalidCipher")
ECGO_INVALID_BFILE_STR = newDmError(6070, "error.invalidBFile")
ECGO_OSAUTH_ERROR = newDmError(6073, "error.osauthError")
ECGO_ERROR_SERVER_VERSION = newDmError(6074, "error.serverVersion")
ECGO_USERNAME_TOO_LONG = newDmError(6075, "error.usernameTooLong")
ECGO_PASSWORD_TOO_LONG = newDmError(6076, "error.passwordTooLong")
ECGO_INVALID_COMPLEX_TYPE_NAME = newDmError(6079, "error.invalidComplexTypeName")
ECGO_STRUCT_MEM_NOT_MATCH = newDmError(6080, "error.structMemNotMatch")
ECGO_INVALID_OBJ_BLOB = newDmError(6081, "error.invalidObjBlob")
ECGO_INVALID_ARRAY_LEN = newDmError(6082, "error.invalidArrayLen")
ECGO_INVALID_SERVER_MODE = newDmError(6091, "error.invalidServerMode")
ECGO_DATA_TOO_LONG = newDmError(6092, "error.dataTooLong")
ECGO_BATCH_ERROR = newDmError(6093, "error.batchError")
ECGO_MSG_TOO_LONG = newDmError(6101, "error.msgTooLong")
ECGO_INVALID_DATETIME_VALUE = newDmError(6103, "error.invalidDateTimeValue")
ECGO_INIT_SSL_FAILED = newDmError(20002, "error.SSLInitFailed")
ECGO_LOB_FREED = newDmError(20003, "error.LobDataHasFreed")
ECGO_FATAL_ERROR = newDmError(20004, "error.fatalError")
)
// Svr Msg Err
var (
ECGO_DATA_OVERFLOW = newDmError(-6102, "error.dataOverflow")
ECGO_DATETIME_OVERFLOW = newDmError(-6112, "error.datetimeOverflow")
EC_RN_EXCEED_ROWSET_SIZE = newDmError(-7036, "")
EC_BP_WITH_ERROR = newDmError(121, "warning.bpWithErr")
)
type DmError struct {
ErrCode int32
ErrText string
stack []uintptr
detail string
}
func newDmError(errCode int32, errText string) *DmError {
de := new(DmError)
de.ErrCode = errCode
de.ErrText = errText
de.stack = nil
de.detail = ""
return de
}
func (dmError *DmError) throw() *DmError {
var pcs [32]uintptr
n := runtime.Callers(2, pcs[:])
dmError.stack = pcs[0:n]
return dmError
}
func (dmError *DmError) Stack() string {
if dmError == nil || dmError.stack == nil {
return ""
}
buffer := bytes.NewBuffer(nil)
index := 1
space := " "
for _, p := range dmError.stack {
if fn := runtime.FuncForPC(p - 1); fn != nil {
file, line := fn.FileLine(p - 1)
buffer.WriteString(fmt.Sprintf(" %d).%s%s\n \t%s:%d\n", index, space, fn.Name(), file, line))
index++
}
}
return buffer.String()
}
func (dmError *DmError) getErrText() string {
return i18n.Get(dmError.ErrText, Locale)
}
func (dmError *DmError) Error() string {
return fmt.Sprintf("Error %d: %s", dmError.ErrCode, dmError.getErrText()) + dmError.detail // + "\n" + "stack info:\n" + dmError.Stack()
}
// 扩充ErrText
func (dmError *DmError) addDetail(detail string) *DmError {
dmError.detail = detail
return dmError
}
func (dmError *DmError) addDetailln(detail string) *DmError {
return dmError.addDetail("\n" + detail)
}
+311
View File
@@ -0,0 +1,311 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
const (
PARAM_COUNT_LIMIT int32 = 65536
IGNORE_TARGET_LENGTH int32 = -1
IGNORE_TARGET_SCALE int32 = -1
IGNORE_TARGET_TYPE = INT32_MIN
TYPE_FLAG_UNKNOWN byte = 0 // 未知类型
TYPE_FLAG_EXACT byte = 1 // 精确类型
TYPE_FLAG_RECOMMEND byte = 2 // 推荐类型
IO_TYPE_UNKNOWN int8 = -1
IO_TYPE_IN int8 = 0
IO_TYPE_OUT int8 = 1
IO_TYPE_INOUT int8 = 2
MASK_ORACLE_DATE int32 = 1
MASK_ORACLE_FLOAT int32 = 2
MASK_BFILE int32 = 3
MASK_LOCAL_DATETIME int32 = 4
)
type execRetInfo struct {
// param
outParamDatas [][]byte
// rs
hasResultSet bool
rsDatas [][][]byte
rsSizeof int // 结果集数据占用多少空间,(消息中结果集起始位置到 rsCacheOffset
// 的空间大小,这和实际的rsDatas占用空间大小有一定出入,这里粗略估算,用于结果集缓存时的空间管理)
rsCacheOffset int32 // 缓存信息,在响应消息体中的偏移,0表示不存在,仅结果集缓存中可以用
rsBdta bool
rsUpdatable bool
rsRowIds []int64
// rs cache
tbIds []int32
tbTss []int64
// print
printLen int32
printMsg string
// explain
explain string
// 影响行数
updateCount int64 // Insert/Update/Delet影响行数, select结果集的总行数
updateCounts []int64 // 批量影响行数
// 键
rowid int64
lastInsertId int64
// other
retSqlType int16 // 执行返回的语句类型
execId int32
serverParams []parameter
nativeSQL string
}
type column struct {
typeName string
colType int32
prec int32
scale int32
name string
tableName string
schemaName string
nullable bool
identity bool
readonly bool // 是否只读
baseName string
// lob info
lob bool
lobTabId int32
lobColId int16
// 用于描述ARRAY、STRUCT类型的特有描述信息
typeDescriptor *TypeDescriptor
isBdta bool
mask int32
}
type parameter struct {
column
typeFlag byte
ioType int8
outJType int32
outScale int32
outObjectName string
cursorStmt *DmStatement
hasDefault bool
}
func (column *column) InitColumn() *column {
column.typeName = ""
column.colType = 0
column.prec = 0
column.scale = 0
column.name = ""
column.tableName = ""
column.schemaName = ""
column.nullable = false
column.identity = false
column.readonly = false
column.baseName = ""
// lob info
column.lob = false
column.lobTabId = 0
column.lobColId = 0
// 用于描述ARRAY、STRUCT类型的特有描述信息
column.typeDescriptor = nil
column.isBdta = false
return column
}
func (parameter *parameter) InitParameter() *parameter {
parameter.InitColumn()
parameter.typeFlag = TYPE_FLAG_UNKNOWN
parameter.ioType = IO_TYPE_UNKNOWN
parameter.outJType = IGNORE_TARGET_TYPE
parameter.outScale = IGNORE_TARGET_SCALE
parameter.outObjectName = ""
parameter.cursorStmt = nil
return parameter
}
func (parameter *parameter) resetType(colType int32) {
parameter.colType = colType
parameter.scale = 0
switch colType {
case BIT, BOOLEAN:
parameter.prec = BIT_PREC
case TINYINT:
parameter.prec = TINYINT_PREC
case SMALLINT:
parameter.prec = SMALLINT_PREC
case INT:
parameter.prec = INT_PREC
case BIGINT:
parameter.prec = BIGINT_PREC
case CHAR, VARCHAR, VARCHAR2:
parameter.prec = VARCHAR_PREC
case CLOB:
parameter.prec = CLOB_PREC
case BINARY, VARBINARY:
parameter.prec = VARBINARY_PREC
case BLOB:
parameter.prec = BLOB_PREC
case DATE:
parameter.prec = DATE_PREC
case TIME:
parameter.prec = TIME_PREC
parameter.scale = 6
case TIME_TZ:
parameter.prec = TIME_TZ_PREC
parameter.scale = 6
case DATETIME:
parameter.prec = DATETIME_PREC
parameter.scale = 6
case DATETIME_TZ:
parameter.prec = DATETIME_TZ_PREC
parameter.scale = 6
case DATETIME2:
parameter.prec = DATETIME2_PREC
parameter.scale = 9
case DATETIME2_TZ:
parameter.prec = DATETIME2_TZ_PREC
parameter.scale = 9
case REAL,DOUBLE,DECIMAL,INTERVAL_YM,INTERVAL_DT,ARRAY,CLASS,PLTYPE_RECORD,SARRAY:
parameter.prec = 0
case UNKNOWN, NULL:
// UNKNOWN 导致服务器断言 // setNull导致服务器报错“字符转换失败”
parameter.colType = VARCHAR
parameter.prec = VARCHAR_PREC
default:
}
}
func (execInfo *execRetInfo) union(other *execRetInfo, startRow int, count int) {
if count == 1 {
execInfo.updateCounts[startRow] = other.updateCount
} else if execInfo.updateCounts != nil && other.updateCounts != nil{
copy(execInfo.updateCounts[startRow:startRow+count], other.updateCounts[0:count])
}
if execInfo.outParamDatas != nil {
execInfo.outParamDatas = append(execInfo.outParamDatas, other.outParamDatas...)
}
}
func NewExceInfo() *execRetInfo {
execInfo := execRetInfo{}
execInfo.outParamDatas = nil
execInfo.hasResultSet = false
execInfo.rsDatas = nil
execInfo.rsSizeof = 0
execInfo.rsCacheOffset = 0
execInfo.rsBdta = false
execInfo.rsUpdatable = false
execInfo.rsRowIds = nil
execInfo.tbIds = nil
execInfo.tbTss = nil
execInfo.printLen = 0
execInfo.printMsg = ""
execInfo.explain = ""
execInfo.updateCount = 0
execInfo.updateCounts = nil
execInfo.rowid = -1
execInfo.lastInsertId = 0
// other
execInfo.retSqlType = -1 // 执行返回的语句类型
execInfo.execId = 0
return &execInfo
}
+444
View File
@@ -0,0 +1,444 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"context"
"database/sql/driver"
"reflect"
"sync"
"sync/atomic"
"time"
)
type filter interface {
DmDriverOpen(filterChain *filterChain, d *DmDriver, dsn string) (*DmConnection, error)
DmDriverOpenConnector(filterChain *filterChain, d *DmDriver, dsn string) (*DmConnector, error)
DmConnectorConnect(filterChain *filterChain, c *DmConnector, ctx context.Context) (*DmConnection, error)
DmConnectorDriver(filterChain *filterChain, c *DmConnector) *DmDriver
DmConnectionBegin(filterChain *filterChain, c *DmConnection) (*DmConnection, error)
DmConnectionBeginTx(filterChain *filterChain, c *DmConnection, ctx context.Context, opts driver.TxOptions) (*DmConnection, error)
DmConnectionCommit(filterChain *filterChain, c *DmConnection) error
DmConnectionRollback(filterChain *filterChain, c *DmConnection) error
DmConnectionClose(filterChain *filterChain, c *DmConnection) error
DmConnectionPing(filterChain *filterChain, c *DmConnection, ctx context.Context) error
DmConnectionExec(filterChain *filterChain, c *DmConnection, query string, args []driver.Value) (*DmResult, error)
DmConnectionExecContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (*DmResult, error)
DmConnectionQuery(filterChain *filterChain, c *DmConnection, query string, args []driver.Value) (*DmRows, error)
DmConnectionQueryContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (*DmRows, error)
DmConnectionPrepare(filterChain *filterChain, c *DmConnection, query string) (*DmStatement, error)
DmConnectionPrepareContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string) (*DmStatement, error)
DmConnectionResetSession(filterChain *filterChain, c *DmConnection, ctx context.Context) error
DmConnectionCheckNamedValue(filterChain *filterChain, c *DmConnection, nv *driver.NamedValue) error
DmStatementClose(filterChain *filterChain, s *DmStatement) error
DmStatementNumInput(filterChain *filterChain, s *DmStatement) int
DmStatementExec(filterChain *filterChain, s *DmStatement, args []driver.Value) (*DmResult, error)
DmStatementExecContext(filterChain *filterChain, s *DmStatement, ctx context.Context, args []driver.NamedValue) (*DmResult, error)
DmStatementQuery(filterChain *filterChain, s *DmStatement, args []driver.Value) (*DmRows, error)
DmStatementQueryContext(filterChain *filterChain, s *DmStatement, ctx context.Context, args []driver.NamedValue) (*DmRows, error)
DmStatementCheckNamedValue(filterChain *filterChain, s *DmStatement, nv *driver.NamedValue) error
DmResultLastInsertId(filterChain *filterChain, r *DmResult) (int64, error)
DmResultRowsAffected(filterChain *filterChain, r *DmResult) (int64, error)
DmRowsColumns(filterChain *filterChain, r *DmRows) []string
DmRowsClose(filterChain *filterChain, r *DmRows) error
DmRowsNext(filterChain *filterChain, r *DmRows, dest []driver.Value) error
DmRowsHasNextResultSet(filterChain *filterChain, r *DmRows) bool
DmRowsNextResultSet(filterChain *filterChain, r *DmRows) error
DmRowsColumnTypeScanType(filterChain *filterChain, r *DmRows, index int) reflect.Type
DmRowsColumnTypeDatabaseTypeName(filterChain *filterChain, r *DmRows, index int) string
DmRowsColumnTypeLength(filterChain *filterChain, r *DmRows, index int) (length int64, ok bool)
DmRowsColumnTypeNullable(filterChain *filterChain, r *DmRows, index int) (nullable, ok bool)
DmRowsColumnTypePrecisionScale(filterChain *filterChain, r *DmRows, index int) (precision, scale int64, ok bool)
}
type IDGenerator int64
var dmDriverIDGenerator = new(IDGenerator)
var dmConntorIDGenerator = new(IDGenerator)
var dmConnIDGenerator = new(IDGenerator)
var dmStmtIDGenerator = new(IDGenerator)
var dmResultIDGenerator = new(IDGenerator)
var dmRowsIDGenerator = new(IDGenerator)
func (g *IDGenerator) incrementAndGet() int64 {
return atomic.AddInt64((*int64)(g), 1)
}
type RWSiteEnum int
const (
PRIMARY RWSiteEnum = iota
STANDBY
ANYSITE
)
var (
goMapMu sync.RWMutex
goMap = make(map[string]goRun, 2)
)
type filterable struct {
filterChain *filterChain
rwInfo *rwInfo
logInfo *logInfo
recoverInfo *recoverInfo
statInfo *statInfo
objId int64
idGenerator *IDGenerator
}
func runLog() {
goMapMu.Lock()
_, ok := goMap["log"]
if !ok {
goMap["log"] = &logWriter{
flushQueue: make(chan []byte, LogFlushQueueSize),
date: time.Now().Format("2006-01-02"),
logFile: nil,
flushFreq: LogFlushFreq,
filePath: LogDir,
filePrefix: "dm_go",
buffer: Dm_build_4(),
}
go goMap["log"].doRun()
}
goMapMu.Unlock()
}
func runStat() {
goMapMu.Lock()
_, ok := goMap["stat"]
if !ok {
goMap["stat"] = newStatFlusher()
go goMap["stat"].doRun()
}
goMapMu.Unlock()
}
func (f *filterable) createFilterChain(bc *DmConnector, props *Properties) {
var filters = make([]filter, 0, 5)
if bc != nil {
if LogLevel != LOG_OFF {
filters = append(filters, &logFilter{})
f.logInfo = &logInfo{logRecord: new(LogRecord)}
runLog()
}
if StatEnable {
filters = append(filters, &statFilter{})
f.statInfo = newStatInfo()
goStatMu.Lock()
if goStat == nil {
goStat = newGoStat(1000)
}
goStatMu.Unlock()
runStat()
}
if bc.doSwitch != DO_SWITCH_OFF {
filters = append(filters, &reconnectFilter{})
f.recoverInfo = newRecoverInfo()
}
if bc.rwSeparate > RW_SEPARATE_OFF {
filters = append(filters, &rwFilter{})
f.rwInfo = newRwInfo()
}
} else if props != nil {
if ParseLogLevel(props) != LOG_OFF {
filters = append(filters, &logFilter{})
f.logInfo = &logInfo{logRecord: new(LogRecord)}
runLog()
}
if props.GetBool("statEnable", StatEnable) {
filters = append(filters, &statFilter{})
f.statInfo = newStatInfo()
goStatMu.Lock()
if goStat == nil {
goStat = newGoStat(1000)
}
goStatMu.Unlock()
runStat()
}
if props.GetInt(DoSwitchKey, int(DO_SWITCH_OFF), 0, 2) != int(DO_SWITCH_OFF) {
filters = append(filters, &reconnectFilter{})
f.recoverInfo = newRecoverInfo()
}
if props.GetBool("rwSeparate", false) {
filters = append(filters, &rwFilter{})
f.rwInfo = newRwInfo()
}
}
f.filterChain = newFilterChain(filters)
}
func (f *filterable) resetFilterable(src *filterable) {
f.filterChain = src.filterChain
f.logInfo = src.logInfo
f.rwInfo = src.rwInfo
f.statInfo = src.statInfo
}
func (f *filterable) getID() int64 {
if f.objId < 0 {
f.objId = f.idGenerator.incrementAndGet()
}
return f.objId
}
type logInfo struct {
logRecord *LogRecord
lastExecuteStartNano time.Time
}
type rwInfo struct {
distribute RWSiteEnum
rwCounter *rwCounter
connStandby *DmConnection
connCurrent *DmConnection
tryRecoverTs int64
stmtStandby *DmStatement
stmtCurrent *DmStatement
readOnly bool
}
func newRwInfo() *rwInfo {
rwInfo := new(rwInfo)
rwInfo.distribute = PRIMARY
rwInfo.readOnly = true
return rwInfo
}
func (rwi *rwInfo) cleanup() {
rwi.distribute = PRIMARY
rwi.rwCounter = nil
rwi.connStandby = nil
rwi.connCurrent = nil
rwi.stmtStandby = nil
rwi.stmtCurrent = nil
}
func (rwi *rwInfo) toPrimary() RWSiteEnum {
if rwi.distribute != PRIMARY {
rwi.rwCounter.countPrimary()
}
rwi.distribute = PRIMARY
return rwi.distribute
}
func (rwi *rwInfo) toAny() RWSiteEnum {
rwi.distribute = rwi.rwCounter.count(ANYSITE, rwi.connStandby)
return rwi.distribute
}
type recoverInfo struct {
checkEpRecoverTs int64
}
func newRecoverInfo() *recoverInfo {
recoverInfo := new(recoverInfo)
recoverInfo.checkEpRecoverTs = 0
return recoverInfo
}
type statInfo struct {
constructNano int64
connStat *connectionStat
lastExecuteStartNano int64
lastExecuteTimeNano int64
lastExecuteType ExecuteTypeEnum
firstResultSet bool
lastExecuteSql string
sqlStat *sqlStat
sql string
cursorIndex int
closeCount int
readStringLength int64
readBytesLength int64
openInputStreamCount int
openReaderCount int
}
var (
goStatMu sync.RWMutex
goStat *GoStat
)
func newStatInfo() *statInfo {
si := new(statInfo)
return si
}
func (si *statInfo) init(conn *DmConnection) {
si.connStat = goStat.createConnStat(conn)
}
func (si *statInfo) setConstructNano() {
si.constructNano = time.Now().UnixNano()
}
func (si *statInfo) getConstructNano() int64 {
return si.constructNano
}
func (si *statInfo) getConnStat() *connectionStat {
return si.connStat
}
func (si *statInfo) getLastExecuteStartNano() int64 {
return si.lastExecuteStartNano
}
func (si *statInfo) setLastExecuteStartNano(lastExecuteStartNano int64) {
si.lastExecuteStartNano = lastExecuteStartNano
}
func (si *statInfo) getLastExecuteTimeNano() int64 {
return si.lastExecuteTimeNano
}
func (si *statInfo) setLastExecuteTimeNano(lastExecuteTimeNano int64) {
si.lastExecuteTimeNano = lastExecuteTimeNano
}
func (si *statInfo) getLastExecuteType() ExecuteTypeEnum {
return si.lastExecuteType
}
func (si *statInfo) setLastExecuteType(lastExecuteType ExecuteTypeEnum) {
si.lastExecuteType = lastExecuteType
}
func (si *statInfo) isFirstResultSet() bool {
return si.firstResultSet
}
func (si *statInfo) setFirstResultSet(firstResultSet bool) {
si.firstResultSet = firstResultSet
}
func (si *statInfo) getLastExecuteSql() string {
return si.lastExecuteSql
}
func (si *statInfo) setLastExecuteSql(lastExecuteSql string) {
si.lastExecuteSql = lastExecuteSql
}
func (si *statInfo) getSqlStat() *sqlStat {
return si.sqlStat
}
func (si *statInfo) setSqlStat(sqlStat *sqlStat) {
si.sqlStat = sqlStat
}
func (si *statInfo) setConnStat(connStat *connectionStat) {
si.connStat = connStat
}
func (si *statInfo) setConstructNanoWithConstructNano(constructNano int64) {
si.constructNano = constructNano
}
func (si *statInfo) afterExecute(nanoSpan int64) {
si.lastExecuteTimeNano = nanoSpan
}
func (si *statInfo) beforeExecute() {
si.lastExecuteStartNano = time.Now().UnixNano()
}
func (si *statInfo) getSql() string {
return si.sql
}
func (si *statInfo) setSql(sql string) {
si.sql = sql
}
func (si *statInfo) getCursorIndex() int {
return si.cursorIndex
}
func (si *statInfo) setCursorIndex(cursorIndex int) {
si.cursorIndex = cursorIndex
}
func (si *statInfo) getCloseCount() int {
return si.closeCount
}
func (si *statInfo) setCloseCount(closeCount int) {
si.closeCount = closeCount
}
func (si *statInfo) getReadStringLength() int64 {
return si.readStringLength
}
func (si *statInfo) setReadStringLength(readStringLength int64) {
si.readStringLength = readStringLength
}
func (si *statInfo) getReadBytesLength() int64 {
return si.readBytesLength
}
func (si *statInfo) setReadBytesLength(readBytesLength int64) {
si.readBytesLength = readBytesLength
}
func (si *statInfo) getOpenInputStreamCount() int {
return si.openInputStreamCount
}
func (si *statInfo) setOpenInputStreamCount(openInputStreamCount int) {
si.openInputStreamCount = openInputStreamCount
}
func (si *statInfo) getOpenReaderCount() int {
return si.openReaderCount
}
func (si *statInfo) setOpenReaderCount(openReaderCount int) {
si.openReaderCount = openReaderCount
}
func (si *statInfo) incrementCloseCount() {
si.closeCount++
}
+403
View File
@@ -0,0 +1,403 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"context"
"database/sql/driver"
"reflect"
)
type filterChain struct {
filters []filter
fpos int
}
func newFilterChain(filters []filter) *filterChain {
fc := new(filterChain)
fc.filters = filters
fc.fpos = 0
return fc
}
func (filterChain *filterChain) reset() *filterChain {
filterChain.fpos = 0
return filterChain
}
func (filterChain *filterChain) DmDriverOpen(d *DmDriver, dsn string) (*DmConnection, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmDriverOpen(filterChain, d, dsn)
}
return d.open(dsn)
}
func (filterChain *filterChain) DmDriverOpenConnector(d *DmDriver, dsn string) (*DmConnector, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmDriverOpenConnector(filterChain, d, dsn)
}
return d.openConnector(dsn)
}
//DmConnector
func (filterChain *filterChain) DmConnectorConnect(c *DmConnector, ctx context.Context) (*DmConnection, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectorConnect(filterChain, c, ctx)
}
return c.connect(ctx)
}
func (filterChain *filterChain) DmConnectorDriver(c *DmConnector) *DmDriver {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectorDriver(filterChain, c)
}
return c.driver()
}
//DmConnection
func (filterChain *filterChain) DmConnectionBegin(c *DmConnection) (*DmConnection, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionBegin(filterChain, c)
}
return c.begin()
}
func (filterChain *filterChain) DmConnectionBeginTx(c *DmConnection, ctx context.Context, opts driver.TxOptions) (*DmConnection, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionBeginTx(filterChain, c, ctx, opts)
}
return c.beginTx(ctx, opts)
}
func (filterChain *filterChain) DmConnectionCommit(c *DmConnection) error {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionCommit(filterChain, c)
}
return c.commit()
}
func (filterChain *filterChain) DmConnectionRollback(c *DmConnection) error {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionRollback(filterChain, c)
}
return c.rollback()
}
func (filterChain *filterChain) DmConnectionClose(c *DmConnection) error {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionClose(filterChain, c)
}
return c.close()
}
func (filterChain *filterChain) DmConnectionPing(c *DmConnection, ctx context.Context) error {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionPing(filterChain, c, ctx)
}
return c.ping(ctx)
}
func (filterChain *filterChain) DmConnectionExec(c *DmConnection, query string, args []driver.Value) (*DmResult, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionExec(filterChain, c, query, args)
}
return c.exec(query, args)
}
func (filterChain *filterChain) DmConnectionExecContext(c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (*DmResult, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionExecContext(filterChain, c, ctx, query, args)
}
return c.execContext(ctx, query, args)
}
func (filterChain *filterChain) DmConnectionQuery(c *DmConnection, query string, args []driver.Value) (*DmRows, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionQuery(filterChain, c, query, args)
}
return c.query(query, args)
}
func (filterChain *filterChain) DmConnectionQueryContext(c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (*DmRows, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionQueryContext(filterChain, c, ctx, query, args)
}
return c.queryContext(ctx, query, args)
}
func (filterChain *filterChain) DmConnectionPrepare(c *DmConnection, query string) (*DmStatement, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionPrepare(filterChain, c, query)
}
return c.prepare(query)
}
func (filterChain *filterChain) DmConnectionPrepareContext(c *DmConnection, ctx context.Context, query string) (*DmStatement, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionPrepareContext(filterChain, c, ctx, query)
}
return c.prepareContext(ctx, query)
}
func (filterChain *filterChain) DmConnectionResetSession(c *DmConnection, ctx context.Context) error {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionResetSession(filterChain, c, ctx)
}
return c.resetSession(ctx)
}
func (filterChain *filterChain) DmConnectionCheckNamedValue(c *DmConnection, nv *driver.NamedValue) error {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmConnectionCheckNamedValue(filterChain, c, nv)
}
return c.checkNamedValue(nv)
}
//DmStatement
func (filterChain *filterChain) DmStatementClose(s *DmStatement) error {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmStatementClose(filterChain, s)
}
return s.close()
}
func (filterChain *filterChain) DmStatementNumInput(s *DmStatement) int {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmStatementNumInput(filterChain, s)
}
return s.numInput()
}
func (filterChain *filterChain) DmStatementExec(s *DmStatement, args []driver.Value) (*DmResult, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmStatementExec(filterChain, s, args)
}
return s.exec(args)
}
func (filterChain *filterChain) DmStatementExecContext(s *DmStatement, ctx context.Context, args []driver.NamedValue) (*DmResult, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmStatementExecContext(filterChain, s, ctx, args)
}
return s.execContext(ctx, args)
}
func (filterChain *filterChain) DmStatementQuery(s *DmStatement, args []driver.Value) (*DmRows, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmStatementQuery(filterChain, s, args)
}
return s.query(args)
}
func (filterChain *filterChain) DmStatementQueryContext(s *DmStatement, ctx context.Context, args []driver.NamedValue) (*DmRows, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmStatementQueryContext(filterChain, s, ctx, args)
}
return s.queryContext(ctx, args)
}
func (filterChain *filterChain) DmStatementCheckNamedValue(s *DmStatement, nv *driver.NamedValue) error {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmStatementCheckNamedValue(filterChain, s, nv)
}
return s.checkNamedValue(nv)
}
//DmResult
func (filterChain *filterChain) DmResultLastInsertId(r *DmResult) (int64, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmResultLastInsertId(filterChain, r)
}
return r.lastInsertId()
}
func (filterChain *filterChain) DmResultRowsAffected(r *DmResult) (int64, error) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmResultRowsAffected(filterChain, r)
}
return r.rowsAffected()
}
//DmRows
func (filterChain *filterChain) DmRowsColumns(r *DmRows) []string {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmRowsColumns(filterChain, r)
}
return r.columns()
}
func (filterChain *filterChain) DmRowsClose(r *DmRows) error {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmRowsClose(filterChain, r)
}
return r.close()
}
func (filterChain *filterChain) DmRowsNext(r *DmRows, dest []driver.Value) error {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmRowsNext(filterChain, r, dest)
}
return r.next(dest)
}
func (filterChain *filterChain) DmRowsHasNextResultSet(r *DmRows) bool {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmRowsHasNextResultSet(filterChain, r)
}
return r.hasNextResultSet()
}
func (filterChain *filterChain) DmRowsNextResultSet(r *DmRows) error {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmRowsNextResultSet(filterChain, r)
}
return r.nextResultSet()
}
func (filterChain *filterChain) DmRowsColumnTypeScanType(r *DmRows, index int) reflect.Type {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmRowsColumnTypeScanType(filterChain, r, index)
}
return r.columnTypeScanType(index)
}
func (filterChain *filterChain) DmRowsColumnTypeDatabaseTypeName(r *DmRows, index int) string {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmRowsColumnTypeDatabaseTypeName(filterChain, r, index)
}
return r.columnTypeDatabaseTypeName(index)
}
func (filterChain *filterChain) DmRowsColumnTypeLength(r *DmRows, index int) (length int64, ok bool) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmRowsColumnTypeLength(filterChain, r, index)
}
return r.columnTypeLength(index)
}
func (filterChain *filterChain) DmRowsColumnTypeNullable(r *DmRows, index int) (nullable, ok bool) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmRowsColumnTypeNullable(filterChain, r, index)
}
return r.columnTypeNullable(index)
}
func (filterChain *filterChain) DmRowsColumnTypePrecisionScale(r *DmRows, index int) (precision, scale int64, ok bool) {
if filterChain.fpos < len(filterChain.filters) {
f := filterChain.filters[filterChain.fpos]
filterChain.fpos++
return f.DmRowsColumnTypePrecisionScale(filterChain, r, index)
}
return r.columnTypePrecisionScale(index)
}
+907
View File
@@ -0,0 +1,907 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"context"
"database/sql/driver"
"fmt"
"io"
"reflect"
"strconv"
"time"
"gitee.com/chunanyong/dm/util"
)
type logFilter struct{}
func (filter *logFilter) DmDriverOpen(filterChain *filterChain, d *DmDriver, dsn string) (ret *DmConnection, err error) {
var logRecord = d.logInfo.logRecord.Reset()
logRecord.Set(d, "open", dsn)
defer func() {
filter.doLog(logRecord)
}()
ret, err = filterChain.DmDriverOpen(d, dsn)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmDriverOpenConnector(filterChain *filterChain, d *DmDriver, dsn string) (ret *DmConnector, err error) {
var logRecord = d.logInfo.logRecord.Reset()
logRecord.Set(d, "openConnector", dsn)
defer func() {
filter.doLog(logRecord)
}()
ret, err = filterChain.DmDriverOpenConnector(d, dsn)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmConnectorConnect(filterChain *filterChain, c *DmConnector, ctx context.Context) (ret *DmConnection, err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "connect")
defer func() {
filter.doLog(logRecord)
}()
ret, err = filterChain.DmConnectorConnect(c, ctx)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmConnectorDriver(filterChain *filterChain, c *DmConnector) (ret *DmDriver) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "driver")
defer func() {
filter.doLog(logRecord)
}()
ret = filterChain.DmConnectorDriver(c)
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmConnectionBegin(filterChain *filterChain, c *DmConnection) (ret *DmConnection, err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "begin")
defer func() {
filter.doLog(logRecord)
}()
ret, err = filterChain.DmConnectionBegin(c)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmConnectionBeginTx(filterChain *filterChain, c *DmConnection, ctx context.Context, opts driver.TxOptions) (ret *DmConnection, err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "beginTx", opts)
defer func() {
filter.doLog(logRecord)
}()
ret, err = filterChain.DmConnectionBeginTx(c, ctx, opts)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmConnectionCommit(filterChain *filterChain, c *DmConnection) (err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "commit")
defer func() {
filter.doLog(logRecord)
}()
err = filterChain.DmConnectionCommit(c)
if err != nil {
logRecord.SetError(err)
return
}
return
}
func (filter *logFilter) DmConnectionRollback(filterChain *filterChain, c *DmConnection) (err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "rollback")
defer func() {
filter.doLog(logRecord)
}()
err = filterChain.DmConnectionRollback(c)
if err != nil {
logRecord.SetError(err)
return
}
return
}
func (filter *logFilter) DmConnectionClose(filterChain *filterChain, c *DmConnection) (err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "close")
defer func() {
filter.doLog(logRecord)
}()
err = filterChain.DmConnectionClose(c)
if err != nil {
logRecord.SetError(err)
return
}
return
}
func (filter *logFilter) DmConnectionPing(filterChain *filterChain, c *DmConnection, ctx context.Context) (err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "ping")
defer func() {
filter.doLog(logRecord)
}()
err = filterChain.DmConnectionPing(c, ctx)
if err != nil {
logRecord.SetError(err)
return
}
return
}
func (filter *logFilter) DmConnectionExec(filterChain *filterChain, c *DmConnection, query string, args []driver.Value) (ret *DmResult, err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "exec", convertParams2(args)...)
defer func() {
filter.executeAfter(c.logInfo, logRecord)
filter.doLog(logRecord)
}()
logRecord.SetSql(query, true)
filter.executeBefore(c.logInfo)
ret, err = filterChain.DmConnectionExec(c, query, args)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmConnectionExecContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (ret *DmResult, err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "execCtx", convertParams1(args)...)
defer func() {
filter.executeAfter(c.logInfo, logRecord)
filter.doLog(logRecord)
}()
logRecord.SetSql(query, true)
filter.executeBefore(c.logInfo)
ret, err = filterChain.DmConnectionExecContext(c, ctx, query, args)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmConnectionQuery(filterChain *filterChain, c *DmConnection, query string, args []driver.Value) (ret *DmRows, err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "query", convertParams2(args)...)
defer func() {
filter.executeAfter(c.logInfo, logRecord)
filter.doLog(logRecord)
}()
logRecord.SetSql(query, true)
filter.executeBefore(c.logInfo)
ret, err = filterChain.DmConnectionQuery(c, query, args)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmConnectionQueryContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (ret *DmRows, err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "queryCtx", convertParams1(args)...)
defer func() {
filter.executeAfter(c.logInfo, logRecord)
filter.doLog(logRecord)
}()
logRecord.SetSql(query, true)
filter.executeBefore(c.logInfo)
ret, err = filterChain.DmConnectionQueryContext(c, ctx, query, args)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmConnectionPrepare(filterChain *filterChain, c *DmConnection, query string) (ret *DmStatement, err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "prepare", query)
defer func() {
filter.doLog(logRecord)
}()
logRecord.SetSql(query, false)
ret, err = filterChain.DmConnectionPrepare(c, query)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmConnectionPrepareContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string) (ret *DmStatement, err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "prepareCtx", query)
defer func() {
filter.doLog(logRecord)
}()
logRecord.SetSql(query, false)
ret, err = filterChain.DmConnectionPrepareContext(c, ctx, query)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmConnectionResetSession(filterChain *filterChain, c *DmConnection, ctx context.Context) (err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "resetSession")
defer func() {
filter.doLog(logRecord)
}()
err = filterChain.DmConnectionResetSession(c, ctx)
if err != nil {
logRecord.SetError(err)
return
}
return
}
func (filter *logFilter) DmConnectionCheckNamedValue(filterChain *filterChain, c *DmConnection, nv *driver.NamedValue) (err error) {
var logRecord = c.logInfo.logRecord.Reset()
logRecord.Set(c, "checkNamedValue", nv.Value)
defer func() {
filter.doLog(logRecord)
}()
err = filterChain.DmConnectionCheckNamedValue(c, nv)
if err != nil {
logRecord.SetError(err)
return
}
return
}
func (filter *logFilter) DmStatementClose(filterChain *filterChain, s *DmStatement) (err error) {
var logRecord = s.logInfo.logRecord.Reset()
logRecord.Set(s, "close")
defer func() {
filter.doLog(logRecord)
}()
err = filterChain.DmStatementClose(s)
if err != nil {
logRecord.SetError(err)
return
}
return
}
func (filter *logFilter) DmStatementNumInput(filterChain *filterChain, s *DmStatement) (ret int) {
var logRecord = s.logInfo.logRecord.Reset()
logRecord.Set(s, "numInput")
defer func() {
filter.doLog(logRecord)
}()
ret = filterChain.DmStatementNumInput(s)
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmStatementExec(filterChain *filterChain, s *DmStatement, args []driver.Value) (ret *DmResult, err error) {
var logRecord = s.logInfo.logRecord.Reset()
logRecord.Set(s, "exec", convertParams2(args)...)
defer func() {
filter.executeAfter(s.logInfo, logRecord)
filter.doLog(logRecord)
}()
logRecord.SetSql(s.nativeSql, true)
filter.executeBefore(s.logInfo)
ret, err = filterChain.DmStatementExec(s, args)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmStatementExecContext(filterChain *filterChain, s *DmStatement, ctx context.Context, args []driver.NamedValue) (ret *DmResult, err error) {
var logRecord = s.logInfo.logRecord.Reset()
logRecord.Set(s, "execCtx", convertParams1(args)...)
defer func() {
filter.executeAfter(s.logInfo, logRecord)
filter.doLog(logRecord)
}()
logRecord.SetSql(s.nativeSql, true)
filter.executeBefore(s.logInfo)
ret, err = filterChain.DmStatementExecContext(s, ctx, args)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmStatementQuery(filterChain *filterChain, s *DmStatement, args []driver.Value) (ret *DmRows, err error) {
var logRecord = s.logInfo.logRecord.Reset()
logRecord.Set(s, "query", convertParams2(args)...)
defer func() {
filter.executeAfter(s.logInfo, logRecord)
filter.doLog(logRecord)
}()
logRecord.SetSql(s.nativeSql, true)
filter.executeBefore(s.logInfo)
ret, err = filterChain.DmStatementQuery(s, args)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmStatementQueryContext(filterChain *filterChain, s *DmStatement, ctx context.Context, args []driver.NamedValue) (ret *DmRows, err error) {
var logRecord = s.logInfo.logRecord.Reset()
logRecord.Set(s, "queryCtx", convertParams1(args)...)
defer func() {
filter.executeAfter(s.logInfo, logRecord)
filter.doLog(logRecord)
}()
logRecord.SetSql(s.nativeSql, true)
filter.executeBefore(s.logInfo)
ret, err = filterChain.DmStatementQueryContext(s, ctx, args)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmStatementCheckNamedValue(filterChain *filterChain, s *DmStatement, nv *driver.NamedValue) (err error) {
var logRecord = s.logInfo.logRecord.Reset()
logRecord.Set(s, "checkNamedValue", nv.Value)
defer func() {
filter.doLog(logRecord)
}()
err = filterChain.DmStatementCheckNamedValue(s, nv)
if err != nil {
logRecord.SetError(err)
return
}
return
}
func (filter *logFilter) DmResultLastInsertId(filterChain *filterChain, r *DmResult) (ret int64, err error) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "lastInsertId")
defer func() {
filter.doLog(logRecord)
}()
ret, err = filterChain.DmResultLastInsertId(r)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmResultRowsAffected(filterChain *filterChain, r *DmResult) (ret int64, err error) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "rowsAffected")
defer func() {
filter.doLog(logRecord)
}()
ret, err = filterChain.DmResultRowsAffected(r)
if err != nil {
logRecord.SetError(err)
return
}
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmRowsColumns(filterChain *filterChain, r *DmRows) (ret []string) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "columns")
defer func() {
filter.doLog(logRecord)
}()
ret = filterChain.DmRowsColumns(r)
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmRowsClose(filterChain *filterChain, r *DmRows) (err error) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "close")
defer func() {
filter.doLog(logRecord)
}()
err = filterChain.DmRowsClose(r)
if err != nil {
logRecord.SetError(err)
return
}
return
}
func (filter *logFilter) DmRowsNext(filterChain *filterChain, r *DmRows, dest []driver.Value) (err error) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "next", convertParams2(dest)...)
defer func() {
filter.doLog(logRecord)
}()
err = filterChain.DmRowsNext(r, dest)
if err != nil && err != io.EOF {
logRecord.SetError(err)
return
}
return
}
func (filter *logFilter) DmRowsHasNextResultSet(filterChain *filterChain, r *DmRows) (ret bool) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "hasNextResultSet")
defer func() {
filter.doLog(logRecord)
}()
ret = filterChain.DmRowsHasNextResultSet(r)
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmRowsNextResultSet(filterChain *filterChain, r *DmRows) (err error) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "nextResultSet")
defer func() {
filter.doLog(logRecord)
}()
err = filterChain.DmRowsNextResultSet(r)
if err != nil && err != io.EOF {
logRecord.SetError(err)
return
}
return
}
func (filter *logFilter) DmRowsColumnTypeScanType(filterChain *filterChain, r *DmRows, index int) (ret reflect.Type) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "columnTypeScanType", index)
defer func() {
filter.doLog(logRecord)
}()
ret = filterChain.DmRowsColumnTypeScanType(r, index)
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmRowsColumnTypeDatabaseTypeName(filterChain *filterChain, r *DmRows, index int) (ret string) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "columnTypeDatabaseTypeName", index)
defer func() {
filter.doLog(logRecord)
}()
ret = filterChain.DmRowsColumnTypeDatabaseTypeName(r, index)
logRecord.SetReturnValue(ret)
return
}
func (filter *logFilter) DmRowsColumnTypeLength(filterChain *filterChain, r *DmRows, index int) (length int64, ok bool) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "columnTypeLength", index)
defer func() {
filter.doLog(logRecord)
}()
length, ok = filterChain.DmRowsColumnTypeLength(r, index)
if ok {
logRecord.SetReturnValue(length)
} else {
logRecord.SetReturnValue(-1)
}
return
}
func (filter *logFilter) DmRowsColumnTypeNullable(filterChain *filterChain, r *DmRows, index int) (nullable, ok bool) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "columnTypeNullable", index)
defer func() {
filter.doLog(logRecord)
}()
nullable, ok = filterChain.DmRowsColumnTypeNullable(r, index)
if ok {
logRecord.SetReturnValue(nullable)
} else {
logRecord.SetReturnValue(false)
}
return
}
func (filter *logFilter) DmRowsColumnTypePrecisionScale(filterChain *filterChain, r *DmRows, index int) (precision, scale int64, ok bool) {
var logRecord = r.logInfo.logRecord.Reset()
logRecord.Set(r, "columnTypePrecisionScale", index)
defer func() {
filter.doLog(logRecord)
}()
precision, scale, ok = filterChain.DmRowsColumnTypePrecisionScale(r, index)
if ok {
logRecord.SetReturnValue(strconv.FormatInt(precision, 10) + "&" + strconv.FormatInt(scale, 10))
} else {
logRecord.SetReturnValue("-1&-1")
}
return
}
func (filter *logFilter) executeBefore(logInfo *logInfo) {
if LogFilterLogger.IsSqlEnabled() {
logInfo.lastExecuteStartNano = time.Now()
}
}
func (filter *logFilter) executeAfter(logInfo *logInfo, record *LogRecord) {
if LogFilterLogger.IsSqlEnabled() {
record.SetUsedTime(time.Since(logInfo.lastExecuteStartNano))
}
}
func (filter *logFilter) doLog(record *LogRecord) {
if record == nil {
return
}
if record.GetError() != nil {
LogFilterLogger.ErrorWithErr(record.ToString(), record.GetError())
} else if record.GetSql() != "" && LogFilterLogger.IsSqlEnabled() {
LogFilterLogger.Sql(record.ToString())
} else {
LogFilterLogger.Info(record.ToString())
}
}
/************************************************************************************************************/
type Logger struct {
}
var LogFilterLogger = &Logger{}
var ConnLogger = &Logger{}
var AccessLogger = &Logger{}
func (logger Logger) IsDebugEnabled() bool {
return LogLevel >= LOG_DEBUG
}
func (logger Logger) IsErrorEnabled() bool {
return LogLevel >= LOG_ERROR
}
func (logger Logger) IsInfoEnabled() bool {
return LogLevel >= LOG_INFO
}
func (logger Logger) IsWarnEnabled() bool {
return LogLevel >= LOG_WARN
}
func (logger Logger) IsSqlEnabled() bool {
return LogLevel >= LOG_SQL
}
func (logger Logger) Debug(msg string) {
if logger.IsDebugEnabled() {
logger.println(logger.formatHead("DEBUG") + msg)
}
}
func (logger Logger) DebugWithErr(msg string, err error) {
if logger.IsDebugEnabled() {
if e, ok := err.(*DmError); ok {
logger.println(logger.formatHead("DEBUG") + msg + util.LINE_SEPARATOR + e.Stack())
} else {
logger.println(logger.formatHead("DEBUG") + msg + util.LINE_SEPARATOR + err.Error())
}
}
}
func (logger Logger) Info(msg string) {
if logger.IsInfoEnabled() {
logger.println(logger.formatHead("INFO ") + msg)
}
}
func (logger Logger) Sql(msg string) {
if logger.IsSqlEnabled() {
logger.println(logger.formatHead("SQL ") + msg)
}
}
func (logger Logger) Warn(msg string) {
if logger.IsWarnEnabled() {
logger.println(logger.formatHead("WARN ") + msg)
}
}
func (logger Logger) ErrorWithErr(msg string, err error) {
//if e, ok := err.(*DmError); ok {
// logger.println(logger.formatHead("ERROR") + msg + util.LINE_SEPARATOR + e.Stack())
//} else {
logger.println(logger.formatHead("ERROR") + msg + util.LINE_SEPARATOR + err.Error())
//}
}
// TODO: 获取goroutine objId
func (logger Logger) formatHead(head string) string {
// return "[" + head + " - " + StringUtil.formatTime() + "] tid:" + Thread.currentThread().getId();
return "[" + head + " - " + util.StringUtil.FormatTime() + "]"
}
func (logger Logger) println(msg string) {
goMap["log"].(*logWriter).WriteLine(msg)
}
/*************************************************************************************************/
func formatSource(source interface{}) string {
if source == nil {
return ""
}
var str string
switch src := source.(type) {
case string:
str += src
case *DmDriver:
str += formatDriver(src)
case *DmConnector:
str += formatContor(src)
case *DmConnection:
str += formatConn(src)
case *DmStatement:
str += formatConn(src.dmConn) + ", "
str += formatStmt(src)
case *DmResult:
str += formatConn(src.dmStmt.dmConn) + ", "
str += formatStmt(src.dmStmt) + ", "
str += formatRs(src)
case *DmRows:
str += formatConn(src.CurrentRows.dmStmt.dmConn) + ", "
str += formatStmt(src.CurrentRows.dmStmt) + ", "
str += formatRows(src)
default:
str += reflect.TypeOf(src).String() + "@" + reflect.ValueOf(src).Addr().String()
}
return str
}
func formatDriver(driver *DmDriver) string {
if driver != nil && driver.logInfo != nil {
return "driver-" + strconv.FormatInt(driver.getID(), 10)
}
return "driver-nil"
}
func formatContor(contor *DmConnector) string {
if contor != nil && contor.logInfo != nil {
return "contor-" + strconv.FormatInt(contor.getID(), 10)
}
return "contor-nil"
}
func formatConn(conn *DmConnection) string {
if conn != nil && conn.logInfo != nil {
return "conn-0x" + strconv.FormatInt(conn.SessionID, 16)
}
return "conn-nil"
}
func formatStmt(stmt *DmStatement) string {
if stmt != nil && stmt.logInfo != nil {
return "stmt-" + strconv.Itoa(int(stmt.id))
}
return "stmt-nil"
}
func formatRs(result *DmResult) string {
if result != nil && result.logInfo != nil {
return "rs-" + strconv.FormatInt(result.getID(), 10)
}
return "rs-nil"
}
func formatRows(rows *DmRows) string {
if rows != nil && rows.logInfo != nil {
return "rows-" + strconv.FormatInt(rows.getID(), 10)
}
return "rows-nil"
}
func formatTrace(source string, sql string, method string, returnValue interface{}, params ...interface{}) string {
var str string
if source != "" {
str += "{ " + source + " } "
}
str += method + "("
var paramStartIndex = 0
if params != nil && len(params) > paramStartIndex {
for i := paramStartIndex; i < len(params); i++ {
if i != paramStartIndex {
str += ", "
}
if params[i] != nil {
str += reflect.TypeOf(params[i]).String()
} else {
str += "nil"
}
}
}
str += ")"
if returnValue != nil {
str += ": " + formatReturn(returnValue)
}
str += "; "
if params != nil && len(params) > paramStartIndex {
str += "[PARAMS]: "
for i := paramStartIndex; i < len(params); i++ {
if i != 0 {
str += ", "
}
//if s, ok := params[i].(driver.NamedValue); ok {
// str += fmt.Sprintf("%v", s.Value)
//} else {
str += fmt.Sprintf("%v", params[i])
//}
}
str += "; "
}
if sql != "" {
str += "[SQL]: " + sql + "; "
}
return str
}
func formatReturn(returnObj interface{}) string {
var str string
switch o := returnObj.(type) {
case *DmConnection:
str = formatConn(o)
case *DmStatement:
str = formatStmt(o)
case *DmResult:
str = formatRs(o)
case *DmRows:
str = formatRows(o)
case string:
str = `"` + o + `"`
case nullData:
str = "nil"
default:
str = "unknown"
}
return str
}
func formatUsedTime(duration time.Duration) string {
return "[USED TIME]: " + duration.String()
}
/************************************************************************************************************/
type nullData struct{}
var null = nullData{}
type LogRecord struct {
source string
method string
params []interface{}
returnValue interface{}
e error
usedTime time.Duration
sql string
logSql bool // 是否需要记录sql(exec,query等需要在日志中记录sql语句)
}
func (record *LogRecord) Reset() *LogRecord {
record.source = ""
record.method = ""
record.params = nil
record.returnValue = nil
record.e = nil
record.usedTime = 0
record.sql = ""
record.logSql = false
return record
}
func (record *LogRecord) Set(source interface{}, method string, params ...interface{}) {
record.source = formatSource(source)
record.method = method
record.params = params
}
func (record *LogRecord) SetReturnValue(retValue interface{}) {
if retValue == nil {
record.returnValue = null
} else {
record.returnValue = retValue
}
}
func (record *LogRecord) GetReturnValue() interface{} {
return record.returnValue
}
func (record *LogRecord) SetSql(sql string, logSql bool) {
record.sql = sql
record.logSql = logSql
}
func (record *LogRecord) GetSql() string {
return record.sql
}
func (record *LogRecord) SetUsedTime(usedTime time.Duration) {
record.usedTime = usedTime
}
func (record *LogRecord) GetUsedTime() time.Duration {
return record.usedTime
}
func (record *LogRecord) SetError(err error) {
record.e = err
}
func (record *LogRecord) GetError() error {
return record.e
}
func (record *LogRecord) ToString() string {
var sql string
if record.logSql && record.sql != "" {
sql = record.sql
}
var str string
str += formatTrace(record.source, sql, record.method, record.returnValue, record.params...)
if record.usedTime > 0 {
str += formatUsedTime(record.usedTime)
}
return str
}
func convertParams1(args []driver.NamedValue) []interface{} {
tmp := make([]interface{}, len(args))
for i := 0; i < len(tmp); i++ {
tmp[i] = args[i].Value
}
return tmp
}
func convertParams2(args []driver.Value) []interface{} {
tmp := make([]interface{}, len(args))
for i := 0; i < len(tmp); i++ {
tmp[i] = args[i]
}
return tmp
}
+516
View File
@@ -0,0 +1,516 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"context"
"database/sql/driver"
"io"
"reflect"
"time"
"gitee.com/chunanyong/dm/util"
)
const SQL_GET_DSC_EP_SITE = "SELECT " +
"dsc.ep_seqno, " +
"(CASE mal.MAL_INST_HOST WHEN '' THEN mal.MAL_HOST ELSE mal.MAL_INST_HOST END) as ep_host, " +
"dcr.EP_PORT, " +
"dsc.EP_STATUS " +
"FROM V$DSC_EP_INFO dsc " +
"LEFT join V$DM_MAL_INI mal " +
"on dsc.EP_NAME = mal.MAL_INST_NAME " +
"LEFT join (SELECT grp.GROUP_TYPE GROUP_TYPE, ep.* FROM SYS.\"V$DCR_GROUP\" grp, SYS.\"V$DCR_EP\" ep where grp.GROUP_NAME = ep.GROUP_NAME) dcr " +
"on dsc.EP_NAME = dcr.EP_NAME and GROUP_TYPE = 'DB' order by dsc.ep_seqno asc;"
type reconnectFilter struct {
}
// 一定抛错
func (rf *reconnectFilter) autoReconnect(connection *DmConnection, err error) error {
if dmErr, ok := err.(*DmError); ok {
if dmErr.ErrCode == ECGO_COMMUNITION_ERROR.ErrCode || dmErr.ErrCode == ECGO_CONNECTION_CLOSED.ErrCode {
if connection.dmConnector.driverReconnect {
return rf.reconnect(connection, dmErr.getErrText())
} else {
connection.Access.Close()
connection.closed.Set(true)
return driver.ErrBadConn
}
}
}
return err
}
// 一定抛错
func (rf *reconnectFilter) reconnect(connection *DmConnection, reason string) error {
// 读写分离,重连需要处理备机
var err error
if connection.dmConnector.rwSeparate > RW_SEPARATE_OFF {
err = RWUtil.reconnect(connection)
} else {
err = connection.reconnect()
}
if err != nil {
connection.closed.Set(true)
return ECGO_CONNECTION_SWITCH_FAILED.addDetailln(reason).throw()
}
// 重连成功
connection.closed.Set(false)
return ECGO_CONNECTION_SWITCHED.addDetailln(reason).throw()
}
func (rf *reconnectFilter) loadDscEpSites(conn *DmConnection) []*ep {
stmt, rs, err := conn.driverQuery(SQL_GET_DSC_EP_SITE)
if err != nil {
return nil
}
defer func() {
rs.close()
stmt.close()
}()
epList := make([]*ep, 0)
dest := make([]driver.Value, 4)
for err = rs.next(dest); err != io.EOF; err = rs.next(dest) {
ep := newEP(dest[1].(string), dest[2].(int32))
ep.epSeqno = dest[0].(int32)
if util.StringUtil.EqualsIgnoreCase(dest[3].(string), "OK") {
ep.epStatus = EP_STATUS_OK
} else {
ep.epStatus = EP_STATUS_ERROR
}
epList = append(epList, ep)
}
return epList
}
func (rf *reconnectFilter) checkAndRecover(conn *DmConnection) error {
if conn.dmConnector.doSwitch != DO_SWITCH_WHEN_EP_RECOVER {
return nil
}
// check trx finish
if !conn.trxFinish {
return nil
}
var curIndex = conn.getIndexOnEPGroup()
if curIndex == 0 || (time.Now().UnixNano()/1000000-conn.recoverInfo.checkEpRecoverTs) < int64(conn.dmConnector.switchInterval) {
return nil
}
// check db recover
var dscEps []*ep
if conn.dmConnector.cluster == CLUSTER_TYPE_DSC {
dscEps = rf.loadDscEpSites(conn)
}
if dscEps == nil || len(dscEps) == 0 {
return nil
}
var recover = false
for _, okEp := range dscEps {
if okEp.epStatus != EP_STATUS_OK {
continue
}
for i := int32(0); i < curIndex; i++ {
ep := conn.dmConnector.group.epList[i]
if okEp.host == ep.host && okEp.port == ep.port {
recover = true
break
}
}
if recover {
break
}
}
conn.recoverInfo.checkEpRecoverTs = time.Now().UnixNano() / 1000000
if !recover {
return nil
}
if conn.dmConnector.driverReconnect {
return conn.reconnect()
} else {
conn.Access.Close()
conn.closed.Set(false)
return ECGO_CONNECTION_CLOSED.throw()
}
//return driver.ErrBadConn
// do reconnect
//return conn.reconnect()
}
// DmDriver
func (rf *reconnectFilter) DmDriverOpen(filterChain *filterChain, d *DmDriver, dsn string) (*DmConnection, error) {
return filterChain.DmDriverOpen(d, dsn)
}
func (rf *reconnectFilter) DmDriverOpenConnector(filterChain *filterChain, d *DmDriver, dsn string) (*DmConnector, error) {
return filterChain.DmDriverOpenConnector(d, dsn)
}
// DmConnector
func (rf *reconnectFilter) DmConnectorConnect(filterChain *filterChain, c *DmConnector, ctx context.Context) (*DmConnection, error) {
return filterChain.DmConnectorConnect(c, ctx)
}
func (rf *reconnectFilter) DmConnectorDriver(filterChain *filterChain, c *DmConnector) *DmDriver {
return filterChain.DmConnectorDriver(c)
}
// DmConnection
func (rf *reconnectFilter) DmConnectionBegin(filterChain *filterChain, c *DmConnection) (*DmConnection, error) {
dc, err := filterChain.DmConnectionBegin(c)
if err != nil {
return nil, rf.autoReconnect(c, err)
}
return dc, err
}
func (rf *reconnectFilter) DmConnectionBeginTx(filterChain *filterChain, c *DmConnection, ctx context.Context, opts driver.TxOptions) (*DmConnection, error) {
dc, err := filterChain.DmConnectionBeginTx(c, ctx, opts)
if err != nil {
return nil, rf.autoReconnect(c, err)
}
return dc, err
}
func (rf *reconnectFilter) DmConnectionCommit(filterChain *filterChain, c *DmConnection) error {
if err := filterChain.DmConnectionCommit(c); err != nil {
return rf.autoReconnect(c, err)
}
if err := rf.checkAndRecover(c); err != nil {
return rf.autoReconnect(c, err)
}
return nil
}
func (rf *reconnectFilter) DmConnectionRollback(filterChain *filterChain, c *DmConnection) error {
err := filterChain.DmConnectionRollback(c)
if err != nil {
err = rf.autoReconnect(c, err)
}
return err
}
func (rf *reconnectFilter) DmConnectionClose(filterChain *filterChain, c *DmConnection) error {
err := filterChain.DmConnectionClose(c)
if err != nil {
err = rf.autoReconnect(c, err)
}
return err
}
func (rf *reconnectFilter) DmConnectionPing(filterChain *filterChain, c *DmConnection, ctx context.Context) error {
err := filterChain.DmConnectionPing(c, ctx)
if err != nil {
err = rf.autoReconnect(c, err)
}
return err
}
func (rf *reconnectFilter) DmConnectionExec(filterChain *filterChain, c *DmConnection, query string, args []driver.Value) (*DmResult, error) {
if err := rf.checkAndRecover(c); err != nil {
return nil, rf.autoReconnect(c, err)
}
dr, err := filterChain.DmConnectionExec(c, query, args)
if err != nil {
return nil, rf.autoReconnect(c, err)
}
return dr, err
}
func (rf *reconnectFilter) DmConnectionExecContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (*DmResult, error) {
if err := rf.checkAndRecover(c); err != nil {
return nil, rf.autoReconnect(c, err)
}
dr, err := filterChain.DmConnectionExecContext(c, ctx, query, args)
if err != nil {
return nil, rf.autoReconnect(c, err)
}
return dr, err
}
func (rf *reconnectFilter) DmConnectionQuery(filterChain *filterChain, c *DmConnection, query string, args []driver.Value) (*DmRows, error) {
if err := rf.checkAndRecover(c); err != nil {
return nil, rf.autoReconnect(c, err)
}
dr, err := filterChain.DmConnectionQuery(c, query, args)
if err != nil {
return nil, rf.autoReconnect(c, err)
}
return dr, err
}
func (rf *reconnectFilter) DmConnectionQueryContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (*DmRows, error) {
if err := rf.checkAndRecover(c); err != nil {
return nil, rf.autoReconnect(c, err)
}
dr, err := filterChain.DmConnectionQueryContext(c, ctx, query, args)
if err != nil {
return nil, rf.autoReconnect(c, err)
}
return dr, err
}
func (rf *reconnectFilter) DmConnectionPrepare(filterChain *filterChain, c *DmConnection, query string) (*DmStatement, error) {
ds, err := filterChain.DmConnectionPrepare(c, query)
if err != nil {
return nil, rf.autoReconnect(c, err)
}
return ds, err
}
func (rf *reconnectFilter) DmConnectionPrepareContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string) (*DmStatement, error) {
ds, err := filterChain.DmConnectionPrepareContext(c, ctx, query)
if err != nil {
return nil, rf.autoReconnect(c, err)
}
return ds, err
}
func (rf *reconnectFilter) DmConnectionResetSession(filterChain *filterChain, c *DmConnection, ctx context.Context) error {
err := filterChain.DmConnectionResetSession(c, ctx)
if err != nil {
err = rf.autoReconnect(c, err)
}
return err
}
func (rf *reconnectFilter) DmConnectionCheckNamedValue(filterChain *filterChain, c *DmConnection, nv *driver.NamedValue) error {
err := filterChain.DmConnectionCheckNamedValue(c, nv)
if err != nil {
err = rf.autoReconnect(c, err)
}
return err
}
// DmStatement
func (rf *reconnectFilter) DmStatementClose(filterChain *filterChain, s *DmStatement) error {
err := filterChain.DmStatementClose(s)
if err != nil {
err = rf.autoReconnect(s.dmConn, err)
}
return err
}
func (rf *reconnectFilter) DmStatementNumInput(filterChain *filterChain, s *DmStatement) int {
var ret int
defer func() {
err := recover()
if err != nil {
rf.autoReconnect(s.dmConn, err.(error))
ret = 0
}
}()
ret = filterChain.DmStatementNumInput(s)
return ret
}
func (rf *reconnectFilter) DmStatementExec(filterChain *filterChain, s *DmStatement, args []driver.Value) (*DmResult, error) {
if err := rf.checkAndRecover(s.dmConn); err != nil {
return nil, rf.autoReconnect(s.dmConn, err)
}
dr, err := filterChain.DmStatementExec(s, args)
if err != nil {
return nil, rf.autoReconnect(s.dmConn, err)
}
return dr, err
}
func (rf *reconnectFilter) DmStatementExecContext(filterChain *filterChain, s *DmStatement, ctx context.Context, args []driver.NamedValue) (*DmResult, error) {
if err := rf.checkAndRecover(s.dmConn); err != nil {
return nil, rf.autoReconnect(s.dmConn, err)
}
dr, err := filterChain.DmStatementExecContext(s, ctx, args)
if err != nil {
return nil, rf.autoReconnect(s.dmConn, err)
}
return dr, err
}
func (rf *reconnectFilter) DmStatementQuery(filterChain *filterChain, s *DmStatement, args []driver.Value) (*DmRows, error) {
if err := rf.checkAndRecover(s.dmConn); err != nil {
return nil, rf.autoReconnect(s.dmConn, err)
}
dr, err := filterChain.DmStatementQuery(s, args)
if err != nil {
return nil, rf.autoReconnect(s.dmConn, err)
}
return dr, err
}
func (rf *reconnectFilter) DmStatementQueryContext(filterChain *filterChain, s *DmStatement, ctx context.Context, args []driver.NamedValue) (*DmRows, error) {
if err := rf.checkAndRecover(s.dmConn); err != nil {
return nil, rf.autoReconnect(s.dmConn, err)
}
dr, err := filterChain.DmStatementQueryContext(s, ctx, args)
if err != nil {
return nil, rf.autoReconnect(s.dmConn, err)
}
return dr, err
}
func (rf *reconnectFilter) DmStatementCheckNamedValue(filterChain *filterChain, s *DmStatement, nv *driver.NamedValue) error {
err := filterChain.DmStatementCheckNamedValue(s, nv)
if err != nil {
err = rf.autoReconnect(s.dmConn, err)
}
return err
}
// DmResult
func (rf *reconnectFilter) DmResultLastInsertId(filterChain *filterChain, r *DmResult) (int64, error) {
i, err := filterChain.DmResultLastInsertId(r)
if err != nil {
err = rf.autoReconnect(r.dmStmt.dmConn, err)
return 0, err
}
return i, err
}
func (rf *reconnectFilter) DmResultRowsAffected(filterChain *filterChain, r *DmResult) (int64, error) {
i, err := filterChain.DmResultRowsAffected(r)
if err != nil {
err = rf.autoReconnect(r.dmStmt.dmConn, err)
return 0, err
}
return i, err
}
// DmRows
func (rf *reconnectFilter) DmRowsColumns(filterChain *filterChain, r *DmRows) []string {
var ret []string
defer func() {
err := recover()
if err != nil {
rf.autoReconnect(r.CurrentRows.dmStmt.dmConn, err.(error))
ret = nil
}
}()
ret = filterChain.DmRowsColumns(r)
return ret
}
func (rf *reconnectFilter) DmRowsClose(filterChain *filterChain, r *DmRows) error {
err := filterChain.DmRowsClose(r)
if err != nil {
err = rf.autoReconnect(r.CurrentRows.dmStmt.dmConn, err)
}
return err
}
func (rf *reconnectFilter) DmRowsNext(filterChain *filterChain, r *DmRows, dest []driver.Value) error {
err := filterChain.DmRowsNext(r, dest)
if err != nil {
err = rf.autoReconnect(r.CurrentRows.dmStmt.dmConn, err)
}
return err
}
func (rf *reconnectFilter) DmRowsHasNextResultSet(filterChain *filterChain, r *DmRows) bool {
var ret bool
defer func() {
err := recover()
if err != nil {
rf.autoReconnect(r.CurrentRows.dmStmt.dmConn, err.(error))
ret = false
}
}()
ret = filterChain.DmRowsHasNextResultSet(r)
return ret
}
func (rf *reconnectFilter) DmRowsNextResultSet(filterChain *filterChain, r *DmRows) error {
err := filterChain.DmRowsNextResultSet(r)
if err != nil {
err = rf.autoReconnect(r.CurrentRows.dmStmt.dmConn, err)
}
return err
}
func (rf *reconnectFilter) DmRowsColumnTypeScanType(filterChain *filterChain, r *DmRows, index int) reflect.Type {
var ret reflect.Type
defer func() {
err := recover()
if err != nil {
rf.autoReconnect(r.CurrentRows.dmStmt.dmConn, err.(error))
ret = scanTypeUnknown
}
}()
ret = filterChain.DmRowsColumnTypeScanType(r, index)
return ret
}
func (rf *reconnectFilter) DmRowsColumnTypeDatabaseTypeName(filterChain *filterChain, r *DmRows, index int) string {
var ret string
defer func() {
err := recover()
if err != nil {
rf.autoReconnect(r.CurrentRows.dmStmt.dmConn, err.(error))
ret = ""
}
}()
ret = filterChain.DmRowsColumnTypeDatabaseTypeName(r, index)
return ret
}
func (rf *reconnectFilter) DmRowsColumnTypeLength(filterChain *filterChain, r *DmRows, index int) (length int64, ok bool) {
defer func() {
err := recover()
if err != nil {
rf.autoReconnect(r.CurrentRows.dmStmt.dmConn, err.(error))
length, ok = 0, false
}
}()
return filterChain.DmRowsColumnTypeLength(r, index)
}
func (rf *reconnectFilter) DmRowsColumnTypeNullable(filterChain *filterChain, r *DmRows, index int) (nullable, ok bool) {
defer func() {
err := recover()
if err != nil {
rf.autoReconnect(r.CurrentRows.dmStmt.dmConn, err.(error))
nullable, ok = false, false
}
}()
return filterChain.DmRowsColumnTypeNullable(r, index)
}
func (rf *reconnectFilter) DmRowsColumnTypePrecisionScale(filterChain *filterChain, r *DmRows, index int) (precision, scale int64, ok bool) {
defer func() {
err := recover()
if err != nil {
rf.autoReconnect(r.CurrentRows.dmStmt.dmConn, err.(error))
precision, scale, ok = 0, 0, false
}
}()
return filterChain.DmRowsColumnTypePrecisionScale(r, index)
}
+310
View File
@@ -0,0 +1,310 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"context"
"database/sql/driver"
"reflect"
)
type rwFilter struct {
}
//DmDriver
func (rwf *rwFilter) DmDriverOpen(filterChain *filterChain, d *DmDriver, dsn string) (*DmConnection, error) {
return filterChain.DmDriverOpen(d, dsn)
}
func (rwf *rwFilter) DmDriverOpenConnector(filterChain *filterChain, d *DmDriver, dsn string) (*DmConnector, error) {
return filterChain.DmDriverOpenConnector(d, dsn)
}
//DmConnector
func (rwf *rwFilter) DmConnectorConnect(filterChain *filterChain, c *DmConnector, ctx context.Context) (*DmConnection, error) {
return RWUtil.connect(c, ctx)
}
func (rwf *rwFilter) DmConnectorDriver(filterChain *filterChain, c *DmConnector) *DmDriver {
return filterChain.DmConnectorDriver(c)
}
//DmConnection
func (rwf *rwFilter) DmConnectionBegin(filterChain *filterChain, c *DmConnection) (*DmConnection, error) {
if RWUtil.isStandbyAlive(c) {
_, err := c.rwInfo.connStandby.begin()
if err != nil {
RWUtil.afterExceptionOnStandby(c, err)
}
}
return filterChain.DmConnectionBegin(c)
}
func (rwf *rwFilter) DmConnectionBeginTx(filterChain *filterChain, c *DmConnection, ctx context.Context, opts driver.TxOptions) (*DmConnection, error) {
if RWUtil.isStandbyAlive(c) {
_, err := c.rwInfo.connStandby.beginTx(ctx, opts)
if err != nil {
RWUtil.afterExceptionOnStandby(c, err)
}
}
return filterChain.DmConnectionBeginTx(c, ctx, opts)
}
func (rwf *rwFilter) DmConnectionCommit(filterChain *filterChain, c *DmConnection) error {
if RWUtil.isStandbyAlive(c) {
err := c.rwInfo.connStandby.commit()
if err != nil {
RWUtil.afterExceptionOnStandby(c, err)
}
}
return filterChain.DmConnectionCommit(c)
}
func (rwf *rwFilter) DmConnectionRollback(filterChain *filterChain, c *DmConnection) error {
if RWUtil.isStandbyAlive(c) {
err := c.rwInfo.connStandby.rollback()
if err != nil {
RWUtil.afterExceptionOnStandby(c, err)
}
}
return filterChain.DmConnectionRollback(c)
}
func (rwf *rwFilter) DmConnectionClose(filterChain *filterChain, c *DmConnection) error {
if RWUtil.isStandbyAlive(c) {
err := c.rwInfo.connStandby.close()
if err != nil {
RWUtil.afterExceptionOnStandby(c, err)
}
}
return filterChain.DmConnectionClose(c)
}
func (rwf *rwFilter) DmConnectionPing(filterChain *filterChain, c *DmConnection, ctx context.Context) error {
return filterChain.DmConnectionPing(c, ctx)
}
func (rwf *rwFilter) DmConnectionExec(filterChain *filterChain, c *DmConnection, query string, args []driver.Value) (*DmResult, error) {
ret, err := RWUtil.executeByConn(c, query, func() (interface{}, error) {
return c.rwInfo.connCurrent.exec(query, args)
}, func(otherConn *DmConnection) (interface{}, error) {
return otherConn.exec(query, args)
})
if err != nil {
return nil, err
}
return ret.(*DmResult), nil
}
func (rwf *rwFilter) DmConnectionExecContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (*DmResult, error) {
ret, err := RWUtil.executeByConn(c, query, func() (interface{}, error) {
return c.rwInfo.connCurrent.execContext(ctx, query, args)
}, func(otherConn *DmConnection) (interface{}, error) {
return otherConn.execContext(ctx, query, args)
})
if err != nil {
return nil, err
}
return ret.(*DmResult), nil
}
func (rwf *rwFilter) DmConnectionQuery(filterChain *filterChain, c *DmConnection, query string, args []driver.Value) (*DmRows, error) {
ret, err := RWUtil.executeByConn(c, query, func() (interface{}, error) {
return c.rwInfo.connCurrent.query(query, args)
}, func(otherConn *DmConnection) (interface{}, error) {
return otherConn.query(query, args)
})
if err != nil {
return nil, err
}
return ret.(*DmRows), nil
}
func (rwf *rwFilter) DmConnectionQueryContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (*DmRows, error) {
ret, err := RWUtil.executeByConn(c, query, func() (interface{}, error) {
return c.rwInfo.connCurrent.queryContext(ctx, query, args)
}, func(otherConn *DmConnection) (interface{}, error) {
return otherConn.queryContext(ctx, query, args)
})
if err != nil {
return nil, err
}
return ret.(*DmRows), nil
}
func (rwf *rwFilter) DmConnectionPrepare(filterChain *filterChain, c *DmConnection, query string) (*DmStatement, error) {
stmt, err := c.prepare(query)
if err != nil {
return nil, err
}
stmt.rwInfo.stmtCurrent = stmt
stmt.rwInfo.readOnly = RWUtil.checkReadonlyByStmt(stmt)
if RWUtil.isCreateStandbyStmt(stmt) {
stmt.rwInfo.stmtStandby, err = c.rwInfo.connStandby.prepare(query)
if err == nil {
stmt.rwInfo.stmtCurrent = stmt.rwInfo.stmtStandby
} else {
RWUtil.afterExceptionOnStandby(c, err)
}
}
return stmt, nil
}
func (rwf *rwFilter) DmConnectionPrepareContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string) (*DmStatement, error) {
stmt, err := c.prepareContext(ctx, query)
if err != nil {
return nil, err
}
stmt.rwInfo.stmtCurrent = stmt
stmt.rwInfo.readOnly = RWUtil.checkReadonlyByStmt(stmt)
if RWUtil.isCreateStandbyStmt(stmt) {
stmt.rwInfo.stmtStandby, err = c.rwInfo.connStandby.prepareContext(ctx, query)
if err == nil {
stmt.rwInfo.stmtCurrent = stmt.rwInfo.stmtStandby
} else {
RWUtil.afterExceptionOnStandby(c, err)
}
}
return stmt, nil
}
func (rwf *rwFilter) DmConnectionResetSession(filterChain *filterChain, c *DmConnection, ctx context.Context) error {
if RWUtil.isStandbyAlive(c) {
err := c.rwInfo.connStandby.resetSession(ctx)
if err != nil {
RWUtil.afterExceptionOnStandby(c, err)
}
}
return filterChain.DmConnectionResetSession(c, ctx)
}
func (rwf *rwFilter) DmConnectionCheckNamedValue(filterChain *filterChain, c *DmConnection, nv *driver.NamedValue) error {
return filterChain.DmConnectionCheckNamedValue(c, nv)
}
//DmStatement
func (rwf *rwFilter) DmStatementClose(filterChain *filterChain, s *DmStatement) error {
if RWUtil.isStandbyStatementValid(s) {
err := s.rwInfo.stmtStandby.close()
if err != nil {
RWUtil.afterExceptionOnStandby(s.dmConn, err)
}
}
return filterChain.DmStatementClose(s)
}
func (rwf *rwFilter) DmStatementNumInput(filterChain *filterChain, s *DmStatement) int {
return filterChain.DmStatementNumInput(s)
}
func (rwf *rwFilter) DmStatementExec(filterChain *filterChain, s *DmStatement, args []driver.Value) (*DmResult, error) {
ret, err := RWUtil.executeByStmt(s, func() (interface{}, error) {
return s.rwInfo.stmtCurrent.exec(args)
}, func(otherStmt *DmStatement) (interface{}, error) {
return otherStmt.exec(args)
})
if err != nil {
return nil, err
}
return ret.(*DmResult), nil
}
func (rwf *rwFilter) DmStatementExecContext(filterChain *filterChain, s *DmStatement, ctx context.Context, args []driver.NamedValue) (*DmResult, error) {
ret, err := RWUtil.executeByStmt(s, func() (interface{}, error) {
return s.rwInfo.stmtCurrent.execContext(ctx, args)
}, func(otherStmt *DmStatement) (interface{}, error) {
return otherStmt.execContext(ctx, args)
})
if err != nil {
return nil, err
}
return ret.(*DmResult), nil
}
func (rwf *rwFilter) DmStatementQuery(filterChain *filterChain, s *DmStatement, args []driver.Value) (*DmRows, error) {
ret, err := RWUtil.executeByStmt(s, func() (interface{}, error) {
return s.rwInfo.stmtCurrent.query(args)
}, func(otherStmt *DmStatement) (interface{}, error) {
return otherStmt.query(args)
})
if err != nil {
return nil, err
}
return ret.(*DmRows), nil
}
func (rwf *rwFilter) DmStatementQueryContext(filterChain *filterChain, s *DmStatement, ctx context.Context, args []driver.NamedValue) (*DmRows, error) {
ret, err := RWUtil.executeByStmt(s, func() (interface{}, error) {
return s.rwInfo.stmtCurrent.queryContext(ctx, args)
}, func(otherStmt *DmStatement) (interface{}, error) {
return otherStmt.queryContext(ctx, args)
})
if err != nil {
return nil, err
}
return ret.(*DmRows), nil
}
func (rwf *rwFilter) DmStatementCheckNamedValue(filterChain *filterChain, s *DmStatement, nv *driver.NamedValue) error {
return filterChain.DmStatementCheckNamedValue(s, nv)
}
//DmResult
func (rwf *rwFilter) DmResultLastInsertId(filterChain *filterChain, r *DmResult) (int64, error) {
return filterChain.DmResultLastInsertId(r)
}
func (rwf *rwFilter) DmResultRowsAffected(filterChain *filterChain, r *DmResult) (int64, error) {
return filterChain.DmResultRowsAffected(r)
}
//DmRows
func (rwf *rwFilter) DmRowsColumns(filterChain *filterChain, r *DmRows) []string {
return filterChain.DmRowsColumns(r)
}
func (rwf *rwFilter) DmRowsClose(filterChain *filterChain, r *DmRows) error {
return filterChain.DmRowsClose(r)
}
func (rwf *rwFilter) DmRowsNext(filterChain *filterChain, r *DmRows, dest []driver.Value) error {
return filterChain.DmRowsNext(r, dest)
}
func (rwf *rwFilter) DmRowsHasNextResultSet(filterChain *filterChain, r *DmRows) bool {
return filterChain.DmRowsHasNextResultSet(r)
}
func (rwf *rwFilter) DmRowsNextResultSet(filterChain *filterChain, r *DmRows) error {
return filterChain.DmRowsNextResultSet(r)
}
func (rwf *rwFilter) DmRowsColumnTypeScanType(filterChain *filterChain, r *DmRows, index int) reflect.Type {
return filterChain.DmRowsColumnTypeScanType(r, index)
}
func (rwf *rwFilter) DmRowsColumnTypeDatabaseTypeName(filterChain *filterChain, r *DmRows, index int) string {
return filterChain.DmRowsColumnTypeDatabaseTypeName(r, index)
}
func (rwf *rwFilter) DmRowsColumnTypeLength(filterChain *filterChain, r *DmRows, index int) (length int64, ok bool) {
return filterChain.DmRowsColumnTypeLength(r, index)
}
func (rwf *rwFilter) DmRowsColumnTypeNullable(filterChain *filterChain, r *DmRows, index int) (nullable, ok bool) {
return filterChain.DmRowsColumnTypeNullable(r, index)
}
func (rwf *rwFilter) DmRowsColumnTypePrecisionScale(filterChain *filterChain, r *DmRows, index int) (precision, scale int64, ok bool) {
return filterChain.DmRowsColumnTypePrecisionScale(r, index)
}
+607
View File
@@ -0,0 +1,607 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"bytes"
"context"
"database/sql/driver"
"fmt"
"reflect"
"time"
)
type statFilter struct {
}
//DmDriver
func (sf *statFilter) DmDriverOpen(filterChain *filterChain, d *DmDriver, dsn string) (*DmConnection, error) {
conn, err := filterChain.DmDriverOpen(d, dsn)
if err != nil {
return nil, err
}
conn.statInfo.init(conn)
conn.statInfo.setConstructNano()
conn.statInfo.getConnStat().incrementConn()
return conn, nil
}
func (sf *statFilter) DmDriverOpenConnector(filterChain *filterChain, d *DmDriver, dsn string) (*DmConnector, error) {
return filterChain.DmDriverOpenConnector(d, dsn)
}
//DmConnector
func (sf *statFilter) DmConnectorConnect(filterChain *filterChain, c *DmConnector, ctx context.Context) (*DmConnection, error) {
conn, err := filterChain.DmConnectorConnect(c, ctx)
if err != nil {
return nil, err
}
conn.statInfo.init(conn)
conn.statInfo.setConstructNano()
conn.statInfo.getConnStat().incrementConn()
return conn, nil
}
func (sf *statFilter) DmConnectorDriver(filterChain *filterChain, c *DmConnector) *DmDriver {
return filterChain.DmConnectorDriver(c)
}
//DmConnection
func (sf *statFilter) DmConnectionBegin(filterChain *filterChain, c *DmConnection) (*DmConnection, error) {
return filterChain.DmConnectionBegin(c)
}
func (sf *statFilter) DmConnectionBeginTx(filterChain *filterChain, c *DmConnection, ctx context.Context, opts driver.TxOptions) (*DmConnection, error) {
return filterChain.DmConnectionBeginTx(c, ctx, opts)
}
func (sf *statFilter) DmConnectionCommit(filterChain *filterChain, c *DmConnection) error {
err := filterChain.DmConnectionCommit(c)
if err != nil {
return err
}
c.statInfo.getConnStat().incrementCommitCount()
return nil
}
func (sf *statFilter) DmConnectionRollback(filterChain *filterChain, c *DmConnection) error {
err := filterChain.DmConnectionRollback(c)
if err != nil {
return err
}
c.statInfo.getConnStat().incrementRollbackCount()
return nil
}
func (sf *statFilter) DmConnectionClose(filterChain *filterChain, c *DmConnection) error {
if !c.closed.IsSet() {
c.statInfo.getConnStat().decrementStmtByActiveStmtCount(int64(getActiveStmtCount(c)))
c.statInfo.getConnStat().decrementConn()
}
return filterChain.DmConnectionClose(c)
}
func (sf *statFilter) DmConnectionPing(filterChain *filterChain, c *DmConnection, ctx context.Context) error {
return c.ping(ctx)
}
func (sf *statFilter) DmConnectionExec(filterChain *filterChain, c *DmConnection, query string, args []driver.Value) (*DmResult, error) {
connExecBefore(c, query)
dr, err := filterChain.DmConnectionExec(c, query, args)
if err != nil {
connExecuteErrorAfter(c, args, err)
return nil, err
}
connExecAfter(c, query, args, int(dr.affectedRows))
return dr, nil
}
func (sf *statFilter) DmConnectionExecContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (*DmResult, error) {
connExecBefore(c, query)
dr, err := filterChain.DmConnectionExecContext(c, ctx, query, args)
if err != nil {
connExecuteErrorAfter(c, args, err)
return nil, err
}
connExecAfter(c, query, args, int(dr.affectedRows))
return dr, nil
}
func (sf *statFilter) DmConnectionQuery(filterChain *filterChain, c *DmConnection, query string, args []driver.Value) (*DmRows, error) {
connQueryBefore(c, query)
dr, err := filterChain.DmConnectionQuery(c, query, args)
if err != nil {
connExecuteErrorAfter(c, args, err)
return nil, err
}
connQueryAfter(c, query, args, dr)
return dr, nil
}
func (sf *statFilter) DmConnectionQueryContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string, args []driver.NamedValue) (*DmRows, error) {
connQueryBefore(c, query)
dr, err := filterChain.DmConnectionQueryContext(c, ctx, query, args)
if err != nil {
connExecuteErrorAfter(c, args, err)
return nil, err
}
connQueryAfter(c, query, args, dr)
return dr, nil
}
func (sf *statFilter) DmConnectionPrepare(filterChain *filterChain, c *DmConnection, query string) (*DmStatement, error) {
stmt, err := filterChain.DmConnectionPrepare(c, query)
if err != nil {
return nil, err
}
statementCreateAfter(c, stmt)
return stmt, nil
}
func (sf *statFilter) DmConnectionPrepareContext(filterChain *filterChain, c *DmConnection, ctx context.Context, query string) (*DmStatement, error) {
stmt, err := filterChain.DmConnectionPrepareContext(c, ctx, query)
if err != nil {
return nil, err
}
statementCreateAfter(c, stmt)
return stmt, nil
}
func (sf *statFilter) DmConnectionResetSession(filterChain *filterChain, c *DmConnection, ctx context.Context) error {
return filterChain.DmConnectionResetSession(c, ctx)
}
func (sf *statFilter) DmConnectionCheckNamedValue(filterChain *filterChain, c *DmConnection, nv *driver.NamedValue) error {
return filterChain.DmConnectionCheckNamedValue(c, nv)
}
//DmStatement
func (sf *statFilter) DmStatementClose(filterChain *filterChain, s *DmStatement) error {
if !s.closed {
statementCloseBefore(s)
}
return filterChain.DmStatementClose(s)
}
func (sf *statFilter) DmStatementNumInput(filterChain *filterChain, s *DmStatement) int {
return filterChain.DmStatementNumInput(s)
}
func (sf *statFilter) DmStatementExec(filterChain *filterChain, s *DmStatement, args []driver.Value) (*DmResult, error) {
stmtExecBefore(s)
dr, err := filterChain.DmStatementExec(s, args)
if err != nil {
statementExecuteErrorAfter(s, args, err)
return nil, err
}
stmtExecAfter(s, args, int(dr.affectedRows))
return dr, nil
}
func (sf *statFilter) DmStatementExecContext(filterChain *filterChain, s *DmStatement, ctx context.Context, args []driver.NamedValue) (*DmResult, error) {
stmtExecBefore(s)
dr, err := filterChain.DmStatementExecContext(s, ctx, args)
if err != nil {
statementExecuteErrorAfter(s, args, err)
return nil, err
}
stmtExecAfter(s, args, int(dr.affectedRows))
return dr, nil
}
func (sf *statFilter) DmStatementQuery(filterChain *filterChain, s *DmStatement, args []driver.Value) (*DmRows, error) {
stmtQueryBefore(s)
dr, err := filterChain.DmStatementQuery(s, args)
if err != nil {
statementExecuteErrorAfter(s, args, err)
return nil, err
}
stmtQueryAfter(s, args, dr)
return dr, nil
}
func (sf *statFilter) DmStatementQueryContext(filterChain *filterChain, s *DmStatement, ctx context.Context, args []driver.NamedValue) (*DmRows, error) {
stmtQueryBefore(s)
dr, err := filterChain.DmStatementQueryContext(s, ctx, args)
if err != nil {
statementExecuteErrorAfter(s, args, err)
return nil, err
}
stmtQueryAfter(s, args, dr)
return dr, nil
}
func (sf *statFilter) DmStatementCheckNamedValue(filterChain *filterChain, s *DmStatement, nv *driver.NamedValue) error {
return filterChain.DmStatementCheckNamedValue(s, nv)
}
//DmResult
func (sf *statFilter) DmResultLastInsertId(filterChain *filterChain, r *DmResult) (int64, error) {
return filterChain.DmResultLastInsertId(r)
}
func (sf *statFilter) DmResultRowsAffected(filterChain *filterChain, r *DmResult) (int64, error) {
return filterChain.DmResultRowsAffected(r)
}
//DmRows
func (sf *statFilter) DmRowsColumns(filterChain *filterChain, r *DmRows) []string {
return filterChain.DmRowsColumns(r)
}
func (sf *statFilter) DmRowsClose(filterChain *filterChain, r *DmRows) error {
if !r.CurrentRows.closed {
resultSetCloseBefore(r)
}
return filterChain.DmRowsClose(r)
}
func (sf *statFilter) DmRowsNext(filterChain *filterChain, r *DmRows, dest []driver.Value) error {
return filterChain.DmRowsNext(r, dest)
}
func (sf *statFilter) DmRowsHasNextResultSet(filterChain *filterChain, r *DmRows) bool {
return filterChain.DmRowsHasNextResultSet(r)
}
func (sf *statFilter) DmRowsNextResultSet(filterChain *filterChain, r *DmRows) error {
return filterChain.DmRowsNextResultSet(r)
}
func (sf *statFilter) DmRowsColumnTypeScanType(filterChain *filterChain, r *DmRows, index int) reflect.Type {
return filterChain.DmRowsColumnTypeScanType(r, index)
}
func (sf *statFilter) DmRowsColumnTypeDatabaseTypeName(filterChain *filterChain, r *DmRows, index int) string {
return filterChain.DmRowsColumnTypeDatabaseTypeName(r, index)
}
func (sf *statFilter) DmRowsColumnTypeLength(filterChain *filterChain, r *DmRows, index int) (length int64, ok bool) {
return filterChain.DmRowsColumnTypeLength(r, index)
}
func (sf *statFilter) DmRowsColumnTypeNullable(filterChain *filterChain, r *DmRows, index int) (nullable, ok bool) {
return filterChain.DmRowsColumnTypeNullable(r, index)
}
func (sf *statFilter) DmRowsColumnTypePrecisionScale(filterChain *filterChain, r *DmRows, index int) (precision, scale int64, ok bool) {
return filterChain.DmRowsColumnTypePrecisionScale(r, index)
}
func getActiveStmtCount(conn *DmConnection) int {
if conn.stmtMap == nil {
return 0
} else {
return len(conn.stmtMap)
}
}
func statementCreateAfter(conn *DmConnection, stmt *DmStatement) {
stmt.statInfo.setConstructNano()
conn.statInfo.getConnStat().incrementStmt()
}
func connExecBefore(conn *DmConnection, sql string) {
conn.statInfo.setLastExecuteSql(sql)
conn.statInfo.setFirstResultSet(false)
conn.statInfo.setLastExecuteType(ExecuteUpdate)
internalBeforeConnExecute(conn, sql)
}
func connExecAfter(conn *DmConnection, sql string, args interface{}, updateCount int) {
internalAfterConnExecute(conn, args, updateCount)
}
func connQueryBefore(conn *DmConnection, sql string) {
conn.statInfo.setLastExecuteSql(sql)
conn.statInfo.setFirstResultSet(true)
conn.statInfo.setLastExecuteType(ExecuteQuery)
internalBeforeConnExecute(conn, sql)
}
func connQueryAfter(conn *DmConnection, sql string, args interface{}, resultSet *DmRows) {
if resultSet != nil {
connResultSetCreateAfter(resultSet, conn)
}
internalAfterConnExecute(conn, args, 0)
}
func stmtExecBefore(stmt *DmStatement) {
stmt.statInfo.setLastExecuteSql(stmt.nativeSql)
stmt.statInfo.setFirstResultSet(false)
stmt.statInfo.setLastExecuteType(ExecuteUpdate)
internalBeforeStatementExecute(stmt, stmt.nativeSql)
}
func stmtExecAfter(stmt *DmStatement, args interface{}, updateCount int) {
internalAfterStatementExecute(stmt, args, updateCount)
}
func stmtQueryBefore(stmt *DmStatement) {
stmt.statInfo.setLastExecuteSql(stmt.nativeSql)
stmt.statInfo.setFirstResultSet(true)
stmt.statInfo.setLastExecuteType(ExecuteQuery)
internalBeforeStatementExecute(stmt, stmt.nativeSql)
}
func stmtQueryAfter(stmt *DmStatement, args interface{}, resultSet *DmRows) {
if resultSet != nil {
stmtResultSetCreateAfter(resultSet, stmt)
}
internalAfterStatementExecute(stmt, args, 0)
}
func internalBeforeConnExecute(conn *DmConnection, sql string) {
connStat := conn.statInfo.getConnStat()
connStat.incrementExecuteCount()
conn.statInfo.beforeExecute()
sqlStat := conn.statInfo.getSqlStat()
if sqlStat == nil || sqlStat.Removed == 1 || !(sqlStat.Sql == sql) {
sqlStat = connStat.createSqlStat(sql)
conn.statInfo.setSqlStat(sqlStat)
}
inTransaction := false
inTransaction = !conn.autoCommit
if sqlStat != nil {
sqlStat.ExecuteLastStartTime = time.Now().UnixNano()
sqlStat.incrementRunningCount()
if inTransaction {
sqlStat.incrementInTransactionCount()
}
}
}
func internalAfterConnExecute(conn *DmConnection, args interface{}, updateCount int) {
nowNano := time.Now().UnixNano()
nanos := nowNano - conn.statInfo.getLastExecuteStartNano()
conn.statInfo.afterExecute(nanos)
sqlStat := conn.statInfo.getSqlStat()
if sqlStat != nil {
sqlStat.incrementExecuteSuccessCount()
sqlStat.decrementRunningCount()
parameters := buildSlowParameters(args)
sqlStat.addExecuteTimeAndResultHoldTimeHistogramRecord(conn.statInfo.getLastExecuteType(), conn.statInfo.isFirstResultSet(),
nanos, parameters)
if !conn.statInfo.isFirstResultSet() &&
conn.statInfo.getLastExecuteType() == ExecuteUpdate {
if updateCount < 0 {
updateCount = 0
}
sqlStat.addUpdateCount(int64(updateCount))
}
}
}
func internalBeforeStatementExecute(stmt *DmStatement, sql string) {
connStat := stmt.dmConn.statInfo.getConnStat()
connStat.incrementExecuteCount()
stmt.statInfo.beforeExecute()
sqlStat := stmt.statInfo.getSqlStat()
if sqlStat == nil || sqlStat.Removed == 1 || !(sqlStat.Sql == sql) {
sqlStat = connStat.createSqlStat(sql)
stmt.statInfo.setSqlStat(sqlStat)
}
inTransaction := false
inTransaction = !stmt.dmConn.autoCommit
if sqlStat != nil {
sqlStat.ExecuteLastStartTime = time.Now().UnixNano()
sqlStat.incrementRunningCount()
if inTransaction {
sqlStat.incrementInTransactionCount()
}
}
}
func internalAfterStatementExecute(stmt *DmStatement, args interface{}, updateCount int) {
nowNano := time.Now().UnixNano()
nanos := nowNano - stmt.statInfo.getLastExecuteStartNano()
stmt.statInfo.afterExecute(nanos)
sqlStat := stmt.statInfo.getSqlStat()
if sqlStat != nil {
sqlStat.incrementExecuteSuccessCount()
sqlStat.decrementRunningCount()
parameters := ""
if stmt.paramCount > 0 {
parameters = buildStmtSlowParameters(stmt, args)
}
sqlStat.addExecuteTimeAndResultHoldTimeHistogramRecord(stmt.statInfo.getLastExecuteType(), stmt.statInfo.isFirstResultSet(),
nanos, parameters)
if (!stmt.statInfo.isFirstResultSet()) &&
stmt.statInfo.getLastExecuteType() == ExecuteUpdate {
updateCount := stmt.execInfo.updateCount
if updateCount < 0 {
updateCount = 0
}
sqlStat.addUpdateCount(updateCount)
}
}
}
func buildSlowParameters(args interface{}) string {
switch v := args.(type) {
case []driver.Value:
sb := bytes.NewBufferString("")
for i := 0; i < len(v); i++ {
if i != 0 {
sb.WriteString(",")
} else {
sb.WriteString("[")
}
sb.WriteString(fmt.Sprint(v[i]))
}
if len(v) > 0 {
sb.WriteString("]")
}
return sb.String()
case []driver.NamedValue:
sb := bytes.NewBufferString("")
for i := 0; i < len(v); i++ {
if i != 0 {
sb.WriteString(",")
} else {
sb.WriteString("[")
}
sb.WriteString(fmt.Sprint(v[i]))
}
if len(v) > 0 {
sb.WriteString("]")
}
return sb.String()
default:
return ""
}
}
func buildStmtSlowParameters(stmt *DmStatement, args interface{}) string {
switch v := args.(type) {
case []driver.Value:
sb := bytes.NewBufferString("")
for i := 0; i < int(stmt.paramCount); i++ {
if i != 0 {
sb.WriteString(",")
} else {
sb.WriteString("[")
}
sb.WriteString(fmt.Sprint(v[i]))
}
if len(v) > 0 {
sb.WriteString("]")
}
return sb.String()
case []driver.NamedValue:
sb := bytes.NewBufferString("")
for i := 0; i < int(stmt.paramCount); i++ {
if i != 0 {
sb.WriteString(",")
} else {
sb.WriteString("[")
}
sb.WriteString(fmt.Sprint(v[i]))
}
if len(v) > 0 {
sb.WriteString("]")
}
return sb.String()
default:
return ""
}
}
func connExecuteErrorAfter(conn *DmConnection, args interface{}, err error) {
nanos := time.Now().UnixNano() - conn.statInfo.getLastExecuteStartNano()
conn.statInfo.getConnStat().incrementErrorCount()
conn.statInfo.afterExecute(nanos)
// SQL
sqlStat := conn.statInfo.getSqlStat()
if sqlStat != nil {
sqlStat.decrementRunningCount()
sqlStat.error(err)
parameters := buildSlowParameters(args)
sqlStat.addExecuteTimeAndResultHoldTimeHistogramRecord(conn.statInfo.getLastExecuteType(), conn.statInfo.isFirstResultSet(),
nanos, parameters)
}
}
func statementExecuteErrorAfter(stmt *DmStatement, args interface{}, err error) {
nanos := time.Now().UnixNano() - stmt.statInfo.getLastExecuteStartNano()
stmt.dmConn.statInfo.getConnStat().incrementErrorCount()
stmt.statInfo.afterExecute(nanos)
// SQL
sqlStat := stmt.statInfo.getSqlStat()
if sqlStat != nil {
sqlStat.decrementRunningCount()
sqlStat.error(err)
parameters := ""
if stmt.paramCount > 0 {
parameters = buildStmtSlowParameters(stmt, args)
}
sqlStat.addExecuteTimeAndResultHoldTimeHistogramRecord(stmt.statInfo.getLastExecuteType(), stmt.statInfo.isFirstResultSet(),
nanos, parameters)
}
}
func statementCloseBefore(stmt *DmStatement) {
stmt.dmConn.statInfo.getConnStat().decrementStmt()
}
func connResultSetCreateAfter(dmdbResultSet *DmRows, conn *DmConnection) {
dmdbResultSet.statInfo.setSql(conn.statInfo.getLastExecuteSql())
dmdbResultSet.statInfo.setSqlStat(conn.statInfo.getSqlStat())
dmdbResultSet.statInfo.setConstructNano()
}
func stmtResultSetCreateAfter(dmdbResultSet *DmRows, stmt *DmStatement) {
dmdbResultSet.statInfo.setSql(stmt.statInfo.getLastExecuteSql())
dmdbResultSet.statInfo.setSqlStat(stmt.statInfo.getSqlStat())
dmdbResultSet.statInfo.setConstructNano()
}
func resultSetCloseBefore(resultSet *DmRows) {
nanos := time.Now().UnixNano() - resultSet.statInfo.getConstructNano()
fetchRowCount := getFetchedRows(resultSet)
sqlStat := resultSet.statInfo.getSqlStat()
if sqlStat != nil && resultSet.statInfo.getCloseCount() == 0 {
sqlStat.addFetchRowCount(fetchRowCount)
stmtExecuteNano := resultSet.statInfo.getLastExecuteTimeNano()
sqlStat.addResultSetHoldTimeNano2(stmtExecuteNano, nanos)
if resultSet.statInfo.getReadStringLength() > 0 {
sqlStat.addStringReadLength(resultSet.statInfo.getReadStringLength())
}
if resultSet.statInfo.getReadBytesLength() > 0 {
sqlStat.addReadBytesLength(resultSet.statInfo.getReadBytesLength())
}
if resultSet.statInfo.getOpenInputStreamCount() > 0 {
sqlStat.addInputStreamOpenCount(int64(resultSet.statInfo.getOpenInputStreamCount()))
}
if resultSet.statInfo.getOpenReaderCount() > 0 {
sqlStat.addReaderOpenCount(int64(resultSet.statInfo.getOpenReaderCount()))
}
}
resultSet.statInfo.incrementCloseCount()
}
func getFetchedRows(rs *DmRows) int64 {
if rs.CurrentRows.currentPos >= rs.CurrentRows.totalRowCount {
return rs.CurrentRows.totalRowCount
} else {
return rs.CurrentRows.currentPos + 1
}
}
+1017
View File
File diff suppressed because it is too large Load Diff
+137
View File
@@ -0,0 +1,137 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
const (
LOB_FLAG_BYTE = 0
LOB_FLAG_CHAR = 1
LOB_IN_ROW = 0x1
LOB_OFF_ROW = 0x2
NBLOB_HEAD_IN_ROW_FLAG = 0
NBLOB_HEAD_BLOBID = NBLOB_HEAD_IN_ROW_FLAG + BYTE_SIZE
NBLOB_HEAD_BLOB_LEN = NBLOB_HEAD_BLOBID + DDWORD_SIZE
NBLOB_HEAD_OUTROW_GROUPID = NBLOB_HEAD_BLOB_LEN + ULINT_SIZE
NBLOB_HEAD_OUTROW_FILEID = NBLOB_HEAD_OUTROW_GROUPID + USINT_SIZE
NBLOB_HEAD_OUTROW_PAGENO = NBLOB_HEAD_OUTROW_FILEID + USINT_SIZE
NBLOB_EX_HEAD_TABLE_ID = NBLOB_HEAD_OUTROW_PAGENO + ULINT_SIZE
NBLOB_EX_HEAD_COL_ID = NBLOB_EX_HEAD_TABLE_ID + ULINT_SIZE
NBLOB_EX_HEAD_ROW_ID = NBLOB_EX_HEAD_COL_ID + USINT_SIZE
NBLOB_EX_HEAD_FPA_GRPID = NBLOB_EX_HEAD_ROW_ID + LINT64_SIZE
NBLOB_EX_HEAD_FPA_FILEID = NBLOB_EX_HEAD_FPA_GRPID + USINT_SIZE
NBLOB_EX_HEAD_FPA_PAGENO = NBLOB_EX_HEAD_FPA_FILEID + USINT_SIZE
NBLOB_EX_HEAD_SIZE = NBLOB_EX_HEAD_FPA_PAGENO + ULINT_SIZE
NBLOB_OUTROW_HEAD_SIZE = NBLOB_HEAD_OUTROW_PAGENO + ULINT_SIZE
NBLOB_INROW_HEAD_SIZE = NBLOB_HEAD_BLOB_LEN + ULINT_SIZE
)
type lob struct {
blobId int64
inRow bool
groupId int16
fileId int16
pageNo int32
tabId int32
colId int16
rowId int64
exGroupId int16
exFileId int16
exPageNo int32
curFileId int16
curPageNo int32
curPageOffset int16
totalOffset int32
readOver bool
connection *DmConnection
local bool
updateable bool
lobFlag int8
length int64
compatibleOracle bool
fetchAll bool
freed bool
modify bool
Valid bool
}
func (lob *lob) GetLength() (int64, error) {
var err error
if err = lob.checkValid(); err != nil {
return -1, err
}
if err = lob.checkFreed(); err != nil {
return -1, err
}
if lob.length == -1 {
if lob.length, err = lob.connection.Access.dm_build_585(lob); err != nil {
return -1, err
}
}
return lob.length, nil
}
func (lob *lob) resetCurrentInfo() {
lob.curFileId = lob.fileId
lob.curPageNo = lob.pageNo
lob.totalOffset = 0
lob.curPageOffset = 0
}
func (lob *lob) getLengthFromHead(head []byte) int64 {
return int64(Dm_build_1346.Dm_build_1448(head, NBLOB_HEAD_BLOB_LEN))
}
func (lob *lob) canOptimized(connection *DmConnection) bool {
return !(lob.inRow || lob.fetchAll || lob.local || connection != lob.connection)
}
func (lob *lob) buildCtlData() (bytes []byte) {
if lob.connection.NewLobFlag {
bytes = make([]byte, NBLOB_EX_HEAD_SIZE, NBLOB_EX_HEAD_SIZE)
} else {
bytes = make([]byte, NBLOB_OUTROW_HEAD_SIZE, NBLOB_OUTROW_HEAD_SIZE)
}
Dm_build_1346.Dm_build_1347(bytes, NBLOB_HEAD_IN_ROW_FLAG, LOB_OFF_ROW)
Dm_build_1346.Dm_build_1367(bytes, NBLOB_HEAD_BLOBID, lob.blobId)
Dm_build_1346.Dm_build_1362(bytes, NBLOB_HEAD_BLOB_LEN, -1)
Dm_build_1346.Dm_build_1357(bytes, NBLOB_HEAD_OUTROW_GROUPID, lob.groupId)
Dm_build_1346.Dm_build_1357(bytes, NBLOB_HEAD_OUTROW_FILEID, lob.fileId)
Dm_build_1346.Dm_build_1362(bytes, NBLOB_HEAD_OUTROW_PAGENO, lob.pageNo)
if lob.connection.NewLobFlag {
Dm_build_1346.Dm_build_1362(bytes, NBLOB_EX_HEAD_TABLE_ID, lob.tabId)
Dm_build_1346.Dm_build_1357(bytes, NBLOB_EX_HEAD_COL_ID, lob.colId)
Dm_build_1346.Dm_build_1367(bytes, NBLOB_EX_HEAD_ROW_ID, lob.rowId)
Dm_build_1346.Dm_build_1357(bytes, NBLOB_EX_HEAD_FPA_GRPID, lob.exGroupId)
Dm_build_1346.Dm_build_1357(bytes, NBLOB_EX_HEAD_FPA_FILEID, lob.exFileId)
Dm_build_1346.Dm_build_1362(bytes, NBLOB_EX_HEAD_FPA_PAGENO, lob.exPageNo)
}
return
}
func (lob *lob) checkFreed() (err error) {
if lob.freed {
err = ECGO_LOB_FREED.throw()
}
return
}
func (lob *lob) checkValid() error {
if !lob.Valid {
return ECGO_IS_NULL.throw()
}
return nil
}
+25
View File
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
const (
ParamDataEnum_Null = 0
/**
* 只有大字段才有行内数据、行外数据的概念
*/
ParamDataEnum_OFF_ROW = 1
)
// JDBC中的Data
type lobCtl struct {
value []byte
}
// lob数据返回信息,自bug610335后,服务器不光返回字节数组,还返回字符数
type lobRetInfo struct {
charLen int64 // 字符长度
data []byte //lob数据
}
+124
View File
@@ -0,0 +1,124 @@
/*
* Copyright (c) 2000-2018, 达梦数据库有限公司.
* All rights reserved.
*/
package dm
import (
"fmt"
"os"
"strconv"
"strings"
"time"
"gitee.com/chunanyong/dm/util"
)
const (
MAX_FILE_SIZE = 100 * 1024 * 1024
FLUSH_SIZE = 32 * 1024
)
type goRun interface {
doRun()
}
type logWriter struct {
flushQueue chan []byte
date string
logFile *os.File
flushFreq int
filePath string
filePrefix string
buffer *Dm_build_0
}
func (lw *logWriter) doRun() {
defer func() {
lw.beforeExit()
lw.closeCurrentFile()
}()
i := 0
for {
var ibytes []byte
select {
case ibytes = <-lw.flushQueue:
if LogLevel != LOG_OFF {
if i == LogFlushQueueSize {
lw.doFlush(lw.buffer)
i = 0
} else {
lw.buffer.Dm_build_26(ibytes, 0, len(ibytes))
i++
}
}
case <-time.After(time.Duration(LogFlushFreq) * time.Millisecond):
if LogLevel != LOG_OFF && lw.buffer.Dm_build_5() > 0 {
lw.doFlush(lw.buffer)
i = 0
}
}
}
}
func (lw *logWriter) doFlush(buffer *Dm_build_0) {
if lw.needCreateNewFile() {
lw.closeCurrentFile()
lw.logFile = lw.createNewFile()
}
if lw.logFile != nil {
buffer.Dm_build_20(lw.logFile, buffer.Dm_build_5())
}
}
func (lw *logWriter) closeCurrentFile() {
if lw.logFile != nil {
lw.logFile.Close()
lw.logFile = nil
}
}
func (lw *logWriter) createNewFile() *os.File {
lw.date = time.Now().Format("2006-01-02")
fileName := lw.filePrefix + "_" + lw.date + "_" + strconv.Itoa(time.Now().Nanosecond()) + ".log"
lw.filePath = LogDir
if len(lw.filePath) > 0 {
if _, err := os.Stat(lw.filePath); err != nil {
os.MkdirAll(lw.filePath, 0755)
}
if _, err := os.Stat(lw.filePath + fileName); err != nil {
logFile, err := os.Create(lw.filePath + fileName)
if err != nil {
fmt.Println(err)
return nil
}
return logFile
}
}
return nil
}
func (lw *logWriter) needCreateNewFile() bool {
now := time.Now().Format("2006-01-02")
fileInfo, err := lw.logFile.Stat()
return now != lw.date || err != nil || lw.logFile == nil || fileInfo.Size() > int64(MAX_FILE_SIZE)
}
func (lw *logWriter) beforeExit() {
close(lw.flushQueue)
var ibytes []byte
for ibytes = <-lw.flushQueue; ibytes != nil; ibytes = <-lw.flushQueue {
lw.buffer.Dm_build_26(ibytes, 0, len(ibytes))
if lw.buffer.Dm_build_5() >= LogBufferSize {
lw.doFlush(lw.buffer)
}
}
if lw.buffer.Dm_build_5() > 0 {
lw.doFlush(lw.buffer)
}
}
func (lw *logWriter) WriteLine(msg string) {
var b = []byte(strings.TrimSpace(msg) + util.LINE_SEPARATOR)
lw.flushQueue <- b
}

Some files were not shown because too many files have changed in this diff Show More