package lkgateway // Поиск базы DEPO на сервере (05.10.2026). Клиент не знает и не должен знать, как называется база учёта у // него на сервере: на шаге «Подключение» он вводит только адрес, логин и пароль, а bj сам находит базы с // учётными таблицами Fancy DEPO (лица OD_FACES и остатки OD_RESTS), к которым у этого логина есть доступ. // Одна база — выбирается сразу, несколько — мастер предлагает выбрать, ни одной — понятная ошибка. import ( "context" "fmt" "strings" "time" ) // depoMarkerTables — по каким таблицам база опознаётся как DEPO (Fancy). var depoMarkerTables = []string{"OD_FACES", "OD_RESTS"} // depoDiscoverMax — сколько баз сервера просматривать (на сервере могут быть десятки баз). const depoDiscoverMax = 60 // depoDiscover — базы DEPO на сервере, доступные этому логину. func depoDiscover(ctx context.Context, s FancyMSSQLSettings) ([]string, error) { cctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() if s.IsPostgres() { return depoDiscoverPG(cctx, s) } conn, err := fancyOpen(s, "") if err != nil { return nil, err } defer conn.Close() rows, err := conn.QueryContext(cctx, `SELECT name FROM sys.databases WHERE state = 0 AND database_id > 4 AND HAS_DBACCESS(name) = 1 ORDER BY name`) if err != nil { return nil, fmt.Errorf("список баз сервера: %w", err) } var names []string for rows.Next() { var n string if rows.Scan(&n) == nil && validDBName(n) && len(names) < depoDiscoverMax { names = append(names, n) } } rows.Close() var found []string for _, n := range names { // имя проверено validDBName — подставлять в [ ] безопасно q := "SELECT CASE WHEN " + strings.Join(func() []string { var c []string for _, t := range depoMarkerTables { c = append(c, "OBJECT_ID(N'["+n+"].dbo."+t+"') IS NOT NULL") } return c }(), " AND ") + " THEN 1 ELSE 0 END" var ok int if conn.QueryRowContext(cctx, q).Scan(&ok) == nil && ok == 1 { found = append(found, n) } } return found, nil } func depoDiscoverPG(ctx context.Context, s FancyMSSQLSettings) ([]string, error) { conn, err := fancyOpen(s, "") if err != nil { return nil, err } defer conn.Close() rows, err := conn.QueryContext(ctx, `SELECT datname FROM pg_database WHERE datallowconn AND NOT datistemplate AND has_database_privilege(datname, 'CONNECT') ORDER BY datname`) if err != nil { return nil, fmt.Errorf("список баз сервера: %w", err) } var names []string for rows.Next() { var n string if rows.Scan(&n) == nil && validDBName(n) && n != "postgres" && len(names) < depoDiscoverMax { names = append(names, n) } } rows.Close() var found []string for _, n := range names { db, err := fancyOpen(s, n) if err != nil { continue } var cnt int err = db.QueryRowContext(ctx, `SELECT count(DISTINCT lower(table_name)) FROM information_schema.tables WHERE lower(table_name) = ANY($1::text[])`, "{"+strings.ToLower(strings.Join(depoMarkerTables, ","))+"}").Scan(&cnt) db.Close() if err == nil && cnt == len(depoMarkerTables) { found = append(found, n) } } return found, nil }