@Override public String visitFrameBound(FrameBound node, Void context) { switch (node.getType()) { case UNBOUNDED_PRECEDING: return "UNBOUNDED PRECEDING"; case PRECEDING: return process(node.getValue().get(), null) + " PRECEDING"; case CURRENT_ROW: return "CURRENT ROW"; case FOLLOWING: return process(node.getValue().get(), null) + " FOLLOWING"; case UNBOUNDED_FOLLOWING: return "UNBOUNDED FOLLOWING"; } throw new IllegalArgumentException("unhandled type: " + node.getType()); }
@Override public String visitWindow(Window node, Void context) { List<String> parts = new ArrayList<>(); if (!node.getPartitionBy().isEmpty()) { parts.add("PARTITION BY " + joinExpressions(node.getPartitionBy())); } if (!node.getOrderBy().isEmpty()) { parts.add("ORDER BY " + formatSortItems(node.getOrderBy())); } if (node.getFrame().isPresent()) { parts.add(process(node.getFrame().get(), null)); } else { if (!windowFunctionName.equalsIgnoreCase("rank")) { if (!node.getOrderBy().isEmpty()) { // Redshift needs to specify frame explicitly if there is order by and the function is not rank() FrameBound fb = new FrameBound(FrameBound.Type.UNBOUNDED_PRECEDING); WindowFrame wf = new WindowFrame(WindowFrame.Type.ROWS, fb, null); parts.add(process(wf, null)); } } } return '(' + Joiner.on(' ').join(parts) + ')'; }
@Override public String visitFrameBound(FrameBound node, StackableAstVisitorContext<Integer> indent) { switch (node.getType()) { case UNBOUNDED_PRECEDING: return "UNBOUNDED PRECEDING"; case PRECEDING: return process(node.getValue().get(), indent) + " PRECEDING"; case CURRENT_ROW: return "CURRENT ROW"; case FOLLOWING: return process(node.getValue().get(), indent) + " FOLLOWING"; case UNBOUNDED_FOLLOWING: return "UNBOUNDED FOLLOWING"; } throw new IllegalArgumentException("unhandled type: " + node.getType()); }
private boolean emptyFrame(int rowPosition, int endPosition) { if (frameInfo.getStartType() != frameInfo.getEndType()) { return false; } FrameBound.Type type = frameInfo.getStartType(); if ((type != PRECEDING) && (type != FOLLOWING)) { return false; } long start = getStartValue(); long end = getEndValue(); if (type == PRECEDING) { return (start < end) || ((start > rowPosition) && (end > rowPosition)); } int positions = endPosition - rowPosition; return (start > end) || ((start > positions) && (end > positions)); }
@Override public String visitFrameBound(FrameBound node, Boolean unmangleNames) { switch (node.getType()) { case UNBOUNDED_PRECEDING: return "UNBOUNDED PRECEDING"; case PRECEDING: return process(node.getValue().get(), unmangleNames) + " PRECEDING"; case CURRENT_ROW: return "CURRENT ROW"; case FOLLOWING: return process(node.getValue().get(), unmangleNames) + " FOLLOWING"; case UNBOUNDED_FOLLOWING: return "UNBOUNDED FOLLOWING"; } throw new IllegalArgumentException("unhandled type: " + node.getType()); }
public FrameInfo( WindowFrame.Type type, FrameBound.Type startType, Optional<Integer> startChannel, FrameBound.Type endType, Optional<Integer> endChannel) { this.type = requireNonNull(type, "type is null"); this.startType = requireNonNull(startType, "startType is null"); this.startChannel = requireNonNull(startChannel, "startChannel is null").orElse(-1); this.endType = requireNonNull(endType, "endType is null"); this.endChannel = requireNonNull(endChannel, "endChannel is null").orElse(-1); }
@JsonCreator public Frame( @JsonProperty("type") WindowFrame.Type type, @JsonProperty("startType") FrameBound.Type startType, @JsonProperty("startValue") Optional<Symbol> startValue, @JsonProperty("endType") FrameBound.Type endType, @JsonProperty("endValue") Optional<Symbol> endValue) { this.startType = requireNonNull(startType, "startType is null"); this.startValue = requireNonNull(startValue, "startValue is null"); this.endType = requireNonNull(endType, "endType is null"); this.endValue = requireNonNull(endValue, "endValue is null"); this.type = requireNonNull(type, "type is null"); }
private static void analyzeWindowFrame(WindowFrame frame) { FrameBound.Type startType = frame.getStart().getType(); FrameBound.Type endType = frame.getEnd().orElse(new FrameBound(CURRENT_ROW)).getType(); if (startType == UNBOUNDED_FOLLOWING) { throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame start cannot be UNBOUNDED FOLLOWING"); } if (endType == UNBOUNDED_PRECEDING) { throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame end cannot be UNBOUNDED PRECEDING"); } if ((startType == CURRENT_ROW) && (endType == PRECEDING)) { throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame starting from CURRENT ROW cannot end with PRECEDING"); } if ((startType == FOLLOWING) && (endType == PRECEDING)) { throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame starting from FOLLOWING cannot end with PRECEDING"); } if ((startType == FOLLOWING) && (endType == CURRENT_ROW)) { throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame starting from FOLLOWING cannot end with CURRENT ROW"); } if ((frame.getType() == RANGE) && ((startType == PRECEDING) || (endType == PRECEDING))) { throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame RANGE PRECEDING is only supported with UNBOUNDED"); } if ((frame.getType() == RANGE) && ((startType == FOLLOWING) || (endType == FOLLOWING))) { throw new SemanticException(INVALID_WINDOW_FRAME, frame, "Window frame RANGE FOLLOWING is only supported with UNBOUNDED"); } }
@Test public void testWindow() throws Exception { PlanNode node = new WindowNode(newId(), filter(baseTableScan, and( equals(AE, BE), equals(BE, CE), lessThan(CE, number(10)))), ImmutableList.of(A), ImmutableList.of(A), ImmutableMap.of(A, SortOrder.ASC_NULLS_LAST), new WindowNode.Frame(WindowFrame.Type.RANGE, FrameBound.Type.UNBOUNDED_PRECEDING, Optional.empty(), FrameBound.Type.CURRENT_ROW, Optional.empty()), ImmutableMap.<Symbol, FunctionCall>of(), ImmutableMap.<Symbol, Signature>of(), Optional.empty(), ImmutableSet.of(), 0); Expression effectivePredicate = EffectivePredicateExtractor.extract(node, TYPES); // Pass through Assert.assertEquals(normalizeConjuncts(effectivePredicate), normalizeConjuncts( equals(AE, BE), equals(BE, CE), lessThan(CE, number(10)))); }
@Override public Node visitWindowFrame(SqlBaseParser.WindowFrameContext context) { return new WindowFrame( getLocation(context), getFrameType(context.frameType), (FrameBound) visit(context.start), visitIfPresent(context.end, FrameBound.class)); }
private static FrameBound.Type getBoundedFrameBoundType(Token token) { switch (token.getType()) { case SqlBaseLexer.PRECEDING: return FrameBound.Type.PRECEDING; case SqlBaseLexer.FOLLOWING: return FrameBound.Type.FOLLOWING; } throw new IllegalArgumentException("Unsupported bound type: " + token.getText()); }
private static FrameBound.Type getUnboundedFrameBoundType(Token token) { switch (token.getType()) { case SqlBaseLexer.PRECEDING: return FrameBound.Type.UNBOUNDED_PRECEDING; case SqlBaseLexer.FOLLOWING: return FrameBound.Type.UNBOUNDED_FOLLOWING; } throw new IllegalArgumentException("Unsupported bound type: " + token.getText()); }
public FrameBound.Type getStartType() { return startType; }
public FrameBound.Type getEndType() { return endType; }
@JsonProperty public FrameBound.Type getStartType() { return startType; }
@JsonProperty public FrameBound.Type getEndType() { return endType; }
@Override public Node visitUnboundedFrame(SqlBaseParser.UnboundedFrameContext context) { return new FrameBound(getLocation(context), getUnboundedFrameBoundType(context.boundType)); }
@Override public Node visitBoundedFrame(SqlBaseParser.BoundedFrameContext context) { return new FrameBound(getLocation(context), getBoundedFrameBoundType(context.boundType), (Expression) visit(context.expression())); }
@Override public Node visitCurrentRowBound(SqlBaseParser.CurrentRowBoundContext context) { return new FrameBound(getLocation(context), FrameBound.Type.CURRENT_ROW); }