Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlparse/engine/statement_splitter.py: 98%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

56 statements  

1# 

2# Copyright (C) 2009-2020 the sqlparse authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of python-sqlparse and is released under 

6# the BSD License: https://opensource.org/licenses/BSD-3-Clause 

7 

8from sqlparse import sql, tokens as T 

9 

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 = False 

20 self._in_case = False 

21 self._is_create = False 

22 self._begin_depth = 0 

23 

24 self.consume_ws = False 

25 self.tokens = [] 

26 self.level = 0 

27 

28 def _change_splitlevel(self, ttype, value): 

29 """Get the new split level (increase, decrease or remain equal)""" 

30 

31 # parenthesis increase/decrease a level 

32 if ttype is T.Punctuation and value == '(': 

33 return 1 

34 elif ttype is T.Punctuation and value == ')': 

35 return -1 

36 elif ttype not in T.Keyword: # if normal token return 

37 return 0 

38 

39 # Everything after here is ttype = T.Keyword 

40 # Also to note, once entered an If statement you are done and basically 

41 # returning 

42 unified = value.upper() 

43 

44 # three keywords begin with CREATE, but only one of them is DDL 

45 # DDL Create though can contain more words such as "or replace" 

46 if ttype is T.Keyword.DDL and unified.startswith('CREATE'): 

47 self._is_create = True 

48 return 0 

49 

50 # can have nested declare inside of being... 

51 if unified == 'DECLARE' and self._is_create and self._begin_depth == 0: 

52 self._in_declare = True 

53 return 1 

54 

55 if unified == 'BEGIN': 

56 self._begin_depth += 1 

57 if self._is_create: 

58 # FIXME(andi): This makes no sense. ## this comment neither 

59 return 1 

60 return 0 

61 

62 # BEGIN and CASE/WHEN both end with END 

63 if unified == 'END': 

64 if not self._in_case: 

65 self._begin_depth = max(0, self._begin_depth - 1) 

66 else: 

67 self._in_case = False 

68 return -1 

69 

70 if (unified in ('IF', 'FOR', 'WHILE', 'CASE') 

71 and self._is_create and self._begin_depth > 0): 

72 if unified == 'CASE': 

73 self._in_case = True 

74 return 1 

75 

76 if unified in ('END IF', 'END FOR', 'END WHILE'): 

77 return -1 

78 

79 # Default 

80 return 0 

81 

82 def process(self, stream): 

83 """Process the stream""" 

84 EOS_TTYPE = T.Whitespace, T.Comment.Single 

85 

86 # Run over all stream tokens 

87 for ttype, value in stream: 

88 # Yield token if we finished a statement and there's no whitespaces 

89 # It will count newline token as a non whitespace. In this context 

90 # whitespace ignores newlines. 

91 # why don't multi line comments also count? 

92 if self.consume_ws and ttype not in EOS_TTYPE: 

93 yield sql.Statement(self.tokens) 

94 

95 # Reset filter and prepare to process next statement 

96 self._reset() 

97 

98 # Change current split level (increase, decrease or remain equal) 

99 self.level += self._change_splitlevel(ttype, value) 

100 

101 # Append the token to the current statement 

102 self.tokens.append(sql.Token(ttype, value)) 

103 

104 # Check if we get the end of a statement 

105 # Issue762: Allow GO (or "GO 2") as statement splitter. 

106 # When implementing a language toggle, it's not only to add 

107 # keywords it's also to change some rules, like this splitting 

108 # rule. 

109 if (self.level <= 0 and ttype is T.Punctuation and value == ';') \ 

110 or (ttype is T.Keyword and value.split()[0] == 'GO'): 

111 self.consume_ws = True 

112 

113 # Yield pending statement (if any) 

114 if self.tokens and not all(t.is_whitespace for t in self.tokens): 

115 yield sql.Statement(self.tokens)