44from sqlalchemy .sql .selectable import Selectable , Join , Alias
55from .base import Struct
66from .op import (
7+ Field ,
78 Op ,
89 Rel ,
910 RelVoid ,
@@ -42,6 +43,7 @@ class From(Struct):
4243 limit : Any = None
4344 order : Any = None
4445 group_by_columns : List [str ] = None
46+ compute : Any = None
4547
4648 def __post_init__ (self ):
4749 if self .group_by_columns is None :
@@ -125,6 +127,9 @@ def to_select(self, value, *compute):
125127
126128
127129def op_to_sql (op : Op , from_obj ):
130+ if from_obj .compute and id (op ) in from_obj .compute :
131+ name = from_obj .compute [id (op )].name
132+ return sa .column (name ), from_obj
128133 if op .expr is not None :
129134 expr_collect (op .expr )
130135 expr = None
@@ -169,29 +174,30 @@ def RelVoid_to_sql(rel: RelVoid, from_obj):
169174@rel_to_sql .register
170175def RelTable_to_sql (rel : RelTable , from_obj ):
171176 if rel .compute :
172- for _ , op in rel .compute :
173- if op .expr is not None :
174- expr_collect (op .expr )
177+ for field in rel .compute . values () :
178+ if field . op .expr is not None :
179+ expr_collect (field . op .expr )
175180
176181 from_obj = From .make (rel .table )
177182
178183 if rel .compute :
179184 at = from_obj .at
180185 columns = []
181- for name , op in rel .compute :
182- expr , from_obj = op_to_sql (op , from_obj .replace (at = at ))
183- columns .append (expr .label (name ))
186+ for field in rel .compute . values () :
187+ expr , from_obj = op_to_sql (field . op , from_obj .replace (at = at ))
188+ columns .append (expr .label (field . name ))
184189 from_obj = from_obj .replace (at = at )
185190 from_obj = From .make (from_obj .to_select (None , * columns ).alias ())
191+ from_obj = from_obj .replace (compute = rel .compute )
186192 return from_obj
187193
188194
189195@rel_to_sql .register
190196def RelJoin_to_sql (rel : RelJoin , from_obj ):
191197 if rel .compute :
192- for _ , op in rel .compute :
193- if op .expr is not None :
194- expr_collect (op .expr )
198+ for field in rel .compute . values () :
199+ if field . op .expr is not None :
200+ expr_collect (field . op .expr )
195201
196202 if isinstance (rel .rel , RelAroundParent ):
197203 table = rel .fk .column .table
@@ -214,9 +220,9 @@ def RelJoin_to_sql(rel: RelJoin, from_obj):
214220 if rel .compute :
215221 at = from_obj .at
216222 columns = []
217- for name , op in rel .compute :
218- expr , from_obj = op_to_sql (op , from_obj .replace (at = at ))
219- columns .append (expr .label (name ))
223+ for field in rel .compute . values () :
224+ expr , from_obj = op_to_sql (field . op , from_obj .replace (at = at ))
225+ columns .append (expr .label (field . name ))
220226 from_obj = from_obj .replace (at = at )
221227 from_obj = From .make (from_obj .to_select (None , * columns ).alias ())
222228
@@ -226,9 +232,9 @@ def RelJoin_to_sql(rel: RelJoin, from_obj):
226232@rel_to_sql .register
227233def RelRevJoin_to_sql (rel : RelRevJoin , from_obj ):
228234 if rel .compute :
229- for _ , op in rel .compute :
230- if op .expr is not None :
231- expr_collect (op .expr )
235+ for field in rel .compute . values () :
236+ if field . op .expr is not None :
237+ expr_collect (field . op .expr )
232238
233239 if isinstance (rel .rel , RelParent ):
234240 table = rel .fk .parent .table .alias ()
@@ -254,9 +260,9 @@ def RelRevJoin_to_sql(rel: RelRevJoin, from_obj):
254260 if rel .compute :
255261 at = from_obj .at
256262 columns = []
257- for name , op in rel .compute :
258- expr , from_obj = op_to_sql (op , from_obj .replace (at = at ))
259- columns .append (expr .label (name ))
263+ for field in rel .compute . values () :
264+ expr , from_obj = op_to_sql (field . op , from_obj .replace (at = at ))
265+ columns .append (expr .label (field . name ))
260266 from_obj = from_obj .replace (at = at )
261267 from_obj = From .make (from_obj .to_select (None , * columns ).alias ())
262268
@@ -364,7 +370,8 @@ def build_kernel():
364370 return from_obj
365371
366372 result_columns = [from_obj .current .columns [c .name ] for c in tuple (columns )]
367- for name , op in rel .compute :
373+ for field in rel .compute .values ():
374+ op = field .op
368375 assert op .sig is not None
369376 columns , kernel = build_kernel ()
370377
@@ -394,7 +401,7 @@ def build_kernel():
394401 inner_sel , * ((c .name , c .name ) for c in columns ), outer = True
395402 )
396403 result_columns .append (
397- sa .func .coalesce (inner_at .c .value , op .sig .unit ).label (name )
404+ sa .func .coalesce (inner_at .c .value , op .sig .unit ).label (field . name )
398405 )
399406
400407 from_obj = From .make (
@@ -433,13 +440,9 @@ def ExprColumn_to_sql(op: ExprColumn, from_obj):
433440
434441@expr_to_sql .register
435442def ExprCompute_to_sql (expr : ExprCompute , from_obj ):
436- found = None
437- for name , op in expr .rel .compute :
438- if op == expr .op :
439- found = name
440- break
441- assert found
442- return sa .column (found , _selectable = from_obj .at ), from_obj
443+ field = expr .rel .compute .get (id (expr .op ))
444+ assert field
445+ return sa .column (field .name , _selectable = from_obj .at ), from_obj
443446
444447
445448@expr_to_sql .register
@@ -498,9 +501,12 @@ def ExprColumn_collect(expr: ExprColumn):
498501
499502@expr_collect .register
500503def ExprAggregate_collect (expr : ExprCompute ):
501- idx = len (expr .rel .compute )
502- name = f"compute_{ idx } "
503- expr .rel .compute .append ((name , expr .op ))
504+ key = id (expr .op )
505+ field = expr .rel .compute .get (key )
506+ if field is None :
507+ idx = len (expr .rel .compute )
508+ name = f"compute_{ idx } "
509+ expr .rel .compute [id (expr .op )] = Field (name = name , op = expr .op )
504510
505511
506512@expr_collect .register
0 commit comments