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        sql: &'a str,
134        output: Output,
135        output_str: &'a str,
136    },
137    /// A `hash-threshold` directive.
138    HashThreshold { threshold: u64 },
139    /// A `halt` directive.
140    Halt,
141    /// A `copy` directive.
142    Copy {
143        table_name: &'a str,
144        tsv_path: &'a str,
145    },
146    /// A `reset-server` directive
147    ResetServer,
148}
149
150/// Specifies the dialect of a sqllogictest file. Different sqllogictest runners
151/// have slightly different behavior.
152#[derive(Debug, Clone, Copy, PartialEq, Eq)]
153pub enum Mode {
154    /// In `Standard` mode, expected query output is formatted so that every
155    /// value is always on its own line, like so:
156    ///
157    ///
158    ///    query II
159    ///    SELECT * FROM VALUES (1, 2), (3, 4)
160    ///    ----
161    ///    1
162    ///    2
163    ///    3
164    ///    4
165    ///
166    /// Row boundaries are not visually represented, but they can be inferred
167    /// because the number of columns per row is specified by the `query`
168    /// directive.
169    Standard,
170
171    /// In `Cockroach` mode, expected query output is formatted so that rows
172    /// can contain multiple whitespace-separated columns:
173    ///
174    ///    query II
175    ///    SELECT * FROM VALUES (1, 2), (3, 4)
176    ///    ----
177    ///    1 2
178    ///    3 4
179    ///
180    /// This formatting, while easier to parse visually, is thoroughly
181    /// frustrating when column values contain whitespace, e.g., strings like
182    /// "one two", as there is no way to know where the column boundaries are.
183    /// We jump through some hoops to make this work. You might want to
184    /// refer to this upstream Cockroach commit [0] for additional details.
185    ///
186    /// [0]: https://github.com/cockroachdb/cockroach/commit/75c3023ec86a76fe6fb60fe1c6f00752b9784801
187    Cockroach,
188}