OmniSciDB  085a039ca4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SqlIdentifierCapturer.java
Go to the documentation of this file.
1 /*
2  * Copyright 2022 HEAVY.AI, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.apache.calcite.prepare;
17 
18 import com.google.common.collect.ImmutableList;
19 
20 import org.apache.calcite.sql.SqlBasicCall;
21 import org.apache.calcite.sql.SqlDataTypeSpec;
22 import org.apache.calcite.sql.SqlDelete;
23 import org.apache.calcite.sql.SqlIdentifier;
24 import org.apache.calcite.sql.SqlInsert;
25 import org.apache.calcite.sql.SqlJoin;
26 import org.apache.calcite.sql.SqlKind;
27 import org.apache.calcite.sql.SqlLiteral;
28 import org.apache.calcite.sql.SqlNode;
29 import org.apache.calcite.sql.SqlNodeList;
30 import org.apache.calcite.sql.SqlOrderBy;
31 import org.apache.calcite.sql.SqlSelect;
32 import org.apache.calcite.sql.SqlUpdate;
33 import org.apache.calcite.sql.SqlWith;
34 import org.apache.calcite.sql.SqlWithItem;
35 import org.apache.calcite.sql.parser.SqlParser;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 
39 import java.lang.reflect.Method;
40 import java.lang.reflect.Modifier;
41 import java.util.Collection;
42 import java.util.HashSet;
43 import java.util.IdentityHashMap;
44 import java.util.Map;
45 import java.util.Set;
46 import java.util.Stack;
47 import java.util.concurrent.ConcurrentHashMap;
48 
53 public class SqlIdentifierCapturer {
54  final static Logger HEAVYDBLOGGER =
55  LoggerFactory.getLogger(SqlIdentifierCapturer.class);
56 
57  private static final Map<Class<?>, Set<Method>> GETTERS_CACHE =
58  new ConcurrentHashMap<>();
59 
60  private IdentityHashMap<SqlNode, SqlNode> visitedNodes = new IdentityHashMap<>();
61 
62  private Stack<Set<ImmutableList<String>>> currentList = new Stack<>();
63 
64  public final Set<ImmutableList<String>> selects = new HashSet<>();
65  public final Set<ImmutableList<String>> inserts = new HashSet<>();
66  public final Set<ImmutableList<String>> updates = new HashSet<>();
67  public final Set<ImmutableList<String>> deletes = new HashSet<>();
68 
69  private final Set<ImmutableList<String>> ignore = new HashSet<>();
70 
71  { currentList.push(ignore); }
72 
73  public void scan(SqlNode root) {
74  if (null == root) {
75  return;
76  }
77 
78  if (root instanceof SqlLiteral || root instanceof SqlDataTypeSpec) {
79  return;
80  }
81 
82  if (null != visitedNodes.put(root, root)) {
83  return;
84  }
85 
86  if (root instanceof SqlNodeList) {
87  SqlNodeList snl = (SqlNodeList) root;
88  for (SqlNode node : snl) {
89  scan(node);
90  }
91  return;
92  }
93 
94  if (root instanceof SqlIdentifier) {
95  // we need all the hierachy now to deal with multischema
96  currentList.peek().add(((SqlIdentifier) root).names.reverse());
97  return;
98  }
99 
100  if (root instanceof SqlBasicCall) {
101  SqlBasicCall call = (SqlBasicCall) root;
102  if (call.getOperator().getKind() == SqlKind.ARGUMENT_ASSIGNMENT) {
103  // We have a => named parameter operator
104  // We need to ignore it as otherwise we will pick up literal args
105  // as tables, EXCEPT if it points to a CURSOR operator, as there
106  // will be at least one table inside of the CURSOR.
107  if (call.operandCount() == 0) {
108  return;
109  }
110  if (call.getOperands()[0].getKind() == SqlKind.CURSOR) {
111  SqlBasicCall cursor_call = (SqlBasicCall) call.getOperands()[0];
112  if (cursor_call.operandCount() == 0) {
113  return;
114  }
115  scan(cursor_call.getOperands()[0]);
116  return;
117  } else {
118  return;
119  }
120  } else if (call.getOperator().getKind() == SqlKind.AS) {
121  // only really interested in the first operand
122  scan(call.getOperands()[0]);
123  return;
124  }
125  }
126 
127  if (root instanceof SqlOrderBy) {
128  scan(((SqlOrderBy) root).fetch);
129  scan(((SqlOrderBy) root).offset);
130  scan(((SqlOrderBy) root).query);
131  return;
132  }
133 
134  boolean needsPop = false;
135  if (root instanceof SqlSelect) {
136  currentList.push(selects);
137  scan(((SqlSelect) root).getFrom());
138  currentList.pop();
139  currentList.push(ignore);
140  needsPop = true;
141  } else if (root instanceof SqlInsert) {
142  currentList.push(inserts);
143  scan(((SqlInsert) root).getTargetTable());
144  currentList.pop();
145  currentList.push(ignore);
146  needsPop = true;
147  } else if (root instanceof SqlUpdate) {
148  currentList.push(updates);
149  scan(((SqlUpdate) root).getTargetTable());
150  currentList.pop();
151  currentList.push(ignore);
152  needsPop = true;
153  } else if (root instanceof SqlDelete) {
154  currentList.push(deletes);
155  scan(((SqlDelete) root).getTargetTable());
156  currentList.pop();
157  currentList.push(ignore);
158  needsPop = true;
159  } else if (root instanceof SqlJoin) {
160  currentList.push(ignore);
161  scan(((SqlJoin) root).getCondition());
162  currentList.pop();
163  }
164 
165  Set<Method> methods = getRelevantGetters(root);
166  for (Method m : methods) {
167  Object value = null;
168  try {
169  value = m.invoke(root);
170  } catch (Exception e) {
171  }
172 
173  if (value instanceof SqlNode[]) {
174  SqlNode[] nodes = (SqlNode[]) value;
175  for (SqlNode node : nodes) {
176  scan(node);
177  }
178  } else if (value instanceof SqlNode) {
179  scan((SqlNode) value);
180  } else if (value instanceof Collection) {
181  for (Object vobj : ((Collection<?>) value)) {
182  if (vobj instanceof SqlNode) {
183  scan((SqlNode) vobj);
184  }
185  }
186  }
187  }
188 
189  if (root instanceof SqlWith) {
190  SqlWith with = (SqlWith) root;
191 
192  for (SqlNode node : with.withList) {
193  SqlWithItem item = (SqlWithItem) node;
194  selects.remove(((SqlIdentifier) item.name).names.reverse());
195  }
196  }
197 
198  if (needsPop) {
199  currentList.pop();
200  }
201  }
202 
203  Set<Method> getRelevantGetters(Object obj) {
204  Class<?> root = obj.getClass();
205 
206  Set<Method> methods = GETTERS_CACHE.get(root);
207  if (null != methods) {
208  return methods;
209  } else {
210  methods = new HashSet<>();
211  }
212 
213  while (root != null) {
214  if (root == SqlNode.class) break;
215 
216  for (Method m : root.getDeclaredMethods()) {
217  if (m.getParameterTypes().length > 0) continue;
218 
219  if (!Modifier.isPublic(m.getModifiers())) continue;
220 
221  Class<?> returnType = m.getReturnType();
222  if (!SqlNode.class.isAssignableFrom(returnType) && SqlNode[].class != returnType
223  && !Collection.class.isAssignableFrom(returnType)) {
224  continue;
225  }
226 
227  methods.add(m);
228  }
229 
230  root = root.getSuperclass();
231  }
232 
233  GETTERS_CACHE.put(obj.getClass(), methods);
234 
235  return methods;
236  }
237 
238  public String toString() {
239  String out = "";
240  out += " Selects: " + selects + "\n";
241  out += " Inserts: " + inserts + "\n";
242  out += " Updates: " + updates + "\n";
243  out += " Deletes: " + deletes + "\n";
244  out += " Ignore : " + ignore + "\n";
245 
246  return out;
247  }
248 
249  public static void main(String[] args) throws Exception {
250  String sql = "UPDATE sales set f=(SELECT max(r.num) from report as r)";
251  sql = "INSER INTO sales (a, b, c ) VALUES(10, (SELECT max(foo) from bob), 0)";
252  sql = "SELECT * FROM sales a left outer join (select (select max(id) from rupert) from report2) r on a.id=(select max(r.di) from test)";
253 
254  SqlParser parser = SqlParser.create(sql);
255 
257  capturer.scan(parser.parseQuery());
258 
259  System.out.println(capturer.selects);
260  System.out.println(capturer.inserts);
261  System.out.println(capturer.updates);
262  System.out.println(capturer.deletes);
263  System.out.println(capturer.ignore);
264  }
265 }
tuple root
Definition: setup.in.py:14
static final Map< Class<?>, Set< Method > > GETTERS_CACHE
IdentityHashMap< SqlNode, SqlNode > visitedNodes
Stack< Set< ImmutableList< String > > > currentList