3
3
TSQL -- Test Suite Query Language
4
4
5
5
This module implements a subset of TSQL, namely the 'select' (or
6
- 'retrieve') queries for extracting data from test suites.
7
-
8
- PyDelphin differences to standard TSQL:
9
-
10
- * `select *` requires a `from` statement
11
- * `select * from item result` does not also include `parse` columns
6
+ 'retrieve') queries for extracting data from test suites. The general
7
+ form of a select query is::
8
+
9
+ [select] <projection> [from <tables>] [where <condition>]*
10
+
11
+ For example, the following selects item identifiers that took more
12
+ than half a second to parse::
13
+
14
+ select i-id from item where total > 500
15
+
16
+ The `select` string is necessary when querying with the generic
17
+ :func:`query` function, but is implied and thus disallowed when using
18
+ the :func:`select` function.
19
+
20
+ The `<projection>` is a list of space-separated field names (e.g.,
21
+ `i-id i-input mrs`), or the special string `*` which selects all
22
+ columns from the joined tables.
23
+
24
+ The optional `from` clause provides a list of table names (e.g.,
25
+ `item parse result`) that are joined on shared keys. The `from`
26
+ clause is required when `*` is used for the projection, but it can
27
+ also be used to select columns from non-standard tables (e.g., `i-id
28
+ from output`). Alternatively, `delphin.itsdb`-style data specifiers
29
+ (see :func:`delphin.itsdb.get_data_specifier`) may be used to specify
30
+ the table on the column name (e.g., `item:i-id`).
31
+
32
+ The `where` clause provide conditions for filtering the list of
33
+ results. Conditions are binary operations that take a column or data
34
+ specifier on the left side and an integer (e.g., `10`), a date (e.g.,
35
+ `2018-10-07`), or a string (e.g., `"sleep"`) on the right side of the
36
+ operator. The allowed conditions are:
37
+
38
+ ================ ======================================
39
+ Condition Form
40
+ ================ ======================================
41
+ Regex match ``<field> ~ "regex"``
42
+ Regex fail ``<field> !~ "regex"``
43
+ Equality ``<field> = (integer|date|"string")``
44
+ Inequality ``<field> != (integer|date|"string")``
45
+ Less-than ``<field> < (integer|date)``
46
+ Less-or-equal ``<field> <= (integer|date)``
47
+ Greater-than ``<field> > (integer|date)``
48
+ Greater-or-equal ``<field> >= (integer|date)``
49
+ ================ ======================================
50
+
51
+ Boolean operators can be used to join multiple conditions or for
52
+ negation:
53
+
54
+ =========== =====================================
55
+ Operation Form
56
+ =========== =====================================
57
+ Disjunction ``X | Y``, ``X || Y``, or ``X or Y``
58
+ Conjunction ``X & Y``, ``X && Y``, or ``X and Y``
59
+ Negation ``!X`` or ``not X``
60
+ =========== =====================================
61
+
62
+ Normally, disjunction scopes over conjunction, but parentheses may be
63
+ used to group clauses, so the following are equivalent::
64
+
65
+ ... where i-id = 10 or i-id = 20 and i-input ~ "[Dd]og"
66
+ ... where i-id = 10 or (i-id = 20 and i-input ~ "[Dd]og")
67
+
68
+ Multiple `where` clauses may also be used as a conjunction that scopes
69
+ over disjunction, so the following are equivalent::
70
+
71
+ ... where (i-id = 10 or i-id = 20) and i-input ~ "[Dd]og"
72
+ ... where i-id = 10 or i-id = 20 where i-input ~ "[Dd]og"
73
+
74
+ This facilitates query construction, where a user may want to apply
75
+ additional global constraints by appending new conditions to the query
76
+ string.
77
+
78
+ PyDelphin has several differences to standard TSQL:
79
+
80
+ * `select *` requires a `from` clause
81
+ * `select * from item result` does not also include columns from the
82
+ intervening `parse` table
12
83
* `select i-input from result` returns a matching `i-input` for every
13
84
row in `result`, rather than only the unique rows
14
85
15
- PyDelphin additions to standard TSQL:
86
+ PyDelphin also adds some features to standard TSQL:
16
87
17
88
* optional table specifications on columns (e.g., `item:i-id`)
18
- * multiple `where` clauses (e.g., `where X where Y` is the same as
19
- `where (X) and (Y)`); this helps when appending to queries
89
+ * multiple `where` clauses (as described above)
20
90
"""
21
91
22
92
import operator
31
101
### QUERY INSPECTION ##########################################################
32
102
33
103
def inspect_query (query ):
104
+ """
105
+ Parse *query* and return the interpreted query object.
106
+
107
+ Example:
108
+ >>> from delphin import tsql
109
+ >>> from pprint import pprint
110
+ >>> pprint(tsql.inspect_query(
111
+ ... 'select i-input from item where i-id < 100'))
112
+ {'querytype': 'select',
113
+ 'projection': ['i-input'],
114
+ 'tables': ['item'],
115
+ 'where': ('<', ('i-id', 100))}
116
+ """
34
117
return _parse_query (query )
35
118
36
119
### QUERY PROCESSING ##########################################################
37
120
38
121
def query (query , ts , ** kwargs ):
122
+ """
123
+ Perform *query* on the testsuite *ts*.
124
+
125
+ Note: currently only 'select' queries are supported.
126
+
127
+ Args:
128
+ query (str): TSQL query string
129
+ ts (:class:`delphin.itsdb.TestSuite`): testsuite to query over
130
+ kwargs: keyword arguments passed to the more specific query
131
+ function (e.g., :func:`select`)
132
+ Example:
133
+ >>> list(tsql.query('select i-id where i-length < 4', ts))
134
+ [[142], [1061]]
135
+ """
39
136
queryobj = _parse_query (query )
40
137
41
138
if queryobj ['querytype' ] in ('select' , 'retrieve' ):
@@ -44,7 +141,8 @@ def query(query, ts, **kwargs):
44
141
queryobj ['tables' ],
45
142
queryobj ['where' ],
46
143
ts ,
47
- ** kwargs )
144
+ mode = kwargs .get ('mode' , 'list' ),
145
+ cast = kwargs .get ('cast' , True ))
48
146
else :
49
147
# not really a syntax error; replace with TSQLError or something
50
148
# when the proper exception class exists
@@ -53,6 +151,23 @@ def query(query, ts, **kwargs):
53
151
54
152
55
153
def select (query , ts , mode = 'list' , cast = True ):
154
+ """
155
+ Perform the TSQL selection query *query* on testsuite *ts*.
156
+
157
+ Note: The `select`/`retrieve` part of the query is not included.
158
+
159
+ Args:
160
+ query (str): TSQL select query
161
+ ts (:class:`delphin.itsdb.TestSuite`): testsuite to query over
162
+ mode (str): how to return the results (see
163
+ :func:`delphin.itsdb.select_rows` for more information
164
+ about the *mode* parameter; default: `list`)
165
+ cast (bool): if `True`, values will be cast to their datatype
166
+ according to the testsuite's relations (default: `True`)
167
+ Example:
168
+ >>> list(tsql.select('i-id where i-length < 4', ts))
169
+ [[142], [1061]]
170
+ """
56
171
queryobj = _parse_select (query )
57
172
return _select (
58
173
queryobj ['projection' ],
@@ -264,7 +379,7 @@ def _parse_select(query):
264
379
265
380
if projection == '*' and not tables :
266
381
raise TSQLSyntaxError (
267
- "'select *' requires a 'from' statement " ,
382
+ "'select *' requires a 'from' clause " ,
268
383
lineno = lineno , text = token )
269
384
270
385
# verify we're at the end of the query (the '.' may have been
0 commit comments