2121
2222CORE_GRAMMAR = r'''
2323 ws = ~r"(\s*(/\*.*\*/)*\s*)*"
24- qs = ~r"\"([^\"]*)\"|'([^\']*)'"
25- number = ~r"-?\d+(\. \d+)?|-?\.d+"
24+ qs = ~r"\"([^\"]*)\"|'([^\']*)'|`([^\`]*)`|(\S+) "
25+ number = ~r"[-+]?(\d*\.)? \d+(e[-+]?\d+)?"i
2626 integer = ~r"-?\d+"
2727 comma = ws "," ws
2828 open_paren = ws "(" ws
3232
3333def get_keywords (grammar : str ) -> Tuple [str , ...]:
3434 """Return all all-caps words from the beginning of the line."""
35- m = re .match (r'^\s*([A-Z0-9_]+(\s+|$))+' , grammar )
35+ m = re .match (r'^\s*([A-Z0-9_]+(\s+|$|; ))+' , grammar )
3636 if not m :
3737 return tuple ()
38- return tuple (re .split (r'\s+' , m .group (0 ).strip ()))
38+ return tuple (re .split (r'\s+' , m .group (0 ).replace ( ';' , '' ). strip ()))
3939
4040
4141def process_optional (m : Any ) -> str :
@@ -322,6 +322,7 @@ class SQLHandler(NodeVisitor):
322322 #: Rule validation functions
323323 validators : Dict [str , Callable [..., Any ]] = {}
324324
325+ _grammar : str = CORE_GRAMMAR
325326 _is_compiled : bool = False
326327
327328 def __init__ (self , connection : Connection ):
@@ -347,6 +348,7 @@ def compile(cls, grammar: str = '') -> None:
347348 cls .grammar , cls .command_key , cls .rule_info , cls .help = \
348349 process_grammar (grammar or cls .__doc__ or '' )
349350
351+ cls ._grammar = grammar or cls .__doc__ or ''
350352 cls ._is_compiled = True
351353
352354 def create_result (self ) -> result .FusionSQLResult :
@@ -384,10 +386,15 @@ def execute(self, sql: str) -> result.FusionSQLResult:
384386 """
385387 type (self ).compile ()
386388 try :
387- res = self .run (self .visit (type (self ).grammar .parse (sql )))
389+ params = self .visit (type (self ).grammar .parse (sql ))
390+ for k , v in params .items ():
391+ params [k ] = self .validate_rule (k , v )
392+ res = self .run (params )
388393 if res is not None :
389394 return res
390- return result .FusionSQLResult (self .connection )
395+ res = result .FusionSQLResult (self .connection )
396+ res .set_rows ([])
397+ return res
391398 except ParseError as exc :
392399 s = str (exc )
393400 msg = ''
@@ -421,7 +428,7 @@ def run(self, params: Dict[str, Any]) -> Optional[result.FusionSQLResult]:
421428 """
422429 raise NotImplementedError
423430
424- def create_like_func (self , like : str ) -> Callable [[str ], bool ]:
431+ def create_like_func (self , like : Optional [ str ] ) -> Callable [[str ], bool ]:
425432 """
426433 Construct a function to apply the LIKE clause.
427434
@@ -457,7 +464,16 @@ def visit_qs(self, node: Node, visited_children: Iterable[Any]) -> Any:
457464 """Quoted strings."""
458465 if node is None :
459466 return None
460- return node .match .group (1 ) or node .match .group (2 )
467+ return node .match .group (1 ) or node .match .group (2 ) or \
468+ node .match .group (3 ) or node .match .group (4 )
469+
470+ def visit_number (self , node : Node , visited_children : Iterable [Any ]) -> Any :
471+ """Numeric value."""
472+ return float (node .match .group (0 ))
473+
474+ def visit_integer (self , node : Node , visited_children : Iterable [Any ]) -> Any :
475+ """Integer value."""
476+ return int (node .match .group (0 ))
461477
462478 def visit_ws (self , node : Node , visited_children : Iterable [Any ]) -> Any :
463479 """Whitespace and comments."""
@@ -505,13 +521,10 @@ def generic_visit(self, node: Node, visited_children: Iterable[Any]) -> Any:
505521 # Filter out stray empty strings
506522 out = [x for x in flatten (visited_children )[n_keywords :] if x ]
507523
508- if repeats :
509- return {node .expr_name : self . validate_rule ( node . expr_name , out ) }
524+ if repeats or len ( out ) > 1 :
525+ return {node .expr_name : out }
510526
511- return {
512- node .expr_name :
513- self .validate_rule (node .expr_name , out [0 ]) if out else True ,
514- }
527+ return {node .expr_name : out [0 ] if out else True }
515528
516529 if hasattr (node , 'match' ):
517530 if not visited_children and not node .match .groups ():
0 commit comments