Skip to content

Commit 3d287e9

Browse files
committed
store: Restructure query generation for FindQuery
Laying out the code like this makes it easier to see how the queries in the RFC at https://graphprotocol.github.io/rfcs/engineering-plans/0001-graphql-query-prefetching.html are reflected in the code, and will make it easier to change the queries for individual types to improve performance.
1 parent f42634d commit 3d287e9

File tree

1 file changed

+156
-68
lines changed

1 file changed

+156
-68
lines changed

store/postgres/src/relational_queries.rs

Lines changed: 156 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,94 +1450,182 @@ impl<'a> FilterWindow<'a> {
14501450
})
14511451
}
14521452

1453-
fn expand_parents(&self, out: &mut AstPass<Pg>) -> QueryResult<()> {
1454-
match &self.link {
1455-
TableLink::Direct(column) => {
1456-
// Type A and B
1457-
// unnest($parent_ids) as p(id)
1458-
out.push_sql("unnest(");
1459-
column.bind_ids(&self.ids, out)?;
1460-
out.push_sql(") as p(id)");
1461-
}
1462-
// For types C and D we always treat the parent ids as strings,
1463-
// regardless of the actual IdType of the parent since we never
1464-
// compare them to anything, and solely copy them through to the
1465-
// result so that they can be matched up with the parent object
1466-
// by the GraphQL query execution
1467-
TableLink::Parent(ParentIds::List(child_ids)) => {
1468-
// Type C
1469-
// unnest($parent_ids, $child_id_matrix) as p(id, child_ids)
1470-
out.push_sql("rows from (unnest(");
1471-
out.push_bind_param::<Array<Text>, _>(&self.ids)?;
1472-
out.push_sql("), reduce_dim(");
1473-
self.table.primary_key().push_matrix(&child_ids, out)?;
1474-
out.push_sql(")) as p(id, child_ids)");
1475-
}
1476-
TableLink::Parent(ParentIds::Scalar(child_ids)) => {
1477-
// Type D
1478-
// unnest($parent_ids, $child_ids) as p(id, child_id)
1479-
out.push_sql("unnest(");
1480-
out.push_bind_param::<Array<Text>, _>(&self.ids)?;
1481-
out.push_sql(",");
1482-
self.table.primary_key().bind_ids(&child_ids, out)?;
1483-
out.push_sql(") as p(id, child_id)");
1484-
}
1453+
fn and_filter(&self, mut out: AstPass<Pg>) -> QueryResult<()> {
1454+
if let Some(filter) = &self.query_filter {
1455+
out.push_sql("\n and ");
1456+
filter.walk_ast(out)?
14851457
}
14861458
Ok(())
14871459
}
14881460

1489-
fn linked_children(&self, out: &mut AstPass<Pg>) -> QueryResult<()> {
1490-
out.push_sql(" and ");
1491-
match &self.link {
1492-
TableLink::Direct(column) => {
1493-
if column.is_list() {
1494-
// Type A
1495-
// p.id = any(c.{parent_field})
1496-
out.push_sql("p.id = any(c.");
1497-
out.push_identifier(column.name.as_str())?;
1498-
out.push_sql(")");
1499-
} else {
1500-
// Type B
1501-
// p.id = c.{parent_field}
1502-
out.push_sql("p.id = c.");
1503-
out.push_identifier(column.name.as_str())?;
1504-
}
1505-
}
1506-
TableLink::Parent(ParentIds::List(_)) => {
1507-
// Type C
1508-
out.push_sql("c.id = any(p.child_ids)");
1509-
}
1510-
TableLink::Parent(ParentIds::Scalar(_)) => {
1511-
// Type D
1512-
out.push_sql("c.id = p.child_id");
1513-
}
1514-
}
1461+
fn children_type_a(
1462+
&self,
1463+
column: &Column,
1464+
limit: ParentLimit<'_>,
1465+
block: BlockNumber,
1466+
out: &mut AstPass<Pg>,
1467+
) -> QueryResult<()> {
1468+
assert!(column.is_list());
1469+
1470+
// Generate
1471+
// from unnest({parent_ids}) as p(id)
1472+
// cross join lateral
1473+
// (select *
1474+
// from children c
1475+
// where p.id = any(c.{parent_field})
1476+
// and .. other conditions on c ..
1477+
// order by c.{sort_key}
1478+
// limit {first} offset {skip}) c
1479+
// order by c.{sort_key}
1480+
1481+
out.push_sql("\n from unnest(");
1482+
column.bind_ids(&self.ids, out)?;
1483+
out.push_sql(") as p(id) cross join lateral (select * from ");
1484+
out.push_sql(self.table.qualified_name.as_str());
1485+
out.push_sql(" c where ");
1486+
BlockRangeContainsClause::new("c.", block).walk_ast(out.reborrow())?;
1487+
limit.filter(out);
1488+
out.push_sql(" and p.id = any(c.");
1489+
out.push_identifier(column.name.as_str())?;
1490+
out.push_sql(")");
1491+
self.and_filter(out.reborrow())?;
1492+
limit.restrict(out)?;
1493+
out.push_sql(") c");
15151494
Ok(())
15161495
}
15171496

1518-
fn children(
1497+
fn children_type_b(
15191498
&self,
1499+
column: &Column,
15201500
limit: ParentLimit<'_>,
15211501
block: BlockNumber,
1522-
mut out: AstPass<Pg>,
1502+
out: &mut AstPass<Pg>,
15231503
) -> QueryResult<()> {
1504+
assert!(!column.is_list());
1505+
1506+
// Generate
1507+
// from unnest({parent_ids}) as p(id)
1508+
// cross join lateral
1509+
// (select *
1510+
// from children c
1511+
// where p.id = c.{parent_field}
1512+
// and .. other conditions on c ..
1513+
// order by c.{sort_key}
1514+
// limit {first} offset {skip}) c
1515+
// order by c.{sort_key}
1516+
1517+
out.push_sql("\n from unnest(");
1518+
column.bind_ids(&self.ids, out)?;
1519+
out.push_sql(") as p(id) cross join lateral (select * from ");
1520+
out.push_sql(self.table.qualified_name.as_str());
1521+
out.push_sql(" c where ");
1522+
BlockRangeContainsClause::new("c.", block).walk_ast(out.reborrow())?;
1523+
limit.filter(out);
1524+
out.push_sql(" and p.id = c.");
1525+
out.push_identifier(column.name.as_str())?;
1526+
self.and_filter(out.reborrow())?;
1527+
limit.restrict(out)?;
1528+
out.push_sql(") c");
1529+
Ok(())
1530+
}
1531+
1532+
fn children_type_c(
1533+
&self,
1534+
child_ids: &Vec<Vec<Option<SafeString>>>,
1535+
limit: ParentLimit<'_>,
1536+
block: BlockNumber,
1537+
out: &mut AstPass<Pg>,
1538+
) -> QueryResult<()> {
1539+
// Generate
1540+
// from rows from (unnest({parent_ids}), reduce_dim({child_id_matrix}))
1541+
// as p(id, child_ids)
1542+
// cross join lateral
1543+
// (select *
1544+
// from children c
1545+
// where c.id = any(p.child_ids)
1546+
// and .. other conditions on c ..
1547+
// order by c.{sort_key}
1548+
// limit {first} offset {skip}) c
1549+
// order by c.{sort_key}
1550+
15241551
out.push_sql("\n from ");
1525-
self.expand_parents(&mut out)?;
1552+
out.push_sql("rows from (unnest(");
1553+
out.push_bind_param::<Array<Text>, _>(&self.ids)?;
1554+
out.push_sql("), reduce_dim(");
1555+
self.table.primary_key().push_matrix(&child_ids, out)?;
1556+
out.push_sql(")) as p(id, child_ids)");
15261557
out.push_sql(" cross join lateral (select * from ");
15271558
out.push_sql(self.table.qualified_name.as_str());
15281559
out.push_sql(" c where ");
15291560
BlockRangeContainsClause::new("c.", block).walk_ast(out.reborrow())?;
1530-
limit.filter(&mut out);
1531-
self.linked_children(&mut out)?;
1532-
if let Some(filter) = &self.query_filter {
1533-
out.push_sql("\n and ");
1534-
filter.walk_ast(out.reborrow())?
1535-
}
1536-
limit.restrict(&mut out)?;
1561+
limit.filter(out);
1562+
out.push_sql(" and c.id = any(p.child_ids)");
1563+
self.and_filter(out.reborrow())?;
1564+
limit.restrict(out)?;
15371565
out.push_sql(") c");
15381566
Ok(())
15391567
}
15401568

1569+
fn children_type_d(
1570+
&self,
1571+
child_ids: &Vec<String>,
1572+
limit: ParentLimit<'_>,
1573+
block: BlockNumber,
1574+
out: &mut AstPass<Pg>,
1575+
) -> QueryResult<()> {
1576+
// Generate
1577+
// select c.*, p.id as parent_id
1578+
// from rows from (unnest({parent_ids}), unnest({child_ids})) as p(id, child_id)
1579+
// cross join lateral
1580+
// (select *
1581+
// from children c
1582+
// where c.id = p.child_id
1583+
// and .. other conditions on c ..
1584+
// order by c.{sort_key}
1585+
// limit {first} offset {skip}) c
1586+
// order by c.{sort_key}
1587+
1588+
out.push_sql("\n from unnest(");
1589+
out.push_bind_param::<Array<Text>, _>(&self.ids)?;
1590+
out.push_sql(",");
1591+
self.table.primary_key().bind_ids(&child_ids, out)?;
1592+
out.push_sql(") as p(id, child_id)");
1593+
out.push_sql(" cross join lateral (select * from ");
1594+
out.push_sql(self.table.qualified_name.as_str());
1595+
out.push_sql(" c where ");
1596+
BlockRangeContainsClause::new("c.", block).walk_ast(out.reborrow())?;
1597+
limit.filter(out);
1598+
out.push_sql(" and ");
1599+
out.push_sql("c.id = p.child_id");
1600+
self.and_filter(out.reborrow())?;
1601+
limit.restrict(out)?;
1602+
out.push_sql(") c");
1603+
Ok(())
1604+
}
1605+
1606+
fn children(
1607+
&self,
1608+
limit: ParentLimit<'_>,
1609+
block: BlockNumber,
1610+
mut out: AstPass<Pg>,
1611+
) -> QueryResult<()> {
1612+
match &self.link {
1613+
TableLink::Direct(column) => {
1614+
if column.is_list() {
1615+
self.children_type_a(column, limit, block, &mut out)
1616+
} else {
1617+
self.children_type_b(column, limit, block, &mut out)
1618+
}
1619+
}
1620+
TableLink::Parent(ParentIds::List(child_ids)) => {
1621+
self.children_type_c(child_ids, limit, block, &mut out)
1622+
}
1623+
TableLink::Parent(ParentIds::Scalar(child_ids)) => {
1624+
self.children_type_d(child_ids, limit, block, &mut out)
1625+
}
1626+
}
1627+
}
1628+
15411629
/// Select a basic subset of columns from the child table for use in
15421630
/// the `matches` CTE of queries that need to retrieve entities of
15431631
/// different types or entities that link differently to their parents

0 commit comments

Comments
 (0)