03918b1e58
Проверено по шагам мастера, что bj берёт из DEPO сам: подготовка базы, префикс счетов (шаг 3), реквизиты (шаг 4), готовность (шаг 10), ручной ввод, подбор клиента для портфеля ЛК. - Реквизиты (Det01): счёт НРД ссылается на исходную версию лица, а ИНН НРД бывает только в новых (в исходной — ИНН НДЦ) — лицо по любой версии (ID или SELF_ID; ИНН НРД или MNEM «НДЦ»); номер счёта — и «счёт/раздел», не только «счёт/LEI/код/раздел». Счёт НРД — тот, с которым записан выбранный раздел (пара «раздел + счёт» — по ней процедуры находят место хранения), а не самый частый. Для MSSQL — только шире: прежнее находится. - Поиск по ФИО, договору и счёту — без учёта регистра, как в MSSQL (Cyrillic_General_CI_AS): в базе Fancy с кодировкой NONE регистр кириллицы различается — Firebird-варианты сравнивают в WIN1251 (подбор клиента для портфеля ЛК — запросы вынесены в fsql/lkportfolio.go, поиск и список договоров). - На копии базы: шаг 4 — счёт, код, разделы, ИНН и наименования организации; поиск «бр-1147», «бубликов» находит; FBLivePrepare — 103 из 103. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
144 lines
4.8 KiB
Go
144 lines
4.8 KiB
Go
package lkgateway
|
|
|
|
// Живая проверка перевода запросов на Firebird: каждый запрос bj к DEPO (fsql) готовится (PREPARE, без
|
|
// выполнения) на настоящей базе Fancy. Запуск — с подключением к копии базы:
|
|
// BJ_FB_TEST_DSN='firebird://SYSDBA:пароль@127.0.0.1:3050/путь_к_базе?charset=WIN1251' go test -run FBLive ./internal/lkgateway
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway/fsql"
|
|
)
|
|
|
|
func readRepoFile(p string) ([]byte, error) { return os.ReadFile(filepath.Join("..", "..", p)) }
|
|
|
|
// fbLiveTodo — запросы, которые на Firebird пока не готовятся (пусто — все).
|
|
var fbLiveTodo = map[string]bool{}
|
|
|
|
var fbLiveFmtRe = regexp.MustCompile(`%(\[\d\])?s`)
|
|
|
|
// fbLiveSkip — не к DEPO (PostgreSQL bj и экспорт QUIK, база МНП QUIK, SPECTRE) или куски составных запросов.
|
|
var fbLiveSkip = map[string]bool{
|
|
"Doc02": true, "Doc03": true, "Doc04": true, "Sn01": true, "Sn02": true, "Sn03": true, "Sn04": true, "Sn05": true,
|
|
"On01": true, "On02": true, "On03": true, "Pr01": true, "Qk01": true, "Cl01": true, "Tbl01": true, "Tbl02": true, "QTb01": true,
|
|
"Ev05": true, // DEPONENT_ID — пишется, только если колонка есть (depoHasColumn)
|
|
"Que04": true, "Que05": true,
|
|
"Ev06": true, "Ev07": true, "Ev08": true, "Ev09": true, "Que01": true, "Que02": true, "Que03": true, "Flow01": true, "Flow02": true,
|
|
}
|
|
|
|
// fbLiveQueries — строки-запросы пакета fsql по именам; шаблоны заполнены так, как их заполняет код.
|
|
func fbLiveQueries(t *testing.T) map[string]string {
|
|
fset := token.NewFileSet()
|
|
pkgs, err := parser.ParseDir(fset, "fsql", nil, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out := map[string]string{}
|
|
for _, p := range pkgs {
|
|
for _, f := range p.Files {
|
|
for _, d := range f.Decls {
|
|
g, ok := d.(*ast.GenDecl)
|
|
if !ok || g.Tok != token.VAR {
|
|
continue
|
|
}
|
|
for _, s := range g.Specs {
|
|
vs := s.(*ast.ValueSpec)
|
|
for i, n := range vs.Names {
|
|
if i >= len(vs.Values) {
|
|
continue
|
|
}
|
|
lit, ok := vs.Values[i].(*ast.BasicLit)
|
|
if !ok || lit.Kind != token.STRING || fbLiveSkip[n.Name] {
|
|
continue
|
|
}
|
|
q, err := strconv.Unquote(lit.Value)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if t, ok := fsql.FB[q]; ok { // шаблон: Firebird-вариант выбирается до подстановки (depoSQL)
|
|
q = t
|
|
}
|
|
q = strings.ReplaceAll(q, "{ACC}", "ВТНКЦ")
|
|
q = strings.ReplaceAll(q, "%d", "10")
|
|
q = fbLiveFmtRe.ReplaceAllString(q, "1")
|
|
out[n.Name] = q
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
doc := "CAST(m.DOC AS varchar(32))"
|
|
out["Ev07+"] = fsql.Ev07 + doc + fsql.Ev08 + doc + fsql.Ev09 + doc + `)
|
|
FROM dbo.FTV_M2M m WHERE m.TimeNew > DATEADD(day,-45,GETDATE()) ORDER BY m.ID DESC`
|
|
out["Que01+"] = fsql.Que01 + "m.TimeNew" + `, ISNULL(` + doc + fsql.Que02 + doc + fsql.Que03 + doc + `)
|
|
FROM dbo.FTV_M2M m ORDER BY m.TimeNew DESC`
|
|
out["Flow01+"] = fsql.Flow01 + doc + fsql.Flow02
|
|
out["Que04+"] = fsql.Que04 + "TimeNew DESC)"
|
|
out["Que05+"] = fsql.Que05 + "TimeNew DESC) ORDER BY e.EVENT_DATE"
|
|
out["Ev06+"] = fsql.Ev06 + "GUID, REFERENCE, ISIN, SEC_CODE, AMOUNT, PLACE_NUMBER, LINK_NUMBER, SHARE, DEPO_NUMBER, TimeNew) VALUES (@g, @r, @i, @c, @a, @p, @lnk, @sh, @acc, GETDATE())"
|
|
return out
|
|
}
|
|
|
|
func TestFBLivePrepare(t *testing.T) {
|
|
dsn := os.Getenv("BJ_FB_TEST_DSN")
|
|
if dsn == "" {
|
|
t.Skip("BJ_FB_TEST_DSN не задан")
|
|
}
|
|
probe, err := sql.Open("firebirdsql", dsn)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
base, err := probe.Driver().(driver.DriverContext).OpenConnector(dsn)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
db := sql.OpenDB(fbConnector{base: base})
|
|
defer db.Close()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
defer cancel()
|
|
qs := fbLiveQueries(t)
|
|
names := make([]string, 0, len(qs))
|
|
for n := range qs {
|
|
names = append(names, n)
|
|
}
|
|
sort.Strings(names)
|
|
var bad []string
|
|
todo := 0
|
|
for _, n := range names {
|
|
st, err := db.PrepareContext(ctx, qs[n])
|
|
if err != nil {
|
|
if fbLiveTodo[n] {
|
|
todo++
|
|
t.Logf("(следующий этап) %s: %v", n, strings.ReplaceAll(err.Error(), "\n", " "))
|
|
continue
|
|
}
|
|
bad = append(bad, fmt.Sprintf("%s: %v\n %s", n, err, depoStmtHead(fbSQL(qs[n]))))
|
|
continue
|
|
}
|
|
st.Close()
|
|
}
|
|
t.Logf("подготовлено %d из %d запросов, отложено %d", len(names)-len(bad)-todo, len(names), todo)
|
|
for _, b := range bad {
|
|
t.Log(b)
|
|
}
|
|
if len(bad) > 0 {
|
|
t.Fail()
|
|
}
|
|
}
|
|
|
|
var _ driver.Connector = fbConnector{}
|