Skip to content

Commit 36cbab6

Browse files
committed
feat(uast): add Go/Rust parsing and projections
1 parent ed484fc commit 36cbab6

13 files changed

Lines changed: 686 additions & 1 deletion

File tree

‎Cargo.lock‎

Lines changed: 60 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"crates/gitbase-git",
66
"crates/gitbase-loader",
77
"crates/gitbase-pgwire",
8+
"crates/gitbase-uast",
89
]
910
resolver = "2"
1011

@@ -19,10 +20,15 @@ async-trait = "0.1"
1920
clap = { version = "4", features = ["derive", "env"] }
2021
gix = "0.66"
2122
pgwire = "0.38"
23+
serde = { version = "1", features = ["derive"] }
24+
serde_json = "1"
2225
sha1 = "0.10"
2326
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio", "migrate", "macros"] }
2427
tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal", "net"] }
2528
tracing = "0.1"
2629
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
2730
futures = "0.3"
31+
tree-sitter = "0.24"
32+
tree-sitter-go = "0.23"
33+
tree-sitter-rust = "0.23"
2834
walkdir = "2"

‎crates/gitbase-cli/src/main.rs‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,21 @@ enum Commands {
8181
#[arg(long, env = "GITBASE_DB_MAX_CONNECTIONS", default_value_t = 5)]
8282
max_connections: u32,
8383
},
84+
85+
/// Build UAST cache and projections
86+
Uast {
87+
/// PostgreSQL connection string
88+
#[arg(long, env = "DATABASE_URL")]
89+
database_url: String,
90+
91+
/// Maximum database connections
92+
#[arg(long, env = "GITBASE_DB_MAX_CONNECTIONS", default_value_t = 5)]
93+
max_connections: u32,
94+
95+
/// Limit number of blobs to process
96+
#[arg(long, env = "GITBASE_UAST_LIMIT")]
97+
limit: Option<i64>,
98+
},
8499
}
85100

86101
#[tokio::main]
@@ -154,6 +169,29 @@ async fn main() -> Result<()> {
154169
gitbase_db::health_check(&pool).await?;
155170
tracing::info!("health check passed");
156171
}
172+
Commands::Uast {
173+
database_url,
174+
max_connections,
175+
limit,
176+
} => {
177+
let pool = gitbase_db::connect(&database_url, max_connections).await?;
178+
gitbase_db::health_check(&pool).await?;
179+
tracing::info!("health check passed");
180+
181+
let report = gitbase_loader::index_uast(
182+
&pool,
183+
&gitbase_loader::UastIndexConfig { max_candidates: limit },
184+
)
185+
.await?;
186+
187+
tracing::info!(
188+
parsed = report.parsed,
189+
skipped_cached = report.skipped_cached,
190+
skipped_missing_content = report.skipped_missing_content,
191+
skipped_unsupported_language = report.skipped_unsupported_language,
192+
"uast indexing completed"
193+
);
194+
}
157195
}
158196

159197
Ok(())

‎crates/gitbase-db/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition.workspace = true
55

66
[dependencies]
77
anyhow.workspace = true
8+
serde_json.workspace = true
89
sqlx.workspace = true
910
tokio.workspace = true
1011
tracing.workspace = true

‎crates/gitbase-db/src/blobs.rs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,12 @@ pub async fn fetch_blob_locations(
9999
})
100100
.collect())
101101
}
102+
103+
pub async fn fetch_blob_content(pool: &PgPool, hash: &str) -> Result<Option<Vec<u8>>> {
104+
let row = sqlx::query("SELECT content FROM gitbase.blobs WHERE hash = $1")
105+
.bind(hash)
106+
.fetch_optional(pool)
107+
.await?;
108+
109+
Ok(row.and_then(|row| row.get::<Option<Vec<u8>>, _>("content")))
110+
}

‎crates/gitbase-db/src/lib.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use sqlx::PgPool;
44

55
pub mod blobs;
66
pub mod metadata;
7+
pub mod uast;
78

89
/// Create a connection pool to PostgreSQL and run migrations.
910
pub async fn connect(database_url: &str, max_connections: u32) -> Result<PgPool> {

‎crates/gitbase-db/src/uast.rs‎

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use anyhow::Result;
2+
use serde_json::Value;
3+
use sqlx::{PgPool, Row};
4+
5+
#[derive(Debug, Clone)]
6+
pub struct UastCandidate {
7+
pub blob_hash: String,
8+
pub path: String,
9+
}
10+
11+
#[derive(Debug, Clone)]
12+
pub struct UastFunctionRecord {
13+
pub blob_hash: String,
14+
pub name: String,
15+
pub start_line: Option<i32>,
16+
pub end_line: Option<i32>,
17+
pub signature: Option<String>,
18+
}
19+
20+
#[derive(Debug, Clone)]
21+
pub struct UastImportRecord {
22+
pub blob_hash: String,
23+
pub source: String,
24+
pub target: Option<String>,
25+
}
26+
27+
pub async fn uast_cache_exists(pool: &PgPool, blob_hash: &str) -> Result<bool> {
28+
let exists = sqlx::query_scalar::<_, bool>(
29+
"SELECT EXISTS(SELECT 1 FROM gitbase.uast_cache WHERE blob_hash = $1)",
30+
)
31+
.bind(blob_hash)
32+
.fetch_one(pool)
33+
.await?;
34+
Ok(exists)
35+
}
36+
37+
pub async fn fetch_uast_candidates(pool: &PgPool, limit: Option<i64>) -> Result<Vec<UastCandidate>> {
38+
let base_query =
39+
"SELECT DISTINCT f.blob_hash, f.path FROM gitbase.files f\
40+
JOIN gitbase.blobs b ON b.hash = f.blob_hash\
41+
WHERE b.content IS NOT NULL";
42+
43+
let rows = if let Some(limit) = limit {
44+
sqlx::query(&format!("{base_query} LIMIT $1"))
45+
.bind(limit)
46+
.fetch_all(pool)
47+
.await?
48+
} else {
49+
sqlx::query(base_query).fetch_all(pool).await?
50+
};
51+
52+
Ok(rows
53+
.into_iter()
54+
.map(|row| UastCandidate {
55+
blob_hash: row.get("blob_hash"),
56+
path: row.get("path"),
57+
})
58+
.collect())
59+
}
60+
61+
pub async fn upsert_uast_cache(
62+
pool: &PgPool,
63+
blob_hash: &str,
64+
language: &str,
65+
uast: &Value,
66+
) -> Result<()> {
67+
sqlx::query(
68+
"INSERT INTO gitbase.uast_cache (blob_hash, language, uast, generated_at)\
69+
VALUES ($1, $2, $3, now())\
70+
ON CONFLICT (blob_hash) DO UPDATE\
71+
SET language = EXCLUDED.language,\
72+
uast = EXCLUDED.uast,\
73+
generated_at = now()",
74+
)
75+
.bind(blob_hash)
76+
.bind(language)
77+
.bind(uast)
78+
.execute(pool)
79+
.await?;
80+
81+
Ok(())
82+
}
83+
84+
pub async fn clear_uast_projections(pool: &PgPool, blob_hash: &str) -> Result<()> {
85+
sqlx::query("DELETE FROM gitbase.uast_functions WHERE blob_hash = $1")
86+
.bind(blob_hash)
87+
.execute(pool)
88+
.await?;
89+
sqlx::query("DELETE FROM gitbase.uast_imports WHERE blob_hash = $1")
90+
.bind(blob_hash)
91+
.execute(pool)
92+
.await?;
93+
Ok(())
94+
}
95+
96+
pub async fn insert_uast_function(pool: &PgPool, record: &UastFunctionRecord) -> Result<()> {
97+
sqlx::query(
98+
"INSERT INTO gitbase.uast_functions (blob_hash, name, start_line, end_line, signature)\
99+
VALUES ($1, $2, $3, $4, $5)\
100+
ON CONFLICT (blob_hash, name, start_line) DO UPDATE\
101+
SET end_line = EXCLUDED.end_line,\
102+
signature = EXCLUDED.signature",
103+
)
104+
.bind(&record.blob_hash)
105+
.bind(&record.name)
106+
.bind(record.start_line)
107+
.bind(record.end_line)
108+
.bind(&record.signature)
109+
.execute(pool)
110+
.await?;
111+
Ok(())
112+
}
113+
114+
pub async fn insert_uast_import(pool: &PgPool, record: &UastImportRecord) -> Result<()> {
115+
sqlx::query(
116+
"INSERT INTO gitbase.uast_imports (blob_hash, source, target)\
117+
VALUES ($1, $2, $3)\
118+
ON CONFLICT (blob_hash, source, target) DO NOTHING",
119+
)
120+
.bind(&record.blob_hash)
121+
.bind(&record.source)
122+
.bind(&record.target)
123+
.execute(pool)
124+
.await?;
125+
Ok(())
126+
}

‎crates/gitbase-loader/Cargo.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ edition.workspace = true
77
anyhow.workspace = true
88
gitbase-db = { path = "../gitbase-db" }
99
gitbase-git = { path = "../gitbase-git" }
10+
gitbase-uast = { path = "../gitbase-uast" }
11+
serde_json.workspace = true
1012
sqlx.workspace = true
1113
tracing.workspace = true
1214

0 commit comments

Comments
 (0)