1616
1717package io .r2dbc .postgresql ;
1818
19+ import io .netty .util .collection .CharObjectHashMap ;
20+ import io .netty .util .collection .CharObjectMap ;
21+
1922import java .util .ArrayList ;
20- import java .util .Arrays ;
23+ import java .util .LinkedList ;
2124import java .util .List ;
2225
2326import static java .lang .Character .isWhitespace ;
2932 */
3033class PostgresqlSqlParser {
3134
32- private static final char [] SPECIAL_AND_OPERATOR_CHARS = {
33- '+' , '-' , '*' , '/' , '<' , '>' , '=' , '~' , '!' , '@' , '#' , '%' , '^' , '&' , '|' , '`' , '?' ,
34- '(' , ')' , '[' , ']' , ',' , ';' , ':' , '*' , '.' , '\'' , '"'
35- };
35+ private static final CharObjectMap <Object > SPECIAL_AND_OPERATOR_CHARS = new CharObjectHashMap <>();
3636
3737 static {
38- Arrays .sort (SPECIAL_AND_OPERATOR_CHARS );
38+ char [] specialCharsAndOperators = {'+' , '-' , '*' , '/' , '<' , '>' , '=' , '~' , '!' , '@' , '#' , '%' , '^' , '&' , '|' , '`' , '?' ,
39+ '(' , ')' , '[' , ']' , ',' , ';' , ':' , '*' , '.' , '\'' , '"' };
40+
41+ for (char c : specialCharsAndOperators ) {
42+ SPECIAL_AND_OPERATOR_CHARS .put (c , new Object ());
43+ }
44+ }
45+
46+ public static ParsedSql parse (String sql ) {
47+ List <ParsedSql .Token > tokens = tokenize (sql );
48+ List <ParsedSql .Statement > statements = new ArrayList <>();
49+ LinkedList <Boolean > functionBodyList = null ;
50+
51+ List <ParsedSql .Token > currentStatementTokens = new ArrayList <>(tokens .size ());
52+
53+ for (int i = 0 ; i < tokens .size (); i ++) {
54+ ParsedSql .Token current = tokens .get (i );
55+ currentStatementTokens .add (current );
56+
57+ if (current .getType () == ParsedSql .TokenType .DEFAULT ) {
58+ String currentValue = current .getValue ();
59+
60+ if (currentValue .equalsIgnoreCase ("BEGIN" )) {
61+ if (functionBodyList == null ) {
62+ functionBodyList = new LinkedList <>();
63+ }
64+ if (hasNextToken (tokens , i ) && peekNext (tokens , i ).getValue ().equalsIgnoreCase ("ATOMIC" )) {
65+ functionBodyList .add (true );
66+ } else {
67+ functionBodyList .add (false );
68+ }
69+ } else if (currentValue .equalsIgnoreCase ("END" ) && functionBodyList != null && !functionBodyList .isEmpty ()) {
70+ functionBodyList .removeLast ();
71+ }
72+ } else if (current .getType ().equals (ParsedSql .TokenType .STATEMENT_END )) {
73+ boolean inFunctionBody = false ;
74+
75+ if (functionBodyList != null ) {
76+ for (boolean b : functionBodyList ) {
77+ inFunctionBody |= b ;
78+ }
79+ }
80+ if (!inFunctionBody ) {
81+ statements .add (new ParsedSql .Statement (currentStatementTokens ));
82+ currentStatementTokens = new ArrayList <>();
83+ }
84+ }
85+ }
86+
87+ if (!currentStatementTokens .isEmpty ()) {
88+ statements .add (new ParsedSql .Statement (currentStatementTokens ));
89+ }
90+
91+ return new ParsedSql (sql , statements );
92+ }
93+
94+ private static ParsedSql .Token peekNext (List <ParsedSql .Token > tokens , int index ) {
95+ return tokens .get (index + 1 );
96+ }
97+
98+ private static boolean hasNextToken (List <ParsedSql .Token > tokens , int index ) {
99+ return tokens .size () > index + 1 ;
100+ }
101+
102+ private static char peekNext (CharSequence sequence , int index ) {
103+ return sequence .charAt (index + 1 );
104+ }
105+
106+ private static boolean hasNextToken (CharSequence sequence , int index ) {
107+ return sequence .length () > index + 1 ;
39108 }
40109
41110 private static List <ParsedSql .Token > tokenize (String sql ) {
@@ -57,12 +126,12 @@ private static List<ParsedSql.Token> tokenize(String sql) {
57126 token = getQuotedIdentifierToken (sql , i );
58127 break ;
59128 case '-' : // Possible start of double-dash comment
60- if (( i + 1 ) < sql . length () && sql . charAt ( i + 1 ) == '-' ) {
129+ if (hasNextToken ( sql , i ) && peekNext ( sql , i ) == '-' ) {
61130 token = getCommentToLineEndToken (sql , i );
62131 }
63132 break ;
64133 case '/' : // Possible start of c-style comment
65- if (( i + 1 ) < sql . length () && sql . charAt ( i + 1 ) == '*' ) {
134+ if (hasNextToken ( sql , i ) && peekNext ( sql , i ) == '*' ) {
66135 token = getBlockCommentToken (sql , i );
67136 }
68137 break ;
@@ -89,48 +158,6 @@ private static List<ParsedSql.Token> tokenize(String sql) {
89158 return tokens ;
90159 }
91160
92- public static ParsedSql parse (String sql ) {
93- List <ParsedSql .Token > tokens = tokenize (sql );
94- List <ParsedSql .Statement > statements = new ArrayList <>();
95- List <Boolean > functionBodyList = new ArrayList <>();
96-
97- List <ParsedSql .Token > currentStatementTokens = new ArrayList <>();
98- for (int i = 0 ; i < tokens .size (); i ++) {
99- ParsedSql .Token current = tokens .get (i );
100- currentStatementTokens .add (current );
101-
102- if (current .getType () == ParsedSql .TokenType .DEFAULT ) {
103- String currentValue = current .getValue ();
104-
105- if (currentValue .equalsIgnoreCase ("BEGIN" )) {
106- if (i + 1 < tokens .size () && tokens .get (i + 1 ).getValue ().equalsIgnoreCase ("ATOMIC" )) {
107- functionBodyList .add (true );
108- } else {
109- functionBodyList .add (false );
110- }
111- } else if (currentValue .equalsIgnoreCase ("END" ) && !functionBodyList .isEmpty ()) {
112- functionBodyList .remove (functionBodyList .size () - 1 );
113- }
114- } else if (current .getType ().equals (ParsedSql .TokenType .STATEMENT_END )) {
115- boolean inFunctionBody = false ;
116-
117- for (boolean b : functionBodyList ) {
118- inFunctionBody |= b ;
119- }
120- if (!inFunctionBody ) {
121- statements .add (new ParsedSql .Statement (currentStatementTokens ));
122- currentStatementTokens = new ArrayList <>();
123- }
124- }
125- }
126-
127- if (!currentStatementTokens .isEmpty ()) {
128- statements .add (new ParsedSql .Statement (currentStatementTokens ));
129- }
130-
131- return new ParsedSql (sql , statements );
132- }
133-
134161 private static ParsedSql .Token getDefaultToken (String sql , int beginIndex ) {
135162 for (int i = beginIndex + 1 ; i < sql .length (); i ++) {
136163 char c = sql .charAt (i );
@@ -142,7 +169,7 @@ private static ParsedSql.Token getDefaultToken(String sql, int beginIndex) {
142169 }
143170
144171 private static boolean isSpecialOrOperatorChar (char c ) {
145- return Arrays . binarySearch ( SPECIAL_AND_OPERATOR_CHARS , c ) >= 0 ;
172+ return SPECIAL_AND_OPERATOR_CHARS . containsKey ( c ) ;
146173 }
147174
148175 private static ParsedSql .Token getBlockCommentToken (String sql , int beginIndex ) {
0 commit comments