Skip to content
This repository was archived by the owner on Jun 16, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
## 2026-04-08 - [Performance: Defer Allocation during Traversal]
**Learning:** During DAG traversals, creating owned variants of identifiers (like `file.to_path_buf()`) *before* checking `visited` HashSets results in heap allocations (O(E)) for every edge instead of every visited node (O(V)). By moving the `&PathBuf` allocation strictly *after* all HashSet `contains` checks using the borrowed reference (`&Path`), we drastically reduce memory churn.
**Action:** Always check `HashSet::contains` with a borrowed reference *before* creating the owned version required by `HashSet::insert`, especially in performance-critical graph traversal paths.
## 2026-04-10 - [Performance: Dynamic SQL Generation]
**Learning:** For dynamic SQL generation (e.g., in `crates/flow/src/targets/d1.rs` for Cloudflare D1 targets), using `String::with_capacity` and the `write!` macro (via `std::fmt::Write`) to construct queries directly instead of intermediate `Vec` allocations and `format!` in loops minimizes heap allocations and string copies.
**Action:** Always use `String::with_capacity` and `write!` macro to build query strings directly.
10 changes: 6 additions & 4 deletions crates/ast-engine/src/tree_sitter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,9 +553,8 @@ impl ContentExt for String {
let mut bytes = std::mem::take(self).into_bytes();
let original_len = bytes.len();
bytes.splice(safe_start..safe_end, full_inserted);
*self = Self::from_utf8(bytes).unwrap_or_else(|e| {
Self::from_utf8_lossy(&e.into_bytes()).into_owned()
});
*self = Self::from_utf8(bytes)
.unwrap_or_else(|e| Self::from_utf8_lossy(&e.into_bytes()).into_owned());

// We calculate new_end_byte using the difference in the new overall string length
// to correctly align the end offset, taking any potential replacement bytes from
Expand Down Expand Up @@ -791,7 +790,10 @@ mod test {

let tree2 = parse_lang(|p| p.parse(&src, Some(&tree)), &Tsx.get_ts_language())?;
let fresh_tree = parse(&src)?;
assert_eq!(tree2.root_node().to_sexp(), fresh_tree.root_node().to_sexp());
assert_eq!(
tree2.root_node().to_sexp(),
fresh_tree.root_node().to_sexp()
);
Ok(())
}
}
82 changes: 54 additions & 28 deletions crates/flow/src/targets/d1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,40 +300,61 @@ impl D1ExportContext {
key: &KeyValue,
values: &FieldValues,
) -> Result<(String, Vec<serde_json::Value>), RecocoError> {
let mut columns = vec![];
let mut placeholders = vec![];
let mut params = vec![];
let mut update_clauses = vec![];
use std::fmt::Write;

// ⚡ Bolt: Optimize SQL generation by writing directly to a pre-allocated string
// to avoid multiple intermediate Vec allocations, string clones, and joins.
let mut sql = String::with_capacity(256);
let mut params =
Vec::with_capacity(self.key_fields_schema.len() + self.value_fields_schema.len());

write!(sql, "INSERT INTO {} (", self.table_name).unwrap();

let mut first = true;
// Extract key parts - KeyValue is a wrapper around Box<[KeyPart]>
for (idx, _key_field) in self.key_fields_schema.iter().enumerate() {
if let Some(key_part) = key.0.get(idx) {
columns.push(self.key_fields_schema[idx].name.clone());
placeholders.push("?".to_string());
if !first {
write!(sql, ", ").unwrap();
}
write!(sql, "{}", self.key_fields_schema[idx].name).unwrap();
params.push(key_part_to_json(key_part)?);
first = false;
}
}

// Add value fields
for (idx, value) in values.fields.iter().enumerate() {
if let Some(value_field) = self.value_fields_schema.get(idx) {
columns.push(value_field.name.clone());
placeholders.push("?".to_string());
if !first {
write!(sql, ", ").unwrap();
}
write!(sql, "{}", value_field.name).unwrap();
params.push(value_to_json(value)?);
update_clauses.push(format!(
"{} = excluded.{}",
value_field.name, value_field.name
));
first = false;
}
}

let sql = format!(
"INSERT INTO {} ({}) VALUES ({}) ON CONFLICT DO UPDATE SET {}",
self.table_name,
columns.join(", "),
placeholders.join(", "),
update_clauses.join(", ")
);
write!(sql, ") VALUES (").unwrap();
for i in 0..params.len() {
if i > 0 {
write!(sql, ", ").unwrap();
}
write!(sql, "?").unwrap();
}
Comment on lines +338 to +344

write!(sql, ") ON CONFLICT DO UPDATE SET ").unwrap();

let mut first_update = true;
for (idx, _) in values.fields.iter().enumerate() {
if let Some(value_field) = self.value_fields_schema.get(idx) {
if !first_update {
write!(sql, ", ").unwrap();
}
write!(sql, "{name} = excluded.{name}", name = value_field.name).unwrap();
first_update = false;
}
}
Comment on lines +346 to +357

Ok((sql, params))
}
Expand All @@ -342,22 +363,27 @@ impl D1ExportContext {
&self,
key: &KeyValue,
) -> Result<(String, Vec<serde_json::Value>), RecocoError> {
let mut where_clauses = vec![];
let mut params = vec![];
use std::fmt::Write;

// ⚡ Bolt: Optimize SQL generation by writing directly to a pre-allocated string
// to avoid multiple intermediate Vec allocations and string copies.
let mut sql = String::with_capacity(128);
let mut params = Vec::with_capacity(self.key_fields_schema.len());

write!(sql, "DELETE FROM {} WHERE ", self.table_name).unwrap();

let mut first = true;
for (idx, _key_field) in self.key_fields_schema.iter().enumerate() {
if let Some(key_part) = key.0.get(idx) {
Comment on lines +373 to 377
where_clauses.push(format!("{} = ?", self.key_fields_schema[idx].name));
if !first {
write!(sql, " AND ").unwrap();
}
write!(sql, "{} = ?", self.key_fields_schema[idx].name).unwrap();
params.push(key_part_to_json(key_part)?);
first = false;
}
}

let sql = format!(
"DELETE FROM {} WHERE {}",
self.table_name,
where_clauses.join(" AND ")
);

Ok((sql, params))
}

Expand Down
8 changes: 4 additions & 4 deletions crates/rule-engine/src/check_var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ fn get_vars_from_rules<'r>(rule: &'r Rule, utils: &'r RuleRegistration) -> Rapid
vars
}

fn check_var_in_constraints<'r>(
fn check_var_in_constraints(
mut vars: RapidSet<String>,
constraints: &'r RapidMap<thread_ast_engine::meta_var::MetaVariableID, Rule>,
constraints: &RapidMap<thread_ast_engine::meta_var::MetaVariableID, Rule>,
) -> RResult<RapidSet<String>> {
for rule in constraints.values() {
for var in rule.defined_vars() {
Expand All @@ -125,9 +125,9 @@ fn check_var_in_constraints<'r>(
Ok(vars)
}

fn check_var_in_transform<'r>(
fn check_var_in_transform(
mut vars: RapidSet<String>,
transform: &'r Option<Transform>,
transform: &Option<Transform>,
) -> RResult<RapidSet<String>> {
let Some(transform) = transform else {
return Ok(vars);
Expand Down
6 changes: 5 additions & 1 deletion crates/rule-engine/src/rule/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,11 @@ impl Rule {

pub fn defined_vars(&self) -> RapidSet<String> {
match self {
Rule::Pattern(p) => p.defined_vars().into_iter().map(|s| s.to_string()).collect(),
Rule::Pattern(p) => p
.defined_vars()
.into_iter()
.map(|s| s.to_string())
.collect(),
Rule::Kind(_) => RapidSet::default(),
Rule::Regex(_) => RapidSet::default(),
Rule::NthChild(n) => n.defined_vars(),
Expand Down
5 changes: 1 addition & 4 deletions crates/rule-engine/src/rule/referent_rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ impl<R> Clone for Registration<R> {

impl<R> Registration<R> {
fn read(&self) -> Arc<RapidMap<String, R>> {
self.0
.read()
.unwrap_or_else(|e| e.into_inner())
.clone()
self.0.read().unwrap_or_else(|e| e.into_inner()).clone()
}
pub(crate) fn contains_key(&self, key: &str) -> bool {
self.read().contains_key(key)
Expand Down