OmniSciDB  72180abbfe
 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  map.put("literal", value2);
335  }
336  map.put("type", literal.getTypeName().name());
337  map.put("target_type", literal.getType().getSqlTypeName().toString());
338  final Object value = literal.getValue();
339  if (value instanceof BigDecimal) {
340  map.put("scale", ((BigDecimal) value).scale());
341  map.put("precision", ((BigDecimal) value).precision());
342  } else {
343  map.put("scale", literal.getType().getScale());
344  map.put("precision", literal.getType().getPrecision());
345  }
346  map.put("type_scale", literal.getType().getScale());
347  map.put("type_precision", literal.getType().getPrecision());
348  return map;
349  case INPUT_REF:
350  map = jsonBuilder.map();
351  map.put("input", ((RexInputRef) node).getIndex());
352  return map;
353  case CORREL_VARIABLE:
354  map = jsonBuilder.map();
355  map.put("correl", ((RexCorrelVariable) node).getName());
356  map.put("type", toJson(node.getType()));
357  return map;
358  default:
359  if (node instanceof RexCall) {
360  final RexCall call = (RexCall) node;
361  map = jsonBuilder.map();
362  map.put("op", toJson(call.getOperator()));
363  final List<Object> list = jsonBuilder.list();
364  for (RexNode operand : call.getOperands()) {
365  list.add(toJson(operand));
366  }
367  map.put("operands", list);
368  map.put("type", toJson(node.getType()));
369  if (node instanceof RexSubQuery) {
370  final MapDRelJsonWriter subqueryWriter = new MapDRelJsonWriter();
371  ((RexSubQuery) node).rel.explain(subqueryWriter);
372  map.put("subquery", subqueryWriter.asJsonMap());
373  }
374  if (node instanceof RexOver) {
375  final RexWindow window = ((RexOver) node).getWindow();
376  final List<Object> partitionKeyList = jsonBuilder.list();
377  for (final RexNode partitionKey : window.partitionKeys) {
378  partitionKeyList.add(toJson(partitionKey));
379  }
380  map.put("partition_keys", partitionKeyList);
381  final List<Object> orderKeyList = jsonBuilder.list();
382  for (final RexFieldCollation orderKey : window.orderKeys) {
383  orderKeyList.add(toJson(orderKey));
384  }
385  map.put("order_keys", orderKeyList);
386  RexWindowBound lower_bound = window.getLowerBound();
387  RexWindowBound upper_bound = window.getUpperBound();
388  map.put("lower_bound", toJson(lower_bound));
389  map.put("upper_bound", toJson(upper_bound));
390  map.put("is_rows", toJson(window.isRows()));
391  }
392  if (call.getOperator() instanceof SqlFunction) {
393  switch (((SqlFunction) call.getOperator()).getFunctionType()) {
394  case USER_DEFINED_CONSTRUCTOR:
395  case USER_DEFINED_FUNCTION:
396  case USER_DEFINED_PROCEDURE:
397  case USER_DEFINED_SPECIFIC_FUNCTION:
398  map.put("class", call.getOperator().getClass().getName());
399  }
400  }
401  return map;
402  }
403  throw new UnsupportedOperationException("unknown rex " + node);
404  }
405  }
406 
407  RexNode toRex(RelInput relInput, Object o) {
408  final RelOptCluster cluster = relInput.getCluster();
409  final RexBuilder rexBuilder = cluster.getRexBuilder();
410  if (o == null) {
411  return null;
412  } else if (o instanceof Map) {
413  Map map = (Map) o;
414  final String op = (String) map.get("op");
415  if (op != null) {
416  final List operands = (List) map.get("operands");
417  final Object jsonType = map.get("type");
418  final SqlOperator operator = toOp(op, map);
419  final List<RexNode> rexOperands = toRexList(relInput, operands);
420  RelDataType type;
421  if (jsonType != null) {
422  type = toType(cluster.getTypeFactory(), jsonType);
423  } else {
424  type = rexBuilder.deriveReturnType(operator, rexOperands);
425  }
426  return rexBuilder.makeCall(type, operator, rexOperands);
427  }
428  final Integer input = (Integer) map.get("input");
429  if (input != null) {
430  List<RelNode> inputNodes = relInput.getInputs();
431  int i = input;
432  for (RelNode inputNode : inputNodes) {
433  final RelDataType rowType = inputNode.getRowType();
434  if (i < rowType.getFieldCount()) {
435  final RelDataTypeField field = rowType.getFieldList().get(i);
436  return rexBuilder.makeInputRef(field.getType(), input);
437  }
438  i -= rowType.getFieldCount();
439  }
440  throw new RuntimeException("input field " + input + " is out of range");
441  }
442  final String field = (String) map.get("field");
443  if (field != null) {
444  final Object jsonExpr = map.get("expr");
445  final RexNode expr = toRex(relInput, jsonExpr);
446  return rexBuilder.makeFieldAccess(expr, field, true);
447  }
448  final String correl = (String) map.get("correl");
449  if (correl != null) {
450  final Object jsonType = map.get("type");
451  RelDataType type = toType(cluster.getTypeFactory(), jsonType);
452  return rexBuilder.makeCorrel(type, new CorrelationId(correl));
453  }
454  if (map.containsKey("literal")) {
455  final Object literal = map.get("literal");
456  final SqlTypeName sqlTypeName =
457  Util.enumVal(SqlTypeName.class, (String) map.get("type"));
458  if (literal == null) {
459  return rexBuilder.makeNullLiteral(sqlTypeName);
460  }
461  return toRex(relInput, literal);
462  }
463  throw new UnsupportedOperationException("cannot convert to rex " + o);
464  } else if (o instanceof Boolean) {
465  return rexBuilder.makeLiteral((Boolean) o);
466  } else if (o instanceof String) {
467  return rexBuilder.makeLiteral((String) o);
468  } else if (o instanceof Number) {
469  final Number number = (Number) o;
470  if (number instanceof Double || number instanceof Float) {
471  return rexBuilder.makeApproxLiteral(BigDecimal.valueOf(number.doubleValue()));
472  } else {
473  return rexBuilder.makeExactLiteral(BigDecimal.valueOf(number.longValue()));
474  }
475  } else {
476  throw new UnsupportedOperationException("cannot convert to rex " + o);
477  }
478  }
479 
480  private List<RexNode> toRexList(RelInput relInput, List operands) {
481  final List<RexNode> list = new ArrayList<RexNode>();
482  for (Object operand : operands) {
483  list.add(toRex(relInput, operand));
484  }
485  return list;
486  }
487 
488  private SqlOperator toOp(String op, Map<String, Object> map) {
489  // TODO: build a map, for more efficient lookup
490  // TODO: look up based on SqlKind
491  final List<SqlOperator> operatorList =
492  SqlStdOperatorTable.instance().getOperatorList();
493  for (SqlOperator operator : operatorList) {
494  if (operator.getName().equals(op)) {
495  return operator;
496  }
497  }
498  String class_ = (String) map.get("class");
499  if (class_ != null) {
500  try {
501  return (SqlOperator) Class.forName(class_).newInstance();
502  } catch (InstantiationException e) {
503  throw new RuntimeException(e);
504  } catch (IllegalAccessException e) {
505  throw new RuntimeException(e);
506  } catch (ClassNotFoundException e) {
507  throw new RuntimeException(e);
508  }
509  }
510  return null;
511  }
512 
513  SqlAggFunction toAggregation(String agg, Map<String, Object> map) {
514  return (SqlAggFunction) toOp(agg, map);
515  }
516 
517  private String toJson(SqlOperator operator) {
518  // User-defined operators are not yet handled.
519  return operator.getName();
520  }
521 }
522 
523 // End RelJson.java
JoinType
Definition: sqldefs.h:107
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)