mz_sqllogictest/
ast.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Abstract syntax tree nodes for sqllogictest.
11
12use std::fmt;
13
14/// A location in a file.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct Location {
17    pub file: String,
18    pub line: usize,
19}
20
21impl fmt::Display for Location {
22    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
23        write!(f, "{}:{}", self.file, self.line)
24    }
25}
26
27/// The declared type of an output column in a query.
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub enum Type {
30    /// A text column. Indicated by `T`.
31    Text,
32    /// An integer column. Indicated by `I`.
33    Integer,
34    /// A "real" number column (i.e., floating point). Indicated by `R`.
35    Real,
36    /// A boolean column. Indicated by `B`. This is a CockroachDB extension.
37    Bool,
38    /// An object ID (OID) column. Indicated by `O`. This is a CockroachDB
39    /// extension.
40    Oid,
41    // Please don't add new types to this enum, unless you are adding support
42    // for a sqllogictest dialect that has already done so. These type
43    // indicators are not meant to be assertions about the output type, but
44    // rather instructions to the test runner about any necessary coercions.
45    // For example, declaring a column as an `Integer` when the query returns
46    // a floating-point will cause the runner to truncate the floating-point
47    // bit.
48    //
49    // In other words, `Bool` and `Oid` are unfortunate additions, as either
50    // can be replaced with `Text` wherever it appears.
51}
52
53/// Whether to apply sorting before checking the results of a query.
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub enum Sort {
56    /// Do not sort. Default. Indicated by the `nosort` query option.
57    No,
58    /// Sort each column in each row lexicographically. Indicated by the
59    /// `rowsort` query option.
60    Row,
61    /// Sort each value as though they're in one big list. That is, values are
62    /// sorted with no respect for column or row boundaries. Indicated by the
63    /// `valuesort` query option.
64    Value,
65}
66
67impl Sort {
68    /// Returns true if any kind of sorting should happen.
69    pub fn yes(&self) -> bool {
70        use Sort::*;
71        match self {
72            No => false,
73            Row | Value => true,
74        }
75    }
76}
77
78/// A specific assertion about the expected output of a query.
79#[derive(Debug, Clone, PartialEq, Eq)]
80pub enum Output {
81    /// The query should produce the specified values. Note that the values may
82    /// need to be sorted according to a [`Sort`] before comparison.
83    Values(Vec<String>),
84    /// There should be `num_values` results that hash to `md5`. As with
85    /// `Output::Values`, the values may need to be sorted according to a
86    /// [`Sort`] before hashing.
87    Hashed { num_values: usize, md5: String },
88}
89
90impl std::fmt::Display for Output {
91    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
92        match self {
93            Output::Values(strings) if strings.len() == 1 => f.write_str(&strings[0]),
94            _ => write!(f, "{:?}", self),
95        }
96    }
97}
98
99/// Instructions for assessing the output of a query.
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct QueryOutput<'a> {
102    pub types: Vec<Type>,
103    pub sort: Sort,
104    pub multiline: bool,
105    pub label: Option<&'a str>,
106    pub column_names: Option<Vec<mz_repr::ColumnName>>,
107    pub mode: Mode,
108    pub output: Output,
109    pub output_str: &'a str,
110}
111
112/// A single directive in a sqllogictest file.
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub enum Record<'a> {
115    // A `statement` directive.
116    Statement {
117        location: Location,
118        expected_error: Option<&'a str>,
119        rows_affected: Option<u64>,
120        sql: &'a str,
121    },
122    /// A `query` directive.
123    Query {
124        location: Location,
125        sql: &'a str,
126        output: Result<QueryOutput<'a>, &'a str>,
127    },
128    /// A `simple` directive.
129    Simple {
130        location: Location,
131        conn: Option<&'a str>,
132        user: Option<&'a str>,
133        password: Option<&'a str>,
134        sql: &'a str,
135        sort: Sort,
136        output: Output,
137        output_str: &'a str,
138    },
139    /// A `hash-threshold` directive.
140    HashThreshold { threshold: u64 },
141    /// A `halt` directive.
142    Halt,
143    /// A `copy` directive.
144    Copy {
145        table_name: &'a str,
146        tsv_path: &'a str,
147    },
148    /// A `reset-server` directive
149    ResetServer,
150}
151
152/// Specifies the dialect of a sqllogictest file. Different sqllogictest runners
153/// have slightly different behavior.
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub enum Mode {
156    /// In `Standard` mode, expected query output is formatted so that every
157    /// value is always on its own line, like so:
158    ///
159    ///
160    ///    query II
161    ///    SELECT * FROM VALUES (1, 2), (3, 4)
162    ///    ----
163    ///    1
164    ///    2
165    ///    3
166    ///    4
167    ///
168    /// Row boundaries are not visually represented, but they can be inferred
169    /// because the number of columns per row is specified by the `query`
170    /// directive.
171    Standard,
172
173    /// In `Cockroach` mode, expected query output is formatted so that rows
174    /// can contain multiple whitespace-separated columns:
175    ///
176    ///    query II
177    ///    SELECT * FROM VALUES (1, 2), (3, 4)
178    ///    ----
179    ///    1 2
180    ///    3 4
181    ///
182    /// This formatting, while easier to parse visually, is thoroughly
183    /// frustrating when column values contain whitespace, e.g., strings like
184    /// "one two", as there is no way to know where the column boundaries are.
185    /// We jump through some hoops to make this work. You might want to
186    /// refer to this upstream Cockroach commit [0] for additional details.
187    ///
188    /// [0]: https://github.com/cockroachdb/cockroach/commit/75c3023ec86a76fe6fb60fe1c6f00752b9784801
189    Cockroach,
190}