codekingpro/portable-devtools
115k
1#2# Copyright (C) 2009-2020 the sqlparse authors and contributors3# <see AUTHORS file>4#5# This module is part of python-sqlparse and is released under6# the BSD License: https://opensource.org/licenses/BSD-3-Clause7 8from sqlparse import sql, tokens as T9 10 11class StatementSplitter:12 """Filter that split stream at individual statements"""13 14 def __init__(self):15 self._reset()16 17 def _reset(self):18 """Set the filter attributes to its default values"""19 self._in_declare = False20 self._is_create = False21 self._begin_depth = 022 23 self.consume_ws = False24 self.tokens = []25 self.level = 026 27 def _change_splitlevel(self, ttype, value):28 """Get the new split level (increase, decrease or remain equal)"""29 30 # parenthesis increase/decrease a level31 if ttype is T.Punctuation and value == '(':32 return 133 elif ttype is T.Punctuation and value == ')':34 return -135 elif ttype not in T.Keyword: # if normal token return36 return 037 38 # Everything after here is ttype = T.Keyword39 # Also to note, once entered an If statement you are done and basically40 # returning41 unified = value.upper()42 43 # three keywords begin with CREATE, but only one of them is DDL44 # DDL Create though can contain more words such as "or replace"45 if ttype is T.Keyword.DDL and unified.startswith('CREATE'):46 self._is_create = True47 return 048 49 # can have nested declare inside of being...50 if unified == 'DECLARE' and self._is_create and self._begin_depth == 0:51 self._in_declare = True52 return 153 54 if unified == 'BEGIN':55 self._begin_depth += 156 if self._is_create:57 # FIXME(andi): This makes no sense. ## this comment neither58 return 159 return 060 61 # Should this respect a preceding BEGIN?62 # In CASE ... WHEN ... END this results in a split level -1.63 # Would having multiple CASE WHEN END and a Assignment Operator64 # cause the statement to cut off prematurely?65 if unified == 'END':66 self._begin_depth = max(0, self._begin_depth - 1)67 return -168 69 if (unified in ('IF', 'FOR', 'WHILE', 'CASE')70 and self._is_create and self._begin_depth > 0):71 return 172 73 if unified in ('END IF', 'END FOR', 'END WHILE'):74 return -175 76 # Default77 return 078 79 def process(self, stream):80 """Process the stream"""81 EOS_TTYPE = T.Whitespace, T.Comment.Single82 83 # Run over all stream tokens84 for ttype, value in stream:85 # Yield token if we finished a statement and there's no whitespaces86 # It will count newline token as a non whitespace. In this context87 # whitespace ignores newlines.88 # why don't multi line comments also count?89 if self.consume_ws and ttype not in EOS_TTYPE:90 yield sql.Statement(self.tokens)91 92 # Reset filter and prepare to process next statement93 self._reset()94 95 # Change current split level (increase, decrease or remain equal)96 self.level += self._change_splitlevel(ttype, value)97 98 # Append the token to the current statement99 self.tokens.append(sql.Token(ttype, value))100 101 # Check if we get the end of a statement102 # Issue762: Allow GO (or "GO 2") as statement splitter.103 # When implementing a language toggle, it's not only to add104 # keywords it's also to change some rules, like this splitting105 # rule.106 if (self.level <= 0 and ttype is T.Punctuation and value == ';') \107 or (ttype is T.Keyword and value.split()[0] == 'GO'):108 self.consume_ws = True109 110 # Yield pending statement (if any)111 if self.tokens and not all(t.is_whitespace for t in self.tokens):112 yield sql.Statement(self.tokens)113 