OmniSciDB  b24e664e58
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
MapDRelJson.java
Go to the documentation of this file.
1 /*
2  * Copyright 2017 MapD Technologies, 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.rel.externalize;
18 
19 import com.google.common.collect.ImmutableList;
20 
21 import org.apache.calcite.avatica.util.TimeUnitRange;
22 import org.apache.calcite.plan.RelOptCluster;
23 import org.apache.calcite.plan.RelTraitSet;
24 import org.apache.calcite.rel.RelCollation;
25 import org.apache.calcite.rel.RelCollationImpl;
26 import org.apache.calcite.rel.RelCollations;
27 import org.apache.calcite.rel.RelDistribution;
28 import org.apache.calcite.rel.RelDistributions;
29 import org.apache.calcite.rel.RelFieldCollation;
30 import org.apache.calcite.rel.RelInput;
31 import org.apache.calcite.rel.RelNode;
32 import org.apache.calcite.rel.core.AggregateCall;
33 import org.apache.calcite.rel.core.CorrelationId;
34 import org.apache.calcite.rel.core.TableModify.Operation;
35 import org.apache.calcite.rel.type.RelDataType;
36 import org.apache.calcite.rel.type.RelDataTypeFactory;
37 import org.apache.calcite.rel.type.RelDataTypeField;
38 import org.apache.calcite.rex.RexBuilder;
39 import org.apache.calcite.rex.RexCall;
40 import org.apache.calcite.rex.RexCorrelVariable;
41 import org.apache.calcite.rex.RexFieldAccess;
42 import org.apache.calcite.rex.RexFieldCollation;
43 import org.apache.calcite.rex.RexInputRef;
44 import org.apache.calcite.rex.RexLiteral;
45 import org.apache.calcite.rex.RexNode;
46 import org.apache.calcite.rex.RexOver;
47 import org.apache.calcite.rex.RexSubQuery;
48 import org.apache.calcite.rex.RexWindow;
49 import org.apache.calcite.rex.RexWindowBound;
51 import org.apache.calcite.sql.SqlAggFunction;
52 import org.apache.calcite.sql.SqlFunction;
53 import org.apache.calcite.sql.SqlOperator;
54 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
55 import org.apache.calcite.sql.type.SqlTypeName;
56 import org.apache.calcite.util.ImmutableBitSet;
57 import org.apache.calcite.util.JsonBuilder;
58 import org.apache.calcite.util.Util;
59 
60 import java.lang.reflect.Constructor;
61 import java.lang.reflect.InvocationTargetException;
62 import java.math.BigDecimal;
63 import java.util.ArrayList;
64 import java.util.HashMap;
65 import java.util.List;
66 import java.util.Map;
67 
72 public class MapDRelJson {
73  private final Map<String, Constructor> constructorMap =
74  new HashMap<String, Constructor>();
75  private final JsonBuilder jsonBuilder;
76 
77  public static final List<String> PACKAGES = ImmutableList.of("org.apache.calcite.rel.",
78  "org.apache.calcite.rel.core.",
79  "org.apache.calcite.rel.logical.",
80  "org.apache.calcite.adapter.jdbc.",
81  "org.apache.calcite.adapter.enumerable.",
82  "org.apache.calcite.adapter.jdbc.JdbcRules$");
83 
84  public MapDRelJson(JsonBuilder jsonBuilder) {
85  this.jsonBuilder = jsonBuilder;
86  }
87 
88  public RelNode create(Map<String, Object> map) {
89  String type = (String) map.get("type");
90  Constructor constructor = getConstructor(type);
91  try {
92  return (RelNode) constructor.newInstance(map);
93  } catch (InstantiationException e) {
94  throw new RuntimeException("while invoking constructor for type '" + type + "'", e);
95  } catch (IllegalAccessException e) {
96  throw new RuntimeException("while invoking constructor for type '" + type + "'", e);
97  } catch (InvocationTargetException e) {
98  throw new RuntimeException("while invoking constructor for type '" + type + "'", e);
99  } catch (ClassCastException e) {
100  throw new RuntimeException("while invoking constructor for type '" + type + "'", e);
101  }
102  }
103 
104  public Constructor getConstructor(String type) {
105  Constructor constructor = constructorMap.get(type);
106  if (constructor == null) {
107  Class clazz = typeNameToClass(type);
108  try {
109  // noinspection unchecked
110  constructor = clazz.getConstructor(RelInput.class);
111  } catch (NoSuchMethodException e) {
112  throw new RuntimeException(
113  "class does not have required constructor, " + clazz + "(RelInput)");
114  }
115  constructorMap.put(type, constructor);
116  }
117  return constructor;
118  }
119 
124  public Class typeNameToClass(String type) {
125  if (!type.contains(".")) {
126  for (String package_ : PACKAGES) {
127  try {
128  return Class.forName(package_ + type);
129  } catch (ClassNotFoundException e) {
130  // ignore
131  }
132  }
133  }
134  try {
135  return Class.forName(type);
136  } catch (ClassNotFoundException e) {
137  throw new RuntimeException("unknown type " + type);
138  }
139  }
140 
144  public String classToTypeName(Class<? extends RelNode> class_) {
145  final String canonicalName = class_.getName();
146  for (String package_ : PACKAGES) {
147  if (canonicalName.startsWith(package_)) {
148  String remaining = canonicalName.substring(package_.length());
149  if (remaining.indexOf('.') < 0 && remaining.indexOf('$') < 0) {
150  return remaining;
151  }
152  }
153  }
154  return canonicalName;
155  }
156 
157  public Object toJson(RelCollationImpl node) {
158  final List<Object> list = new ArrayList<Object>();
159  for (RelFieldCollation fieldCollation : node.getFieldCollations()) {
160  final Map<String, Object> map = jsonBuilder.map();
161  map.put("field", fieldCollation.getFieldIndex());
162  map.put("direction", fieldCollation.getDirection().name());
163  map.put("nulls", fieldCollation.nullDirection.name());
164  list.add(map);
165  }
166  return list;
167  }
168 
169  public Object toJson(RexFieldCollation node) {
170  final Map<String, Object> map = jsonBuilder.map();
171  map.put("field", toJson(node.left));
172  map.put("direction", node.getDirection().name());
173  map.put("nulls", node.getNullDirection().name());
174  return map;
175  }
176 
177  public RelCollation toCollation(List<Map<String, Object>> jsonFieldCollations) {
178  final List<RelFieldCollation> fieldCollations = new ArrayList<RelFieldCollation>();
179  for (Map<String, Object> map : jsonFieldCollations) {
180  fieldCollations.add(toFieldCollation(map));
181  }
182  return RelCollations.of(fieldCollations);
183  }
184 
185  public RelFieldCollation toFieldCollation(Map<String, Object> map) {
186  final Integer field = (Integer) map.get("field");
187  final RelFieldCollation.Direction direction = Util.enumVal(
188  RelFieldCollation.Direction.class, (String) map.get("direction"));
189  final RelFieldCollation.NullDirection nullDirection = Util.enumVal(
190  RelFieldCollation.NullDirection.class, (String) map.get("nulls"));
191  return new RelFieldCollation(field, direction, nullDirection);
192  }
193 
194  public RelDistribution toDistribution(Object o) {
195  return RelDistributions.ANY; // TODO:
196  }
197 
198  public RelDataType toType(RelDataTypeFactory typeFactory, Object o) {
199  if (o instanceof List) {
200  @SuppressWarnings("unchecked")
201  final List<Map<String, Object>> jsonList = (List<Map<String, Object>>) o;
202  final RelDataTypeFactory.FieldInfoBuilder builder = typeFactory.builder();
203  for (Map<String, Object> jsonMap : jsonList) {
204  builder.add((String) jsonMap.get("name"), toType(typeFactory, jsonMap));
205  }
206  return builder.build();
207  } else {
208  final Map<String, Object> map = (Map<String, Object>) o;
209  final SqlTypeName sqlTypeName =
210  Util.enumVal(SqlTypeName.class, (String) map.get("type"));
211  final Integer precision = (Integer) map.get("precision");
212  final Integer scale = (Integer) map.get("scale");
213  final RelDataType type;
214  if (precision == null) {
215  type = typeFactory.createSqlType(sqlTypeName);
216  } else if (scale == null) {
217  type = typeFactory.createSqlType(sqlTypeName, precision);
218  } else {
219  type = typeFactory.createSqlType(sqlTypeName, precision, scale);
220  }
221  final boolean nullable = (Boolean) map.get("nullable");
222  return typeFactory.createTypeWithNullability(type, nullable);
223  }
224  }
225 
226  public Object toJson(AggregateCall node) {
227  final Map<String, Object> map = jsonBuilder.map();
228  map.put("agg", toJson(node.getAggregation()));
229  map.put("type", toJson(node.getType()));
230  map.put("distinct", node.isDistinct());
231  map.put("operands", node.getArgList());
232  return map;
233  }
234 
235  Object toJson(Object value) {
236  if (value == null || value instanceof Number || value instanceof String
237  || value instanceof Boolean) {
238  return value;
239  } else if (value instanceof RexNode) {
240  return toJson((RexNode) value);
241  } else if (value instanceof CorrelationId) {
242  return toJson((CorrelationId) value);
243  } else if (value instanceof List) {
244  final List<Object> list = jsonBuilder.list();
245  for (Object o : (List) value) {
246  list.add(toJson(o));
247  }
248  return list;
249  } else if (value instanceof ImmutableBitSet) {
250  final List<Object> list = jsonBuilder.list();
251  for (Integer integer : (ImmutableBitSet) value) {
252  list.add(toJson(integer));
253  }
254  return list;
255  } else if (value instanceof AggregateCall) {
256  return toJson((AggregateCall) value);
257  } else if (value instanceof RelCollationImpl) {
258  return toJson((RelCollationImpl) value);
259  } else if (value instanceof RexFieldCollation) {
260  return toJson((RexFieldCollation) value);
261  } else if (value instanceof RelDataType) {
262  return toJson((RelDataType) value);
263  } else if (value instanceof RelDataTypeField) {
264  return toJson((RelDataTypeField) value);
265  } else if (value instanceof JoinType) {
266  return value.toString();
267  } else if (value instanceof Operation) {
268  return value.toString();
269  } else {
270  throw new UnsupportedOperationException("type not serializable: " + value
271  + " (type " + value.getClass().getCanonicalName() + ")");
272  }
273  }
274 
275  private Object toJson(RelDataType node) {
276  if (node.isStruct()) {
277  final List<Object> list = jsonBuilder.list();
278  for (RelDataTypeField field : node.getFieldList()) {
279  list.add(toJson(field));
280  }
281  return list;
282  } else {
283  final Map<String, Object> map = jsonBuilder.map();
284  map.put("type", node.getSqlTypeName().name());
285  map.put("nullable", node.isNullable());
286  if (node.getSqlTypeName().allowsPrec()) {
287  map.put("precision", node.getPrecision());
288  }
289  if (node.getSqlTypeName().allowsScale()) {
290  map.put("scale", node.getScale());
291  }
292  return map;
293  }
294  }
295 
296  private Object toJson(RelDataTypeField node) {
297  final Map<String, Object> map = (Map<String, Object>) toJson(node.getType());
298  map.put("name", node.getName());
299  return map;
300  }
301 
302  private Object toJson(CorrelationId node) {
303  return node.getId();
304  }
305 
306  private Object toJson(final RexWindowBound window_bound) {
307  final Map<String, Object> map = jsonBuilder.map();
308  map.put("unbounded", toJson(window_bound.isUnbounded()));
309  map.put("preceding", toJson(window_bound.isPreceding()));
310  map.put("following", toJson(window_bound.isFollowing()));
311  map.put("is_current_row", toJson(window_bound.isCurrentRow()));
312  map.put("offset",
313  window_bound.getOffset() != null ? toJson(window_bound.getOffset()) : null);
314  map.put("order_key", toJson(window_bound.getOrderKey()));
315  return map;
316  }
317 
318  private Object toJson(RexNode node) {
319  final Map<String, Object> map;
320  switch (node.getKind()) {
321  case FIELD_ACCESS:
322  map = jsonBuilder.map();
323  final RexFieldAccess fieldAccess = (RexFieldAccess) node;
324  map.put("field", fieldAccess.getField().getName());
325  map.put("expr", toJson(fieldAccess.getReferenceExpr()));
326  return map;
327  case LITERAL:
328  final RexLiteral literal = (RexLiteral) node;
329  final Object value2 = literal.getValue2();
330  map = jsonBuilder.map();
331  if (value2 instanceof TimeUnitRange) {
332  map.put("literal", value2.toString());
333  } else {
334  if (value2 instanceof String) {
335  map.put("literal", ((String) value2).replace("\\", "\\\\"));
336  } else {
337  map.put("literal", value2);
338  }
339  }
340  map.put("type", literal.getTypeName().name());
341  map.put("target_type", literal.getType().getSqlTypeName().toString());
342  final Object value = literal.getValue();
343  if (value instanceof BigDecimal) {
344  map.put("scale", ((BigDecimal) value).scale());
345  map.put("precision", ((BigDecimal) value).precision());
346  } else {
347  map.put("scale", literal.getType().getScale());
348  map.put("precision", literal.getType().getPrecision());
349  }
350  map.put("type_scale", literal.getType().getScale());
351  map.put("type_precision", literal.getType().getPrecision());
352  return map;
353  case INPUT_REF:
354  map = jsonBuilder.map();
355  map.put("input", ((RexInputRef) node).getIndex());
356  return map;
357  case CORREL_VARIABLE:
358  map = jsonBuilder.map();
359  map.put("correl", ((RexCorrelVariable) node).getName());
360  map.put("type", toJson(node.getType()));
361  return map;
362  default:
363  if (node instanceof RexCall) {
364  final RexCall call = (RexCall) node;
365  map = jsonBuilder.map();
366  map.put("op", toJson(call.getOperator()));
367  final List<Object> list = jsonBuilder.list();
368  for (RexNode operand : call.getOperands()) {
369  list.add(toJson(operand));
370  }
371  map.put("operands", list);
372  map.put("type", toJson(node.getType()));
373  if (node instanceof RexSubQuery) {
374  final MapDRelJsonWriter subqueryWriter = new MapDRelJsonWriter();
375  ((RexSubQuery) node).rel.explain(subqueryWriter);
376  map.put("subquery", subqueryWriter.asJsonMap());
377  }
378  if (node instanceof RexOver) {
379  final RexWindow window = ((RexOver) node).getWindow();
380  final List<Object> partitionKeyList = jsonBuilder.list();
381  for (final RexNode partitionKey : window.partitionKeys) {
382  partitionKeyList.add(toJson(partitionKey));
383  }
384  map.put("partition_keys", partitionKeyList);
385  final List<Object> orderKeyList = jsonBuilder.list();
386  for (final RexFieldCollation orderKey : window.orderKeys) {
387  orderKeyList.add(toJson(orderKey));
388  }
389  map.put("order_keys", orderKeyList);
390  RexWindowBound lower_bound = window.getLowerBound();
391  RexWindowBound upper_bound = window.getUpperBound();
392  map.put("lower_bound", toJson(lower_bound));
393  map.put("upper_bound", toJson(upper_bound));
394  map.put("is_rows", toJson(window.isRows()));
395  }
396  if (call.getOperator() instanceof SqlFunction) {
397  switch (((SqlFunction) call.getOperator()).getFunctionType()) {
398  case USER_DEFINED_CONSTRUCTOR:
399  case USER_DEFINED_FUNCTION:
400  case USER_DEFINED_PROCEDURE:
401  case USER_DEFINED_SPECIFIC_FUNCTION:
402  map.put("class", call.getOperator().getClass().getName());
403  }
404  }
405  return map;
406  }
407  throw new UnsupportedOperationException("unknown rex " + node);
408  }
409  }
410 
411  RexNode toRex(RelInput relInput, Object o) {
412  final RelOptCluster cluster = relInput.getCluster();
413  final RexBuilder rexBuilder = cluster.getRexBuilder();
414  if (o == null) {
415  return null;
416  } else if (o instanceof Map) {
417  Map map = (Map) o;
418  final String op = (String) map.get("op");
419  if (op != null) {
420  final List operands = (List) map.get("operands");
421  final Object jsonType = map.get("type");
422  final SqlOperator operator = toOp(op, map);
423  final List<RexNode> rexOperands = toRexList(relInput, operands);
424  RelDataType type;
425  if (jsonType != null) {
426  type = toType(cluster.getTypeFactory(), jsonType);
427  } else {
428  type = rexBuilder.deriveReturnType(operator, rexOperands);
429  }
430  return rexBuilder.makeCall(type, operator, rexOperands);
431  }
432  final Integer input = (Integer) map.get("input");
433  if (input != null) {
434  List<RelNode> inputNodes = relInput.getInputs();
435  int i = input;
436  for (RelNode inputNode : inputNodes) {
437  final RelDataType rowType = inputNode.getRowType();
438  if (i < rowType.getFieldCount()) {
439  final RelDataTypeField field = rowType.getFieldList().get(i);
440  return rexBuilder.makeInputRef(field.getType(), input);
441  }
442  i -= rowType.getFieldCount();
443  }
444  throw new RuntimeException("input field " + input + " is out of range");
445  }
446  final String field = (String) map.get("field");
447  if (field != null) {
448  final Object jsonExpr = map.get("expr");
449  final RexNode expr = toRex(relInput, jsonExpr);
450  return rexBuilder.makeFieldAccess(expr, field, true);
451  }
452  final String correl = (String) map.get("correl");
453  if (correl != null) {
454  final Object jsonType = map.get("type");
455  RelDataType type = toType(cluster.getTypeFactory(), jsonType);
456  return rexBuilder.makeCorrel(type, new CorrelationId(correl));
457  }
458  if (map.containsKey("literal")) {
459  final Object literal = map.get("literal");
460  final SqlTypeName sqlTypeName =
461  Util.enumVal(SqlTypeName.class, (String) map.get("type"));
462  if (literal == null) {
463  return rexBuilder.makeNullLiteral(sqlTypeName);
464  }
465  return toRex(relInput, literal);
466  }
467  throw new UnsupportedOperationException("cannot convert to rex " + o);
468  } else if (o instanceof Boolean) {
469  return rexBuilder.makeLiteral((Boolean) o);
470  } else if (o instanceof String) {
471  return rexBuilder.makeLiteral((String) o);
472  } else if (o instanceof Number) {
473  final Number number = (Number) o;
474  if (number instanceof Double || number instanceof Float) {
475  return rexBuilder.makeApproxLiteral(BigDecimal.valueOf(number.doubleValue()));
476  } else {
477  return rexBuilder.makeExactLiteral(BigDecimal.valueOf(number.longValue()));
478  }
479  } else {
480  throw new UnsupportedOperationException("cannot convert to rex " + o);
481  }
482  }
483 
484  private List<RexNode> toRexList(RelInput relInput, List operands) {
485  final List<RexNode> list = new ArrayList<RexNode>();
486  for (Object operand : operands) {
487  list.add(toRex(relInput, operand));
488  }
489  return list;
490  }
491 
492  private SqlOperator toOp(String op, Map<String, Object> map) {
493  // TODO: build a map, for more efficient lookup
494  // TODO: look up based on SqlKind
495  final List<SqlOperator> operatorList =
496  SqlStdOperatorTable.instance().getOperatorList();
497  for (SqlOperator operator : operatorList) {
498  if (operator.getName().equals(op)) {
499  return operator;
500  }
501  }
502  String class_ = (String) map.get("class");
503  if (class_ != null) {
504  try {
505  return (SqlOperator) Class.forName(class_).newInstance();
506  } catch (InstantiationException e) {
507  throw new RuntimeException(e);
508  } catch (IllegalAccessException e) {
509  throw new RuntimeException(e);
510  } catch (ClassNotFoundException e) {
511  throw new RuntimeException(e);
512  }
513  }
514  return null;
515  }
516 
517  SqlAggFunction toAggregation(String agg, Map<String, Object> map) {
518  return (SqlAggFunction) toOp(agg, map);
519  }
520 
521  private String toJson(SqlOperator operator) {
522  // User-defined operators are not yet handled.
523  return operator.getName();
524  }
525 }
526 
527 // End RelJson.java
JoinType
Definition: sqldefs.h:98
RexNode toRex(RelInput relInput, Object o)
RelDataType toType(RelDataTypeFactory typeFactory, Object o)
RelNode create(Map< String, Object > map)
String classToTypeName(Class<?extends RelNode > class_)
const rapidjson::Value & field(const rapidjson::Value &obj, const char field[]) noexcept
Definition: JsonAccessors.h:31
Object toJson(RexFieldCollation node)
SqlOperator toOp(String op, Map< String, Object > map)
RelCollation toCollation(List< Map< String, Object >> jsonFieldCollations)
final Map< String, Constructor > constructorMap
SqlAggFunction toAggregation(String agg, Map< String, Object > map)
Object toJson(final RexWindowBound window_bound)
List< RexNode > toRexList(RelInput relInput, List operands)
RelFieldCollation toFieldCollation(Map< String, Object > map)