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}