1use rupg_common::{Error, Result, SqlState};
4use rupg_pgcatalog::builtin;
5use rupg_sql::nodes::{CoercionForm, GroupingSetKind, List, Node, SortBy, SortByDir, SortByNulls};
6use rupg_types::oid;
7
8use crate::Analyzer;
9use crate::agg::{Kind, grouping_error};
10use crate::coerce::{AtOpt, Context};
11use crate::expr::{Expr, ExprKind};
12use crate::select::Target;
13use crate::typcache::{self, is_binary_coercible};
14use crate::typename::place;
15use crate::types;
16
17#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct SortGroup {
20 pub target: usize,
22 pub operator: u32,
24 pub sort: u32,
26 pub equal: u32,
28 pub nulls_first: bool,
30 pub hashable: bool,
32}
33
34#[derive(Clone, Debug, PartialEq, Eq)]
36pub enum GroupingSet {
37 Empty,
39 Simple(Vec<usize>),
41 Rollup(Vec<GroupingSet>),
43 Cube(Vec<GroupingSet>),
45 Sets(Vec<GroupingSet>),
47}
48
49impl GroupingSet {
50 fn expand(&self) -> Vec<Vec<usize>> {
52 fn items(set: &GroupingSet) -> &[usize] {
53 match set {
54 GroupingSet::Simple(items) => items,
55 _ => &[],
56 }
57 }
58 match self {
59 GroupingSet::Empty => vec![Vec::new()],
60 GroupingSet::Simple(items) => vec![items.clone()],
61 GroupingSet::Rollup(content) => {
62 let mut out: Vec<Vec<usize>> = (1..=content.len())
63 .rev()
64 .map(|n| content[..n].iter().flat_map(|s| items(s).iter().copied()).collect())
65 .collect();
66 out.push(Vec::new());
67 out
68 }
69 GroupingSet::Cube(content) => (0..1u32 << content.len())
70 .map(|mask| {
71 content
72 .iter()
73 .enumerate()
74 .filter(|(i, _)| mask & (1 << i) != 0)
75 .flat_map(|(_, s)| items(s).iter().copied())
76 .collect()
77 })
78 .collect(),
79 GroupingSet::Sets(content) => content.iter().flat_map(GroupingSet::expand).collect(),
80 }
81 }
82}
83
84pub fn expand_grouping_sets(
86 sets: &[GroupingSet],
87 distinct: bool,
88 limit: usize,
89) -> Option<Vec<Vec<usize>>> {
90 fn union(a: &[usize], b: &[usize]) -> Vec<usize> {
92 let mut out = a.to_vec();
93 for &item in b {
94 if !out.contains(&item) {
95 out.push(item);
96 }
97 }
98 out
99 }
100 let mut count = 1.0;
101 let mut expanded = Vec::with_capacity(sets.len());
102 for set in sets {
103 let lists = set.expand();
104 count *= lists.len() as f64;
105 if count > limit as f64 {
106 return None;
107 }
108 expanded.push(lists);
109 }
110 let Some((first, rest)) = expanded.split_first() else { return Some(Vec::new()) };
111 let mut result: Vec<Vec<usize>> = first.iter().map(|l| union(&[], l)).collect();
112 for lists in rest {
113 result = result.iter().flat_map(|q| lists.iter().map(|l| union(q, l))).collect();
114 }
115 if distinct && result.len() > 1 {
116 for list in &mut result {
117 list.sort_unstable();
118 }
119 result.sort_by(|a, b| a.len().cmp(&b.len()).then_with(|| a.cmp(b)));
120 result.dedup();
121 } else {
122 result.sort_by_key(Vec::len);
123 }
124 Some(result)
125}
126
127enum Flat<'a> {
129 Item(Option<&'a Node>),
131 Row(Vec<Option<&'a Node>>),
133 Set(GroupingSetKind, Vec<Flat<'a>>, Option<usize>),
135}
136
137fn implicit_row(node: &Node) -> Option<&List> {
139 match node {
140 Node::RowExpr(row) if row.row_format == CoercionForm::COERCE_IMPLICIT_CAST => {
141 Some(&row.args)
142 }
143 _ => None,
144 }
145}
146
147fn row_items<'a>(list: &'a List, out: &mut Vec<Option<&'a Node>>) {
149 for node in list {
150 match node.as_ref().and_then(implicit_row) {
151 Some(args) => row_items(args, out),
152 None => out.push(node.as_ref()),
153 }
154 }
155}
156
157fn flatten_top<'a>(list: &'a List, has_sets: &mut bool) -> Vec<Flat<'a>> {
159 let mut out = Vec::new();
160 for node in list {
161 match node {
162 Some(Node::GroupingSet(set)) => {
163 *has_sets = true;
164 if set.kind != GroupingSetKind::GROUPING_SET_EMPTY {
165 out.push(Flat::Set(
166 set.kind,
167 flatten_content(&set.content),
168 place(set.location),
169 ));
170 }
171 }
172 node => match node.as_ref().and_then(implicit_row) {
173 Some(args) => {
174 let mut items = Vec::new();
175 row_items(args, &mut items);
176 out.extend(items.into_iter().map(Flat::Item));
177 }
178 None => out.push(Flat::Item(node.as_ref())),
179 },
180 }
181 }
182 out
183}
184
185fn flatten_content(list: &List) -> Vec<Flat<'_>> {
187 let mut out = Vec::new();
188 for node in list {
189 match node {
190 Some(Node::GroupingSet(set)) if set.kind == GroupingSetKind::GROUPING_SET_SETS => {
191 out.extend(flatten_content(&set.content));
192 }
193 Some(Node::GroupingSet(set)) => {
194 out.push(Flat::Set(set.kind, flatten_content(&set.content), place(set.location)));
195 }
196 node => match node.as_ref().and_then(implicit_row) {
197 Some(args) => {
198 let mut items = Vec::new();
199 row_items(args, &mut items);
200 out.push(Flat::Row(items));
201 }
202 None => out.push(Flat::Item(node.as_ref())),
203 },
204 }
205 }
206 out
207}
208
209fn leftmost(a: Option<usize>, b: Option<usize>) -> Option<usize> {
211 match (a, b) {
212 (Some(a), Some(b)) => Some(a.min(b)),
213 (a, b) => a.or(b),
214 }
215}
216
217pub(crate) fn list_location(list: &List) -> Option<usize> {
219 list.iter().find_map(|node| raw_location(node.as_ref()))
220}
221
222pub(crate) fn raw_location(node: Option<&Node>) -> Option<usize> {
224 match node? {
225 Node::ColumnRef(c) => place(c.location),
226 Node::SortBy(s) => raw_location(s.node.as_ref()),
227 Node::ParamRef(p) => place(p.location),
228 Node::A_Const(c) => place(c.location),
229 Node::A_ArrayExpr(a) => place(a.location),
230 Node::CaseExpr(c) => place(c.location),
231 Node::CoalesceExpr(c) => place(c.location),
232 Node::MinMaxExpr(m) => place(m.location),
233 Node::SQLValueFunction(f) => place(f.location),
234 Node::RowExpr(r) => place(r.location),
235 Node::A_Expr(a) => leftmost(place(a.location), raw_location(a.lexpr.as_ref())),
236 Node::FuncCall(f) => leftmost(place(f.location), list_location(&f.args)),
237 Node::BoolExpr(b) => leftmost(place(b.location), list_location(&b.args)),
238 Node::NullTest(n) => leftmost(place(n.location), raw_location(n.arg.as_ref())),
239 Node::BooleanTest(b) => leftmost(place(b.location), raw_location(b.arg.as_ref())),
240 Node::SubLink(s) => leftmost(raw_location(s.testexpr.as_ref()), place(s.location)),
241 Node::CollateClause(c) => raw_location(c.arg.as_ref()),
242 Node::TypeCast(t) => {
243 let at = raw_location(t.arg.as_ref());
244 let at = leftmost(at, t.typeName.as_ref().and_then(|n| place(n.location)));
245 leftmost(at, place(t.location))
246 }
247 _ => None,
248 }
249}
250
251pub(crate) fn invalid_reference(message: String, at: Option<usize>) -> Error {
253 Error::new(SqlState::INVALID_COLUMN_REFERENCE, message).at_opt(at)
254}
255
256impl Analyzer<'_> {
257 fn target_sql92(
259 &mut self,
260 node: Option<&Node>,
261 targets: &mut Vec<Target>,
262 kind: Kind,
263 ) -> Result<usize> {
264 if let Some(Node::ColumnRef(c)) = node
265 && let [Some(Node::String(name))] = c.fields.as_slice()
266 && (kind != Kind::GroupBy || self.column_by_name(name, place(c.location))?.is_none())
267 {
268 let mut found: Option<usize> = None;
269 for (i, target) in targets.iter().enumerate() {
270 if target.junk || target.name != **name {
271 continue;
272 }
273 match found {
274 Some(first) if !targets[first].expr.same(&target.expr) => {
275 return Err(Error::new(
276 SqlState::AMBIGUOUS_COLUMN,
277 format!("{} \"{name}\" is ambiguous", kind.name()),
278 )
279 .at_opt(place(c.location)));
280 }
281 Some(_) => {}
282 None => found = Some(i),
283 }
284 }
285 if let Some(i) = found {
286 self.check_sql92(&targets[i], kind)?;
287 return Ok(i);
288 }
289 }
290 if let Some(Node::A_Const(c)) = node {
291 let at = place(c.location);
292 let Some(Node::Integer(position)) = c.val.as_ref().filter(|_| !c.isnull) else {
293 return Err(Error::new(
294 SqlState::SYNTAX_ERROR,
295 format!("non-integer constant in {}", kind.name()),
296 )
297 .at_opt(at));
298 };
299 let visible = targets.iter().enumerate().filter(|(_, t)| !t.junk);
300 let mut seen = 0;
301 for (i, _) in visible {
302 seen += 1;
303 if seen == *position {
304 self.check_sql92(&targets[i], kind)?;
305 return Ok(i);
306 }
307 }
308 return Err(invalid_reference(
309 format!("{} position {position} is not in select list", kind.name()),
310 at,
311 ));
312 }
313 self.target_sql99(node, targets, kind)
314 }
315
316 fn check_sql92(&self, target: &Target, kind: Kind) -> Result<()> {
318 if kind == Kind::GroupBy
319 && self.has_aggs
320 && let Some(agg) = target.expr.first_agg()
321 {
322 return Err(grouping_error(
323 format!("aggregate functions are not allowed in {}", kind.name()),
324 agg.location,
325 ));
326 }
327 Ok(())
328 }
329
330 fn target_sql99(
332 &mut self,
333 node: Option<&Node>,
334 targets: &mut Vec<Target>,
335 kind: Kind,
336 ) -> Result<usize> {
337 let expr = self.with_kind(kind, |a| a.transform(node))?;
338 if let Some(i) = targets.iter().position(|t| t.expr.strip_implicit().same(&expr)) {
339 return Ok(i);
340 }
341 targets.push(Target { name: String::new(), expr, origin: None, junk: true });
342 Ok(targets.len() - 1)
343 }
344
345 fn resolve_unknown(&mut self, target: &mut Target) -> Result<()> {
347 if target.expr.ty == oid::UNKNOWN {
348 let expr = std::mem::replace(&mut target.expr, Expr::new(ExprKind::CaseTest, 0));
349 target.expr = self.coerce(expr, oid::TEXT, -1, Context::Implicit, None)?;
350 }
351 Ok(())
352 }
353
354 pub(crate) fn sort_clause(
356 &mut self,
357 list: &List,
358 targets: &mut Vec<Target>,
359 ) -> Result<Vec<SortGroup>> {
360 self.sort_list(list, targets, false, Kind::OrderBy)
361 }
362
363 pub(crate) fn sort_clause_sql99(
365 &mut self,
366 list: &List,
367 targets: &mut Vec<Target>,
368 ) -> Result<Vec<SortGroup>> {
369 self.sort_list(list, targets, true, Kind::OrderBy)
370 }
371
372 pub(crate) fn window_order(
374 &mut self,
375 list: &List,
376 targets: &mut Vec<Target>,
377 ) -> Result<Vec<SortGroup>> {
378 self.sort_list(list, targets, true, Kind::WindowOrder)
379 }
380
381 pub(crate) fn window_partition(
383 &mut self,
384 list: &List,
385 targets: &mut Vec<Target>,
386 order: &[SortGroup],
387 ) -> Result<Vec<SortGroup>> {
388 let mut result = Vec::new();
389 for node in list {
390 let target = self.target_sql99(node.as_ref(), targets, Kind::WindowPartition)?;
391 if result.iter().any(|g: &SortGroup| g.target == target) {
392 continue;
393 }
394 if let Some(item) = order.iter().find(|s| s.target == target) {
395 result.push(item.clone());
396 continue;
397 }
398 self.add_group(target, targets, &mut result, raw_location(node.as_ref()))?;
399 }
400 Ok(result)
401 }
402
403 fn sort_list(
404 &mut self,
405 list: &List,
406 targets: &mut Vec<Target>,
407 sql99: bool,
408 kind: Kind,
409 ) -> Result<Vec<SortGroup>> {
410 let mut sort = Vec::new();
411 for item in list {
412 let Some(Node::SortBy(by)) = item else {
413 return Err(Error::internal("an item of ORDER BY that is not SortBy"));
414 };
415 let target = if sql99 {
416 self.target_sql99(by.node.as_ref(), targets, kind)?
417 } else {
418 self.target_sql92(by.node.as_ref(), targets, Kind::OrderBy)?
419 };
420 self.add_sort(by, target, targets, &mut sort)?;
421 }
422 Ok(sort)
423 }
424
425 pub(crate) fn group_clause(
427 &mut self,
428 list: &List,
429 targets: &mut Vec<Target>,
430 sort: &[SortGroup],
431 ) -> Result<(Vec<SortGroup>, Vec<GroupingSet>)> {
432 let mut has_sets = false;
433 let flat = flatten_top(list, &mut has_sets);
434 let mut result = Vec::new();
435 let mut sets = Vec::new();
436 if flat.is_empty() && has_sets {
437 sets.push(GroupingSet::Empty);
438 }
439 let mut seen = Vec::new();
440 for item in &flat {
441 match item {
442 Flat::Set(kind, content, at) => {
443 sets.push(self.grouping_set(
444 *kind,
445 content,
446 *at,
447 targets,
448 sort,
449 &mut result,
450 )?);
451 }
452 Flat::Row(_) => return Err(Error::internal("a row at the top of GROUP BY")),
453 Flat::Item(node) => {
454 let target =
455 self.group_item(*node, Some(&mut seen), targets, sort, &mut result, true)?;
456 if let Some(target) = target
457 && has_sets
458 {
459 sets.push(GroupingSet::Simple(vec![target]));
460 }
461 }
462 }
463 }
464 Ok((result, sets))
465 }
466
467 fn group_item(
469 &mut self,
470 node: Option<&Node>,
471 seen: Option<&mut Vec<usize>>,
472 targets: &mut Vec<Target>,
473 sort: &[SortGroup],
474 result: &mut Vec<SortGroup>,
475 toplevel: bool,
476 ) -> Result<Option<usize>> {
477 let target = self.target_sql92(node, targets, Kind::GroupBy)?;
478 if let Some(seen) = seen {
479 if seen.contains(&target) {
480 return Ok(None);
481 }
482 seen.push(target);
483 }
484 if result.iter().any(|g| g.target == target) {
485 return Ok(Some(target));
486 }
487 match sort.iter().find(|s| s.target == target) {
488 Some(item) => {
489 result
490 .push(SortGroup { nulls_first: item.nulls_first && toplevel, ..item.clone() });
491 }
492 None => self.add_group(target, targets, result, raw_location(node))?,
493 }
494 Ok(Some(target))
495 }
496
497 fn grouping_set(
499 &mut self,
500 kind: GroupingSetKind,
501 content: &[Flat<'_>],
502 at: Option<usize>,
503 targets: &mut Vec<Target>,
504 sort: &[SortGroup],
505 result: &mut Vec<SortGroup>,
506 ) -> Result<GroupingSet> {
507 let mut sets = Vec::with_capacity(content.len());
508 for item in content {
509 sets.push(match item {
510 Flat::Row(nodes) => {
511 let mut seen = Vec::new();
512 let mut items = Vec::with_capacity(nodes.len());
513 for &node in nodes {
514 let target =
515 self.group_item(node, Some(&mut seen), targets, sort, result, false)?;
516 items.extend(target);
517 }
518 GroupingSet::Simple(items)
519 }
520 Flat::Set(kind, content, at) => {
521 self.grouping_set(*kind, content, *at, targets, sort, result)?
522 }
523 Flat::Item(node) => {
524 let target = self.group_item(*node, None, targets, sort, result, false)?;
525 GroupingSet::Simple(target.into_iter().collect())
526 }
527 });
528 }
529 if kind == GroupingSetKind::GROUPING_SET_CUBE && sets.len() > 12 {
531 return Err(
532 Error::new(SqlState::TOO_MANY_COLUMNS, "CUBE is limited to 12 elements").at_opt(at)
533 );
534 }
535 Ok(match kind {
536 GroupingSetKind::GROUPING_SET_EMPTY => GroupingSet::Empty,
537 GroupingSetKind::GROUPING_SET_ROLLUP => GroupingSet::Rollup(sets),
538 GroupingSetKind::GROUPING_SET_CUBE => GroupingSet::Cube(sets),
539 _ => GroupingSet::Sets(sets),
540 })
541 }
542
543 pub(crate) fn add_sort(
545 &mut self,
546 by: &SortBy,
547 target: usize,
548 targets: &mut [Target],
549 sort: &mut Vec<SortGroup>,
550 ) -> Result<()> {
551 self.resolve_unknown(&mut targets[target])?;
552 let ty = targets[target].expr.ty;
553 let at = place(by.location).or_else(|| raw_location(by.node.as_ref()));
555 let (sort_op, eq_op, reverse) = match by.sortby_dir {
556 SortByDir::SORTBY_USING => self.using_operator(&by.useOp, ty, at)?,
557 dir => {
558 let reverse = dir == SortByDir::SORTBY_DESC;
559 let ops = typcache::operators(ty);
560 let sort_op = if reverse { ops.gt } else { ops.lt };
561 if sort_op == 0 {
562 return Err(no_ordering(ty, at));
563 }
564 if ops.eq == 0 {
565 return Err(no_equality(ty, at));
566 }
567 (sort_op, ops.eq, reverse)
568 }
569 };
570 let nulls_first = match by.sortby_nulls {
571 SortByNulls::SORTBY_NULLS_FIRST => true,
572 SortByNulls::SORTBY_NULLS_LAST => false,
573 _ => reverse,
574 };
575 let commutator = |op: u32| builtin::operator_by_oid(op).map_or(0, |o| o.commutator);
577 if !sort.iter().any(|s| {
578 s.target == target && (s.operator == sort_op || commutator(s.operator) == sort_op)
579 }) {
580 sort.push(SortGroup {
581 target,
582 operator: sort_op,
583 sort: function_of(sort_op),
584 equal: function_of(eq_op),
585 nulls_first,
586 hashable: typcache::hashable(eq_op, ty),
587 });
588 }
589 Ok(())
590 }
591
592 fn using_operator(&self, names: &List, ty: u32, at: Option<usize>) -> Result<(u32, u32, bool)> {
594 let names: Vec<&str> = names
595 .iter()
596 .map(|n| match n {
597 Some(Node::String(s)) => Ok(&**s),
598 _ => Err(Error::internal("an operator name that is not a string")),
599 })
600 .collect::<Result<_>>()?;
601 let op = self.find_operator(&names, ty, ty, at)?;
602 if !is_binary_coercible(ty, op.left) || !is_binary_coercible(ty, op.right) {
604 let name = names.join(".");
605 let ty = types::name(ty);
606 return Err(Error::new(
607 SqlState::UNDEFINED_FUNCTION,
608 format!("operator requires run-time type coercion: {ty} {name} {ty}"),
609 )
610 .at_opt(at));
611 }
612 let Some((eq, reverse)) = typcache::equality_for_ordering(op.oid) else {
613 return Err(Error::new(
614 SqlState::WRONG_OBJECT_TYPE,
615 format!(
616 "operator {} is not a valid ordering operator",
617 names.last().copied().unwrap_or_default()
618 ),
619 )
620 .with_hint(
621 "Ordering operators must be \"<\" or \">\" members of btree operator families.",
622 )
623 .at_opt(at));
624 };
625 Ok((op.oid, eq, reverse))
626 }
627
628 fn add_group(
630 &mut self,
631 target: usize,
632 targets: &mut [Target],
633 list: &mut Vec<SortGroup>,
634 at: Option<usize>,
635 ) -> Result<()> {
636 self.resolve_unknown(&mut targets[target])?;
637 if list.iter().any(|s| s.target == target) {
638 return Ok(());
639 }
640 let ty = targets[target].expr.ty;
641 let ops = typcache::operators(ty);
642 if ops.eq == 0 {
643 return Err(no_equality(ty, at));
644 }
645 list.push(SortGroup {
646 target,
647 operator: ops.lt,
648 sort: function_of(ops.lt),
649 equal: function_of(ops.eq),
650 nulls_first: false,
651 hashable: typcache::hashable(ops.eq, ty),
652 });
653 Ok(())
654 }
655
656 pub(crate) fn distinct_clause(
658 &mut self,
659 targets: &mut [Target],
660 sort: &[SortGroup],
661 is_agg: bool,
662 ) -> Result<Vec<SortGroup>> {
663 let mut list = Vec::new();
664 for item in sort {
665 let target = &targets[item.target];
666 if target.junk {
667 let message = if is_agg {
668 "in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list"
669 } else {
670 "for SELECT DISTINCT, ORDER BY expressions must appear in select list"
671 };
672 return Err(invalid_reference(message.into(), target.expr.place()));
673 }
674 list.push(item.clone());
675 }
676 for i in 0..targets.len() {
677 if !targets[i].junk {
678 let at = targets[i].expr.place();
679 self.add_group(i, targets, &mut list, at)?;
680 }
681 }
682 if list.is_empty() {
683 let message = if is_agg {
684 "an aggregate with DISTINCT must have at least one argument"
685 } else {
686 "SELECT DISTINCT must have at least one column"
687 };
688 return Err(Error::new(SqlState::SYNTAX_ERROR, message));
689 }
690 Ok(list)
691 }
692
693 pub(crate) fn distinct_on_clause(
695 &mut self,
696 on: &List,
697 targets: &mut Vec<Target>,
698 sort: &[SortGroup],
699 ) -> Result<Vec<SortGroup>> {
700 let mut refs = Vec::with_capacity(on.len());
701 for node in on {
702 refs.push(self.target_sql92(node.as_ref(), targets, Kind::DistinctOn)?);
703 }
704 let mismatch = |at| {
705 invalid_reference(
706 "SELECT DISTINCT ON expressions must match initial ORDER BY expressions".into(),
707 at,
708 )
709 };
710 let mut list = Vec::new();
711 let mut skipped = false;
712 for item in sort {
713 match refs.iter().position(|&r| r == item.target) {
714 Some(_) if skipped => {
715 let first = refs.iter().position(|&r| r == item.target);
716 return Err(mismatch(first.and_then(|i| raw_location(on[i].as_ref()))));
717 }
718 Some(_) => list.push(item.clone()),
719 None => skipped = true,
720 }
721 }
722 for (node, &target) in on.iter().zip(&refs) {
723 if list.iter().any(|s| s.target == target) {
724 continue;
725 }
726 let at = raw_location(node.as_ref());
727 if skipped {
728 return Err(mismatch(at));
729 }
730 self.add_group(target, targets, &mut list, at)?;
731 }
732 Ok(list)
733 }
734
735 pub(crate) fn limit_clause(
737 &mut self,
738 node: Option<&Node>,
739 construct: &str,
740 with_ties: bool,
741 ) -> Result<Option<Expr>> {
742 let Some(node) = node else { return Ok(None) };
743 let expr = self.transform(Some(node))?;
744 let expr = self.coerce_to_specific(expr, oid::INT8, construct)?;
745 if let Some(var) = expr.first_var() {
746 return Err(invalid_reference(
747 format!("argument of {construct} must not contain variables"),
748 var.location,
749 ));
750 }
751 if with_ties && construct == "LIMIT" && matches!(node, Node::A_Const(c) if c.isnull) {
752 return Err(Error::new(
753 SqlState::INVALID_ROW_COUNT_IN_LIMIT_CLAUSE,
754 "row count cannot be null in FETCH FIRST ... WITH TIES clause",
755 ));
756 }
757 Ok(Some(expr))
758 }
759}
760
761fn function_of(operator: u32) -> u32 {
763 builtin::operator_by_oid(operator).map_or(0, |op| op.code)
764}
765
766fn no_ordering(ty: u32, at: Option<usize>) -> Error {
767 Error::new(
768 SqlState::UNDEFINED_FUNCTION,
769 format!("could not identify an ordering operator for type {}", types::name(ty)),
770 )
771 .with_hint("Use an explicit ordering operator or modify the query.")
772 .at_opt(at)
773}
774
775pub(crate) fn set_group(target: usize, ty: u32, at: Option<usize>) -> Result<SortGroup> {
777 let ops = typcache::operators(ty);
778 if ops.eq == 0 {
779 return Err(no_equality(ty, at));
780 }
781 Ok(SortGroup {
782 target,
783 operator: ops.lt,
784 sort: function_of(ops.lt),
785 equal: function_of(ops.eq),
786 nulls_first: false,
787 hashable: typcache::hashable(ops.eq, ty),
788 })
789}
790
791fn no_equality(ty: u32, at: Option<usize>) -> Error {
792 Error::new(
793 SqlState::UNDEFINED_FUNCTION,
794 format!("could not identify an equality operator for type {}", types::name(ty)),
795 )
796 .at_opt(at)
797}