Skip to content
代码片段 群组 项目
提交 a6d498cd 编辑于 作者: 张钧's avatar 张钧
浏览文件

Merge branch 'dev' into 'master'

优化执行流程,新增InsertOne功能

See merge request !1
No related branches found
No related tags found
无相关合并请求
# pgxtool
用于简化PostgreSQL数据上传(CopyFrom)与数据下载(Select)的工具,可以根据Golang结构体Tag中的`db`字段解析列名,生成相应的SQL等。
用于简化 PostgreSQL 数据上传(CopyFrom/InsertOne)与数据下载(Select)的工具,可以根据 Golang 结构体 Tag 中的`db`字段解析列名,生成相应的 SQL 等。
## 接口
- `New(values ...any) *PGXTool`: 构建实例,可传入多个指向结构体的**指针**作为表结构Schema,pgxtool会按顺序解析所有含有`db`字段的变量,转换为平铺的SQL表。
- `tool.ColumnNames`: 用于CopyFrom操作的列名。
- `tool.BuildSelectSQL(...)`: 生成SELECT语句。
- `tool.BuildCreateTableSQL(...)`: 生成CREATE TABLE语句。
- `tool.Scan(rows pgx.Rows, values ...any)`: 从SELECT得到的`rows`中解析一行数据到`values`中,要求`values`必须为指向结构体的指针,且类型与构建时一致。
- `tool.CopyRow(values ...any)`: 生成用于CopyFrom的数据行,要求`values`必须为指向结构体的指针,且类型与构建时一致。
- `New(values ...any) *PGXTool`: 构建实例,可传入多个指向结构体的**指针**作为表结构 Schema,pgxtool 会按顺序解析所有含有`db`字段的变量,转换为平铺的 SQL 表。
- `tool.ColumnNames`: 用于 CopyFrom 操作的列名。
- `tool.BuildSelectSQL(...)`: 生成 SELECT 语句。
- `tool.BuildCreateTableSQL(...)`: 生成 CREATE TABLE 语句。
- `tool.BuildInsertSQL(...)`: 生成 INSERT INTO 语句。
- `tool.Scan(rows pgx.Rows, values ...any)`: 从 SELECT 得到的`rows`中解析一行数据到`values`中,要求`values`必须为指向结构体的指针,且类型与构建时一致。
- `tool.Flatten(values ...any)`: 利用预生成信息展平转入的数据以生成可用于 CopyFrom 或 InsertOne 的数据行,要求`values`必须为指向结构体的指针,且类型与构建时一致。
- `tool.InsertOne(...)`: 利用传入的信息向数据库中插入一行数据并返回插入结果。
- `tool.CopyFrom(...)`: 利用传入信息构建用于执行 CopyFrom 的实例,该实例通过`Add(...)`添加数据,通过`Done(...)`提交结果并返回。
## 安装方式
1. 在本机Git上添加登录方式:
1. 在本机 Git 上添加登录方式:
```bash
git config --global url."https://${user}:${personal_access_token}@git.tsingroc.com".insteadOf "https://git.tsingroc.com"
# 其中`${user}`替换为Gitlab用户名,`${personal_access_token}`替换为个人访问令牌(具备read_repository权限即可)
```
2. 设置环境变量`GOPRIVATE=git.tsingroc.com`(开发容器中已设置)
3. `go get git.tsingroc.com/utils/pgxtool`
......
package pgxtool
import (
"context"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgxpool"
)
type CopyTool struct {
parent *PGXTool
rows [][]any
pool *pgxpool.Pool
tableName string
}
// 添加一条数据
func (c *CopyTool) Add(values ...any) {
c.rows = append(c.rows, c.parent.Flatten(values...))
}
// 上传数据
func (c *CopyTool) Done(ctx context.Context) (int64, error) {
return c.pool.CopyFrom(
ctx, pgx.Identifier{c.tableName},
c.parent.ColumnNames,
pgx.CopyFromRows(c.rows),
)
}
......@@ -45,7 +45,7 @@ func TestCopyRowOk(t *testing.T) {
Number float64 `db:"number"`
}{Id: 123, Number: 1.0},
}
assert.ElementsMatch(pgxtool.New(values...).CopyRow(values...), []interface{}{10, 123, 1.0})
assert.ElementsMatch(pgxtool.New(values...).Flatten(values...), []interface{}{10, 123, 1.0})
}
func TestCopyRowNull(t *testing.T) {
......@@ -57,7 +57,7 @@ func TestCopyRowNull(t *testing.T) {
Number []float64 `db:"number"`
More []string `db:"more"`
}{Id: nil, Number: []float64{1, 2}, More: make([]string, 0)}}
listA := pgxtool.New(values...).CopyRow(values...)
listA := pgxtool.New(values...).Flatten(values...)
assert.Nil(listA[0])
assert.Nil(listA[1])
assert.ElementsMatch(listA[2], []float64{1, 2})
......
......@@ -12,6 +12,7 @@ require (
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.11.0 // indirect
github.com/jackc/pgx/v4 v4.16.1 // indirect
github.com/jackc/puddle v1.2.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/samber/lo v1.21.0 // indirect
github.com/stretchr/testify v1.7.2 // indirect
......
package pgxtool
import (
"context"
"fmt"
"reflect"
"strings"
"github.com/jackc/pgconn"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/samber/lo"
)
......@@ -14,6 +17,7 @@ type PGXTool struct {
selectSQL string // SELECT SQL(不含FROM WHERE)
createTableSQL string // CREATE TABLE SQL(表名用%s代替)
insertSQL string // INSERT INTO SQL(表名用%s代替)
types []reflect.Type // 用于检查输入的value是否类型正确
indice [][]int // 记录存在db tag的field编号
}
......@@ -92,6 +96,15 @@ func New(values ...any) *PGXTool {
})
result.selectSQL = fmt.Sprintf("SELECT %s", strings.Join(tags, ","))
result.createTableSQL = fmt.Sprintf("CREATE TABLE %s(%s)", "%s", strings.Join(createColumns, ","))
placeholders := make([]string, len(tags))
for i := 0; i < len(tags); i++ {
placeholders[i] = fmt.Sprintf("$%d", i+1)
}
result.insertSQL = fmt.Sprintf("INSERT INTO %s(%s) VALUES (%s)",
"%s",
strings.Join(tags, ","),
strings.Join(placeholders, ","),
)
return result
}
......@@ -110,6 +123,11 @@ func (p *PGXTool) BuildCreateTableSQL(tableName string) string {
return fmt.Sprintf(p.createTableSQL, tableName)
}
func (p *PGXTool) BuildInsertSQL(tableName string) string {
tableName = beautifyTableName(tableName)
return fmt.Sprintf(p.insertSQL, tableName)
}
func (p *PGXTool) Scan(rows pgx.Rows, values ...any) error {
dest := make([]interface{}, 0)
if len(values) != len(p.types) {
......@@ -128,16 +146,16 @@ func (p *PGXTool) Scan(rows pgx.Rows, values ...any) error {
return rows.Scan(dest...)
}
func (p *PGXTool) CopyRow(values ...any) []any {
func (p *PGXTool) Flatten(values ...any) []any {
row := make([]any, 0)
if len(values) != len(p.types) {
panic(fmt.Sprintf("CopyRow: incorrect number of values: expect %d but got %d", len(p.types), len(values)))
panic(fmt.Sprintf("Flatten: incorrect number of values: expect %d but got %d", len(p.types), len(values)))
}
for ind, value := range values {
t := getElemType(value)
v := getElemValue(value)
if t != p.types[ind] {
panic(fmt.Sprintf("CopyRow: unmatched type: expect %v but got %v", p.types[ind], t))
panic(fmt.Sprintf("Flatten: unmatched type: expect %v but got %v", p.types[ind], t))
}
for _, i := range p.indice[ind] {
row = append(row, v.Field(i).Interface())
......@@ -145,3 +163,18 @@ func (p *PGXTool) CopyRow(values ...any) []any {
}
return row
}
func (p *PGXTool) InsertOne(
pool *pgxpool.Pool, ctx context.Context, tableName string, values ...any,
) (pgconn.CommandTag, error) {
return pool.Exec(ctx, p.BuildInsertSQL(tableName), p.Flatten(values...)...)
}
func (p *PGXTool) CopyFrom(pool *pgxpool.Pool, tableName string) *CopyTool {
return &CopyTool{
parent: p,
rows: make([][]any, 0),
pool: pool,
tableName: tableName,
}
}
......@@ -134,3 +134,21 @@ func TestBuildCreateTableSQLOk(t *testing.T) {
F8 float64 `db:"f8"`
}{}).BuildCreateTableSQL("%s"), "CREATE TABLE %s(F4 FLOAT4[],S TEXT,B BOOL NOT NULL,F8 FLOAT8 NOT NULL)")
}
func TestBuildInsertSQLOk(t *testing.T) {
assert := assert.New(t)
assert.Equal(pgxtool.New(&struct {
F4 []float32 `db:"f4"`
S *string `db:"s"`
B bool `db:"b"`
Ignore string
}{}, &struct {
F8 float64 `db:"f8"`
}{}).BuildInsertSQL("%s"), "INSERT INTO %s(F4,S,B,F8) VALUES ($1,$2,$3,$4)")
assert.Equal(pgxtool.New(&struct {
F4 []float32 `db:"f4"`
Ignore string
}{}).BuildInsertSQL("Test"), "INSERT INTO TEST(F4) VALUES ($1)")
}
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册