package db import ( "context" "database/sql" "fmt" "os" "strings" _ "modernc.org/sqlite" ) // openSQLite 打开 SQLite 库文件;文件不存在时报错而非静默建空库。 // DSN 追加 busy_timeout / foreign_keys pragma。 func openSQLite(c ConnReq) (*sql.DB, error) { path := c.Database if path == "" { path = c.Host } if path == "" { return nil, fmt.Errorf("SQLite 需要 database 填文件路径") } dsn := path switch { case path == ":memory:": // 统一走 file: URI 以便携带 _pragma 参数 dsn = "file::memory:" case strings.HasPrefix(path, "file:"): // 调用方自带 URI 参数,不再做存在性检查 default: if _, err := os.Stat(path); err != nil { return nil, fmt.Errorf("SQLite 文件不存在: %s", path) } dsn = "file:" + path } sep := "?" if strings.Contains(dsn, "?") { sep = "&" } dsn += sep + "_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)" return sql.Open("sqlite", dsn) } func quoteSQLiteIdent(s string) string { return `"` + strings.ReplaceAll(s, `"`, `""`) + `"` } func listDatabasesSQLite(_ *sql.DB) ([]string, error) { return []string{"main"}, nil } func listTablesSQLite(ctx context.Context, db *sql.DB, _ string) ([]string, error) { rows, err := db.QueryContext(ctx, `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY 1`) if err != nil { return nil, err } defer rows.Close() out := []string{} for rows.Next() { var name string if err := rows.Scan(&name); err != nil { return nil, err } out = append(out, name) } return out, rows.Err() } func listColumnsSQLite(ctx context.Context, dbh *sql.DB, _, table string) ([]ColumnInfo, error) { rows, err := dbh.QueryContext(ctx, `PRAGMA table_info(`+quoteSQLiteIdent(table)+`)`) if err != nil { return nil, err } defer rows.Close() var out []ColumnInfo for rows.Next() { var cid int var name, typ string var notnull, pk int var def sql.NullString if err := rows.Scan(&cid, &name, &typ, ¬null, &def, &pk); err != nil { return nil, err } col := ColumnInfo{Name: name, Type: typ, Nullable: notnull == 0} if pk > 0 { col.Key = "PRI" } if def.Valid { col.Default = def.String } out = append(out, col) } if out == nil { out = []ColumnInfo{} } return out, rows.Err() }