50e420f4a0
Мастер, шаг «Учёт DEPO», и «Обмен с DEPO» в настройках: СУБД «Firebird» (Fancy на Firebird 2.5). - Подключение: драйвер nakagami/firebirdsql, путь к файлу базы или псевдоним, WIN1251. Запросы bj к DEPO (T-SQL) подключение переводит само (fancyfb.go): механически (dbo., TOP, ISNULL, GETDATE, LEN, N, exec) вне строк и комментариев, точные тексты — fsql/fb.go (каталоги, LIKE [_], COUNT(*) OVER, SELECT без FROM, PATINDEX); @имена — позиционные ?, GUID — в верхнем регистре. Системные запросы из кода — в fsql/depocatalog.go. - «Подготовить» логином SYSDBA (depo_setup_fb.go): проверка базы и UDF Fancy, логин SYSDBA_BJ (Fancy даёт права на документы логинам SYSDBA%; для Firebird — обычный пользователь), оператор-робот Fancy, кодировщик M2M, привязки FTV_M2M_SETTINGS по признакам Fancy, таблицы, процедуры, права; обработку запускает bj. «Получить скрипт» — то же для isql в WIN1251; письмо администратору, установка процедур, плашка обновления. - Скрипты Firebird встроены (depoprocs/firebird, тест сверяет с deploy/); количества — DOUBLE PRECISION: из диалекта 3 в базу диалекта 1 процедуру с NUMERIC(18,7) не создать. - Проверено на копии базы Fancy (отдельный экземпляр bj): мастер с нуля — все проверки, BJ_PING; тестовый режим DEPO — К1 Бубликов, зачисление и списание до исполнения без ошибок; очередь, справочник НРД. Тест FBLive готовит на базе все запросы bj к DEPO: 94 из 101, оставшиеся 7 — ЛК и ручной ввод (следующий этап). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
138 lines
4.8 KiB
Go
138 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"
|
|
"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 — следующий этап (YEAR(), %, сложение строк).
|
|
var fbLiveTodo = map[string]bool{"Arch02": true, "Arch03": true, "Arch07": true, "Arch08": true, "Arch10": true, "Hist02": true, "Man01": true}
|
|
|
|
// 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
|
|
}
|
|
q = strings.ReplaceAll(q, "{ACC}", "ВТНКЦ")
|
|
q = strings.ReplaceAll(q, "%d", "10")
|
|
q = strings.ReplaceAll(q, "%s", "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{}
|