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