1use super::*;
3use zirql::ValueExpression as E;
4
5pub type ProgramValue = E<zirql::Expression<ProgramNative, zirql::NativeSetOperator, ()>>;
8pub type ProgramFunction = zirql::UnresolvedPatternFunctionDeclaration<
10 zirql::Expression<ProgramNative, zirql::NativeSetOperator, ()>,
11>;
12
13#[derive(Debug)]
15pub enum ProgramNative {
16 Query(Query),
17}
18
19struct Inputs {
20 functions: Vec<ProgramFunction>,
21 bindings: Vec<(zirql::Pattern, ProgramValue)>,
22 report: Option<ProgramValue>,
23}
24impl Drop for Inputs {
25 fn drop(&mut self) {
26 for mut function in std::mem::take(&mut self.functions) {
27 for parameter in &mut function.parameters {
28 dispose_signature(std::mem::replace(
29 &mut parameter.value.declaration.value,
30 zirql::SignatureType::Integer,
31 ));
32 }
33 dispose_signature(std::mem::replace(
34 &mut function.result.value,
35 zirql::SignatureType::Integer,
36 ));
37 zirql::dispose_unresolved_pattern_functions(vec![function]);
38 }
39 for (pattern, value) in std::mem::take(&mut self.bindings) {
40 zirql::dispose_pattern(pattern);
41 zirql::dispose_native_value(value);
42 }
43 if let Some(value) = self.report.take() {
44 zirql::dispose_native_value(value);
45 }
46 }
47}
48impl Program {
49 pub fn from_values(
67 functions: Vec<ProgramFunction>,
68 bindings: Vec<(zirql::Pattern, ProgramValue)>,
69 final_report: ProgramValue,
70 limits: ProgramLimits,
71 ) -> Result<Self, QueryError> {
72 let mut inputs = Inputs {
73 functions,
74 bindings,
75 report: Some(final_report),
76 };
77 let mut budget = Budget::for_program(limits.core());
78 let range = inputs.report.as_ref().unwrap().range.clone();
79 let mut names = std::collections::BTreeSet::new();
80 for function in &inputs.functions {
81 functions::check_signature(&function.result, &mut budget, limits.nesting)?;
82 for parameter in &function.parameters {
83 functions::check_signature(
84 ¶meter.value.declaration,
85 &mut budget,
86 limits.nesting,
87 )?;
88 }
89 for query in functions::check_value(&function.value, &mut budget, limits.nesting)? {
90 check_native(query, &mut budget, limits.nesting)?;
91 }
92 }
93 for (pattern, value) in &inputs.bindings {
94 let selected =
95 zirql::pattern_names(pattern, &mut budget).map_err(pipeline::core_error)?;
96 for name in &selected {
97 if !names.insert(name.value.clone()) {
98 return Err(QueryError::query(
99 name.range.clone(),
100 "binding is already defined",
101 ));
102 }
103 }
104 for query in functions::check_value(value, &mut budget, limits.nesting)? {
105 check_native(query, &mut budget, limits.nesting)?;
106 }
107 }
108 for query in
109 functions::check_value(inputs.report.as_ref().unwrap(), &mut budget, limits.nesting)?
110 {
111 check_native(query, &mut budget, limits.nesting)?;
112 }
113 let mut queries = Vec::new();
114 let mut lower = |query, range: Range<usize>| {
115 let index = queries.len();
116 queries.push(ProgramQuery {
117 query,
118 arguments: Rc::new(Vec::new()),
119 copy_units: range.len() + 1,
120 });
121 Stage::Query(index, Rc::new(Vec::new()))
122 };
123 let mut functions = std::mem::take(&mut inputs.functions)
124 .into_iter()
125 .map(|function| zirql::UnresolvedPatternFunctionDeclaration {
126 name: function.name,
127 parameters: function.parameters,
128 result: function.result,
129 value: zirql::map_value_payloads(function.value, &mut |query| {
130 map_native(query, &mut lower)
131 }),
132 })
133 .collect::<Vec<_>>();
134 let mut statements = Vec::new();
135 for (pattern, value) in std::mem::take(&mut inputs.bindings) {
136 let range = value.range.clone();
137 statements.push(Located::new(
138 zirql::Statement::BindPattern {
139 pattern: Arc::new(pattern),
140 value: zirql::map_value_payloads(value, &mut |query| {
141 map_native(query, &mut lower)
142 })
143 .into(),
144 },
145 range,
146 ));
147 }
148 statements.push(Located::new(
149 zirql::Statement::Evaluate {
150 expression: core_value(
151 zirql::map_value_payloads(inputs.report.take().unwrap(), &mut |query| {
152 map_native(query, &mut lower)
153 }),
154 range.clone(),
155 ),
156 implicit_output: Some(Emitter::Json),
157 },
158 range.clone(),
159 ));
160 for function in &mut functions {
161 functions::normalize_value(&mut function.value, &BTreeMap::new(), &queries);
162 }
163 for statement in &mut statements {
164 let expression = match &mut statement.value {
165 zirql::Statement::Bind { expression, .. }
166 | zirql::Statement::BindFields { expression, .. }
167 | zirql::Statement::Evaluate { expression, .. } => expression,
168 zirql::Statement::BindPattern { value, .. } => {
169 functions::normalize_value(
170 Arc::get_mut(value).expect("builder-owned value"),
171 &BTreeMap::new(),
172 &queries,
173 );
174 continue;
175 }
176 zirql::Statement::Parameter { .. } => continue,
177 };
178 if let ExpressionKind::Sequence { steps, .. } = &mut expression.value {
179 for step in steps {
180 let value = match &mut step.value {
181 ExpressionStep::Value(value) => {
182 Arc::get_mut(value).expect("builder-owned value")
183 }
184 _ => continue,
185 };
186 functions::normalize_value(value, &BTreeMap::new(), &queries);
187 }
188 }
189 }
190 let report_model_demand = queries
191 .iter()
192 .any(|query| query.query.requires_model_facts())
193 || functions.iter().any(|function| {
194 functions::signature_needs_model(&function.result.value)
195 || function.parameters.iter().any(|parameter| {
196 functions::signature_needs_model(¶meter.value.declaration.value)
197 })
198 });
199 Ok(Self {
200 parameters: Vec::new(),
201 program: zirql::Program::new(statements, range.clone()),
202 preflight_work: limits.work - budget.remaining_work(),
203 preflight_results: limits.results - budget.remaining_results().unwrap(),
204 preflight_range: Some(range),
205 queries,
206 functions,
207 function_limits: zirql::FunctionLimits { active_depth: 64 },
208 report_model_demand,
209 editing: false,
210 prepared: true,
211 prepared_values: false,
212 })
213 }
214}
215
216type TypedExpression = zirql::Expression<ProgramNative, zirql::NativeSetOperator, ()>;
217
218fn map_native(
219 query: TypedExpression,
220 lower: &mut impl FnMut(Query, Range<usize>) -> Stage,
221) -> Expression {
222 let range = query.range;
223 Located::new(
224 match query.value {
225 ExpressionKind::Input => ExpressionKind::Input,
226 ExpressionKind::Binding(name) => ExpressionKind::Binding(name),
227 ExpressionKind::Group(value) => {
228 ExpressionKind::Group(Box::new(map_native(*value, lower)))
229 }
230 ExpressionKind::Field { value, field } => ExpressionKind::Field {
231 value: Box::new(map_native(*value, lower)),
232 field,
233 },
234 ExpressionKind::Record(fields) => ExpressionKind::Record(
235 fields
236 .into_iter()
237 .map(|(name, value)| (name, map_native(value, lower)))
238 .collect(),
239 ),
240 ExpressionKind::Set {
241 operator,
242 left,
243 right,
244 } => ExpressionKind::Set {
245 operator,
246 left: Box::new(map_native(*left, lower)),
247 right: Box::new(map_native(*right, lower)),
248 },
249 ExpressionKind::Sequence { input, steps } => ExpressionKind::Sequence {
250 input: Box::new(map_native(*input, lower)),
251 steps: steps
252 .into_iter()
253 .map(|step| {
254 let range = step.range;
255 Located::new(map_step(step.value, lower, &range), range)
256 })
257 .collect(),
258 },
259 },
260 range,
261 )
262}
263
264fn map_body(
265 body: zirql::NativeBody<TypedExpression>,
266 lower: &mut impl FnMut(Query, Range<usize>) -> Stage,
267) -> zirql::NativeBody<Expression> {
268 let value = body.into_value();
269 let zirql::ValueExpressionKind::NativeValue(query) = value.value else {
270 unreachable!("native body")
271 };
272 zirql::NativeBody::new(map_native(*query, lower), value.range)
273}
274
275fn map_step(
276 step: zirql::ExpressionStep<ProgramNative, (), zirql::NativeSetOperator>,
277 lower: &mut impl FnMut(Query, Range<usize>) -> Stage,
278 range: &Range<usize>,
279) -> ExpressionStep<Stage, Emitter, zirql::NativeSetOperator> {
280 let mut map_value =
281 |value| zirql::map_value_payloads(value, &mut |query| map_native(query, lower));
282 match step {
283 ExpressionStep::Value(value) => ExpressionStep::Value(
284 map_value(Arc::try_unwrap(value).expect("builder-owned value")).into(),
285 ),
286 ExpressionStep::Head(value) => ExpressionStep::Head(
287 map_value(Arc::try_unwrap(value).expect("builder-owned bound")).into(),
288 ),
289 ExpressionStep::Tail(value) => ExpressionStep::Tail(
290 map_value(Arc::try_unwrap(value).expect("builder-owned bound")).into(),
291 ),
292 ExpressionStep::Check { operand, message } => ExpressionStep::Check {
293 operand: operand.map(|value| {
294 map_value(Arc::try_unwrap(value).expect("builder-owned check operand")).into()
295 }),
296 message: message.map(|value| {
297 map_value(Arc::try_unwrap(value).expect("builder-owned check message")).into()
298 }),
299 },
300 ExpressionStep::Projection(projection) => {
301 let projection = Arc::try_unwrap(projection).expect("builder-owned projection");
302 ExpressionStep::Projection(
303 zirql::Projection {
304 subject: projection.subject,
305 fields: projection
306 .fields
307 .into_iter()
308 .map(|(label, value)| (label, map_value(value)))
309 .collect(),
310 }
311 .into(),
312 )
313 }
314 ExpressionStep::Ordering(ordering) => {
315 let ordering = Arc::try_unwrap(ordering).expect("builder-owned ordering");
316 ExpressionStep::Ordering(
317 zirql::NativeOrdering {
318 order: ordering.order,
319 selector: ordering.selector.map(|body| map_body(body, lower)),
320 }
321 .into(),
322 )
323 }
324 ExpressionStep::Mapping(mapping) => {
325 let mapping = Arc::try_unwrap(mapping).expect("builder-owned mapping");
326 ExpressionStep::Mapping(
327 zirql::NativeMap {
328 key: map_body(mapping.key, lower),
329 value: map_body(mapping.value, lower),
330 }
331 .into(),
332 )
333 }
334 ExpressionStep::Group(body) => ExpressionStep::Group(
335 map_body(Arc::try_unwrap(body).expect("builder-owned group"), lower).into(),
336 ),
337 ExpressionStep::WhereExists(body) => ExpressionStep::WhereExists(
338 map_body(
339 Arc::try_unwrap(body).expect("builder-owned relation"),
340 lower,
341 )
342 .into(),
343 ),
344 ExpressionStep::FixedPoint(body) => ExpressionStep::FixedPoint(
345 map_body(Arc::try_unwrap(body).expect("builder-owned body"), lower).into(),
346 ),
347 ExpressionStep::MapSorting => ExpressionStep::MapSorting,
348 ExpressionStep::Collection(operation) => ExpressionStep::Collection(operation),
349 ExpressionStep::Stage(ProgramNative::Query(query)) => {
350 ExpressionStep::Stage(lower(query, range.clone()))
351 }
352 ExpressionStep::Filter(_)
353 | ExpressionStep::Match(_)
354 | ExpressionStep::Paths(_)
355 | ExpressionStep::Emit { .. } => {
356 unreachable!("unsupported typed payload rejected before lowering")
357 }
358 }
359}
360
361fn check_native(
362 query: &zirql::Expression<ProgramNative, zirql::NativeSetOperator, ()>,
363 budget: &mut Budget,
364 nesting: usize,
365) -> Result<(), QueryError> {
366 zirql::walk_expression(query, budget, nesting, |_, _| Ok(())).map_err(pipeline::core_error)?;
367 let mut pending = vec![(query, 0)];
368 while let Some((query, depth)) = pending.pop() {
369 if depth >= nesting.min(64) {
370 return Err(QueryError::query(
371 query.range.clone(),
372 "native expression nesting limit exceeded",
373 ));
374 }
375 budget
376 .charge(1, zirql::Location::Query(query.range.clone()))
377 .map_err(pipeline::core_error)?;
378 match &query.value {
379 ExpressionKind::Input | ExpressionKind::Binding(_) => {}
380 ExpressionKind::Group(value) | ExpressionKind::Field { value, .. } => {
381 pending.push((value, depth + 1))
382 }
383 ExpressionKind::Record(fields) => {
384 let location = zirql::Location::Query(query.range.clone());
385 budget
386 .charge(fields.len(), location.clone())
387 .map_err(pipeline::core_error)?;
388 budget
389 .charge_results(fields.len(), location)
390 .map_err(pipeline::core_error)?;
391 pending.extend(fields.iter().map(|(_, v)| (v, depth + 1)))
392 }
393 ExpressionKind::Set { left, right, .. } => {
394 pending.push((left, depth + 1));
395 pending.push((right, depth + 1));
396 }
397 ExpressionKind::Sequence { input, steps } => {
398 pending.push((input, depth + 1));
399 for step in steps {
400 budget
401 .charge_results(1, zirql::Location::Query(step.range.clone()))
402 .map_err(pipeline::core_error)?;
403 let owners = match &step.value {
404 ExpressionStep::Value(value)
405 | ExpressionStep::Head(value)
406 | ExpressionStep::Tail(value) => Arc::strong_count(value),
407 ExpressionStep::Check { operand, message } => operand
408 .iter()
409 .chain(message)
410 .map(Arc::strong_count)
411 .max()
412 .unwrap_or(1),
413 ExpressionStep::Projection(value) => Arc::strong_count(value),
414 ExpressionStep::Ordering(value) => Arc::strong_count(value),
415 ExpressionStep::Mapping(value) => Arc::strong_count(value),
416 ExpressionStep::Group(value)
417 | ExpressionStep::WhereExists(value)
418 | ExpressionStep::FixedPoint(value) => Arc::strong_count(value),
419 ExpressionStep::Filter(_)
420 | ExpressionStep::Match(_)
421 | ExpressionStep::Paths(_) => {
422 return Err(QueryError::query(
423 step.range.clone(),
424 "typed native filter, match and path payloads are unsupported",
425 ));
426 }
427 ExpressionStep::Emit { .. } => {
428 return Err(QueryError::query(
429 step.range.clone(),
430 "typed report values must be pure",
431 ));
432 }
433 _ => 1,
434 };
435 if owners != 1 {
436 return Err(QueryError::query(
437 step.range.clone(),
438 "typed native syntax must have one owner before lowering",
439 ));
440 }
441 let mut checked = Ok(());
442 step.value.for_each_value(|value| {
443 if checked.is_ok() {
444 checked = functions::check_value(
445 value,
446 budget,
447 nesting.saturating_sub(depth + 1),
448 )
449 .map(|queries| {
450 pending.extend(queries.into_iter().map(|query| (query, depth + 1)))
451 });
452 }
453 });
454 checked?;
455 let mut checked = Ok(());
456 step.value.for_each_payload(|ProgramNative::Query(query)| {
457 if checked.is_err() {
458 return;
459 }
460 checked = check_query(query, budget, &step.range);
461 });
462 checked?;
463 }
464 }
465 }
466 }
467 Ok(())
468}
469
470fn check_query(query: &Query, budget: &mut Budget, range: &Range<usize>) -> Result<(), QueryError> {
471 if query.is_editing()
472 || query.stages.iter().any(|stage| {
473 matches!(
474 stage.kind,
475 language::StageKind::Emit
476 | language::StageKind::Json
477 | language::StageKind::EmitDocument
478 )
479 })
480 {
481 return Err(QueryError::query(
482 range.clone(),
483 "typed report values must be pure",
484 ));
485 }
486 let location = zirql::Location::Query(range.clone());
487 budget
488 .charge(query.range.len() + query.stages.len(), location.clone())
489 .map_err(pipeline::core_error)?;
490 budget
491 .charge_results(query.stages.len(), location)
492 .map_err(pipeline::core_error)
493}
494
495fn dispose_signature(signature: zirql::SignatureType) {
496 use zirql::SignatureType as S;
497 let mut pending = vec![signature];
498 while let Some(signature) = pending.pop() {
499 match signature {
500 S::Optional(value)
501 | S::Sequence(value)
502 | S::Set(value)
503 | S::Keyed(value)
504 | S::Queue(value)
505 | S::IdentitySet(value) => pending.push(value.value),
506 S::Dictionary(key, value) => {
507 pending.push(key.value);
508 pending.push(value.value);
509 }
510 S::Tuple(values) => pending.extend(values.into_iter().map(|value| value.value)),
511 S::Record(fields) => pending.extend(fields.into_iter().map(|(_, value)| value.value)),
512 S::Value | S::Boolean | S::Integer | S::Float | S::String | S::Named(_) => {}
513 }
514 }
515}