From e30942df18a6f5e932fa7140e439555dc4312d35 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Thu, 25 Sep 2025 22:43:16 +0800 Subject: [PATCH 1/2] Fix "Can't determine which FROM clause to join from" SQLAlchemy error Something changed in SQLAlchemy 1.3 after we upgraded (maybe in 5b02472b4d4f2d355e9d1b81330310a3114049ad?) which caused the graph and matrix queries to start erroring, showing something like: > InvalidRequestError("Can't determine which FROM clause to join from, there are multiple FROMS which can join to this entity. Please use the .select_from() method to establish an explicit left side, as well as providing an explcit ON clause if not present already to help resolve the ambiguity.") This fixes the errors by explicitly specifying which table to use as the FROM query. From what I understand we only have this error if there are multiple joins in the same query. The query in load_geomean_data also seemed to be missing a join on the samples table. I guess SQLAlchemy must have previously inferred the join or something? Since the relations go run -> sample -> test. This affected both the sqlite and postgres backends, and I've tested this fixes the error with both. --- lnt/server/ui/views.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lnt/server/ui/views.py b/lnt/server/ui/views.py index 5e97cbd5..25947ddc 100644 --- a/lnt/server/ui/views.py +++ b/lnt/server/ui/views.py @@ -881,7 +881,7 @@ def load_graph_data(plot_parameter, show_failures, limit, xaxis_date, revision_c # we want to load. Actually, we should just make this a single query. values = session.query(plot_parameter.field.column, ts.Order, ts.Run.start_time, ts.Run.id) \ - .join(ts.Run).join(ts.Order) \ + .select_from(ts.Run).join(ts.Order) \ .filter(ts.Run.machine_id == plot_parameter.machine.id) \ .filter(ts.Sample.test == plot_parameter.test) \ .filter(plot_parameter.field.column.isnot(None)) @@ -924,7 +924,7 @@ def load_geomean_data(field, machine, limit, xaxis_date, revision_cache=None): values = session.query(sqlalchemy.sql.func.min(field.column), ts.Order, sqlalchemy.sql.func.min(ts.Run.start_time)) \ - .join(ts.Run).join(ts.Order).join(ts.Test) \ + .select_from(ts.Run).join(ts.Order).join(ts.Sample).join(ts.Test) \ .filter(ts.Run.machine_id == machine.id) \ .filter(field.column.isnot(None)) \ .group_by(ts.Order.llvm_project_revision, ts.Test) @@ -1106,7 +1106,7 @@ def v4_graph(): q_baseline = session.query(req.field.column, ts.Order.llvm_project_revision, ts.Run.start_time, ts.Machine.name) \ - .join(ts.Run).join(ts.Order).join(ts.Machine) \ + .select_from(ts.Run).join(ts.Order).join(ts.Machine) \ .filter(ts.Run.id == baseline.id) \ .filter(ts.Sample.test == req.test) \ .filter(req.field.column.isnot(None)) @@ -1903,7 +1903,7 @@ def v4_matrix(): for req in plot_parameters: q = session.query(req.field.column, ts.Order.llvm_project_revision, ts.Order.id) \ - .join(ts.Run) \ + .select_from(ts.Run) \ .join(ts.Order) \ .filter(ts.Run.machine_id == req.machine.id) \ .filter(ts.Sample.test == req.test) \ @@ -1939,7 +1939,7 @@ def v4_matrix(): q_baseline = session.query(req.field.column, ts.Order.llvm_project_revision, ts.Order.id) \ - .join(ts.Run) \ + .select_from(ts.Run) \ .join(ts.Order) \ .filter(ts.Run.machine_id == req.machine.id) \ .filter(ts.Sample.test == req.test) \ From 8b789bb0e5e9db281eac0d12bccd0827aacb449d Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 3 Oct 2025 11:31:37 -0400 Subject: [PATCH 2/2] Fix not selecting from the samples table --- lnt/server/ui/views.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lnt/server/ui/views.py b/lnt/server/ui/views.py index 25947ddc..a39bd9f4 100644 --- a/lnt/server/ui/views.py +++ b/lnt/server/ui/views.py @@ -881,7 +881,8 @@ def load_graph_data(plot_parameter, show_failures, limit, xaxis_date, revision_c # we want to load. Actually, we should just make this a single query. values = session.query(plot_parameter.field.column, ts.Order, ts.Run.start_time, ts.Run.id) \ - .select_from(ts.Run).join(ts.Order) \ + .select_from(ts.Sample) \ + .join(ts.Run).join(ts.Order) \ .filter(ts.Run.machine_id == plot_parameter.machine.id) \ .filter(ts.Sample.test == plot_parameter.test) \ .filter(plot_parameter.field.column.isnot(None)) @@ -924,7 +925,8 @@ def load_geomean_data(field, machine, limit, xaxis_date, revision_cache=None): values = session.query(sqlalchemy.sql.func.min(field.column), ts.Order, sqlalchemy.sql.func.min(ts.Run.start_time)) \ - .select_from(ts.Run).join(ts.Order).join(ts.Sample).join(ts.Test) \ + .select_from(ts.Sample) \ + .join(ts.Run).join(ts.Order).join(ts.Test) \ .filter(ts.Run.machine_id == machine.id) \ .filter(field.column.isnot(None)) \ .group_by(ts.Order.llvm_project_revision, ts.Test) @@ -1106,7 +1108,8 @@ def v4_graph(): q_baseline = session.query(req.field.column, ts.Order.llvm_project_revision, ts.Run.start_time, ts.Machine.name) \ - .select_from(ts.Run).join(ts.Order).join(ts.Machine) \ + .select_from(ts.Sample) \ + .join(ts.Run).join(ts.Order).join(ts.Machine) \ .filter(ts.Run.id == baseline.id) \ .filter(ts.Sample.test == req.test) \ .filter(req.field.column.isnot(None)) @@ -1903,7 +1906,8 @@ def v4_matrix(): for req in plot_parameters: q = session.query(req.field.column, ts.Order.llvm_project_revision, ts.Order.id) \ - .select_from(ts.Run) \ + .select_from(ts.Sample) \ + .join(ts.Run) \ .join(ts.Order) \ .filter(ts.Run.machine_id == req.machine.id) \ .filter(ts.Sample.test == req.test) \ @@ -1939,7 +1943,8 @@ def v4_matrix(): q_baseline = session.query(req.field.column, ts.Order.llvm_project_revision, ts.Order.id) \ - .select_from(ts.Run) \ + .select_from(ts.Sample) \ + .join(ts.Run) \ .join(ts.Order) \ .filter(ts.Run.machine_id == req.machine.id) \ .filter(ts.Sample.test == req.test) \