1use rupg_common::{Error, Result, SqlState};
4use rupg_sql::nodes::{
5 CTEMaterialize, CommonTableExpr, JoinType, List, Node, RangeVar, SelectStmt, SetOperation,
6 WithClause,
7};
8use rupg_types::oid;
9
10use crate::Analyzer;
11use crate::coerce::AtOpt;
12use crate::expr::Expr;
13use crate::from::{Column, Item, Relation};
14use crate::searchcycle::{self, CycleClause, SearchClause};
15use crate::select::Query;
16use crate::sort::{list_location, raw_location};
17use crate::typename::place;
18use crate::types;
19use crate::walk::{self, Walker};
20
21#[derive(Clone, Debug, PartialEq)]
23pub struct Cte {
24 pub name: String,
26 pub aliases: Vec<String>,
28 pub columns: Vec<Column>,
30 pub query: Box<Query>,
32 pub recursive: bool,
34 pub materialized: Option<bool>,
36 pub search: Option<SearchClause>,
38 pub cycle: Option<CycleClause>,
40 pub write: Option<usize>,
42}
43
44#[derive(Clone, Debug, PartialEq)]
46pub struct CteRef {
47 pub name: String,
49 pub work: Option<usize>,
51 pub write: Option<usize>,
53 pub origins: Vec<Option<(u32, i16)>>,
55 pub transition: bool,
57}
58
59#[derive(Clone, Debug)]
61pub(crate) struct CteItem {
62 name: String,
63 location: Option<usize>,
65 aliases: Vec<String>,
66 recursive: bool,
67 materialized: Option<bool>,
68 visible: bool,
70 columns: Option<Vec<Column>>,
72 query: Option<Box<Query>>,
74 written: Option<Box<Query>>,
76 work: Option<usize>,
78 search: Option<SearchClause>,
79 cycle: Option<CycleClause>,
80 write: Option<usize>,
82 returning: bool,
84 origins: Vec<Option<(u32, i16)>>,
86}
87
88impl CteItem {
89 fn new(cte: &CommonTableExpr, recursive: bool, visible: bool) -> CteItem {
90 let materialized = match cte.ctematerialized {
91 CTEMaterialize::CTEMaterializeAlways => Some(true),
92 CTEMaterialize::CTEMaterializeNever => Some(false),
93 _ => None,
94 };
95 CteItem {
96 name: cte_name(cte).to_string(),
97 location: place(cte.location),
98 aliases: strings(&cte.aliascolnames),
99 recursive,
100 materialized,
101 visible,
102 columns: None,
103 query: None,
104 written: None,
105 work: None,
106 search: None,
107 cycle: None,
108 write: None,
109 returning: false,
110 origins: Vec::new(),
111 }
112 }
113
114 fn reference_columns(&self) -> Option<Vec<Column>> {
116 let mut columns = self.columns.clone()?;
117 columns.extend(searchcycle::extra_columns(self.search.as_ref(), self.cycle.as_ref()));
118 Some(columns)
119 }
120
121 fn into_cte(self) -> Result<Cte> {
123 let (Some(columns), Some(query)) = (self.reference_columns(), self.query) else {
124 return Err(Error::internal("a query of WITH with no analysis"));
125 };
126 Ok(Cte {
127 name: self.name,
128 aliases: self.aliases,
129 columns,
130 query: self.written.unwrap_or(query),
131 recursive: self.recursive,
132 materialized: self.materialized,
133 search: self.search,
134 cycle: self.cycle,
135 write: self.write,
136 })
137 }
138
139 fn columns(&self, shown: Vec<(String, u32, i32)>) -> Result<Vec<Column>> {
141 let mut columns = Vec::with_capacity(shown.len());
142 for (i, (name, ty, typmod)) in shown.into_iter().enumerate() {
143 let name = self.aliases.get(i).cloned().unwrap_or(name);
144 let (ty, typmod) =
145 if self.recursive && ty == oid::UNKNOWN { (oid::TEXT, -1) } else { (ty, typmod) };
146 columns.push(Column { name, ty, typmod, not_null: false });
147 }
148 if columns.len() < self.aliases.len() {
149 return Err(Error::new(
150 SqlState::INVALID_COLUMN_REFERENCE,
151 format!(
152 "WITH query \"{}\" has {} columns available but {} columns specified",
153 self.name,
154 columns.len(),
155 self.aliases.len()
156 ),
157 )
158 .at_opt(self.location));
159 }
160 Ok(columns)
161 }
162}
163
164fn cte_name(cte: &CommonTableExpr) -> &str {
166 cte.ctename.as_deref().unwrap_or_default()
167}
168
169fn strings(list: &List) -> Vec<String> {
171 list.iter()
172 .filter_map(|n| match n {
173 Some(Node::String(s)) => Some(s.to_string()),
174 _ => None,
175 })
176 .collect()
177}
178
179fn ctes(with: &WithClause) -> Vec<&CommonTableExpr> {
181 with.ctes
182 .iter()
183 .filter_map(|n| match n {
184 Some(Node::CommonTableExpr(cte)) => Some(&**cte),
185 _ => None,
186 })
187 .collect()
188}
189
190fn unqualified(rv: &RangeVar) -> Option<&str> {
192 match rv.schemaname {
193 Some(_) => None,
194 None => Some(rv.relname.as_deref().unwrap_or_default()),
195 }
196}
197
198#[derive(Default)]
200struct Inner(Vec<Vec<String>>);
201
202impl Inner {
203 fn hides(&self, name: &str) -> bool {
204 self.0.iter().flatten().any(|n| n == name)
205 }
206}
207
208struct Deps<'a> {
210 names: &'a [&'a str],
211 current: usize,
212 inner: Inner,
213 depends: Vec<usize>,
214 recursive: bool,
215}
216
217impl Deps<'_> {
218 fn inner_with(&mut self, s: &SelectStmt, with: &WithClause) -> bool {
220 let list = ctes(with);
221 if with.recursive {
222 self.inner.0.push(list.iter().map(|c| cte_name(c).to_string()).collect());
223 for cte in &list {
224 walk::one(self, cte.ctequery.as_ref());
225 }
226 } else {
227 self.inner.0.push(Vec::new());
228 for cte in &list {
229 walk::one(self, cte.ctequery.as_ref());
230 if let Some(level) = self.inner.0.last_mut() {
231 level.push(cte_name(cte).to_string());
232 }
233 }
234 }
235 walk::select(self, s);
236 self.inner.0.pop();
237 false
238 }
239}
240
241impl Walker for Deps<'_> {
242 fn node(&mut self, node: &Node) -> bool {
243 match node {
244 Node::RangeVar(rv) => {
245 if let Some(name) = unqualified(rv)
246 && !self.inner.hides(name)
247 && let Some(i) = self.names.iter().position(|n| *n == name)
248 {
249 if i == self.current {
250 self.recursive = true;
251 } else if !self.depends.contains(&i) {
252 self.depends.push(i);
253 }
254 }
255 false
256 }
257 Node::SelectStmt(s) => self.select(s),
258 Node::WithClause(_) => false,
259 _ => walk::walk(self, node),
260 }
261 }
262
263 fn select(&mut self, s: &SelectStmt) -> bool {
264 match s.withClause.as_deref() {
265 Some(with) => self.inner_with(s, with),
266 None => walk::select(self, s),
267 }
268 }
269}
270
271#[derive(Clone, Copy, PartialEq, Eq)]
273enum Context {
274 Ok,
275 NonRecursiveTerm,
276 SubLink,
277 OuterJoin,
278 Intersect,
279 Except,
280}
281
282impl Context {
283 fn message(self, name: &str) -> String {
285 let place = match self {
286 Context::Ok => "",
287 Context::NonRecursiveTerm => "its non-recursive term",
288 Context::SubLink => "a subquery",
289 Context::OuterJoin => "an outer join",
290 Context::Intersect => "INTERSECT",
291 Context::Except => "EXCEPT",
292 };
293 format!("recursive reference to query \"{name}\" must not appear within {place}")
294 }
295}
296
297struct Recursion<'a> {
299 name: &'a str,
300 inner: Inner,
301 count: usize,
302 context: Context,
303 error: Option<Error>,
304}
305
306impl Recursion<'_> {
307 fn new(name: &str, context: Context) -> Recursion<'_> {
308 Recursion { name, inner: Inner::default(), count: 0, context, error: None }
309 }
310
311 fn fail(&mut self, error: Error) -> bool {
313 self.error = Some(error);
314 true
315 }
316
317 fn select_stmt(&mut self, s: &SelectStmt) -> bool {
319 let save = self.context;
320 if save != Context::Ok {
321 return walk::select(self, s);
322 }
323 let (left, right) = match s.op {
324 SetOperation::SETOP_INTERSECT if s.all => (Context::Intersect, Context::Intersect),
325 SetOperation::SETOP_INTERSECT => (save, save),
326 SetOperation::SETOP_EXCEPT if s.all => (Context::Except, Context::Except),
327 SetOperation::SETOP_EXCEPT => (save, Context::Except),
328 _ => return walk::select(self, s),
329 };
330 self.context = left;
331 if s.larg.as_deref().is_some_and(|l| self.select(l)) {
332 return true;
333 }
334 self.context = right;
335 if s.rarg.as_deref().is_some_and(|r| self.select(r)) {
336 return true;
337 }
338 self.context = save;
339 walk::list(self, &s.sortClause)
340 || walk::one(self, s.limitOffset.as_ref())
341 || walk::one(self, s.limitCount.as_ref())
342 || walk::list(self, &s.lockingClause)
343 }
344
345 fn within(&mut self, context: Context, save: Context, node: Option<&Node>) -> bool {
347 self.context = context;
348 let stop = walk::one(self, node);
349 self.context = save;
350 stop
351 }
352}
353
354impl Walker for Recursion<'_> {
355 fn node(&mut self, node: &Node) -> bool {
356 let save = self.context;
357 let outer = if save == Context::Ok { Context::OuterJoin } else { save };
358 match node {
359 Node::RangeVar(rv) => {
360 let Some(name) = unqualified(rv) else { return false };
361 if self.inner.hides(name) || name != self.name {
362 return false;
363 }
364 let at = place(rv.location);
365 if self.context != Context::Ok {
366 let message = self.context.message(self.name);
367 return self.fail(Error::new(SqlState::INVALID_RECURSION, message).at_opt(at));
368 }
369 self.count += 1;
370 if self.count > 1 {
371 let message = format!(
372 "recursive reference to query \"{}\" must not appear more than once",
373 self.name
374 );
375 return self.fail(Error::new(SqlState::INVALID_RECURSION, message).at_opt(at));
376 }
377 false
378 }
379 Node::SelectStmt(s) => self.select(s),
380 Node::WithClause(_) => false,
381 Node::JoinExpr(j) => {
382 let (larg, rarg) = (j.larg.as_ref(), j.rarg.as_ref());
383 let sides = match j.jointype {
384 JoinType::JOIN_LEFT => walk::one(self, larg) || self.within(outer, save, rarg),
385 JoinType::JOIN_FULL => {
386 self.within(outer, save, larg) || self.within(outer, save, rarg)
387 }
388 JoinType::JOIN_RIGHT => self.within(outer, save, larg) || walk::one(self, rarg),
389 _ => walk::one(self, larg) || walk::one(self, rarg),
390 };
391 sides || walk::one(self, j.quals.as_ref())
392 }
393 Node::SubLink(s) => {
394 self.within(Context::SubLink, save, s.subselect.as_ref())
395 || walk::one(self, s.testexpr.as_ref())
396 }
397 _ => walk::walk(self, node),
398 }
399 }
400
401 fn select(&mut self, s: &SelectStmt) -> bool {
402 let Some(with) = s.withClause.as_deref() else { return self.select_stmt(s) };
403 let list = ctes(with);
404 if with.recursive {
405 self.inner.0.push(list.iter().map(|c| cte_name(c).to_string()).collect());
406 for cte in &list {
407 if walk::one(self, cte.ctequery.as_ref()) {
408 return true;
409 }
410 }
411 } else {
412 self.inner.0.push(Vec::new());
413 for cte in &list {
414 if walk::one(self, cte.ctequery.as_ref()) {
415 return true;
416 }
417 if let Some(level) = self.inner.0.last_mut() {
418 level.push(cte_name(cte).to_string());
419 }
420 }
421 }
422 let stop = self.select_stmt(s);
423 self.inner.0.pop();
424 stop
425 }
426}
427
428fn check_walk(
430 name: &str,
431 context: Context,
432 f: impl FnOnce(&mut Recursion<'_>) -> bool,
433) -> Result<usize> {
434 let mut walker = Recursion::new(name, context);
435 f(&mut walker);
436 match walker.error {
437 Some(error) => Err(error),
438 None => Ok(walker.count),
439 }
440}
441
442fn check_recursion(cte: &CommonTableExpr) -> Result<()> {
444 let name = cte_name(cte);
445 let Some(Node::SelectStmt(stmt)) = &cte.ctequery else {
446 return Err(Error::new(
447 SqlState::INVALID_RECURSION,
448 format!("recursive query \"{name}\" must not contain data-modifying statements"),
449 )
450 .at_opt(place(cte.location)));
451 };
452 if stmt.op != SetOperation::SETOP_UNION {
453 return Err(Error::new(
454 SqlState::INVALID_RECURSION,
455 format!(
456 "recursive query \"{name}\" does not have the form non-recursive-term UNION [ALL] recursive-term"
457 ),
458 )
459 .at_opt(place(cte.location)));
460 }
461 if let Some(with) = stmt.withClause.as_deref() {
463 check_walk(name, Context::SubLink, |w| walk::list(w, &with.ctes))?;
464 }
465 let not_implemented = |what: &str, at: Option<usize>| {
466 Err(Error::new(
467 SqlState::FEATURE_NOT_SUPPORTED,
468 format!("{what} in a recursive query is not implemented"),
469 )
470 .at_opt(at))
471 };
472 if !stmt.sortClause.is_empty() {
473 return not_implemented("ORDER BY", list_location(&stmt.sortClause));
474 }
475 if stmt.limitOffset.is_some() {
476 return not_implemented("OFFSET", raw_location(stmt.limitOffset.as_ref()));
477 }
478 if stmt.limitCount.is_some() {
479 return not_implemented("LIMIT", raw_location(stmt.limitCount.as_ref()));
480 }
481 if !stmt.lockingClause.is_empty() {
482 return not_implemented("FOR UPDATE/SHARE", None);
483 }
484 let larg = stmt.larg.as_deref();
485 check_walk(name, Context::NonRecursiveTerm, |w| larg.is_some_and(|l| w.select(l)))?;
486 let rarg = stmt.rarg.as_deref();
487 let count = check_walk(name, Context::Ok, |w| rarg.is_some_and(|r| w.select(r)))?;
488 if count != 1 {
489 return Err(Error::internal("missing recursive reference"));
490 }
491 Ok(())
492}
493
494fn raise_outer(query: &mut Query, levels: usize) {
496 query.each_expr_mut(0, &mut |expr, depth| expr.raise_from(levels, depth + 1));
497}
498
499impl Analyzer<'_> {
500 pub(crate) fn transform_with(&mut self, with: &WithClause) -> Result<()> {
502 let list = ctes(with);
503 for (i, cte) in list.iter().enumerate() {
504 if let Some(again) = list[i + 1..].iter().find(|c| cte_name(c) == cte_name(cte)) {
505 return Err(Error::new(
506 SqlState::DUPLICATE_ALIAS,
507 format!("WITH query name \"{}\" specified more than once", cte_name(again)),
508 )
509 .at_opt(place(again.location)));
510 }
511 if !matches!(cte.ctequery, Some(Node::SelectStmt(_))) && !self.outer.is_empty() {
513 return Err(Error::new(
514 SqlState::FEATURE_NOT_SUPPORTED,
515 "WITH clause containing a data-modifying statement must be at the top level",
516 )
517 .at_opt(place(cte.location)));
518 }
519 }
520 let base = self.scope.ctes.len();
521 if !with.recursive {
522 self.scope.ctes.extend(list.iter().map(|c| CteItem::new(c, false, false)));
523 for (i, cte) in list.iter().enumerate() {
524 self.analyze_cte(base + i, cte)?;
525 self.scope.ctes[base + i].visible = true;
526 }
527 return Ok(());
528 }
529 let names: Vec<&str> = list.iter().map(|c| cte_name(c)).collect();
531 let mut order = Vec::with_capacity(list.len());
532 for (i, cte) in list.iter().enumerate() {
533 let mut deps = Deps {
534 names: &names,
535 current: i,
536 inner: Inner::default(),
537 depends: Vec::new(),
538 recursive: false,
539 };
540 walk::one(&mut deps, cte.ctequery.as_ref());
541 order.push((i, deps.depends, deps.recursive));
542 }
543 for i in 0..order.len() {
544 let Some(j) = (i..order.len()).find(|&j| order[j].1.is_empty()) else {
545 return Err(Error::new(
546 SqlState::FEATURE_NOT_SUPPORTED,
547 "mutual recursion between WITH items is not implemented",
548 )
549 .at_opt(place(list[order[i].0].location)));
550 };
551 order.swap(i, j);
552 let id = order[i].0;
553 for later in &mut order[i + 1..] {
554 later.1.retain(|d| *d != id);
555 }
556 }
557 for (id, _, recursive) in &order {
558 if *recursive {
559 check_recursion(list[*id])?;
560 }
561 }
562 self.scope
563 .ctes
564 .extend(order.iter().map(|(id, _, rec)| CteItem::new(list[*id], *rec, true)));
565 for (k, (id, _, _)) in order.iter().enumerate() {
566 self.analyze_cte(base + k, list[*id])?;
567 }
568 Ok(())
569 }
570
571 fn analyze_cte(&mut self, slot: usize, cte: &CommonTableExpr) -> Result<()> {
573 let stmt = match &cte.ctequery {
574 Some(Node::SelectStmt(stmt)) => stmt,
575 Some(node) => return self.analyze_write_cte(slot, cte, node),
576 None => return Err(Error::internal("a query of WITH with no statement")),
577 };
578 self.scope.ctes[slot].search = cte.search_clause.as_deref().map(searchcycle::search_clause);
580 if let Some(raw) = cte.cycle_clause.as_deref() {
581 self.scope.ctes[slot].cycle = Some(self.cycle_clause(raw)?);
582 }
583 if self.scope.ctes[slot].recursive {
584 self.scope.ctes[slot].work = Some(self.works);
585 self.works += 1;
586 }
587 self.parent_cte = Some(slot);
588 let query = self.sub_select(stmt, true);
590 self.parent_cte = None;
591 let query = query?;
592 let item = &self.scope.ctes[slot];
593 let shown = query.targets.iter().filter(|t| !t.junk);
594 if item.recursive {
595 let Some(columns) = &item.columns else {
596 return Err(Error::internal("a recursive query with no columns"));
597 };
598 for (i, (target, column)) in shown.zip(columns).enumerate() {
599 let expr = &target.expr;
600 if expr.ty != column.ty || expr.typmod != column.typmod {
601 return Err(Error::new(
602 SqlState::DATATYPE_MISMATCH,
603 format!(
604 "recursive query \"{}\" column {} has type {} in non-recursive term but type {} overall",
605 item.name,
606 i + 1,
607 types::with_typmod(column.ty, column.typmod),
608 types::with_typmod(expr.ty, expr.typmod)
609 ),
610 )
611 .with_hint("Cast the output of the non-recursive term to the correct type.")
612 .at_opt(expr.place()));
613 }
614 }
615 } else {
616 let shown = shown.map(|t| (t.name.clone(), t.expr.ty, t.expr.typmod)).collect();
617 let columns = item.columns(shown)?;
618 self.scope.ctes[slot].columns = Some(columns);
619 }
620 let item = &mut self.scope.ctes[slot];
621 let (search, cycle) = (item.search.as_ref(), item.cycle.as_ref());
622 if search.is_some() || cycle.is_some() {
623 let columns = item.columns.as_deref().unwrap_or_default();
624 searchcycle::check(columns, item.recursive, item.location, search, cycle, &query)?;
625 let rewritten =
626 searchcycle::rewrite(&item.name, columns, item.work, search, cycle, &query)?;
627 item.written = Some(Box::new(query));
628 item.query = Some(Box::new(rewritten));
629 return Ok(());
630 }
631 item.query = Some(Box::new(query));
632 Ok(())
633 }
634
635 fn analyze_write_cte(&mut self, slot: usize, cte: &CommonTableExpr, node: &Node) -> Result<()> {
637 let query = self.sub_level(true, |analyzer| match node {
638 Node::InsertStmt(s) => analyzer.insert(s),
639 Node::UpdateStmt(s) => analyzer.update(s),
640 Node::DeleteStmt(s) => analyzer.delete(s),
641 Node::MergeStmt(s) => analyzer.merge(s),
642 _ => Err(Error::internal("a query of WITH that is not a statement of a query")),
643 })?;
644 let returning = query.write.as_ref().and_then(|w| w.returning.as_deref());
645 let shown: Vec<&crate::select::Target> =
646 returning.map(|r| r.targets.iter().filter(|t| !t.junk).collect()).unwrap_or_default();
647 let item = &self.scope.ctes[slot];
648 let columns = item
649 .columns(shown.iter().map(|t| (t.name.clone(), t.expr.ty, t.expr.typmod)).collect())?;
650 if cte.search_clause.is_some() || cte.cycle_clause.is_some() {
651 return Err(Error::new(SqlState::SYNTAX_ERROR, "WITH query is not recursive")
652 .at_opt(item.location));
653 }
654 let item = &mut self.scope.ctes[slot];
655 item.origins = shown.iter().map(|t| t.origin).collect();
656 item.returning = returning.is_some();
657 item.columns = Some(columns);
658 item.query = Some(Box::new(query));
659 item.write = Some(self.writes);
660 self.writes += 1;
661 Ok(())
662 }
663
664 pub(crate) fn recursive_parent(&self, parent: Option<usize>) -> Option<&CteItem> {
666 let item = self.outer.last()?.ctes.get(parent?)?;
667 item.recursive.then_some(item)
668 }
669
670 pub(crate) fn work_table(&self, parent: Option<usize>) -> Option<usize> {
672 self.recursive_parent(parent).and_then(|item| item.work)
673 }
674
675 pub(crate) fn recursive_columns(
677 &mut self,
678 parent: usize,
679 leftmost: usize,
680 exprs: &[Expr],
681 ) -> Result<()> {
682 let Some(query) = self.scope.relations[leftmost].subquery.as_deref() else {
683 return Err(Error::internal("a set operation with no leftmost SELECT"));
684 };
685 let names = query.targets.iter().filter(|t| !t.junk).map(|t| t.name.clone());
686 let shown = names.zip(exprs).map(|(n, e)| (n, e.ty, e.typmod)).collect();
687 let Some(item) = self.outer.last_mut().and_then(|o| o.ctes.get_mut(parent)) else {
688 return Err(Error::internal("a recursive query that is not in the scope"));
689 };
690 item.columns = Some(item.columns(shown)?);
691 Ok(())
692 }
693
694 pub(crate) fn cte_reference(&mut self, rv: &RangeVar) -> Result<Option<Item>> {
696 let Some(name) = unqualified(rv) else { return Ok(None) };
697 let found = self.levels().find_map(|(level, scope)| {
698 scope.ctes.iter().find(|c| c.visible && c.name == name).map(|c| (level, c))
699 });
700 let Some((level, item)) = found else { return Ok(None) };
701 let Some(columns) = item.reference_columns() else {
702 return Err(Error::internal("a reference to a query of WITH with no columns"));
703 };
704 if let Some(write) = item.write {
705 if !item.returning {
706 return Err(Error::new(
707 SqlState::FEATURE_NOT_SUPPORTED,
708 format!("WITH query \"{name}\" does not have a RETURNING clause"),
709 )
710 .at_opt(place(rv.location)));
711 }
712 let mut relation = Relation::new(0, columns, None, None, None);
713 relation.cte = Some(CteRef {
714 name: name.to_string(),
715 work: None,
716 write: Some(write),
717 origins: item.origins.clone(),
718 transition: false,
719 });
720 let alias = rv.alias.as_deref();
721 let refname = alias.and_then(|a| a.aliasname.as_deref()).unwrap_or(name).to_string();
722 return self.add_relation(relation, refname, alias).map(Some);
723 }
724 let available = item.columns.as_ref().map_or(0, Vec::len);
725 let extra = columns.len() - available;
726 let mut relation = match &item.query {
727 Some(query) => {
728 let mut query = (**query).clone();
729 if level > 0 {
730 raise_outer(&mut query, level);
731 }
732 Relation::new(0, columns, Some(Box::new(query)), None, None)
733 }
734 None => Relation::new(0, columns, None, None, None),
735 };
736 let work = if item.query.is_none() { item.work } else { None };
737 relation.cte = Some(CteRef {
738 name: name.to_string(),
739 work,
740 write: None,
741 origins: Vec::new(),
742 transition: false,
743 });
744 let alias = rv.alias.as_deref();
745 let refname = alias.and_then(|a| a.aliasname.as_deref()).unwrap_or(name).to_string();
746 let given = alias.map_or(0, |a| a.colnames.len());
748 if extra > 0 && given > available {
749 return Err(Error::new(
750 SqlState::INVALID_COLUMN_REFERENCE,
751 format!(
752 "table \"{refname}\" has {available} columns available but {given} columns specified"
753 ),
754 ));
755 }
756 let hidden = if level > 0 { extra } else { 0 };
758 let mut item = self.add_relation(relation, refname, alias)?;
759 item.hide_last(hidden);
760 Ok(Some(item))
761 }
762
763 pub(crate) fn transition_reference(&mut self, rv: &RangeVar) -> Result<Option<Item>> {
765 let Some(name) = unqualified(rv) else { return Ok(None) };
766 let Some(table) = self.env.transition_table(name) else { return Ok(None) };
767 let Some(rel) = self.env.catalog().and_then(|c| c.relation(table)) else {
768 return Ok(None);
769 };
770 let columns = rel
771 .columns
772 .iter()
773 .map(|c| Column {
774 name: if c.dropped { String::new() } else { c.name.clone() },
775 ty: if c.dropped { 0 } else { c.ty },
776 typmod: c.typmod,
777 not_null: false,
778 })
779 .collect();
780 let mut relation = Relation::new(0, columns, None, None, None);
781 relation.cte = Some(CteRef {
782 name: name.to_string(),
783 work: None,
784 write: None,
785 origins: Vec::new(),
786 transition: true,
787 });
788 let alias = rv.alias.as_deref();
789 let refname = alias.and_then(|a| a.aliasname.as_deref()).unwrap_or(name).to_string();
790 self.add_relation(relation, refname, alias).map(Some)
791 }
792
793 pub(crate) fn future_cte(&self, name: &str) -> bool {
795 self.levels().any(|(_, scope)| scope.ctes.iter().any(|c| !c.visible && c.name == name))
796 }
797
798 pub(crate) fn write_with(&mut self, with: Option<&WithClause>) -> Result<()> {
800 match with {
801 Some(with) => self.transform_with(with),
802 None => Ok(()),
803 }
804 }
805
806 pub(crate) fn end_write_with(
808 &mut self,
809 query: &mut Query,
810 with: Option<&WithClause>,
811 ) -> Result<()> {
812 self.finish_auto_view(query);
813 query.recursive = with.is_some_and(|w| w.recursive);
814 query.ctes = self.take_ctes()?;
815 Ok(())
816 }
817
818 pub(crate) fn take_ctes(&mut self) -> Result<Vec<Cte>> {
820 std::mem::take(&mut self.scope.ctes).into_iter().map(CteItem::into_cte).collect()
821 }
822
823 pub(crate) fn check_recursive_aggs<'e>(
825 &self,
826 exprs: impl Iterator<Item = &'e Expr>,
827 ) -> Result<()> {
828 let self_reference =
829 self.scope.relations.iter().any(|r| r.cte.as_ref().is_some_and(|c| c.work.is_some()));
830 if !self.has_aggs || !self_reference {
831 return Ok(());
832 }
833 let at = exprs.into_iter().find_map(Expr::first_agg).and_then(Expr::place);
834 Err(Error::new(
835 SqlState::INVALID_RECURSION,
836 "aggregate functions are not allowed in a recursive query's recursive term",
837 )
838 .at_opt(at))
839 }
840}