Files
Bridge-and-Join-s/internal/lkgateway/fancyfb_live_test.go
T
fontvielle 312e4f70f1 ЛК на Fancy с Firebird: все запросы к DEPO, курсы, база SPECTRE
ЛК (bj-lk берёт данные у bj-server) сам работает с той СУБД, что выбрана для DEPO.
- Перевод на Firebird: YEAR/MONTH/DAY(x) — EXTRACT, AS float — AS DOUBLE PRECISION (float T-SQL — 8 байт,
  FLOAT Firebird — 4: точность сумм и цен в ЛК); шаблоны со сложением строк (история операций Hist02, поиск
  клиента Man01) — Firebird-тексты до подстановки (depoSQL); UPPER по кириллице — в WIN1251 (база NONE).
- Курсы валют (Fx01): на Firebird запрос от всех курсов шёл больше 200 с (лимит 15 с — курсов нет) — для
  Firebird от 20 валют, 0,2 с.
- База SPECTRE (архив портфеля и операций ЛК): поле в «Трансферные таблицы DEPO»; у Firebird — путь или строка
  подключения Fancy (может быть на другом компьютере); не задана — ЛК пишет, где указать. На данных SPECTRE
  не проверено: базы нет.
- Тест FBLiveLK: функции ЛК, ручного ввода, сверки переводов и QUIK на копии базы по договору К1 Бубликова —
  все работают, 0,07–0,26 с каждая; FBLivePrepare — 101 из 101 запроса.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
2026-10-27 20:00:28 +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{}