feat(db): 添加对达梦数据库的支持
- 在应用程序中新增对达梦数据库(DM)的配置和连接支持 - 实现 SetDmDB 函数以配置达梦数据库连接 - 更新数据库操作逻辑,支持达梦特有的 SQL 语法和功能 - 在相关文件中添加达梦数据库的处理逻辑,包括表创建、数据插入和查询 - 更新 go.mod 和 go.sum 文件以引入达梦数据库驱动 - 增强文档,详细说明达梦数据库的配置和使用方法
This commit is contained in:
@@ -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.go:MySQL DDL 建表逻辑(与 setup_dm.go 对应)
|
||||
status: completed
|
||||
- id: extract-seed
|
||||
content: 提取通用种子数据函数 seedTestData,DM 和 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 路径问题 + 完善双数据库测试数据
|
||||
|
||||
## 问题 1:Swagger 和 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 中实现 DMDialect(Quote/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.go(MySQL 特有 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.go(TestMain+TestApi),集成测试框架
|
||||
status: pending
|
||||
- id: example-config
|
||||
content: 修改 example/config/config.json,mysql 改为 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 一致
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
||||
Vendored
+54
-3
@@ -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{
|
||||
|
||||
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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) }},
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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) }},
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
. "code.hoteas.com/golang/hotime"
|
||||
)
|
||||
|
||||
var Project = Proj{
|
||||
"test": TestCtr,
|
||||
"mysql": MysqlCtr,
|
||||
"dmdb": DmdbCtr,
|
||||
"cache": CacheCtr,
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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) }},
|
||||
}
|
||||
@@ -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_TABLES(SYSDBA 自身 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")`)
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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"))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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());
|
||||
@@ -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
@@ -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">← 选择一个接口开始</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">▶</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">▶</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':'')+'">▶</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()">×</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()">×</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()">×</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()">×</button></div>';
|
||||
qDef+='<div class="kv"><input placeholder="key"><input placeholder="value"><button onclick="this.parentElement.remove()">×</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()">×</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':'')+'">▶</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()">×</button></div></div></div>';
|
||||
l+='<div class="bd-hd'+(defBody==='json'?' on':'')+'" data-t="json" onclick="togBody(\'json\')"><span class="ar'+(defBody==='json'?' o':'')+'">▶</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()">×</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()">×</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()">×</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()">×</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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
||||
</script></body></html>
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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{
|
||||
"注释": "可配置memory,db,redis,默认启用memory,默认优先级为memory>redis>db,memory与数据库缓存设置项一致,缓存数据填充会自动反方向反哺,加入memory缓存过期将自动从redis更新,但memory永远不会更新redis,如果是集群建议不要开启memory,配置即启用",
|
||||
|
||||
+43
@@ -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
@@ -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
@@ -0,0 +1,3 @@
|
||||
#8.1.4.170
|
||||
#2025.11.21
|
||||
#43114
|
||||
+863
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+8
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+1455
File diff suppressed because it is too large
Load Diff
+494
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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(¤tRows.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
File diff suppressed because it is too large
Load Diff
+38
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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. loginMode,loginStatus匹配
|
||||
* 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+137
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user