1
1
use graph:: prelude:: BlockNumber ;
2
+ use graph:: schema:: AggregationInterval ;
2
3
use sqlparser:: ast:: {
3
- Expr , Ident , ObjectName , Offset , Query , SetExpr , Statement , TableAlias , TableFactor , Value ,
4
- VisitMut , VisitorMut ,
4
+ Expr , FunctionArg , FunctionArgExpr , Ident , ObjectName , Offset , Query , SetExpr , Statement ,
5
+ TableAlias , TableFactor , Value , VisitMut , VisitorMut ,
5
6
} ;
6
7
use sqlparser:: parser:: Parser ;
7
8
use std:: result:: Result ;
@@ -22,12 +23,18 @@ pub enum Error {
22
23
NotSelectQuery ,
23
24
#[ error( "Unknown table {0}" ) ]
24
25
UnknownTable ( String ) ,
26
+ #[ error( "Unknown aggregation interval `{1}` for table {0}" ) ]
27
+ UnknownAggregationInterval ( String , String ) ,
28
+ #[ error( "Invalid syntax for aggregation {0}" ) ]
29
+ InvalidAggregationSyntax ( String ) ,
25
30
#[ error( "Only constant numbers are supported for LIMIT and OFFSET." ) ]
26
31
UnsupportedLimitOffset ,
27
32
#[ error( "The limit of {0} is greater than the maximum allowed limit of {1}." ) ]
28
33
UnsupportedLimit ( u32 , u32 ) ,
29
34
#[ error( "The offset of {0} is greater than the maximum allowed offset of {1}." ) ]
30
35
UnsupportedOffset ( u32 , u32 ) ,
36
+ #[ error( "Qualified table names are not supported: {0}" ) ]
37
+ NoQualifiedTables ( String ) ,
31
38
}
32
39
33
40
pub struct Validator < ' a > {
@@ -151,25 +158,79 @@ impl VisitorMut for Validator<'_> {
151
158
& mut self ,
152
159
table_factor : & mut TableFactor ,
153
160
) -> ControlFlow < Self :: Break > {
161
+ /// Check whether `args` is a single string argument and return that
162
+ /// string
163
+ fn extract_string_arg ( args : & Vec < FunctionArg > ) -> Option < String > {
164
+ if args. len ( ) != 1 {
165
+ return None ;
166
+ }
167
+ match & args[ 0 ] {
168
+ FunctionArg :: Unnamed ( FunctionArgExpr :: Expr ( Expr :: Value (
169
+ Value :: SingleQuotedString ( s) ,
170
+ ) ) ) => Some ( s. clone ( ) ) ,
171
+ _ => None ,
172
+ }
173
+ }
174
+
154
175
if let TableFactor :: Table {
155
176
name, args, alias, ..
156
177
} = table_factor
157
178
{
158
- if args. is_some ( ) {
159
- return self . validate_function_name ( name) ;
179
+ if name. 0 . len ( ) != 1 {
180
+ // We do not support schema qualified table names
181
+ return ControlFlow :: Break ( Error :: NoQualifiedTables ( name. to_string ( ) ) ) ;
160
182
}
161
- let table = if let Some ( table_name) = name. 0 . last ( ) {
162
- let name = & table_name. value ;
163
- let Some ( table) = self . layout . table ( name) else {
164
- if self . ctes . contains ( name) {
165
- return ControlFlow :: Continue ( ( ) ) ;
166
- } else {
167
- return ControlFlow :: Break ( Error :: UnknownTable ( name. to_string ( ) ) ) ;
168
- }
169
- } ;
170
- table
171
- } else {
183
+ let table_name = & name. 0 [ 0 ] . value ;
184
+
185
+ // CTES override subgraph tables
186
+ if self . ctes . contains ( & table_name. to_lowercase ( ) ) && args. is_none ( ) {
172
187
return ControlFlow :: Continue ( ( ) ) ;
188
+ }
189
+
190
+ let table = match ( self . layout . table ( table_name) , args) {
191
+ ( None , None ) => {
192
+ return ControlFlow :: Break ( Error :: UnknownTable ( table_name. clone ( ) ) ) ;
193
+ }
194
+ ( Some ( _) , Some ( _) ) => {
195
+ // Table exists but has args, must be a function
196
+ return self . validate_function_name ( & name) ;
197
+ }
198
+ ( None , Some ( args) ) => {
199
+ // Table does not exist but has args, is either an
200
+ // aggregation table in the form <name>(<interval>) or
201
+ // must be a function
202
+
203
+ if !self . layout . has_aggregation ( table_name) {
204
+ // Not an aggregation, must be a function
205
+ return self . validate_function_name ( & name) ;
206
+ }
207
+
208
+ let Some ( intv) = extract_string_arg ( args) else {
209
+ // Looks like an aggregation, but argument is not a single string
210
+ return ControlFlow :: Break ( Error :: InvalidAggregationSyntax (
211
+ table_name. clone ( ) ,
212
+ ) ) ;
213
+ } ;
214
+ let Some ( intv) = intv. parse :: < AggregationInterval > ( ) . ok ( ) else {
215
+ return ControlFlow :: Break ( Error :: UnknownAggregationInterval (
216
+ table_name. clone ( ) ,
217
+ intv,
218
+ ) ) ;
219
+ } ;
220
+
221
+ let Some ( table) = self . layout . aggregation_table ( table_name, intv) else {
222
+ return self . validate_function_name ( & name) ;
223
+ } ;
224
+ table
225
+ }
226
+ ( Some ( table) , None ) => {
227
+ if !table. object . is_object_type ( ) {
228
+ // Interfaces and aggregations can not be queried
229
+ // with the table name directly
230
+ return ControlFlow :: Break ( Error :: UnknownTable ( table_name. clone ( ) ) ) ;
231
+ }
232
+ table
233
+ }
173
234
} ;
174
235
175
236
// Change 'from table [as alias]' to 'from (select {columns} from table) as alias'
0 commit comments