1use rupg_common::SqlState;
4
5use crate::binary::Recv;
6use crate::error::TypeError;
7use crate::tsvector::{TSVECTOR_MAX_DATA, TSVECTOR_MAX_WORD, is_space, ts_compare_string};
8
9pub const TSQUERY_NOT: u8 = 1;
11
12pub const TSQUERY_AND: u8 = 2;
14
15pub const TSQUERY_OR: u8 = 3;
17
18pub const TSQUERY_PHRASE: u8 = 4;
20
21pub const TSQUERY_MAX_DISTANCE: u16 = 1 << 14;
23
24const STACK_DEPTH: usize = 32;
26
27const MAX_DEPTH: usize = 1000;
29
30const MAX_ITEMS: u32 = 0x3fff_ffff / 12;
32
33#[derive(Clone, Debug, PartialEq, Eq)]
35pub enum QueryItem {
36 Operand { word: String, weight: u8, prefix: bool },
38 Operator { op: u8, distance: u16, left: u32 },
40}
41
42fn priority(op: u8) -> i32 {
44 match op {
45 TSQUERY_NOT => 4,
46 TSQUERY_PHRASE => 3,
47 TSQUERY_AND => 2,
48 _ => 1,
49 }
50}
51
52fn is_operator(c: char) -> bool {
54 matches!(c, '!' | '&' | '|' | '(' | ')' | '<')
55}
56
57pub(crate) fn too_deep() -> TypeError {
59 let mut error =
60 TypeError::new(SqlState::STATEMENT_TOO_COMPLEX, "stack depth limit exceeded".to_string());
61 error.hint = Some("Increase the configuration parameter \"max_stack_depth\" (currently 2048kB), after ensuring the platform's stack depth limit is adequate.".to_string());
62 error
63}
64
65fn internal(message: String) -> TypeError {
67 TypeError::new(SqlState::INTERNAL_ERROR, message)
68}
69
70pub fn tsquery_bad_distance() -> TypeError {
72 TypeError::new(
73 SqlState::INVALID_PARAMETER_VALUE,
74 format!(
75 "distance in phrase operator must be an integer value between zero and {TSQUERY_MAX_DISTANCE} inclusive"
76 ),
77 )
78}
79
80fn crc_table(index: u32) -> u32 {
82 let mut c = index;
83 for _ in 0..8 {
84 c = if c & 1 == 0 { c >> 1 } else { (c >> 1) ^ 0xedb8_8320 };
85 }
86 c
87}
88
89pub fn legacy_crc32(bytes: &[u8]) -> i32 {
91 let mut crc = u32::MAX;
92 for &b in bytes {
93 crc = crc_table(((crc >> 24) ^ u32::from(b)) & 0xff) ^ (crc << 8);
94 }
95 (!crc).cast_signed()
96}
97
98fn link(items: &mut [QueryItem]) -> Result<(), TypeError> {
104 let mut open: Vec<(usize, u8)> = Vec::new();
106 let mut lefts = Vec::new();
107 for (i, item) in items.iter().enumerate() {
108 if i > 0 && open.is_empty() {
109 return Err(internal("malformed tsquery: extra nodes".to_string()));
110 }
111 if open.len() >= MAX_DEPTH {
112 return Err(too_deep());
113 }
114 match item {
115 QueryItem::Operator { op, .. } => {
116 open.push((i, if *op == TSQUERY_NOT { 1 } else { 2 }))
117 }
118 QueryItem::Operand { .. } => {
119 while let Some((at, wait)) = open.last_mut() {
121 *wait -= 1;
122 if *wait == 1 {
123 lefts.push((*at, i + 1 - *at));
124 break;
125 }
126 open.pop();
127 }
128 }
129 }
130 }
131 if items.is_empty() || !open.is_empty() {
132 return Err(internal("malformed tsquery: operand not found".to_string()));
133 }
134 for (at, offset) in lefts {
135 if let QueryItem::Operator { left, .. } = &mut items[at] {
136 *left = u32::try_from(offset).unwrap_or(u32::MAX);
137 }
138 }
139 Ok(())
140}
141
142#[derive(Clone, Copy, PartialEq, Eq)]
144enum Wait {
145 FirstOperand,
146 Operand,
147 Operator,
148}
149
150#[derive(Clone, Copy, PartialEq, Eq)]
152enum Word {
153 Start,
154 End,
155 Next,
156 EndComplex,
157 CharComplex,
158}
159
160enum Token {
162 End,
163 Bad,
165 Operand(String, u8, bool),
166 Operator(u8, u16),
167 Open,
168 Close,
169}
170
171#[derive(Clone, Copy, Debug, PartialEq, Eq)]
173pub enum TsQueryMode {
174 Standard,
176 Plain,
178 Web,
180}
181
182#[derive(Debug)]
184enum Pol {
185 Item(QueryItem),
186 Stop,
187}
188
189#[derive(Debug)]
191pub struct TsQueryPush<'a> {
192 text: &'a str,
193 items: Vec<Pol>,
194 data: usize,
196}
197
198impl TsQueryPush<'_> {
199 pub fn value(&mut self, word: String, weight: u8, prefix: bool) -> Result<(), TypeError> {
205 if word.len() > TSVECTOR_MAX_WORD {
206 return Err(TypeError::new(
207 SqlState::PROGRAM_LIMIT_EXCEEDED,
208 format!("word is too long in tsquery: \"{}\"", self.text),
209 ));
210 }
211 if self.data > TSVECTOR_MAX_DATA {
212 return Err(TypeError::new(
213 SqlState::PROGRAM_LIMIT_EXCEEDED,
214 format!("value is too big in tsquery: \"{}\"", self.text),
215 ));
216 }
217 self.data += word.len() + 1;
218 self.items.push(Pol::Item(QueryItem::Operand { word, weight, prefix }));
219 Ok(())
220 }
221
222 pub fn operator(&mut self, op: u8, distance: u16) {
224 let distance = if op == TSQUERY_PHRASE { distance } else { 0 };
225 self.items.push(Pol::Item(QueryItem::Operator { op, distance, left: 0 }));
226 }
227
228 pub fn stop(&mut self) {
230 self.items.push(Pol::Stop);
231 }
232}
233
234pub type TsQueryPushVal<'f> =
236 dyn FnMut(&mut TsQueryPush<'_>, &str, u8, bool) -> Result<(), TypeError> + 'f;
237
238struct Parser<'a> {
240 text: &'a str,
241 at: usize,
242 count: i32,
244 wait: Wait,
245 mode: TsQueryMode,
246 push: TsQueryPush<'a>,
247}
248
249impl Parser<'_> {
250 fn syntax(&self) -> TypeError {
251 TypeError::new(
252 SqlState::SYNTAX_ERROR,
253 format!("syntax error in tsquery: \"{}\"", self.text),
254 )
255 }
256
257 fn peek(&self) -> Option<char> {
258 self.text[self.at..].chars().next()
259 }
260
261 fn word(&mut self) -> Result<Option<String>, TypeError> {
263 let web = self.mode == TsQueryMode::Web;
264 let mut word = String::new();
265 let mut state = Word::Start;
266 let mut old = Word::End;
267 loop {
268 let c = self.peek();
269 match state {
270 Word::Start => match c {
271 None => return Ok(None),
272 Some('\'') if !web => state = Word::EndComplex,
273 Some('\\') if !web => {
274 state = Word::Next;
275 old = Word::End;
276 }
277 Some(c) if is_operator(c) || (web && c == '"') => return Err(self.syntax()),
278 Some(c) if is_space(c) => {}
279 Some(c) => {
280 word.push(c);
281 state = Word::End;
282 }
283 },
284 Word::Next => match c {
285 None => {
286 return Err(TypeError::new(
287 SqlState::SYNTAX_ERROR,
288 format!("there is no escaped character: \"{}\"", self.text),
289 ));
290 }
291 Some(c) => {
292 word.push(c);
293 state = old;
294 }
295 },
296 Word::End => match c {
297 Some('\\') if !web => {
298 state = Word::Next;
299 old = Word::End;
300 }
301 None => break,
302 Some(c) if is_space(c) || is_operator(c) || c == ':' || (web && c == '"') => {
303 break;
304 }
305 Some(c) => word.push(c),
306 },
307 Word::EndComplex => match c {
308 Some('\'') => state = Word::CharComplex,
309 Some('\\') => {
310 state = Word::Next;
311 old = Word::EndComplex;
312 }
313 None => return Err(self.syntax()),
314 Some(c) => word.push(c),
315 },
316 Word::CharComplex => {
317 if c != Some('\'') {
318 if word.is_empty() {
319 return Err(self.syntax());
320 }
321 break;
322 }
323 word.push('\'');
324 state = Word::EndComplex;
325 }
326 }
327 self.at += c.map_or(0, char::len_utf8);
328 }
329 Ok(Some(word))
330 }
331
332 fn modifiers(&mut self) -> (u8, bool) {
334 let bytes = self.text.as_bytes();
335 let (mut weight, mut prefix) = (0, false);
336 if bytes.get(self.at) != Some(&b':') {
337 return (weight, prefix);
338 }
339 self.at += 1;
340 while let Some(&b) = bytes.get(self.at) {
341 match b {
342 b'a' | b'A' => weight |= 8,
343 b'b' | b'B' => weight |= 4,
344 b'c' | b'C' => weight |= 2,
345 b'd' | b'D' => weight |= 1,
346 b'*' => prefix = true,
347 _ => break,
348 }
349 self.at += 1;
350 }
351 (weight, prefix)
352 }
353
354 fn phrase(&mut self) -> Result<Option<u16>, TypeError> {
356 let bytes = self.text.as_bytes();
357 let mut p = self.at + 1;
358 let distance = match bytes.get(p) {
359 Some(b'-') => {
360 p += 1;
361 1
362 }
363 Some(d) if d.is_ascii_digit() => {
364 let digits = bytes[p..].iter().take_while(|b| b.is_ascii_digit()).count();
365 let value = bytes[p..p + digits].iter().try_fold(0u64, |v, d| {
367 v.checked_mul(10).and_then(|v| v.checked_add(u64::from(d - b'0')))
368 });
369 p += digits;
370 match value.and_then(|v| u16::try_from(v).ok()) {
371 Some(v) if v <= TSQUERY_MAX_DISTANCE => v,
372 _ => return Err(tsquery_bad_distance()),
373 }
374 }
375 _ => return Ok(None),
376 };
377 if bytes.get(p) != Some(&b'>') || p + 1 >= bytes.len() {
378 return Ok(None);
379 }
380 self.at = p + 1;
381 Ok(Some(distance))
382 }
383
384 fn token(&mut self) -> Result<Token, TypeError> {
386 match self.mode {
387 TsQueryMode::Standard => self.token_standard(),
388 TsQueryMode::Plain => Ok(self.token_plain()),
389 TsQueryMode::Web => self.token_web(),
390 }
391 }
392
393 fn token_plain(&mut self) -> Token {
395 if self.at >= self.text.len() {
396 return Token::End;
397 }
398 let word = self.text[self.at..].to_string();
399 self.at = self.text.len();
400 self.count += 1;
401 Token::Operand(word, 0, false)
402 }
403
404 fn or_operator(&mut self) -> bool {
406 let rest = &self.text[self.at..];
407 if !rest.get(..2).is_some_and(|or| or.eq_ignore_ascii_case("or")) {
408 return false;
409 }
410 let mut after = rest[2..].chars();
411 match after.next() {
412 None => return false,
413 Some(c) if c == '-' || c == '_' || c.is_ascii_alphanumeric() => return false,
414 Some(_) => {}
415 }
416 if !after.any(|c| !is_space(c)) {
417 return false;
418 }
419 self.at += 2;
420 true
421 }
422
423 fn token_web(&mut self) -> Result<Token, TypeError> {
425 loop {
426 let c = self.peek();
427 match self.wait {
428 Wait::FirstOperand | Wait::Operand => match c {
429 Some('-') => {
430 self.at += 1;
431 self.wait = Wait::Operand;
432 return Ok(Token::Operator(TSQUERY_NOT, 0));
433 }
434 Some('"') => {
435 let start = self.at + 1;
436 let len = self.text[start..].find('"').unwrap_or(self.text.len() - start);
437 let word = self.text[start..start + len].to_string();
438 self.at = (start + len + 1).min(self.text.len());
439 self.wait = Wait::Operator;
440 self.count += 1;
441 return Ok(Token::Operand(word, 0, false));
442 }
443 Some(c) if is_operator(c) => {
444 self.at += 1;
445 self.wait = Wait::Operand;
446 continue;
447 }
448 Some(c) if is_space(c) => {}
449 _ => {
450 return Ok(match self.word()? {
451 Some(word) => {
452 self.wait = Wait::Operator;
453 Token::Operand(word, 0, false)
454 }
455 None if self.wait == Wait::FirstOperand => Token::End,
456 None => {
457 self.push.stop();
459 Token::End
460 }
461 });
462 }
463 },
464 Wait::Operator => match c {
465 None => return Ok(Token::End),
466 Some(_) if self.or_operator() => {
467 self.wait = Wait::Operand;
468 return Ok(Token::Operator(TSQUERY_OR, 0));
469 }
470 Some(c) if is_operator(c) => {
471 self.at += 1;
472 continue;
473 }
474 Some(c) if !is_space(c) => {
475 self.wait = Wait::Operand;
476 return Ok(Token::Operator(TSQUERY_AND, 0));
477 }
478 Some(_) => {}
479 },
480 }
481 self.at += c.map_or(0, char::len_utf8);
482 }
483 }
484
485 fn token_standard(&mut self) -> Result<Token, TypeError> {
487 loop {
488 let c = self.peek();
489 match self.wait {
490 Wait::FirstOperand | Wait::Operand => match c {
491 Some('!') => {
492 self.at += 1;
493 self.wait = Wait::Operand;
494 return Ok(Token::Operator(TSQUERY_NOT, 0));
495 }
496 Some('(') => {
497 self.at += 1;
498 self.wait = Wait::Operand;
499 self.count += 1;
500 return Ok(Token::Open);
501 }
502 Some(':') => return Ok(Token::Bad),
503 Some(c) if is_space(c) => {}
504 _ => {
505 return match self.word()? {
506 Some(word) => {
507 let (weight, prefix) = self.modifiers();
508 self.wait = Wait::Operator;
509 Ok(Token::Operand(word, weight, prefix))
510 }
511 None if self.wait == Wait::FirstOperand => Ok(Token::End),
512 None => Err(TypeError::new(
513 SqlState::SYNTAX_ERROR,
514 format!("no operand in tsquery: \"{}\"", self.text),
515 )),
516 };
517 }
518 },
519 Wait::Operator => match c {
520 Some('&') => {
521 self.at += 1;
522 self.wait = Wait::Operand;
523 return Ok(Token::Operator(TSQUERY_AND, 0));
524 }
525 Some('|') => {
526 self.at += 1;
527 self.wait = Wait::Operand;
528 return Ok(Token::Operator(TSQUERY_OR, 0));
529 }
530 Some('<') => {
531 let Some(distance) = self.phrase()? else { return Ok(Token::Bad) };
532 self.wait = Wait::Operand;
533 return Ok(Token::Operator(TSQUERY_PHRASE, distance));
534 }
535 Some(')') => {
536 self.at += 1;
537 self.count -= 1;
538 return Ok(if self.count < 0 { Token::Bad } else { Token::Close });
539 }
540 None => return Ok(if self.count == 0 { Token::End } else { Token::Bad }),
541 Some(c) if is_space(c) => {}
542 Some(_) => return Ok(Token::Bad),
543 },
544 }
545 self.at += c.map_or(0, char::len_utf8);
546 }
547 }
548
549 fn clean(&mut self, stack: &mut Vec<(u8, u16)>, op: u8) {
551 let p = priority(op);
552 while let Some(&(top, distance)) = stack.last() {
553 let q = priority(top);
554 if (op != TSQUERY_NOT && p > q) || (op == TSQUERY_NOT && p >= q) {
555 break;
556 }
557 stack.pop();
558 self.push.operator(top, distance);
559 }
560 }
561
562 fn makepol(&mut self, depth: usize, pushval: &mut TsQueryPushVal<'_>) -> Result<(), TypeError> {
564 if depth >= MAX_DEPTH {
565 return Err(too_deep());
566 }
567 let mut stack: Vec<(u8, u16)> = Vec::new();
568 loop {
569 match self.token()? {
570 Token::End => break,
571 Token::Operand(word, weight, prefix) => {
572 pushval(&mut self.push, &word, weight, prefix)?
573 }
574 Token::Operator(op, distance) => {
575 self.clean(&mut stack, op);
576 if stack.len() == STACK_DEPTH {
577 return Err(internal("tsquery stack too small".to_string()));
578 }
579 stack.push((op, distance));
580 }
581 Token::Open => self.makepol(depth + 1, pushval)?,
582 Token::Close => break,
583 Token::Bad => return Err(self.syntax()),
584 }
585 }
586 self.clean(&mut stack, TSQUERY_OR);
587 Ok(())
588 }
589}
590
591pub fn tsquery_parse(text: &str) -> Result<Vec<QueryItem>, TypeError> {
597 let mut asis = |push: &mut TsQueryPush<'_>, word: &str, weight: u8, prefix: bool| {
598 push.value(word.to_string(), weight, prefix)
599 };
600 Ok(tsquery_parse_with(text, TsQueryMode::Standard, &mut asis)?.0)
601}
602
603pub fn tsquery_parse_with(
609 text: &str,
610 mode: TsQueryMode,
611 pushval: &mut TsQueryPushVal<'_>,
612) -> Result<(Vec<QueryItem>, bool), TypeError> {
613 let push = TsQueryPush { text, items: Vec::new(), data: 0 };
614 let mut parser = Parser { text, at: 0, count: 0, wait: Wait::FirstOperand, mode, push };
615 parser.makepol(0, pushval)?;
616 let mut items = parser.push.items;
617 if items.is_empty() {
618 return Ok((Vec::new(), false));
619 }
620 items.reverse();
621 if items.iter().any(|item| matches!(item, Pol::Stop)) {
622 let mut rest = items.into_iter();
623 let tree = tree(&mut rest, 0)?;
624 if rest.next().is_some() {
625 return Err(internal("malformed tsquery: extra nodes".to_string()));
626 }
627 return Ok(match clean_stops(tree).0 {
628 Some(tree) => (plain(tree), false),
629 None => (Vec::new(), true),
630 });
631 }
632 let mut items: Vec<QueryItem> = items
633 .into_iter()
634 .filter_map(|item| match item {
635 Pol::Item(item) => Some(item),
636 Pol::Stop => None,
637 })
638 .collect();
639 link(&mut items)?;
640 Ok((items, false))
641}
642
643enum Node {
645 Operand(QueryItem),
646 Stop,
647 Operator { op: u8, distance: u16, right: Box<Node>, left: Option<Box<Node>> },
648}
649
650fn tree(items: &mut std::vec::IntoIter<Pol>, depth: usize) -> Result<Node, TypeError> {
652 if depth >= MAX_DEPTH {
653 return Err(too_deep());
654 }
655 match items.next() {
656 None => Err(internal("malformed tsquery: operand not found".to_string())),
657 Some(Pol::Stop) => Ok(Node::Stop),
658 Some(Pol::Item(QueryItem::Operator { op, distance, .. })) => {
659 let right = Box::new(tree(items, depth + 1)?);
660 let left =
661 if op == TSQUERY_NOT { None } else { Some(Box::new(tree(items, depth + 1)?)) };
662 Ok(Node::Operator { op, distance, right, left })
663 }
664 Some(Pol::Item(item)) => Ok(Node::Operand(item)),
665 }
666}
667
668fn clean_stops(node: Node) -> (Option<Node>, i32, i32) {
670 let Node::Operator { op, distance, right, left } = node else {
671 return match node {
672 Node::Stop => (None, 0, 0),
673 node => (Some(node), 0, 0),
674 };
675 };
676 let (right, rladd, rradd) = clean_stops(*right);
677 let Some(left) = left else {
678 let node = right.map(|right| Node::Operator { op, distance, right: Box::new(right), left });
680 return (node, rladd, rradd);
681 };
682 let (left, lladd, lradd) = clean_stops(*left);
683 let phrase = op == TSQUERY_PHRASE;
684 let own = if phrase { i32::from(distance) } else { 0 };
685 match (left, right) {
686 (None, None) => {
687 let add = if phrase { lladd + own + rladd } else { lladd.max(rladd) };
688 (None, add, add)
689 }
690 (None, Some(right)) if phrase => (Some(right), lladd + own + rladd, rradd),
691 (None, Some(right)) => (Some(right), rladd, rradd),
692 (Some(left), None) if phrase => (Some(left), lladd, lradd + own + rradd),
693 (Some(left), None) => (Some(left), lladd, lradd),
694 (Some(left), Some(right)) => {
695 let (right, left) = (Box::new(right), Some(Box::new(left)));
696 if phrase {
697 let distance = u16::try_from(own + lradd + rladd).unwrap_or(u16::MAX);
698 (Some(Node::Operator { op, distance, right, left }), lladd, rradd)
699 } else {
700 (Some(Node::Operator { op, distance, right, left }), 0, 0)
701 }
702 }
703 }
704}
705
706fn plain(node: Node) -> Vec<QueryItem> {
708 match node {
709 Node::Operand(item) => vec![item],
710 Node::Stop => Vec::new(),
711 Node::Operator { op, distance, right, left: None } => {
712 let mut items = vec![QueryItem::Operator { op, distance, left: 1 }];
713 items.extend(plain(*right));
714 items
715 }
716 Node::Operator { op, distance, right, left: Some(left) } => {
717 join(op, distance, plain(*right), plain(*left))
718 }
719 }
720}
721
722fn infix(items: &[QueryItem], at: usize, parent: i32, right_phrase: bool, out: &mut Vec<u8>) {
724 match &items[at] {
725 QueryItem::Operand { word, weight, prefix } => {
726 out.push(b'\'');
727 for &b in word.as_bytes() {
728 if b == b'\'' || b == b'\\' {
729 out.push(b);
730 }
731 out.push(b);
732 }
733 out.push(b'\'');
734 if *weight != 0 || *prefix {
735 out.push(b':');
736 if *prefix {
737 out.push(b'*');
738 }
739 for (bit, letter) in [(8, b'A'), (4, b'B'), (2, b'C'), (1, b'D')] {
740 if weight & bit != 0 {
741 out.push(letter);
742 }
743 }
744 }
745 }
746 QueryItem::Operator { op, distance, left } => {
747 let p = priority(*op);
748 let paren = p < parent || (*op == TSQUERY_PHRASE && right_phrase);
749 if paren {
750 out.extend_from_slice(b"( ");
751 }
752 if *op == TSQUERY_NOT {
753 out.push(b'!');
754 } else {
755 infix(items, at + *left as usize, p, false, out);
756 match *op {
757 TSQUERY_OR => out.extend_from_slice(b" | "),
758 TSQUERY_AND => out.extend_from_slice(b" & "),
759 _ if *distance == 1 => out.extend_from_slice(b" <-> "),
760 _ => out.extend_from_slice(format!(" <{distance}> ").as_bytes()),
761 }
762 }
763 infix(items, at + 1, p, *op == TSQUERY_PHRASE, out);
764 if paren {
765 out.extend_from_slice(b" )");
766 }
767 }
768 }
769}
770
771pub fn tsquery_deparse(items: &[QueryItem], out: &mut Vec<u8>) {
773 if !items.is_empty() {
774 infix(items, 0, -1, false, out);
775 }
776}
777
778pub fn tsquery_send(items: &[QueryItem], out: &mut Vec<u8>) {
780 let count = i32::try_from(items.len()).unwrap_or(i32::MAX);
781 out.extend_from_slice(&count.to_be_bytes());
782 for item in items {
783 match item {
784 QueryItem::Operand { word, weight, prefix } => {
785 out.extend_from_slice(&[1, *weight, u8::from(*prefix)]);
786 out.extend_from_slice(word.as_bytes());
787 out.push(0);
788 }
789 QueryItem::Operator { op, distance, .. } => {
790 out.extend_from_slice(&[2, *op]);
791 if *op == TSQUERY_PHRASE {
792 out.extend_from_slice(&distance.to_be_bytes());
793 }
794 }
795 }
796 }
797}
798
799pub fn tsquery_recv(recv: &mut Recv<'_>) -> Result<Vec<QueryItem>, TypeError> {
805 let size = recv.u32()?;
806 if size > MAX_ITEMS {
807 return Err(internal("invalid size of tsquery".to_string()));
808 }
809 let mut items = Vec::with_capacity(recv.remaining().min(size as usize));
810 let mut data = 0;
811 for i in 0..size {
812 let kind = recv.bytes(1)?[0].cast_signed();
813 match kind {
814 1 => {
815 let weight = recv.bytes(1)?[0];
816 let prefix = recv.bytes(1)?[0] != 0;
817 let word = recv.cstring()?;
818 let error = if weight > 0xf {
819 "invalid tsquery: invalid weight bitmap"
820 } else if word.is_empty() {
821 "invalid tsquery: empty operand"
822 } else if word.len() > TSVECTOR_MAX_WORD {
823 "invalid tsquery: operand too long"
824 } else if data > TSVECTOR_MAX_DATA {
825 "invalid tsquery: total operand length exceeded"
826 } else {
827 ""
828 };
829 if !error.is_empty() {
830 return Err(internal(error.to_string()));
831 }
832 data += word.len() + 1;
833 items.push(QueryItem::Operand { word: word.to_string(), weight, prefix });
834 }
835 2 => {
836 let op = recv.bytes(1)?[0].cast_signed();
837 let Ok(op @ TSQUERY_NOT..=TSQUERY_PHRASE) = u8::try_from(op) else {
838 return Err(internal(format!(
839 "invalid tsquery: unrecognized operator type {op}"
840 )));
841 };
842 if i == size - 1 {
843 return Err(internal("invalid pointer to right operand".to_string()));
844 }
845 let mut distance = 0;
846 if op == TSQUERY_PHRASE {
847 distance = recv.i16()?.cast_unsigned();
848 if distance > TSQUERY_MAX_DISTANCE {
849 return Err(internal(format!(
850 "invalid tsquery: invalid phrase distance {distance}"
851 )));
852 }
853 }
854 items.push(QueryItem::Operator { op, distance, left: 0 });
855 }
856 _ => return Err(internal(format!("unrecognized tsquery node type: {kind}"))),
857 }
858 }
859 link(&mut items)?;
860 Ok(items)
861}
862
863fn data_len(items: &[QueryItem]) -> usize {
865 let words = items.iter().filter_map(|item| match item {
866 QueryItem::Operand { word, .. } => Some(word.len() + 1),
867 QueryItem::Operator { .. } => None,
868 });
869 words.sum()
870}
871
872fn node_cmp(a: &[QueryItem], i: usize, b: &[QueryItem], j: usize) -> i32 {
874 match (&a[i], &b[j]) {
875 (QueryItem::Operator { .. }, QueryItem::Operand { .. }) => -1,
876 (QueryItem::Operand { .. }, QueryItem::Operator { .. }) => 1,
877 (
878 QueryItem::Operator { op: x, distance: dx, left: lx },
879 QueryItem::Operator { op: y, distance: dy, left: ly },
880 ) => {
881 if x != y {
882 return if x > y { -1 } else { 1 };
883 }
884 let right = node_cmp(a, i + 1, b, j + 1);
885 if right != 0 {
886 return right;
887 }
888 if *x != TSQUERY_NOT {
889 let left = node_cmp(a, i + *lx as usize, b, j + *ly as usize);
890 if left != 0 {
891 return left;
892 }
893 }
894 if *x == TSQUERY_PHRASE && dx != dy {
895 return if dx > dy { -1 } else { 1 };
896 }
897 0
898 }
899 (QueryItem::Operand { word: x, .. }, QueryItem::Operand { word: y, .. }) => {
900 let (cx, cy) = (legacy_crc32(x.as_bytes()), legacy_crc32(y.as_bytes()));
901 if cx != cy {
902 return if cx > cy { -1 } else { 1 };
903 }
904 ts_compare_string(x.as_bytes(), y.as_bytes())
905 }
906 }
907}
908
909pub fn tsquery_cmp(a: &[QueryItem], b: &[QueryItem]) -> i32 {
911 if a.len() != b.len() {
912 return if a.len() < b.len() { -1 } else { 1 };
913 }
914 let (x, y) = (data_len(a), data_len(b));
915 if x != y {
916 return if x < y { -1 } else { 1 };
917 }
918 if a.is_empty() { 0 } else { node_cmp(a, 0, b, 0) }
919}
920
921fn join(op: u8, distance: u16, right: Vec<QueryItem>, left: Vec<QueryItem>) -> Vec<QueryItem> {
923 let offset = u32::try_from(right.len() + 1).unwrap_or(u32::MAX);
924 let mut items = Vec::with_capacity(1 + right.len() + left.len());
925 items.push(QueryItem::Operator { op, distance, left: offset });
926 items.extend(right);
927 items.extend(left);
928 items
929}
930
931pub fn tsquery_join(
937 op: u8,
938 distance: u16,
939 left: &[QueryItem],
940 right: &[QueryItem],
941) -> Result<Vec<QueryItem>, TypeError> {
942 let mut items = if op == TSQUERY_NOT {
943 let mut items = vec![QueryItem::Operator { op, distance: 0, left: 1 }];
944 items.extend_from_slice(right);
945 items
946 } else {
947 join(op, distance, right.to_vec(), left.to_vec())
948 };
949 let mut data = 0;
951 for item in &items {
952 if let QueryItem::Operand { word, .. } = item {
953 if data > TSVECTOR_MAX_DATA {
954 return Err(TypeError::new(
955 SqlState::PROGRAM_LIMIT_EXCEEDED,
956 "tsquery is too large".to_string(),
957 ));
958 }
959 data += word.len() + 1;
960 }
961 }
962 link(&mut items)?;
963 Ok(items)
964}
965
966fn clean_not(items: &[QueryItem], at: usize) -> Option<Vec<QueryItem>> {
968 match &items[at] {
969 QueryItem::Operand { .. } => Some(vec![items[at].clone()]),
970 QueryItem::Operator { op: TSQUERY_NOT, .. } => None,
971 QueryItem::Operator { op: TSQUERY_OR, left, .. } => {
972 let left = clean_not(items, at + *left as usize)?;
973 let right = clean_not(items, at + 1)?;
974 Some(join(TSQUERY_OR, 0, right, left))
975 }
976 QueryItem::Operator { op, distance, left } => {
977 let left = clean_not(items, at + *left as usize);
978 let right = clean_not(items, at + 1);
979 match (left, right) {
980 (Some(left), Some(right)) => Some(join(*op, *distance, right, left)),
981 (left, right) => left.or(right),
982 }
983 }
984 }
985}
986
987pub fn tsquery_tree(items: &[QueryItem]) -> String {
989 if items.is_empty() {
990 return String::new();
991 }
992 let Some(tree) = clean_not(items, 0) else { return "T".to_string() };
993 let mut out = Vec::new();
994 tsquery_deparse(&tree, &mut out);
995 String::from_utf8(out).unwrap_or_default()
996}
997
998fn sorted_words(items: &[QueryItem]) -> Vec<&[u8]> {
1000 let mut words: Vec<&[u8]> = items
1001 .iter()
1002 .filter_map(|item| match item {
1003 QueryItem::Operand { word, .. } => Some(word.as_bytes()),
1004 QueryItem::Operator { .. } => None,
1005 })
1006 .collect();
1007 words.sort_unstable();
1008 words.dedup();
1009 words
1010}
1011
1012pub fn tsquery_contains(query: &[QueryItem], ex: &[QueryItem]) -> bool {
1014 let (query, ex) = (sorted_words(query), sorted_words(ex));
1015 ex.iter().all(|word| query.binary_search(word).is_ok())
1016}
1017
1018#[cfg(test)]
1019mod tests {
1020 use super::*;
1021
1022 fn text(input: &str) -> String {
1023 let mut out = Vec::new();
1024 tsquery_deparse(&tsquery_parse(input).unwrap(), &mut out);
1025 String::from_utf8(out).unwrap()
1026 }
1027
1028 fn text_in(mode: TsQueryMode, input: &str) -> (String, bool) {
1030 let mut pushval = |push: &mut TsQueryPush<'_>, word: &str, weight: u8, prefix: bool| {
1031 if word == "stop" {
1032 push.stop();
1033 return Ok(());
1034 }
1035 push.value(word.to_string(), weight, prefix)
1036 };
1037 let (items, stops) = tsquery_parse_with(input, mode, &mut pushval).unwrap();
1038 let mut out = Vec::new();
1039 tsquery_deparse(&items, &mut out);
1040 (String::from_utf8(out).unwrap(), stops)
1041 }
1042
1043 #[test]
1044 fn modes() {
1045 let web = |input| text_in(TsQueryMode::Web, input).0;
1046 assert_eq!(web("a or -b c"), "'a' | !'b' & 'c'");
1047 assert_eq!(web("\"a b\" or_c"), "'a b' & 'or_c'");
1048 assert_eq!(web("a & | ( ) < b"), "'a' & 'b'");
1049 assert_eq!(web("it's a\\b"), "'it''s' & 'a\\\\b'");
1050 assert_eq!(web("a or"), "'a' & 'or'");
1051 assert_eq!(text_in(TsQueryMode::Plain, "a & b").0, "'a & b'");
1052 assert_eq!(text_in(TsQueryMode::Standard, "a <-> stop <-> b").0, "'a' <2> 'b'");
1053 assert_eq!(text_in(TsQueryMode::Standard, "(a <-> stop) <-> b").0, "'a' <2> 'b'");
1054 assert_eq!(text_in(TsQueryMode::Standard, "stop & !stop"), (String::new(), true));
1055 assert_eq!(text_in(TsQueryMode::Standard, "stop | a").0, "'a'");
1056 }
1057
1058 fn error(input: &str) -> (String, String) {
1059 let error = tsquery_parse(input).unwrap_err();
1060 (error.sqlstate.as_str().to_string(), error.message)
1061 }
1062
1063 #[test]
1064 fn parse_and_deparse() {
1065 assert_eq!(text("a & b | c & d"), "'a' & 'b' | 'c' & 'd'");
1066 assert_eq!(text("(a | b) & (c | d)"), "( 'a' | 'b' ) & ( 'c' | 'd' )");
1067 assert_eq!(text("a & !(b | c)"), "'a' & !( 'b' | 'c' )");
1068 assert_eq!(text("a <-> (b <-> c)"), "'a' <-> ( 'b' <-> 'c' )");
1069 assert_eq!(text("(a <-> b) <-> c"), "'a' <-> 'b' <-> 'c'");
1070 assert_eq!(text("a | b & !c <-> d"), "'a' | 'b' & !'c' <-> 'd'");
1071 assert_eq!(text("a <0> b & c <1> d"), "'a' <0> 'b' & 'c' <-> 'd'");
1072 assert_eq!(text("a:dcba* & b:"), "'a':*ABCD & 'b'");
1073 assert_eq!(text(r"'it''s' & c\'d & e\\f"), r"'it''s' & 'c''d' & 'e\\f'");
1074 assert_eq!(text("aé&ß"), "'aé' & 'ß'");
1075 assert_eq!(tsquery_parse(" ").unwrap(), Vec::new());
1076 }
1077
1078 #[test]
1079 fn parse_errors() {
1080 let syntax =
1081 |input: &str| ("42601".to_string(), format!("syntax error in tsquery: \"{input}\""));
1082 for input in [
1083 "& a", "a b", "a & (b", "a & b)", "()", "a <-", "a <->", "a <x> b", "a:x", ":a", "'a",
1084 "''", "a<b",
1085 ] {
1086 assert_eq!(error(input), syntax(input), "{input}");
1087 }
1088 assert_eq!(
1089 error("a &"),
1090 ("42601".to_string(), "no operand in tsquery: \"a &\"".to_string())
1091 );
1092 assert_eq!(error("!").1, "no operand in tsquery: \"!\"");
1093 assert_eq!(error(r"a\").1, r#"there is no escaped character: "a\""#);
1094 assert_eq!(error("a <16385> b").0, "22023");
1095 assert_eq!(error("a <99999999999999999999> b").0, "22023");
1096 assert_eq!(text("a <00016384> b"), "'a' <16384> 'b'");
1097 assert_eq!(error(&"x".repeat(2048)).0, "54000");
1098 assert_eq!(
1099 error(&format!("{}a", "!".repeat(33))),
1100 ("XX000".to_string(), "tsquery stack too small".to_string())
1101 );
1102 assert!(tsquery_parse(&format!("{}a", "!".repeat(32))).is_ok());
1103 assert_eq!(error(&format!("{}a", "(".repeat(1000))).0, "54001");
1104 assert!(tsquery_parse(&format!("{}a{}", "(".repeat(999), ")".repeat(999))).is_ok());
1105 }
1106
1107 #[test]
1108 fn order() {
1109 let cmp =
1110 |a: &str, b: &str| tsquery_cmp(&tsquery_parse(a).unwrap(), &tsquery_parse(b).unwrap());
1111 assert_eq!(legacy_crc32(b"a"), 397_917_884);
1112 assert_eq!(legacy_crc32(b"b"), -1_900_089_594);
1113 assert_eq!(
1114 (cmp("a", "b"), cmp("b", "a"), cmp("a", "a & b"), cmp("ab", "a")),
1115 (-1, 1, -1, 1)
1116 );
1117 assert_eq!((cmp("a & b", "a | b"), cmp("a <1> b", "a <2> b"), cmp("!a", "b")), (1, 1, 1));
1118 assert_eq!((cmp("a:A", "a"), cmp("a:*", "a"), cmp("a & b", "b & a")), (0, 0, 1));
1119 }
1120
1121 #[test]
1122 fn functions() {
1123 let q = |input: &str| tsquery_parse(input).unwrap();
1124 let show = |items: &[QueryItem]| {
1125 let mut out = Vec::new();
1126 tsquery_deparse(items, &mut out);
1127 String::from_utf8(out).unwrap()
1128 };
1129 let tree = |input: &str| tsquery_tree(&q(input));
1130 assert_eq!(
1131 [tree("a & !b"), tree("!a"), tree("!a | b"), tree("a <-> !b"), tree("(a | !b) & c")],
1132 ["'a'", "T", "T", "'a'", "'c'"]
1133 );
1134 assert_eq!(
1135 show(&tsquery_join(TSQUERY_AND, 0, &q("a | b"), &q("c")).unwrap()),
1136 "( 'a' | 'b' ) & 'c'"
1137 );
1138 assert_eq!(
1139 show(&tsquery_join(TSQUERY_PHRASE, 1, &q("a"), &q("b <-> c")).unwrap()),
1140 "'a' <-> ( 'b' <-> 'c' )"
1141 );
1142 assert_eq!(
1143 show(&tsquery_join(TSQUERY_NOT, 0, &[], &q("a & b")).unwrap()),
1144 "!( 'a' & 'b' )"
1145 );
1146 assert!(tsquery_contains(&q("a & b"), &q("a")));
1147 assert!(!tsquery_contains(&q("a"), &q("a & b")));
1148 assert!(tsquery_contains(&q("a & a"), &q("a & a & a")));
1149 }
1150
1151 #[test]
1152 fn binary() {
1153 let items = tsquery_parse("x <3> !y:*").unwrap();
1154 let mut out = Vec::new();
1155 tsquery_send(&items, &mut out);
1156 assert_eq!(out, b"\0\0\0\x04\x02\x04\x00\x03\x02\x01\x01\x00\x01y\x00\x01\x00\x00x\x00");
1157 assert_eq!(tsquery_recv(&mut Recv::new(&out)).unwrap(), items);
1158 let message = |bytes: &[u8]| tsquery_recv(&mut Recv::new(bytes)).unwrap_err().message;
1159 assert_eq!(message(b"\0\0\0\0"), "malformed tsquery: operand not found");
1160 assert_eq!(message(b"\0\0\0\x02\x01\0\0a\0\x01\0\0b\0"), "malformed tsquery: extra nodes");
1161 assert_eq!(message(b"\0\0\0\x01\x02\x02"), "invalid pointer to right operand");
1162 assert_eq!(message(b"\0\0\0\x02\x02\x09"), "invalid tsquery: unrecognized operator type 9");
1163 assert_eq!(message(b"\0\0\0\x01\x01\x10\0a\0"), "invalid tsquery: invalid weight bitmap");
1164 assert_eq!(message(b"\0\0\0\x01\x01\0\0\0"), "invalid tsquery: empty operand");
1165 assert_eq!(message(b"\0\0\0\x01\xff"), "unrecognized tsquery node type: -1");
1166 assert_eq!(message(b"\x7f\0\0\0"), "invalid size of tsquery");
1167 assert_eq!(
1168 message(b"\0\0\0\x03\x02\x04\x40\x01"),
1169 "invalid tsquery: invalid phrase distance 16385"
1170 );
1171 }
1172}