IR Graph
How Pulsar models code as a graph of typed nodes
The IR (Intermediate Representation) graph is the core data structure in Pulsar. Every piece of analyzed code becomes a node in a directed graph, with edges representing semantic relationships.
Think of it as a map of your code's interaction with the database:
- Each Drizzle ORM call (
db.select(),db.insert(), etc.) becomes anOrmNode. - Each SQL query it generates becomes a
SQLNode. - Each database table from your schema becomes a
SchemaNode. - Each raw SQL expression (
sql\...`,db.$queryRaw()) becomes aRawSqlNode`.
Edges connect these nodes: an ORM call generates a SQL query, a SQL query accesses a table, and sometimes an ORM call maps directly to a schema table.
Node types
OrmNode
Represents a Drizzle ORM method call chain (.select(), .findMany(), .findFirst(), .insert(), .update(), .delete()).
struct OrmNode {
method: OrmMethod, // Select, Insert, Update, Delete
args: OrmArgs, // columns, where_clause, limit, include
loop_kind: LoopKind, // NotInLoop, ForLoop, ForOf, WhileLoop, ForEach, Map
in_callback: bool, // true if inside a callback (Promise.all, .map(), etc.)
missing_await: bool, // true if expression lacks await
location: SourceLocation,
}SQLNode
Represents a parsed SQL query (either extracted from an ORM chain or parsed from a raw SQL string).
struct SQLNode {
kind: SqlKind, // Select, Insert, Update, Delete
columns: Vec<ColumnRef>, // referenced columns
table: Option<TableRef>, // target table
limit: bool, // has LIMIT clause
where_clause: bool, // has WHERE clause
in_callback: bool,
location: SourceLocation,
}RawSqlNode
Represents a raw SQL expression in TypeScript code.
struct RawSqlNode {
kind: RawSqlKind, // TaggedTemplate or DbRawMethod
has_interpolation: bool, // contains string interpolation
location: SourceLocation,
}SchemaNode
Represents a database table parsed from a Prisma schema.
struct SchemaNode {
table_name: String,
columns: Vec<SchemaColumn>, // name, col_type, is_nullable, is_indexed, is_unique, foreign_key
indexes: Vec<SchemaIndex>, // columns, is_unique, is_partial
}Edge types
| Edge | When it is created |
|---|---|
Generates | When parsing a Drizzle chain like db.select(...).from(users), the resulting OrmNode generates a SQLNode |
Accesses | When a SQLNode references a table that exists in the loaded Prisma schema, an edge to the matching SchemaNode is added |
MapsTo | When schema-aware rules link an OrmNode directly to a SchemaNode (shorthand for the full Generates → Accesses path) |
Graph traversal
The graph is built using petgraph::Graph<NodeKind, EdgeKind>.
Rules traverse the graph using helper methods:
schema_for_orm(orm_id)— followsMapsToorGenerates → Accessesto find the linked schema nodeschema_for_sql(sql_id)— followsAccessesto find the linked schema nodeedges_from(node, kind)— returns all target nodes connected by a specific edge kind
Example
For this Drizzle query:
await db.select({ id: users.id }).from(users).where(eq(users.id, 1)).limit(1);The graph would contain:
Last updated on