Skip to main content

rupg_types/
tsquery.rs

1//! The `tsquery` type of text search, from `tsqueryin`, `tsqueryout`, `tsqueryrecv` and `tsquerysend` of `tsquery.c`, with the order of `tsquery_op.c` and `tsquery_util.c`. A value is the list of its operands and operators in prefix order, as PostgreSQL keeps it: an operator comes first, then its right operand, then its left operand. The operator `!` has only the right operand.
2
3use 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
9/// `OP_NOT`: the operator `!`.
10pub const TSQUERY_NOT: u8 = 1;
11
12/// `OP_AND`: the operator `&`.
13pub const TSQUERY_AND: u8 = 2;
14
15/// `OP_OR`: the operator `|`.
16pub const TSQUERY_OR: u8 = 3;
17
18/// `OP_PHRASE`: the operator `<N>`, which `<->` writes when the distance is 1.
19pub const TSQUERY_PHRASE: u8 = 4;
20
21/// `MAXENTRYPOS`: the largest distance of the operator `<N>`.
22pub const TSQUERY_MAX_DISTANCE: u16 = 1 << 14;
23
24/// `STACKDEPTH`: the number of operators that can wait for their right operand in one level of parentheses.
25const STACK_DEPTH: usize = 32;
26
27/// The deepest tree that a value can have. PostgreSQL stops a deep tree when it reaches `max_stack_depth`, so its limit changes with the size of the frames of the functions. The functions here walk a tree by recursion, and this limit keeps them on the stack. It is the limit of the analyzer.
28const MAX_DEPTH: usize = 1000;
29
30/// The largest number of items in a binary value: `MaxAllocSize` divided by the size of a `QueryItem`.
31const MAX_ITEMS: u32 = 0x3fff_ffff / 12;
32
33/// One item of a `tsquery`.
34#[derive(Clone, Debug, PartialEq, Eq)]
35pub enum QueryItem {
36    /// `QI_VAL`: a lexeme, the bits of its weights, 8 for `A` down to 1 for `D`, and true for a prefix match.
37    Operand { word: String, weight: u8, prefix: bool },
38    /// `QI_OPR`: one of the `TSQUERY_` operators. The right operand is the next item, and the left operand is `left` items after the operator. `distance` is the distance of `<N>` and 0 for the other operators.
39    Operator { op: u8, distance: u16, left: u32 },
40}
41
42/// `tsearch_op_priority`: `!` binds the most, then `<N>`, `&` and `|`.
43fn priority(op: u8) -> i32 {
44    match op {
45        TSQUERY_NOT => 4,
46        TSQUERY_PHRASE => 3,
47        TSQUERY_AND => 2,
48        _ => 1,
49    }
50}
51
52/// `ISOPERATOR`: a character that ends a lexeme of a query.
53fn is_operator(c: char) -> bool {
54    matches!(c, '!' | '&' | '|' | '(' | ')' | '<')
55}
56
57/// The error of `check_stack_depth`.
58pub(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
65/// The error of `elog`.
66fn internal(message: String) -> TypeError {
67    TypeError::new(SqlState::INTERNAL_ERROR, message)
68}
69
70/// The error of a distance of `<N>` that is out of range.
71pub 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
80/// The table of CRC-32, `pg_crc32_table`.
81fn 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
89/// `COMP_LEGACY_CRC32`, the first key of the order of two operands. It uses the table of CRC-32 with the steps of the other bit order, so it is not the CRC-32 of zlib.
90pub 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
98/// `findoprnd`: sets `left` of each operator and checks that the items make one tree.
99///
100/// # Errors
101///
102/// `XX000` for items that are not one tree, and `54001` for a tree that is too deep.
103fn link(items: &mut [QueryItem]) -> Result<(), TypeError> {
104    // The operators that wait for an operand, with the number of operands that they wait for.
105    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                // The operand ends the operators that have all their operands now.
120                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/// The states of `gettoken_query_standard`.
143#[derive(Clone, Copy, PartialEq, Eq)]
144enum Wait {
145    FirstOperand,
146    Operand,
147    Operator,
148}
149
150/// The states of `gettoken_tsvector` for the lexeme of a query.
151#[derive(Clone, Copy, PartialEq, Eq)]
152enum Word {
153    Start,
154    End,
155    Next,
156    EndComplex,
157    CharComplex,
158}
159
160/// A token of a query.
161enum Token {
162    End,
163    /// `PT_ERR`, which is a syntax error.
164    Bad,
165    Operand(String, u8, bool),
166    Operator(u8, u16),
167    Open,
168    Close,
169}
170
171/// The tokenizer of `parse_tsquery`.
172#[derive(Clone, Copy, Debug, PartialEq, Eq)]
173pub enum TsQueryMode {
174    /// `gettoken_query_standard`: the syntax of `tsqueryin` and `to_tsquery`.
175    Standard,
176    /// `gettoken_query_plain`: the whole text is one operand, for `plainto_tsquery` and `phraseto_tsquery`.
177    Plain,
178    /// `gettoken_query_websearch`: the syntax of `websearch_to_tsquery`, with `-`, `or` and text in double quotes.
179    Web,
180}
181
182/// An item that the parser makes, or `QI_VALSTOP`, the place of a stop word.
183#[derive(Debug)]
184enum Pol {
185    Item(QueryItem),
186    Stop,
187}
188
189/// The items of a query in postfix order, as `pushValue`, `pushOperator` and `pushStop` add them. The function for the operands of [`tsquery_parse_with`] adds the items of each operand here.
190#[derive(Debug)]
191pub struct TsQueryPush<'a> {
192    text: &'a str,
193    items: Vec<Pol>,
194    /// The bytes of the operands with a zero byte after each one.
195    data: usize,
196}
197
198impl TsQueryPush<'_> {
199    /// `pushValue`: one lexeme.
200    ///
201    /// # Errors
202    ///
203    /// `54000` for a lexeme or a query that is too long.
204    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    /// `pushOperator`: an operator for the items before it. Only `<N>` keeps the distance.
223    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    /// `pushStop`: the place of a stop word, which the parser takes out of the query at the end.
229    pub fn stop(&mut self) {
230        self.items.push(Pol::Stop);
231    }
232}
233
234/// The function that adds the items of an operand of a query: the text of the operand, its weights and its prefix flag.
235pub type TsQueryPushVal<'f> =
236    dyn FnMut(&mut TsQueryPush<'_>, &str, u8, bool) -> Result<(), TypeError> + 'f;
237
238/// The parser of the text of a `tsquery`.
239struct Parser<'a> {
240    text: &'a str,
241    at: usize,
242    /// The number of parentheses that are open.
243    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    /// `gettoken_tsvector` with the flags of a query: a lexeme ends at an operator or a colon, and has no positions. `None` at the end of the text.
262    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    /// `get_modifiers`: the weights and the prefix flag after a colon, such as `:AB*`. Any other byte ends them.
333    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    /// `parse_phrase_operator` at a `<`: the distance of `<->` or `<N>`, or `None` when the text is not one of them. The operator must have a character after it.
355    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                // strtol gives ERANGE above the range of long, and the check takes that as out of range too.
366                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    /// The next token of the tokenizer of the mode.
385    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    /// `gettoken_query_plain`: the rest of the text as one operand.
394    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    /// `parse_or_operator`: true at the word `or` when an operand comes after it. The word must end after `or`.
405    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    /// `gettoken_query_websearch`: words with `-` for `!`, `or` for `|`, and text in double quotes for one operand. Other operators are ignored, and two operands with nothing between them have `&` between them.
424    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                                // An operator at the end must have an operand.
458                                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    /// `gettoken_query_standard`.
486    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    /// `cleanOpStack`: moves the operators that bind at least as much as `op` to the items. `!` binds to the right, so it moves only the operators that bind more.
550    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    /// `makepol`: the items of one level of parentheses in postfix order.
563    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
591/// `tsqueryin`: lexemes with the operators `!`, `&`, `|`, `<->` and `<N>` and parentheses. A lexeme can be in quotes, as in a `tsvector`, and can have weights and `*` after a colon. A query with no lexemes is empty, and the caller sends the notice of PostgreSQL for it.
592///
593/// # Errors
594///
595/// `42601` for bad syntax, `22023` for a distance out of range, `54000` for a lexeme or a query that is too long, `XX000` for more than 32 operators that wait in one level of parentheses, and `54001` for a query that is too deep.
596pub 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
603/// `parse_tsquery`: the items of a query in the syntax of the mode. `pushval` adds the items of each operand: one lexeme, a query of lexemes, or the place of a stop word. The places of the stop words go out of the query at the end, as `cleanup_tsquery_stopwords` takes them out. The flag is true when the query had only stop words, so nothing is left. The caller sends the notice of PostgreSQL for an empty query.
604///
605/// # Errors
606///
607/// The errors of [`tsquery_parse`] and of `pushval`.
608pub 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
643/// A node of the tree of `maketree`, for `clean_stopword_intree`.
644enum Node {
645    Operand(QueryItem),
646    Stop,
647    Operator { op: u8, distance: u16, right: Box<Node>, left: Option<Box<Node>> },
648}
649
650/// `findoprnd` and `maketree` for the items of a query with stop words, in prefix order.
651fn 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
668/// `clean_stopword_intree`: the tree without the stop words, or `None` when nothing is left. The two numbers are the distances that the removed words add on the left side and on the right side of the tree, which a `<N>` above it adds to its distance.
669fn 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        // `!` does not change the width of the pattern.
679        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
706/// `plaintree`: the items of a tree in prefix order.
707fn 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
722/// `infix`: the item at `at` and its operands. An operator that binds less than its parent has parentheses, and so has a `<N>` on the right side of a `<N>`.
723fn 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
771/// `tsqueryout`: the query with the operators between their operands, each lexeme in quotes, and parentheses where the order of the operators needs them. An empty query is an empty string.
772pub fn tsquery_deparse(items: &[QueryItem], out: &mut Vec<u8>) {
773    if !items.is_empty() {
774        infix(items, 0, -1, false, out);
775    }
776}
777
778/// `tsquerysend`: the number of items, then each item in prefix order. An operand is the type 1, its weights, its prefix flag and the lexeme with a zero byte after it. An operator is the type 2 and the operator, with the distance as two bytes for `<N>`.
779pub 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
799/// `tsqueryrecv`: the binary form of [`tsquery_send`].
800///
801/// # Errors
802///
803/// `08P01` for a value that is too short or a lexeme with no zero byte after it, `22021` for a lexeme that is not UTF-8, `XX000` for a bad item or items that are not one tree, which includes an empty query, and `54001` for a query that is too deep.
804pub 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
863/// The bytes of the lexemes of a query with a zero byte after each one.
864fn 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
872/// `QTNodeCompare`: an operator comes before an operand, and a larger operator code first. The operands of two equal operators compare from the right one, then a larger distance comes first. Two operands compare by the CRC of the lexeme, the larger first, then by the bytes. The weights and the prefix flag do not count.
873fn 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
909/// `CompareTSQ`, the order of the B-tree operator class: the number of items, the size of the value, then the trees.
910pub 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
921/// The items of an operator with its right and left operands.
922fn 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
931/// `join_tsqueries` and `QTN2QT`: the query `left op right`, or `!right` for `TSQUERY_NOT` with an empty `left`.
932///
933/// # Errors
934///
935/// `54000` when the lexemes are too long for one query, and `54001` for a query that is too deep.
936pub 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    // `fillQT` checks the place of each lexeme in the data of the new value.
950    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
966/// `clean_NOT_intree`: the tree with no `!`. A `|` with a `!` under it goes, and a `&` or a `<N>` keeps the operand that has no `!`.
967fn 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
987/// `tsquerytree`: the text of the query with no `!`, as an index uses it. `T` when nothing is left, and an empty string for an empty query.
988pub 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
998/// The lexemes of a query in byte order with no two equal lexemes.
999fn 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
1012/// `tsq_mcontains`: true when each lexeme of `ex` is a lexeme of `query`. The operators, the weights and the prefix flags do not count.
1013pub 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    /// The text of a query in the mode, with `stop` as the only stop word.
1029    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}