Groups Tokens that have beginning and end.
(tlist, cls, depth=0)
| 25 | |
| 26 | |
| 27 | def _group_matching(tlist, cls, depth=0): |
| 28 | """Groups Tokens that have beginning and end.""" |
| 29 | if MAX_GROUPING_DEPTH is not None and depth > MAX_GROUPING_DEPTH: |
| 30 | raise SQLParseError( |
| 31 | f"Maximum grouping depth exceeded ({MAX_GROUPING_DEPTH})." |
| 32 | ) |
| 33 | |
| 34 | # Limit the number of tokens to prevent DoS attacks |
| 35 | if MAX_GROUPING_TOKENS is not None \ |
| 36 | and len(tlist.tokens) > MAX_GROUPING_TOKENS: |
| 37 | raise SQLParseError( |
| 38 | f"Maximum number of tokens exceeded ({MAX_GROUPING_TOKENS})." |
| 39 | ) |
| 40 | |
| 41 | opens = [] |
| 42 | tidx_offset = 0 |
| 43 | token_list = list(tlist) |
| 44 | |
| 45 | for idx, token in enumerate(token_list): |
| 46 | tidx = idx - tidx_offset |
| 47 | |
| 48 | if token.is_whitespace: |
| 49 | # ~50% of tokens will be whitespace. Will checking early |
| 50 | # for them avoid 3 comparisons, but then add 1 more comparison |
| 51 | # for the other ~50% of tokens... |
| 52 | continue |
| 53 | |
| 54 | if token.is_group and not isinstance(token, cls): |
| 55 | # Check inside previously grouped (i.e. parenthesis) if group |
| 56 | # of different type is inside (i.e., case). though ideally should |
| 57 | # should check for all open/close tokens at once to avoid recursion |
| 58 | _group_matching(token, cls, depth + 1) |
| 59 | continue |
| 60 | |
| 61 | if token.match(*cls.M_OPEN): |
| 62 | opens.append(tidx) |
| 63 | |
| 64 | elif token.match(*cls.M_CLOSE): |
| 65 | try: |
| 66 | open_idx = opens.pop() |
| 67 | except IndexError: |
| 68 | # this indicates invalid sql and unbalanced tokens. |
| 69 | # instead of break, continue in case other "valid" groups exist |
| 70 | continue |
| 71 | close_idx = tidx |
| 72 | tlist.group_tokens(cls, open_idx, close_idx) |
| 73 | tidx_offset += close_idx - open_idx |
| 74 | |
| 75 | |
| 76 | def group_brackets(tlist): |
no test coverage detected
searching dependent graphs…