feat(go-analyzer): add SQLite writer, complete analyzer pipeline
Add sqlite.go with DBWriter that writes analysis results (modules, features, tests, dependencies, library mappings) to the porting database. Successfully analyzes nats-server: 12 modules, 3673 features, 3257 tests, 36 library mappings, 11 dependencies.
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
module github.com/natsnet/go-analyzer
|
||||
|
||||
go 1.25.5
|
||||
|
||||
require github.com/mattn/go-sqlite3 v1.14.34
|
||||
|
||||
2
tools/go-analyzer/go.sum
Normal file
2
tools/go-analyzer/go.sum
Normal file
@@ -0,0 +1,2 @@
|
||||
github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk=
|
||||
github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
154
tools/go-analyzer/sqlite.go
Normal file
154
tools/go-analyzer/sqlite.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
// OpenDB opens or creates the SQLite database and applies the schema.
|
||||
func OpenDB(dbPath, schemaPath string) (*sql.DB, error) {
|
||||
db, err := sql.Open("sqlite3", dbPath+"?_journal_mode=WAL&_foreign_keys=ON")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("opening database: %w", err)
|
||||
}
|
||||
|
||||
schema, err := os.ReadFile(schemaPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading schema: %w", err)
|
||||
}
|
||||
|
||||
if _, err := db.Exec(string(schema)); err != nil {
|
||||
return nil, fmt.Errorf("applying schema: %w", err)
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// DBWriter writes analysis results to the SQLite database.
|
||||
type DBWriter struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewDBWriter creates a new DBWriter.
|
||||
func NewDBWriter(db *sql.DB) *DBWriter {
|
||||
return &DBWriter{db: db}
|
||||
}
|
||||
|
||||
// WriteAll writes all analysis results to the database in a single transaction.
|
||||
func (w *DBWriter) WriteAll(result *AnalysisResult) error {
|
||||
tx, err := w.db.Begin()
|
||||
if err != nil {
|
||||
return fmt.Errorf("beginning transaction: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
moduleIDs := make(map[string]int64)
|
||||
featureIDs := make(map[string]int64)
|
||||
|
||||
for _, mod := range result.Modules {
|
||||
modID, err := w.insertModule(tx, &mod)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting module %s: %w", mod.Name, err)
|
||||
}
|
||||
moduleIDs[mod.Name] = modID
|
||||
|
||||
for _, feat := range mod.Features {
|
||||
featID, err := w.insertFeature(tx, modID, &feat)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting feature %s: %w", feat.Name, err)
|
||||
}
|
||||
featureIDs[mod.Name+":"+feat.Name] = featID
|
||||
}
|
||||
|
||||
for _, test := range mod.Tests {
|
||||
var featureID *int64
|
||||
if test.FeatureName != "" {
|
||||
if fid, ok := featureIDs[mod.Name+":"+test.FeatureName]; ok {
|
||||
featureID = &fid
|
||||
}
|
||||
}
|
||||
if err := w.insertTest(tx, modID, featureID, &test); err != nil {
|
||||
return fmt.Errorf("inserting test %s: %w", test.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, dep := range result.Dependencies {
|
||||
sourceID, ok := moduleIDs[dep.SourceModule]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
targetID, ok := moduleIDs[dep.TargetModule]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if err := w.insertDependency(tx, "module", sourceID, "module", targetID, dep.DependencyKind); err != nil {
|
||||
return fmt.Errorf("inserting dependency %s->%s: %w", dep.SourceModule, dep.TargetModule, err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, imp := range result.Imports {
|
||||
if imp.IsStdlib {
|
||||
continue
|
||||
}
|
||||
if err := w.insertLibrary(tx, &imp); err != nil {
|
||||
return fmt.Errorf("inserting library %s: %w", imp.ImportPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (w *DBWriter) insertModule(tx *sql.Tx, mod *Module) (int64, error) {
|
||||
res, err := tx.Exec(
|
||||
`INSERT INTO modules (name, description, go_package, go_file, go_line_count, status)
|
||||
VALUES (?, ?, ?, ?, ?, 'not_started')`,
|
||||
mod.Name, mod.Description, mod.GoPackage, mod.GoFile, mod.GoLineCount,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.LastInsertId()
|
||||
}
|
||||
|
||||
func (w *DBWriter) insertFeature(tx *sql.Tx, moduleID int64, feat *Feature) (int64, error) {
|
||||
res, err := tx.Exec(
|
||||
`INSERT INTO features (module_id, name, description, go_file, go_class, go_method, go_line_number, go_line_count, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'not_started')`,
|
||||
moduleID, feat.Name, feat.Description, feat.GoFile, feat.GoClass, feat.GoMethod, feat.GoLineNumber, feat.GoLineCount,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.LastInsertId()
|
||||
}
|
||||
|
||||
func (w *DBWriter) insertTest(tx *sql.Tx, moduleID int64, featureID *int64, test *TestFunc) error {
|
||||
_, err := tx.Exec(
|
||||
`INSERT INTO unit_tests (module_id, feature_id, name, description, go_file, go_class, go_method, go_line_number, go_line_count, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'not_started')`,
|
||||
moduleID, featureID, test.Name, test.Description, test.GoFile, test.GoClass, test.GoMethod, test.GoLineNumber, test.GoLineCount,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *DBWriter) insertDependency(tx *sql.Tx, srcType string, srcID int64, tgtType string, tgtID int64, kind string) error {
|
||||
_, err := tx.Exec(
|
||||
`INSERT OR IGNORE INTO dependencies (source_type, source_id, target_type, target_id, dependency_kind)
|
||||
VALUES (?, ?, ?, ?, ?)`,
|
||||
srcType, srcID, tgtType, tgtID, kind,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *DBWriter) insertLibrary(tx *sql.Tx, imp *ImportInfo) error {
|
||||
_, err := tx.Exec(
|
||||
`INSERT OR IGNORE INTO library_mappings (go_import_path, go_library_name, status)
|
||||
VALUES (?, ?, 'not_mapped')`,
|
||||
imp.ImportPath, imp.ImportPath,
|
||||
)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user