Files
Bridge-and-Join-s/internal/lkgateway/fancyfb_live_test.go
T
fontvielle 03918b1e58 Firebird: автоопределение из DEPO как на MSSQL — реквизиты организации и поиск без учёта регистра
Проверено по шагам мастера, что 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
2026-10-27 20:09:59 +03:00

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{}