mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-12 09:34:05 +08:00
- SQL、CSV、JSON 与 XLSX 改为资源受限的流式解析,并支持压缩、编码和方言预检 - 增加遇错停止/继续策略、持久任务、错误行导出、源文件身份与安全取消 - 为数据库驱动补齐上下文事务和写入结果未知语义,避免失败重放与脏会话复用
95 lines
2.6 KiB
Go
95 lines
2.6 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
"errors"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"GoNavi-Wails/internal/connection"
|
|
)
|
|
|
|
const postgresApplyChangesContextDriverName = "postgres_apply_changes_context"
|
|
|
|
var (
|
|
registerPostgresApplyChangesContextDriverOnce sync.Once
|
|
postgresApplyChangesContextExecStarted = make(chan struct{}, 1)
|
|
)
|
|
|
|
type postgresApplyChangesContextDriver struct{}
|
|
|
|
type postgresApplyChangesContextConn struct{}
|
|
|
|
type postgresApplyChangesContextTx struct{}
|
|
|
|
func (postgresApplyChangesContextDriver) Open(string) (driver.Conn, error) {
|
|
return postgresApplyChangesContextConn{}, nil
|
|
}
|
|
|
|
func (postgresApplyChangesContextConn) Prepare(string) (driver.Stmt, error) {
|
|
return nil, driver.ErrSkip
|
|
}
|
|
|
|
func (postgresApplyChangesContextConn) Close() error { return nil }
|
|
|
|
func (postgresApplyChangesContextConn) Begin() (driver.Tx, error) {
|
|
return nil, errors.New("legacy Begin must not be used")
|
|
}
|
|
|
|
func (postgresApplyChangesContextConn) BeginTx(context.Context, driver.TxOptions) (driver.Tx, error) {
|
|
return postgresApplyChangesContextTx{}, nil
|
|
}
|
|
|
|
func (postgresApplyChangesContextConn) ExecContext(ctx context.Context, _ string, _ []driver.NamedValue) (driver.Result, error) {
|
|
select {
|
|
case postgresApplyChangesContextExecStarted <- struct{}{}:
|
|
default:
|
|
}
|
|
<-ctx.Done()
|
|
return nil, ctx.Err()
|
|
}
|
|
|
|
func (postgresApplyChangesContextTx) Commit() error { return nil }
|
|
func (postgresApplyChangesContextTx) Rollback() error { return nil }
|
|
|
|
func TestPostgresApplyChangesContextCancelsInFlightStatement(t *testing.T) {
|
|
registerPostgresApplyChangesContextDriverOnce.Do(func() {
|
|
sql.Register(postgresApplyChangesContextDriverName, postgresApplyChangesContextDriver{})
|
|
})
|
|
|
|
conn, err := sql.Open(postgresApplyChangesContextDriverName, "")
|
|
if err != nil {
|
|
t.Fatalf("open context-aware test database: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = conn.Close() })
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
errCh := make(chan error, 1)
|
|
go func() {
|
|
errCh <- (&PostgresDB{conn: conn}).ApplyChangesContext(ctx, "public.orders", connection.ChangeSet{
|
|
Deletes: []map[string]interface{}{{"id": 42}},
|
|
})
|
|
}()
|
|
|
|
select {
|
|
case <-postgresApplyChangesContextExecStarted:
|
|
cancel()
|
|
case <-time.After(time.Second):
|
|
cancel()
|
|
t.Fatal("ApplyChangesContext did not reach the context-aware SQL execution path")
|
|
}
|
|
|
|
select {
|
|
case err := <-errCh:
|
|
if err == nil || !strings.Contains(err.Error(), context.Canceled.Error()) {
|
|
t.Fatalf("ApplyChangesContext error = %v, want context cancellation", err)
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatal("ApplyChangesContext did not return after cancellation")
|
|
}
|
|
}
|