All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.jet.sql.impl.aggregate.WindowUtils Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright 2024 Hazelcast Inc.
 *
 * Licensed under the Hazelcast Community License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://hazelcast.com/hazelcast-community-license
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.jet.sql.impl.aggregate;

import com.hazelcast.jet.Traverser;
import com.hazelcast.jet.Traversers;
import com.hazelcast.jet.core.SlidingWindowPolicy;
import com.hazelcast.jet.sql.impl.validate.ValidatorResource;
import com.hazelcast.sql.impl.expression.Expression;
import com.hazelcast.sql.impl.expression.ExpressionEvalContext;
import com.hazelcast.sql.impl.row.EmptyRow;
import com.hazelcast.sql.impl.row.JetSqlRow;
import com.hazelcast.sql.impl.type.QueryDataType;
import com.hazelcast.sql.impl.type.SqlDaySecondInterval;
import com.hazelcast.shaded.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.shaded.org.apache.calcite.rel.type.RelDataTypeField;
import com.hazelcast.shaded.org.apache.calcite.sql.SqlCall;
import com.hazelcast.shaded.org.apache.calcite.sql.SqlCallBinding;
import com.hazelcast.shaded.org.apache.calcite.sql.SqlIdentifier;
import com.hazelcast.shaded.org.apache.calcite.sql.SqlNode;
import com.hazelcast.shaded.org.apache.calcite.sql.SqlUtil;
import com.hazelcast.shaded.org.apache.calcite.sql.validate.SqlValidator;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.util.Arrays;
import java.util.List;

import static com.hazelcast.jet.sql.impl.validate.ValidationUtil.unwrapFunctionOperand;
import static com.hazelcast.sql.impl.expression.datetime.DateTimeUtils.asTimestampWithTimezone;
import static com.hazelcast.sql.impl.type.converter.AbstractTemporalConverter.DEFAULT_ZONE;
import static com.hazelcast.shaded.org.apache.calcite.util.Static.RESOURCE;

public final class WindowUtils {

    private WindowUtils() {
    }

    /**
     * Inject window bounds into the row, according to the mapping.
     * 

* The result row will always have `mapping.length` fields. Where the * mapping has value of -1, windowStart will be used. Where it has a value * of -2, windowEnd will be used. Otherwise, the field from input row * referenced by the (non-negative) mapping value will be used. * * @return row with inserted bounds */ @SuppressWarnings("checkstyle:MagicNumber") public static JetSqlRow insertWindowBound( JetSqlRow row, long windowStart, long windowEnd, QueryDataType descriptorType, int[] mapping ) { Object[] result = new Object[mapping.length]; for (int i = 0; i < mapping.length; i++) { if (mapping[i] == -1) { result[i] = convertWindowBound(windowStart, descriptorType); } else if (mapping[i] == -2) { result[i] = convertWindowBound(windowEnd, descriptorType); } else { result[i] = row.get(mapping[i]); } } return new JetSqlRow(row.getSerializationService(), result); } /** * Returns a traverser of rows with two added fields: the * window_start and window_end. For tumbling windows, the returned * traverser always contains a single row. For hopping windows, it * contains multiple rows. */ public static Traverser addWindowBounds(JetSqlRow row, int timeStampIndex, SlidingWindowPolicy windowPolicy) { Object value = row.get(timeStampIndex); if (value == null) { return Traversers.singleton(row.extendedRow(2)); } long millis = extractMillis(value); long slideStep = windowPolicy.frameSize(); long firstWindowStart = windowPolicy.floorFrameTs(millis - windowPolicy.windowSize() + slideStep); return new Traverser() { long currentStart = firstWindowStart; @Override public JetSqlRow next() { if (currentStart >= firstWindowStart + windowPolicy.windowSize()) { return null; } try { return addWindowBoundsSingleRow(row, value, currentStart, currentStart + windowPolicy.windowSize()); } finally { currentStart += slideStep; } } }; } private static Object convertWindowBound(long boundary, QueryDataType descriptorType) { if (descriptorType.getTypeFamily().isTemporal()) { return descriptorType.convert(asTimestampWithTimezone(boundary, DEFAULT_ZONE)); } else { return descriptorType.convert(boundary); } } private static JetSqlRow addWindowBoundsSingleRow(JetSqlRow row, Object timeStamp, long windowStart, long windowEnd) { Object[] result = Arrays.copyOf(row.getValues(), row.getFieldCount() + 2); if (timeStamp instanceof Byte) { result[result.length - 2] = (byte) windowStart; result[result.length - 1] = (byte) windowEnd; } else if (timeStamp instanceof Short) { result[result.length - 2] = (short) windowStart; result[result.length - 1] = (short) windowEnd; } else if (timeStamp instanceof Integer) { result[result.length - 2] = (int) windowStart; result[result.length - 1] = (int) windowEnd; } else if (timeStamp instanceof Long) { result[result.length - 2] = windowStart; result[result.length - 1] = windowEnd; } else if (timeStamp instanceof LocalTime) { result[result.length - 2] = asTimestampWithTimezone(windowStart, DEFAULT_ZONE).toLocalTime(); result[result.length - 1] = asTimestampWithTimezone(windowEnd, DEFAULT_ZONE).toLocalTime(); } else if (timeStamp instanceof LocalDate) { result[result.length - 2] = asTimestampWithTimezone(windowStart, DEFAULT_ZONE).toLocalDate(); result[result.length - 1] = asTimestampWithTimezone(windowEnd, DEFAULT_ZONE).toLocalDate(); } else if (timeStamp instanceof LocalDateTime) { result[result.length - 2] = asTimestampWithTimezone(windowStart, DEFAULT_ZONE).toLocalDateTime(); result[result.length - 1] = asTimestampWithTimezone(windowEnd, DEFAULT_ZONE).toLocalDateTime(); } else { result[result.length - 2] = asTimestampWithTimezone(windowStart, DEFAULT_ZONE); result[result.length - 1] = asTimestampWithTimezone(windowEnd, DEFAULT_ZONE); } return new JetSqlRow(row.getSerializationService(), result); } public static long extractMillis(Object value) { if (value instanceof Number) { return ((Number) value).longValue(); } else if (value instanceof LocalTime) { return QueryDataType.TIME.getConverter().asTimestampWithTimezone(value).toInstant().toEpochMilli(); } else if (value instanceof LocalDate) { return QueryDataType.DATE.getConverter().asTimestampWithTimezone(value).toInstant().toEpochMilli(); } else if (value instanceof LocalDateTime) { return QueryDataType.TIMESTAMP.getConverter().asTimestampWithTimezone(value).toInstant().toEpochMilli(); } else if (value instanceof OffsetDateTime) { return ((OffsetDateTime) value).toInstant().toEpochMilli(); } else { return value.hashCode(); } } public static long extractMillis(Expression expression, ExpressionEvalContext evalContext) { Object lag = expression.eval(EmptyRow.INSTANCE, evalContext); return lag instanceof Number ? ((Number) lag).longValue() : ((SqlDaySecondInterval) lag).getMillis(); } /** * Return the datatype of the target column referenced by the DESCRIPTOR argument. */ public static RelDataType getOrderingColumnType(SqlCallBinding binding, int orderingColumnParameterIndex) { SqlNode input = binding.operand(0); SqlCall descriptor = (SqlCall) unwrapFunctionOperand(binding.operand(orderingColumnParameterIndex)); List columnIdentifiers = descriptor.getOperandList(); if (columnIdentifiers.size() != 1) { throw SqlUtil.newContextException(descriptor.getParserPosition(), ValidatorResource.RESOURCE.mustUseSingleOrderingColumn()); } // `descriptor` is the DESCRIPTOR call, its operand is an SqlIdentifier having the column name SqlIdentifier orderingColumnIdentifier = (SqlIdentifier) descriptor.getOperandList().get(0); String orderingColumnName = orderingColumnIdentifier.getSimple(); SqlValidator validator = binding.getValidator(); RelDataTypeField columnField = validator .getValidatedNodeType(input) .getField(orderingColumnName, validator.getCatalogReader().nameMatcher().isCaseSensitive(), false); if (columnField == null) { throw SqlUtil.newContextException(descriptor.getParserPosition(), RESOURCE.unknownIdentifier(orderingColumnName)); } return columnField.getType(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy