LENS-885: Cleanup of Cube test cases
Project: http://git-wip-us.apache.org/repos/asf/lens/repo Commit: http://git-wip-us.apache.org/repos/asf/lens/commit/7c7c86da Tree: http://git-wip-us.apache.org/repos/asf/lens/tree/7c7c86da Diff: http://git-wip-us.apache.org/repos/asf/lens/diff/7c7c86da Branch: refs/heads/current-release-line Commit: 7c7c86daed2e9907bda92f5ed29e73ed99a9a726 Parents: 7e9e47e Author: Rajat Khandelwal <pro...@apache.org> Authored: Fri Dec 11 18:40:59 2015 +0530 Committer: Rajat Khandelwal <rajatgupt...@gmail.com> Committed: Fri Dec 11 18:41:00 2015 +0530 ---------------------------------------------------------------------- .../apache/lens/cube/metadata/CubeColumn.java | 1 - .../lens/cube/metadata/CubeFactTable.java | 1 - .../org/apache/lens/cube/metadata/DateUtil.java | 396 ++++++++++++++++ .../lens/cube/metadata/TimePartitionRange.java | 1 - .../apache/lens/cube/metadata/TimeRange.java | 219 +++++++++ .../apache/lens/cube/metadata/UpdatePeriod.java | 84 +++- .../timeline/EndsAndHolesPartitionTimeline.java | 2 +- .../apache/lens/cube/parse/CandidateFact.java | 5 +- .../cube/parse/CandidateTablePruneCause.java | 2 + .../org/apache/lens/cube/parse/DateUtil.java | 456 ------------------ .../lens/cube/parse/ExpressionResolver.java | 11 +- .../lens/cube/parse/SingleFactHQLContext.java | 2 +- .../lens/cube/parse/StorageTableResolver.java | 2 +- .../org/apache/lens/cube/parse/TimeRange.java | 220 --------- .../lens/cube/parse/TimerangeResolver.java | 5 +- .../lens/cube/metadata/CubeFactTableTest.java | 1 - .../apache/lens/cube/metadata/DateFactory.java | 196 ++++++++ .../cube/metadata/TestCubeMetastoreClient.java | 115 ++--- .../apache/lens/cube/metadata/TestDateUtil.java | 297 ++++++++++++ .../apache/lens/cube/parse/CubeTestSetup.java | 191 ++------ .../FieldsCannotBeQueriedTogetherTest.java | 8 +- .../lens/cube/parse/TestAggregateResolver.java | 1 + .../lens/cube/parse/TestBaseCubeQueries.java | 5 +- .../cube/parse/TestBetweenTimeRangeWriter.java | 25 +- .../lens/cube/parse/TestCubeRewriter.java | 459 +++++++++---------- .../apache/lens/cube/parse/TestDateUtil.java | 299 ------------ .../cube/parse/TestDenormalizationResolver.java | 28 +- .../lens/cube/parse/TestExpressionContext.java | 4 +- .../lens/cube/parse/TestExpressionResolver.java | 1 + .../lens/cube/parse/TestJoinResolver.java | 1 + .../lens/cube/parse/TestORTimeRangeWriter.java | 40 +- .../lens/cube/parse/TestQueryMetrics.java | 2 +- .../lens/cube/parse/TestRewriterPlan.java | 2 +- .../apache/lens/cube/parse/TestStorageUtil.java | 98 ++-- .../lens/cube/parse/TestTimeRangeExtractor.java | 33 +- .../lens/cube/parse/TestTimeRangeResolver.java | 2 +- .../lens/cube/parse/TestTimeRangeWriter.java | 48 +- .../parse/TestTimeRangeWriterWithQuery.java | 134 +++--- .../lens/server/query/QueryResultPurger.java | 2 +- 39 files changed, 1693 insertions(+), 1706 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeColumn.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeColumn.java b/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeColumn.java index a2a00d2..b04532f 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeColumn.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeColumn.java @@ -24,7 +24,6 @@ import java.util.Date; import java.util.Map; import java.util.TimeZone; -import org.apache.lens.cube.parse.TimeRange; import com.google.common.base.Optional; http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeFactTable.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeFactTable.java b/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeFactTable.java index d6bfb79..dd0adb7 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeFactTable.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeFactTable.java @@ -21,7 +21,6 @@ package org.apache.lens.cube.metadata; import java.util.*; import org.apache.lens.cube.metadata.UpdatePeriod.UpdatePeriodComparator; -import org.apache.lens.cube.parse.DateUtil; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.hive.metastore.api.FieldSchema; http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/metadata/DateUtil.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/metadata/DateUtil.java b/lens-cube/src/main/java/org/apache/lens/cube/metadata/DateUtil.java new file mode 100644 index 0000000..b76c567 --- /dev/null +++ b/lens-cube/src/main/java/org/apache/lens/cube/metadata/DateUtil.java @@ -0,0 +1,396 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.lens.cube.metadata; + +import static java.util.Calendar.MONTH; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.lens.cube.error.LensCubeErrorCode; +import org.apache.lens.server.api.error.LensException; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.time.DateUtils; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public final class DateUtil { + private DateUtil() { + + } + + /* + * NOW -> new java.util.Date() NOW-7DAY -> a date one week earlier NOW (+-) + * <NUM>UNIT or Hardcoded dates in DD-MM-YYYY hh:mm:ss,sss + */ + public static final String UNIT; + + static { + StringBuilder sb = new StringBuilder(); + String sep = ""; + for (UpdatePeriod up : UpdatePeriod.values()) { + sb.append(sep).append(up.getUnitName()); + sep = "|"; + } + UNIT = sb.toString(); + } + + public static final String GRANULARITY = "\\.(" + UNIT + ")"; + public static final String RELATIVE = "(now)(" + GRANULARITY + ")?"; + public static final Pattern P_RELATIVE = Pattern.compile(RELATIVE, Pattern.CASE_INSENSITIVE); + + public static final String WSPACE = "\\s+"; + public static final String OPTIONAL_WSPACE = "\\s*"; + + public static final String SIGNAGE = "\\+|\\-"; + public static final Pattern P_SIGNAGE = Pattern.compile(SIGNAGE); + + public static final String QUANTITY = "\\d+"; + public static final Pattern P_QUANTITY = Pattern.compile(QUANTITY); + + public static final Pattern P_UNIT = Pattern.compile(UNIT, Pattern.CASE_INSENSITIVE); + + public static final String RELDATE_VALIDATOR_STR = RELATIVE + OPTIONAL_WSPACE + "((" + SIGNAGE + ")" + "(" + + WSPACE + ")?" + "(" + QUANTITY + ")" + OPTIONAL_WSPACE + "(" + UNIT + "))?" + "(s?)"; + + public static final Pattern RELDATE_VALIDATOR = Pattern.compile(RELDATE_VALIDATOR_STR, Pattern.CASE_INSENSITIVE); + + public static final String YEAR_FMT = "[0-9]{4}"; + public static final String MONTH_FMT = YEAR_FMT + "-[0-9]{2}"; + public static final String DAY_FMT = MONTH_FMT + "-[0-9]{2}"; + public static final String HOUR_FMT = DAY_FMT + "-[0-9]{2}"; + public static final String MINUTE_FMT = HOUR_FMT + ":[0-9]{2}"; + public static final String SECOND_FMT = MINUTE_FMT + ":[0-9]{2}"; + public static final String MILLISECOND_FMT = SECOND_FMT + ",[0-9]{3}"; + public static final String ABSDATE_FMT = "yyyy-MM-dd-HH:mm:ss,SSS"; + public static final String HIVE_QUERY_DATE_FMT = "yyyy-MM-dd HH:mm:ss"; + + public static final ThreadLocal<DateFormat> ABSDATE_PARSER = + new ThreadLocal<DateFormat>() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat(ABSDATE_FMT); + } + }; + public static final ThreadLocal<DateFormat> HIVE_QUERY_DATE_PARSER = + new ThreadLocal<DateFormat>() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat(HIVE_QUERY_DATE_FMT); + } + }; + + public static String getAbsDateFormatString(String str) { + if (str.matches(YEAR_FMT)) { + return str + "-01-01-00:00:00,000"; + } else if (str.matches(MONTH_FMT)) { + return str + "-01-00:00:00,000"; + } else if (str.matches(DAY_FMT)) { + return str + "-00:00:00,000"; + } else if (str.matches(HOUR_FMT)) { + return str + ":00:00,000"; + } else if (str.matches(MINUTE_FMT)) { + return str + ":00,000"; + } else if (str.matches(SECOND_FMT)) { + return str + ",000"; + } else if (str.matches(MILLISECOND_FMT)) { + return str; + } + throw new IllegalArgumentException("Unsupported formatting for date" + str); + } + + public static Date resolveDate(String str, Date now) throws LensException { + if (RELDATE_VALIDATOR.matcher(str).matches()) { + return resolveRelativeDate(str, now); + } else { + return resolveAbsoluteDate(str); + } + } + + public static String relativeToAbsolute(String relative) throws LensException { + return relativeToAbsolute(relative, new Date()); + } + + public static String relativeToAbsolute(String relative, Date now) throws LensException { + if (RELDATE_VALIDATOR.matcher(relative).matches()) { + return ABSDATE_PARSER.get().format(resolveRelativeDate(relative, now)); + } else { + return relative; + } + } + + static Cache<String, Date> stringToDateCache = CacheBuilder.newBuilder() + .expireAfterWrite(2, TimeUnit.HOURS).maximumSize(100).build(); + + public static Date resolveAbsoluteDate(final String str) throws LensException { + try { + return stringToDateCache.get(str, new Callable<Date>() { + @Override + public Date call() throws ParseException { + return ABSDATE_PARSER.get().parse(getAbsDateFormatString(str)); + } + }); + } catch (Exception e) { + log.error("Invalid date format. expected only {} date provided:{}", ABSDATE_FMT, str, e); + throw new LensException(LensCubeErrorCode.WRONG_TIME_RANGE_FORMAT.getLensErrorInfo(), ABSDATE_FMT, str); + } + } + + public static Date resolveRelativeDate(String str, Date now) throws LensException { + if (StringUtils.isBlank(str)) { + throw new LensException(LensCubeErrorCode.NULL_DATE_VALUE.getLensErrorInfo()); + } + + // Resolve NOW with proper granularity + Calendar calendar = Calendar.getInstance(); + calendar.setTime(now); + + str = str.toLowerCase(); + Matcher relativeMatcher = P_RELATIVE.matcher(str); + if (relativeMatcher.find()) { + String nowWithGranularity = relativeMatcher.group(); + nowWithGranularity = nowWithGranularity.replaceAll("now", ""); + nowWithGranularity = nowWithGranularity.replaceAll("\\.", ""); + + Matcher granularityMatcher = P_UNIT.matcher(nowWithGranularity); + if (granularityMatcher.find()) { + calendar = UpdatePeriod.fromUnitName(granularityMatcher.group().toLowerCase()).truncate(calendar); + } + } + + // Get rid of 'now' part and whitespace + String diffStr = str.replaceAll(RELATIVE, "").replace(WSPACE, ""); + TimeDiff diff = TimeDiff.parseFrom(diffStr); + return diff.offsetFrom(calendar.getTime()); + } + + public static Date getCeilDate(Date date, UpdatePeriod interval) { + return interval.getCeilDate(date); + } + + public static Date getFloorDate(Date date, UpdatePeriod interval) { + return interval.getFloorDate(date); + } + + public static CoveringInfo getMonthlyCoveringInfo(Date from, Date to) { + // Move 'from' to end of month, unless its the first day of month + boolean coverable = true; + if (!from.equals(DateUtils.truncate(from, MONTH))) { + from = DateUtils.addMonths(DateUtils.truncate(from, MONTH), 1); + coverable = false; + } + + // Move 'to' to beginning of next month, unless its the first day of the month + if (!to.equals(DateUtils.truncate(to, MONTH))) { + to = DateUtils.truncate(to, MONTH); + coverable = false; + } + + int months = 0; + while (from.before(to)) { + from = DateUtils.addMonths(from, 1); + months++; + } + return new CoveringInfo(months, coverable); + } + + public static CoveringInfo getQuarterlyCoveringInfo(Date from, Date to) { + CoveringInfo monthlyCoveringInfo = getMonthlyCoveringInfo(from, to); + if (monthlyCoveringInfo.getCountBetween() < 3) { + return new CoveringInfo(0, false); + } + boolean coverable = monthlyCoveringInfo.isCoverable(); + if (!from.equals(DateUtils.truncate(from, MONTH))) { + from = DateUtils.addMonths(DateUtils.truncate(from, MONTH), 1); + coverable = false; + } + Calendar cal = Calendar.getInstance(); + cal.setTime(from); + int fromMonth = cal.get(MONTH); + + // Get the start date of the quarter + int beginOffset = (3 - fromMonth % 3) % 3; + int endOffset = (monthlyCoveringInfo.getCountBetween() - beginOffset) % 3; + if (beginOffset > 0 || endOffset > 0) { + coverable = false; + } + return new CoveringInfo((monthlyCoveringInfo.getCountBetween() - beginOffset - endOffset) / 3, coverable); + } + + + public static CoveringInfo getYearlyCoveringInfo(Date from, Date to) { + CoveringInfo monthlyCoveringInfo = getMonthlyCoveringInfo(from, to); + if (monthlyCoveringInfo.getCountBetween() < 12) { + return new CoveringInfo(0, false); + } + boolean coverable = monthlyCoveringInfo.isCoverable(); + if (!from.equals(DateUtils.truncate(from, MONTH))) { + from = DateUtils.addMonths(DateUtils.truncate(from, MONTH), 1); + coverable = false; + } + Calendar cal = Calendar.getInstance(); + cal.setTime(from); + int fromMonth = cal.get(MONTH); + int beginOffset = (12 - fromMonth % 12) % 12; + int endOffset = (monthlyCoveringInfo.getCountBetween() - beginOffset) % 12; + if (beginOffset > 0 || endOffset > 0) { + coverable = false; + } + return new CoveringInfo((monthlyCoveringInfo.getCountBetween() - beginOffset - endOffset) / 12, coverable); + } + + public static CoveringInfo getWeeklyCoveringInfo(Date from, Date to) { + int dayDiff = 0; + Date tmpFrom = from; + while (tmpFrom.before(to)) { + tmpFrom = DateUtils.addDays(tmpFrom, 1); + dayDiff++; + } + + if (dayDiff < 7) { + return new CoveringInfo(0, false); + } + + Calendar cal = Calendar.getInstance(); + cal.setTime(from); + int fromDay = cal.get(Calendar.DAY_OF_WEEK); + cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); + Date fromWeekStartDate = cal.getTime(); + boolean coverable = dayDiff % 7 == 0; + if (fromWeekStartDate.before(from)) { + // Count from the start of next week + dayDiff -= (cal.getActualMaximum(Calendar.DAY_OF_WEEK) - (fromDay - Calendar.SUNDAY)); + coverable = false; + } + + return new CoveringInfo(dayDiff / 7, coverable); + } + + static CoveringInfo getCoveringInfo(Date from, Date to, UpdatePeriod interval) { + switch (interval) { + case SECONDLY: + case CONTINUOUS: + return getMilliSecondCoveringInfo(from, to, 1000); + case MINUTELY: + case HOURLY: + case DAILY: + return getMilliSecondCoveringInfo(from, to, interval.weight()); + case WEEKLY: + return getWeeklyCoveringInfo(from, to); + case MONTHLY: + return getMonthlyCoveringInfo(from, to); + case QUARTERLY: + return getQuarterlyCoveringInfo(from, to); + case YEARLY: + return getYearlyCoveringInfo(from, to); + default: + return new CoveringInfo(0, false); + } + } + + private static CoveringInfo getMilliSecondCoveringInfo(Date from, Date to, long millisInInterval) { + long diff = to.getTime() - from.getTime(); + return new CoveringInfo((int) (diff / millisInInterval), diff % millisInInterval == 0); + } + + static boolean isCoverableBy(Date from, Date to, Set<UpdatePeriod> intervals) { + for (UpdatePeriod period : intervals) { + if (getCoveringInfo(from, to, period).isCoverable()) { + return true; + } + } + return false; + } + + public static int getTimeDiff(Date fromDate, Date toDate, UpdatePeriod updatePeriod) { + if (fromDate.before(toDate)) { + return getCoveringInfo(fromDate, toDate, updatePeriod).getCountBetween(); + } else { + return -getCoveringInfo(toDate, fromDate, updatePeriod).getCountBetween(); + } + } + + @Data + public static class CoveringInfo { + int countBetween; + boolean coverable; + + public CoveringInfo(int countBetween, boolean coverable) { + this.countBetween = countBetween; + this.coverable = coverable; + } + } + + @EqualsAndHashCode + public static class TimeDiff { + int quantity; + UpdatePeriod updatePeriod; + + private TimeDiff(int quantity, UpdatePeriod updatePeriod) { + this.quantity = quantity; + this.updatePeriod = updatePeriod; + } + + public static TimeDiff parseFrom(String diffStr) throws LensException { + // Get the relative diff part to get eventual date based on now. + Matcher qtyMatcher = P_QUANTITY.matcher(diffStr); + int qty = 1; + if (qtyMatcher.find()) { + qty = Integer.parseInt(qtyMatcher.group()); + } + + Matcher signageMatcher = P_SIGNAGE.matcher(diffStr); + if (signageMatcher.find()) { + String sign = signageMatcher.group(); + if ("-".equals(sign)) { + qty = -qty; + } + } + + Matcher unitMatcher = P_UNIT.matcher(diffStr); + if (unitMatcher.find()) { + return new TimeDiff(qty, UpdatePeriod.fromUnitName(unitMatcher.group().toLowerCase())); + } + return new TimeDiff(0, UpdatePeriod.CONTINUOUS); + } + + public Date offsetFrom(Date time) { + return DateUtils.add(time, updatePeriod.calendarField(), quantity); + } + + public Date negativeOffsetFrom(Date time) { + return DateUtils.add(time, updatePeriod.calendarField(), -quantity); + } + } + +} http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/metadata/TimePartitionRange.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/metadata/TimePartitionRange.java b/lens-cube/src/main/java/org/apache/lens/cube/metadata/TimePartitionRange.java index 01069a5..2e85111 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/metadata/TimePartitionRange.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/metadata/TimePartitionRange.java @@ -21,7 +21,6 @@ package org.apache.lens.cube.metadata; import java.util.Date; import java.util.Iterator; -import org.apache.lens.cube.parse.DateUtil; import org.apache.lens.server.api.error.LensException; import lombok.Data; http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/metadata/TimeRange.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/metadata/TimeRange.java b/lens-cube/src/main/java/org/apache/lens/cube/metadata/TimeRange.java new file mode 100644 index 0000000..bf6cc5c --- /dev/null +++ b/lens-cube/src/main/java/org/apache/lens/cube/metadata/TimeRange.java @@ -0,0 +1,219 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.lens.cube.metadata; + +import static org.apache.lens.cube.metadata.DateUtil.ABSDATE_PARSER; + +import java.util.Calendar; +import java.util.Date; +import java.util.TreeSet; + +import org.apache.lens.cube.error.LensCubeErrorCode; +import org.apache.lens.server.api.error.LensException; + +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.hive.ql.parse.ASTNode; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; + +import lombok.Data; +import lombok.Getter; + +/** + * Timerange data structure + */ +@JsonIgnoreProperties({"astNode", "parent"}) +@Data +public class TimeRange { + private String partitionColumn; + private Date toDate; + private Date fromDate; + private ASTNode astNode; + private ASTNode parent; + private int childIndex; + + public boolean isCoverableBy(TreeSet<UpdatePeriod> updatePeriods) { + return DateUtil.isCoverableBy(fromDate, toDate, updatePeriods); + } + + + public static class TimeRangeBuilder { + private final TimeRange range; + + public TimeRangeBuilder() { + this.range = new TimeRange(); + } + + public TimeRangeBuilder partitionColumn(String col) { + range.partitionColumn = col; + return this; + } + + public TimeRangeBuilder toDate(Date to) { + range.toDate = to; + return this; + } + + public TimeRangeBuilder fromDate(Date from) { + range.fromDate = from; + return this; + } + + public TimeRangeBuilder astNode(ASTNode node) { + range.astNode = node; + return this; + } + + public TimeRangeBuilder parent(ASTNode parent) { + range.parent = parent; + return this; + } + + public TimeRangeBuilder childIndex(int childIndex) { + range.childIndex = childIndex; + return this; + } + + public TimeRange build() { + return range; + } + } + + public static TimeRangeBuilder getBuilder() { + return new TimeRangeBuilder(); + } + + private TimeRange() { + + } + + public void validate() throws LensException { + if (partitionColumn == null || fromDate == null || toDate == null || fromDate.equals(toDate)) { + throw new LensException(LensCubeErrorCode.INVALID_TIME_RANGE.getLensErrorInfo()); + } + + if (fromDate.after(toDate)) { + throw new LensException(LensCubeErrorCode.FROM_AFTER_TO.getLensErrorInfo(), + fromDate.toString(), toDate.toString()); + } + } + + public String toTimeDimWhereClause() { + return toTimeDimWhereClause(null, partitionColumn); + } + + public String toTimeDimWhereClause(String prefix, String column) { + if (StringUtils.isNotBlank(column)) { + column = prefix + "." + column; + } + return new StringBuilder() + .append(column).append(" >= '").append(DateUtil.HIVE_QUERY_DATE_PARSER.get().format(fromDate)).append("'") + .append(" AND ") + .append(column).append(" < '").append(DateUtil.HIVE_QUERY_DATE_PARSER.get().format(toDate)).append("'") + .toString(); + } + + @Override + public String toString() { + return partitionColumn + " [" + ABSDATE_PARSER.get().format(fromDate) + " to " + + ABSDATE_PARSER.get().format(toDate) + ")"; + } + + /** iterable from fromDate(including) to toDate(excluding) incrementing increment units of updatePeriod */ + public static Iterable iterable(Date fromDate, Date toDate, UpdatePeriod updatePeriod, int increment) { + return TimeRange.getBuilder().fromDate(fromDate).toDate(toDate).build().iterable(updatePeriod, increment); + } + + /** iterable from fromDate(including) incrementing increment units of updatePeriod. Do this numIters times */ + public static Iterable iterable(Date fromDate, int numIters, UpdatePeriod updatePeriod, int increment) { + return TimeRange.getBuilder().fromDate(fromDate).build().iterable(updatePeriod, numIters, increment); + } + + private Iterable iterable(UpdatePeriod updatePeriod, int numIters, int increment) { + return new Iterable(updatePeriod, numIters, increment); + } + + public Iterable iterable(UpdatePeriod updatePeriod, int increment) { + if (increment == 0) { + throw new UnsupportedOperationException("Can't iterate if iteration increment is zero"); + } + long numIters = DateUtil.getTimeDiff(fromDate, toDate, updatePeriod) / increment; + return new Iterable(updatePeriod, numIters, increment); + } + + /** Iterable so that foreach is supported */ + public class Iterable implements java.lang.Iterable<Date> { + private UpdatePeriod updatePeriod; + private long numIters; + private int increment; + + public Iterable(UpdatePeriod updatePeriod, long numIters, int increment) { + this.updatePeriod = updatePeriod; + this.numIters = numIters; + if (this.numIters < 0) { + this.numIters = 0; + } + this.increment = increment; + } + + @Override + public Iterator iterator() { + return new Iterator(); + } + + public class Iterator implements java.util.Iterator<Date> { + Calendar calendar; + // Tracks the index of the item returned after the last next() call. + // Index here refers to the index if the iterator were iterated and converted into a list. + @Getter + int counter = -1; + + public Iterator() { + calendar = Calendar.getInstance(); + calendar.setTime(fromDate); + } + + @Override + public boolean hasNext() { + return counter < numIters - 1; + } + + @Override + public Date next() { + Date cur = calendar.getTime(); + updatePeriod.increment(calendar, increment); + counter++; + return cur; + } + + public Date peekNext() { + return calendar.getTime(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("remove from timerange iterator"); + } + + public long getNumIters() { + return numIters; + } + } + } +} http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/metadata/UpdatePeriod.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/metadata/UpdatePeriod.java b/lens-cube/src/main/java/org/apache/lens/cube/metadata/UpdatePeriod.java index 4c76a69..4238066 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/metadata/UpdatePeriod.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/metadata/UpdatePeriod.java @@ -278,7 +278,7 @@ public enum UpdatePeriod implements Named { return cal.getTime(); case QUARTERLY: Date dt = DateUtils.truncate(date, this.calendarField()); - dt.setMonth(dt.getMonth() - dt.getMonth() % 3); + dt.setMonth(dt.getMonth() - (dt.getMonth() % 3)); return dt; default: return DateUtils.truncate(date, this.calendarField()); @@ -299,6 +299,86 @@ public enum UpdatePeriod implements Named { calendar.add(calendarField(), increment); } + public Date getCeilDate(Date date) { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + boolean hasFraction = false; + switch (this) { + case YEARLY: + if (cal.get(MONTH) != 0) { + hasFraction = true; + break; + } + case MONTHLY: + if (cal.get(DAY_OF_MONTH) != 1) { + hasFraction = true; + break; + } + case DAILY: + if (cal.get(Calendar.HOUR_OF_DAY) != 0) { + hasFraction = true; + break; + } + case HOURLY: + if (cal.get(Calendar.MINUTE) != 0) { + hasFraction = true; + break; + } + case MINUTELY: + if (cal.get(Calendar.SECOND) != 0) { + hasFraction = true; + break; + } + case SECONDLY: + case CONTINUOUS: + if (cal.get(Calendar.MILLISECOND) != 0) { + hasFraction = true; + } + break; + case WEEKLY: + if (cal.get(Calendar.DAY_OF_WEEK) != 1) { + hasFraction = true; + break; + } + } + + if (hasFraction) { + cal.add(this.calendarField(), 1); + return getFloorDate(cal.getTime()); + } else { + return date; + } + } + + public Date getFloorDate(Date date) { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + switch(this) { + case WEEKLY: + cal.set(Calendar.DAY_OF_WEEK, 1); + break; + } + switch (this) { + case YEARLY: + cal.set(MONTH, 0); + case MONTHLY: + cal.set(DAY_OF_MONTH, 1); + case WEEKLY: + // Already covered, only here for fall through cases + case DAILY: + cal.set(Calendar.HOUR_OF_DAY, 0); + case HOURLY: + cal.set(Calendar.MINUTE, 0); + case MINUTELY: + cal.set(Calendar.SECOND, 0); + case SECONDLY: + case CONTINUOUS: + cal.set(Calendar.MILLISECOND, 0); + break; + } + return cal.getTime(); + } + public static class UpdatePeriodComparator implements Comparator<UpdatePeriod> { @Override public int compare(UpdatePeriod o1, UpdatePeriod o2) { @@ -306,7 +386,7 @@ public enum UpdatePeriod implements Named { return -1; } else if (o1 != null && o2 == null) { return 1; - } else if (o1 == null && o2 == null) { + } else if (o1 == null) { return 0; } else { if (o1.weight > o2.weight) { http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/metadata/timeline/EndsAndHolesPartitionTimeline.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/metadata/timeline/EndsAndHolesPartitionTimeline.java b/lens-cube/src/main/java/org/apache/lens/cube/metadata/timeline/EndsAndHolesPartitionTimeline.java index 9d5e264..c588dc7 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/metadata/timeline/EndsAndHolesPartitionTimeline.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/metadata/timeline/EndsAndHolesPartitionTimeline.java @@ -23,8 +23,8 @@ import java.util.*; import org.apache.lens.cube.metadata.MetastoreUtil; import org.apache.lens.cube.metadata.TimePartition; +import org.apache.lens.cube.metadata.TimeRange; import org.apache.lens.cube.metadata.UpdatePeriod; -import org.apache.lens.cube.parse.TimeRange; import org.apache.lens.server.api.error.LensException; import com.google.common.base.Strings; http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateFact.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateFact.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateFact.java index 7f81461..1884bde 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateFact.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateFact.java @@ -22,10 +22,7 @@ import static org.apache.hadoop.hive.ql.parse.HiveParser.*; import java.util.*; -import org.apache.lens.cube.metadata.AbstractCubeTable; -import org.apache.lens.cube.metadata.CubeFactTable; -import org.apache.lens.cube.metadata.CubeInterface; -import org.apache.lens.cube.metadata.FactPartition; +import org.apache.lens.cube.metadata.*; import org.apache.lens.cube.parse.HQLParser.ASTNodeVisitor; import org.apache.lens.cube.parse.HQLParser.TreeNode; import org.apache.lens.server.api.error.LensException; http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTablePruneCause.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTablePruneCause.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTablePruneCause.java index 9c8b5b9..78fb21d 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTablePruneCause.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTablePruneCause.java @@ -22,6 +22,8 @@ import static org.apache.lens.cube.parse.CandidateTablePruneCause.CandidateTable import java.util.*; +import org.apache.lens.cube.metadata.TimeRange; + import org.codehaus.jackson.annotate.JsonWriteNullProperties; import com.google.common.collect.Lists; http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/parse/DateUtil.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/DateUtil.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/DateUtil.java deleted file mode 100644 index cd05c68..0000000 --- a/lens-cube/src/main/java/org/apache/lens/cube/parse/DateUtil.java +++ /dev/null @@ -1,456 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.lens.cube.parse; - -import static java.util.Calendar.*; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.lens.cube.error.LensCubeErrorCode; -import org.apache.lens.cube.metadata.UpdatePeriod; -import org.apache.lens.server.api.error.LensException; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.time.DateUtils; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public final class DateUtil { - private DateUtil() { - - } - /* - * NOW -> new java.util.Date() NOW-7DAY -> a date one week earlier NOW (+-) - * <NUM>UNIT or Hardcoded dates in DD-MM-YYYY hh:mm:ss,sss - */ - public static final String UNIT; - - static { - StringBuilder sb = new StringBuilder(); - String sep = ""; - for (UpdatePeriod up : UpdatePeriod.values()) { - sb.append(sep).append(up.getUnitName()); - sep = "|"; - } - UNIT = sb.toString(); - } - - public static final String GRANULARITY = "\\.(" + UNIT + ")"; - public static final String RELATIVE = "(now)(" + GRANULARITY + ")?"; - public static final Pattern P_RELATIVE = Pattern.compile(RELATIVE, Pattern.CASE_INSENSITIVE); - - public static final String WSPACE = "\\s+"; - public static final String OPTIONAL_WSPACE = "\\s*"; - - public static final String SIGNAGE = "\\+|\\-"; - public static final Pattern P_SIGNAGE = Pattern.compile(SIGNAGE); - - public static final String QUANTITY = "\\d+"; - public static final Pattern P_QUANTITY = Pattern.compile(QUANTITY); - - public static final Pattern P_UNIT = Pattern.compile(UNIT, Pattern.CASE_INSENSITIVE); - - public static final String RELDATE_VALIDATOR_STR = RELATIVE + OPTIONAL_WSPACE + "((" + SIGNAGE + ")" + "(" - + WSPACE + ")?" + "(" + QUANTITY + ")" + OPTIONAL_WSPACE + "(" + UNIT + "))?" + "(s?)"; - - public static final Pattern RELDATE_VALIDATOR = Pattern.compile(RELDATE_VALIDATOR_STR, Pattern.CASE_INSENSITIVE); - - public static final String YEAR_FMT = "[0-9]{4}"; - public static final String MONTH_FMT = YEAR_FMT + "-[0-9]{2}"; - public static final String DAY_FMT = MONTH_FMT + "-[0-9]{2}"; - public static final String HOUR_FMT = DAY_FMT + "-[0-9]{2}"; - public static final String MINUTE_FMT = HOUR_FMT + ":[0-9]{2}"; - public static final String SECOND_FMT = MINUTE_FMT + ":[0-9]{2}"; - public static final String MILLISECOND_FMT = SECOND_FMT + ",[0-9]{3}"; - public static final String ABSDATE_FMT = "yyyy-MM-dd-HH:mm:ss,SSS"; - public static final String HIVE_QUERY_DATE_FMT = "yyyy-MM-dd HH:mm:ss"; - - public static final ThreadLocal<DateFormat> ABSDATE_PARSER = - new ThreadLocal<DateFormat>() { - @Override - protected SimpleDateFormat initialValue() { - return new SimpleDateFormat(ABSDATE_FMT); - } - }; - public static final ThreadLocal<DateFormat> HIVE_QUERY_DATE_PARSER = - new ThreadLocal<DateFormat>() { - @Override - protected SimpleDateFormat initialValue() { - return new SimpleDateFormat(HIVE_QUERY_DATE_FMT); - } - }; - - public static String getAbsDateFormatString(String str) { - if (str.matches(YEAR_FMT)) { - return str + "-01-01-00:00:00,000"; - } else if (str.matches(MONTH_FMT)) { - return str + "-01-00:00:00,000"; - } else if (str.matches(DAY_FMT)) { - return str + "-00:00:00,000"; - } else if (str.matches(HOUR_FMT)) { - return str + ":00:00,000"; - } else if (str.matches(MINUTE_FMT)) { - return str + ":00,000"; - } else if (str.matches(SECOND_FMT)) { - return str + ",000"; - } else if (str.matches(MILLISECOND_FMT)) { - return str; - } - throw new IllegalArgumentException("Unsupported formatting for date" + str); - } - - public static Date resolveDate(String str, Date now) throws LensException { - if (RELDATE_VALIDATOR.matcher(str).matches()) { - return resolveRelativeDate(str, now); - } else { - return resolveAbsoluteDate(str); - } - } - - public static String relativeToAbsolute(String relative) throws LensException { - return relativeToAbsolute(relative, new Date()); - } - - public static String relativeToAbsolute(String relative, Date now) throws LensException { - if (RELDATE_VALIDATOR.matcher(relative).matches()) { - return ABSDATE_PARSER.get().format(resolveRelativeDate(relative, now)); - } else { - return relative; - } - } - - public static Date resolveAbsoluteDate(String str) throws LensException { - try { - return ABSDATE_PARSER.get().parse(getAbsDateFormatString(str)); - } catch (ParseException e) { - log.error("Invalid date format. expected only {} date provided:{}", ABSDATE_FMT, str, e); - throw new LensException(LensCubeErrorCode.WRONG_TIME_RANGE_FORMAT.getLensErrorInfo(), ABSDATE_FMT, str); - } - } - - public static Date resolveRelativeDate(String str, Date now) throws LensException { - if (StringUtils.isBlank(str)) { - throw new LensException(LensCubeErrorCode.NULL_DATE_VALUE.getLensErrorInfo()); - } - - // Resolve NOW with proper granularity - Calendar calendar = Calendar.getInstance(); - calendar.setTime(now); - - str = str.toLowerCase(); - Matcher relativeMatcher = P_RELATIVE.matcher(str); - if (relativeMatcher.find()) { - String nowWithGranularity = relativeMatcher.group(); - nowWithGranularity = nowWithGranularity.replaceAll("now", ""); - nowWithGranularity = nowWithGranularity.replaceAll("\\.", ""); - - Matcher granularityMatcher = P_UNIT.matcher(nowWithGranularity); - if (granularityMatcher.find()) { - calendar = UpdatePeriod.fromUnitName(granularityMatcher.group().toLowerCase()).truncate(calendar); - } - } - - // Get rid of 'now' part and whitespace - String diffStr = str.replaceAll(RELATIVE, "").replace(WSPACE, ""); - TimeDiff diff = TimeDiff.parseFrom(diffStr); - return diff.offsetFrom(calendar.getTime()); - } - - public static Date getCeilDate(Date fromDate, UpdatePeriod interval) { - Calendar cal = Calendar.getInstance(); - cal.setTime(fromDate); - boolean hasFraction = false; - switch (interval) { - case YEARLY: - if (cal.get(MONTH) != 0) { - hasFraction = true; - break; - } - case MONTHLY: - if (cal.get(DAY_OF_MONTH) != 1) { - hasFraction = true; - break; - } - case DAILY: - if (cal.get(Calendar.HOUR_OF_DAY) != 0) { - hasFraction = true; - break; - } - case HOURLY: - if (cal.get(Calendar.MINUTE) != 0) { - hasFraction = true; - break; - } - case MINUTELY: - if (cal.get(Calendar.SECOND) != 0) { - hasFraction = true; - break; - } - case SECONDLY: - case CONTINUOUS: - if (cal.get(Calendar.MILLISECOND) != 0) { - hasFraction = true; - } - break; - case WEEKLY: - if (cal.get(Calendar.DAY_OF_WEEK) != 1) { - hasFraction = true; - break; - } - } - - if (hasFraction) { - cal.add(interval.calendarField(), 1); - return getFloorDate(cal.getTime(), interval); - } else { - return fromDate; - } - } - - public static Date getFloorDate(Date toDate, UpdatePeriod interval) { - Calendar cal = Calendar.getInstance(); - cal.setTime(toDate); - switch (interval) { - case YEARLY: - cal.set(MONTH, 0); - case MONTHLY: - cal.set(DAY_OF_MONTH, 1); - case DAILY: - cal.set(Calendar.HOUR_OF_DAY, 0); - case HOURLY: - cal.set(Calendar.MINUTE, 0); - case MINUTELY: - cal.set(Calendar.SECOND, 0); - case SECONDLY: - case CONTINUOUS: - cal.set(Calendar.MILLISECOND, 0); - break; - case WEEKLY: - cal.set(Calendar.DAY_OF_WEEK, 1); - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - break; - } - return cal.getTime(); - } - - public static CoveringInfo getMonthlyCoveringInfo(Date from, Date to) { - // Move 'from' to end of month, unless its the first day of month - boolean coverable = true; - if (!from.equals(DateUtils.truncate(from, MONTH))) { - from = DateUtils.addMonths(DateUtils.truncate(from, MONTH), 1); - coverable = false; - } - - // Move 'to' to beginning of next month, unless its the first day of the month - if (!to.equals(DateUtils.truncate(to, MONTH))) { - to = DateUtils.truncate(to, MONTH); - coverable = false; - } - - int months = 0; - while (from.before(to)) { - from = DateUtils.addMonths(from, 1); - months++; - } - return new CoveringInfo(months, coverable); - } - - public static CoveringInfo getQuarterlyCoveringInfo(Date from, Date to) { - CoveringInfo monthlyCoveringInfo = getMonthlyCoveringInfo(from, to); - if (monthlyCoveringInfo.getCountBetween() < 3) { - return new CoveringInfo(0, false); - } - boolean coverable = monthlyCoveringInfo.isCoverable(); - if (!from.equals(DateUtils.truncate(from, MONTH))) { - from = DateUtils.addMonths(DateUtils.truncate(from, MONTH), 1); - coverable = false; - } - Calendar cal = Calendar.getInstance(); - cal.setTime(from); - int fromMonth = cal.get(MONTH); - - // Get the start date of the quarter - int beginOffset = (3 - fromMonth % 3) % 3; - int endOffset = (monthlyCoveringInfo.getCountBetween() - beginOffset) % 3; - if (beginOffset > 0 || endOffset > 0) { - coverable = false; - } - return new CoveringInfo((monthlyCoveringInfo.getCountBetween() - beginOffset - endOffset) / 3, coverable); - } - - - public static CoveringInfo getYearlyCoveringInfo(Date from, Date to) { - CoveringInfo monthlyCoveringInfo = getMonthlyCoveringInfo(from, to); - if (monthlyCoveringInfo.getCountBetween() < 12) { - return new CoveringInfo(0, false); - } - boolean coverable = monthlyCoveringInfo.isCoverable(); - if (!from.equals(DateUtils.truncate(from, MONTH))) { - from = DateUtils.addMonths(DateUtils.truncate(from, MONTH), 1); - coverable = false; - } - Calendar cal = Calendar.getInstance(); - cal.setTime(from); - int fromMonth = cal.get(MONTH); - int beginOffset = (12 - fromMonth % 12) % 12; - int endOffset = (monthlyCoveringInfo.getCountBetween() - beginOffset) % 12; - if (beginOffset > 0 || endOffset > 0) { - coverable = false; - } - return new CoveringInfo((monthlyCoveringInfo.getCountBetween() - beginOffset - endOffset) / 12, coverable); - } - - public static CoveringInfo getWeeklyCoveringInfo(Date from, Date to) { - int dayDiff = 0; - Date tmpFrom = from; - while (tmpFrom.before(to)) { - tmpFrom = DateUtils.addDays(tmpFrom, 1); - dayDiff++; - } - - if (dayDiff < 7) { - return new CoveringInfo(0, false); - } - - Calendar cal = Calendar.getInstance(); - cal.setTime(from); - int fromDay = cal.get(Calendar.DAY_OF_WEEK); - cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); - Date fromWeekStartDate = cal.getTime(); - boolean coverable = dayDiff % 7 == 0; - if (fromWeekStartDate.before(from)) { - // Count from the start of next week - dayDiff -= (cal.getActualMaximum(Calendar.DAY_OF_WEEK) - (fromDay - Calendar.SUNDAY)); - coverable = false; - } - - return new CoveringInfo(dayDiff / 7, coverable); - } - - static CoveringInfo getCoveringInfo(Date from, Date to, UpdatePeriod interval) { - switch (interval) { - case SECONDLY: - case CONTINUOUS: - return getMilliSecondCoveringInfo(from, to, 1000); - case MINUTELY: - case HOURLY: - case DAILY: - return getMilliSecondCoveringInfo(from, to, interval.weight()); - case WEEKLY: - return getWeeklyCoveringInfo(from, to); - case MONTHLY: - return getMonthlyCoveringInfo(from, to); - case QUARTERLY: - return getQuarterlyCoveringInfo(from, to); - case YEARLY: - return getYearlyCoveringInfo(from, to); - default: - return new CoveringInfo(0, false); - } - } - - private static CoveringInfo getMilliSecondCoveringInfo(Date from, Date to, long millisInInterval) { - long diff = to.getTime() - from.getTime(); - return new CoveringInfo((int) (diff / millisInInterval), diff % millisInInterval == 0); - } - - static boolean isCoverableBy(Date from, Date to, Set<UpdatePeriod> intervals) { - for (UpdatePeriod period : intervals) { - if (getCoveringInfo(from, to, period).isCoverable()) { - return true; - } - } - return false; - } - - public static int getTimeDiff(Date fromDate, Date toDate, UpdatePeriod updatePeriod) { - if (fromDate.before(toDate)) { - return getCoveringInfo(fromDate, toDate, updatePeriod).getCountBetween(); - } else { - return -getCoveringInfo(toDate, fromDate, updatePeriod).getCountBetween(); - } - } - - @Data - public static class CoveringInfo { - int countBetween; - boolean coverable; - - public CoveringInfo(int countBetween, boolean coverable) { - this.countBetween = countBetween; - this.coverable = coverable; - } - } - - @EqualsAndHashCode - public static class TimeDiff { - int quantity; - UpdatePeriod updatePeriod; - - private TimeDiff(int quantity, UpdatePeriod updatePeriod) { - this.quantity = quantity; - this.updatePeriod = updatePeriod; - } - - public static TimeDiff parseFrom(String diffStr) throws LensException { - // Get the relative diff part to get eventual date based on now. - Matcher qtyMatcher = P_QUANTITY.matcher(diffStr); - int qty = 1; - if (qtyMatcher.find()) { - qty = Integer.parseInt(qtyMatcher.group()); - } - - Matcher signageMatcher = P_SIGNAGE.matcher(diffStr); - if (signageMatcher.find()) { - String sign = signageMatcher.group(); - if ("-".equals(sign)) { - qty = -qty; - } - } - - Matcher unitMatcher = P_UNIT.matcher(diffStr); - if (unitMatcher.find()) { - return new TimeDiff(qty, UpdatePeriod.fromUnitName(unitMatcher.group().toLowerCase())); - } - return new TimeDiff(0, UpdatePeriod.CONTINUOUS); - } - - public Date offsetFrom(Date time) { - return DateUtils.add(time, updatePeriod.calendarField(), quantity); - } - - public Date negativeOffsetFrom(Date time) { - return DateUtils.add(time, updatePeriod.calendarField(), -quantity); - } - } - -} http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/parse/ExpressionResolver.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/ExpressionResolver.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/ExpressionResolver.java index 200a48c..776021d 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/parse/ExpressionResolver.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/ExpressionResolver.java @@ -19,18 +19,11 @@ package org.apache.lens.cube.parse; -import static org.apache.hadoop.hive.ql.parse.HiveParser.DOT; -import static org.apache.hadoop.hive.ql.parse.HiveParser.Identifier; -import static org.apache.hadoop.hive.ql.parse.HiveParser.TOK_TABLE_OR_COL; +import static org.apache.hadoop.hive.ql.parse.HiveParser.*; import java.util.*; -import org.apache.lens.cube.metadata.AbstractBaseTable; -import org.apache.lens.cube.metadata.AbstractCubeTable; -import org.apache.lens.cube.metadata.CubeColumn; -import org.apache.lens.cube.metadata.CubeInterface; -import org.apache.lens.cube.metadata.Dimension; -import org.apache.lens.cube.metadata.ExprColumn; +import org.apache.lens.cube.metadata.*; import org.apache.lens.cube.metadata.ExprColumn.ExprSpec; import org.apache.lens.cube.parse.CandidateTablePruneCause.CandidateTablePruneCode; import org.apache.lens.cube.parse.HQLParser.ASTNodeVisitor; http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/parse/SingleFactHQLContext.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/SingleFactHQLContext.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/SingleFactHQLContext.java index 60b2dde..f7271e5 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/parse/SingleFactHQLContext.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/SingleFactHQLContext.java @@ -21,7 +21,7 @@ package org.apache.lens.cube.parse; import java.util.Map; import org.apache.lens.cube.metadata.Dimension; - +import org.apache.lens.cube.metadata.TimeRange; import org.apache.lens.server.api.error.LensException; import org.apache.commons.lang.StringUtils; http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/parse/StorageTableResolver.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/StorageTableResolver.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/StorageTableResolver.java index cc8e68c..62cc071 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/parse/StorageTableResolver.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/StorageTableResolver.java @@ -18,6 +18,7 @@ */ package org.apache.lens.cube.parse; +import static org.apache.lens.cube.metadata.DateUtil.WSPACE; import static org.apache.lens.cube.metadata.MetastoreUtil.getFactOrDimtableStorageTableName; import static org.apache.lens.cube.metadata.MetastoreUtil.getStoragetableEndTimesKey; import static org.apache.lens.cube.metadata.MetastoreUtil.getStoragetableStartTimesKey; @@ -25,7 +26,6 @@ import static org.apache.lens.cube.parse.CandidateTablePruneCause.*; import static org.apache.lens.cube.parse.CandidateTablePruneCause.CandidateTablePruneCode.*; import static org.apache.lens.cube.parse.CandidateTablePruneCause.SkipStorageCode.PART_COL_DOES_NOT_EXIST; import static org.apache.lens.cube.parse.CandidateTablePruneCause.SkipStorageCode.RANGE_NOT_ANSWERABLE; -import static org.apache.lens.cube.parse.DateUtil.WSPACE; import static org.apache.lens.cube.parse.StorageUtil.joinWithAnd; import java.text.DateFormat; http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/parse/TimeRange.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/TimeRange.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/TimeRange.java deleted file mode 100644 index 7be7ace..0000000 --- a/lens-cube/src/main/java/org/apache/lens/cube/parse/TimeRange.java +++ /dev/null @@ -1,220 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.lens.cube.parse; - -import static org.apache.lens.cube.parse.DateUtil.ABSDATE_PARSER; - -import java.util.Calendar; -import java.util.Date; -import java.util.TreeSet; - -import org.apache.lens.cube.error.LensCubeErrorCode; -import org.apache.lens.cube.metadata.UpdatePeriod; -import org.apache.lens.server.api.error.LensException; - -import org.apache.commons.lang.StringUtils; -import org.apache.hadoop.hive.ql.parse.ASTNode; - -import org.codehaus.jackson.annotate.JsonIgnoreProperties; - -import lombok.Data; -import lombok.Getter; - -/** - * Timerange data structure - */ -@JsonIgnoreProperties({"astNode", "parent"}) -@Data -public class TimeRange { - private String partitionColumn; - private Date toDate; - private Date fromDate; - private ASTNode astNode; - private ASTNode parent; - private int childIndex; - - public boolean isCoverableBy(TreeSet<UpdatePeriod> updatePeriods) { - return DateUtil.isCoverableBy(fromDate, toDate, updatePeriods); - } - - - public static class TimeRangeBuilder { - private final TimeRange range; - - public TimeRangeBuilder() { - this.range = new TimeRange(); - } - - public TimeRangeBuilder partitionColumn(String col) { - range.partitionColumn = col; - return this; - } - - public TimeRangeBuilder toDate(Date to) { - range.toDate = to; - return this; - } - - public TimeRangeBuilder fromDate(Date from) { - range.fromDate = from; - return this; - } - - public TimeRangeBuilder astNode(ASTNode node) { - range.astNode = node; - return this; - } - - public TimeRangeBuilder parent(ASTNode parent) { - range.parent = parent; - return this; - } - - public TimeRangeBuilder childIndex(int childIndex) { - range.childIndex = childIndex; - return this; - } - - public TimeRange build() { - return range; - } - } - - public static TimeRangeBuilder getBuilder() { - return new TimeRangeBuilder(); - } - - private TimeRange() { - - } - - public void validate() throws LensException { - if (partitionColumn == null || fromDate == null || toDate == null || fromDate.equals(toDate)) { - throw new LensException(LensCubeErrorCode.INVALID_TIME_RANGE.getLensErrorInfo()); - } - - if (fromDate.after(toDate)) { - throw new LensException(LensCubeErrorCode.FROM_AFTER_TO.getLensErrorInfo(), - fromDate.toString(), toDate.toString()); - } - } - - public String toTimeDimWhereClause() { - return toTimeDimWhereClause(null, partitionColumn); - } - - public String toTimeDimWhereClause(String prefix, String column) { - if (StringUtils.isNotBlank(column)) { - column = prefix + "." + column; - } - return new StringBuilder() - .append(column).append(" >= '").append(DateUtil.HIVE_QUERY_DATE_PARSER.get().format(fromDate)).append("'") - .append(" AND ") - .append(column).append(" < '").append(DateUtil.HIVE_QUERY_DATE_PARSER.get().format(toDate)).append("'") - .toString(); - } - - @Override - public String toString() { - return partitionColumn + " [" + ABSDATE_PARSER.get().format(fromDate) + " to " - + ABSDATE_PARSER.get().format(toDate) + ")"; - } - - /** iterable from fromDate(including) to toDate(excluding) incrementing increment units of updatePeriod */ - public static Iterable iterable(Date fromDate, Date toDate, UpdatePeriod updatePeriod, int increment) { - return TimeRange.getBuilder().fromDate(fromDate).toDate(toDate).build().iterable(updatePeriod, increment); - } - - /** iterable from fromDate(including) incrementing increment units of updatePeriod. Do this numIters times */ - public static Iterable iterable(Date fromDate, int numIters, UpdatePeriod updatePeriod, int increment) { - return TimeRange.getBuilder().fromDate(fromDate).build().iterable(updatePeriod, numIters, increment); - } - - private Iterable iterable(UpdatePeriod updatePeriod, int numIters, int increment) { - return new Iterable(updatePeriod, numIters, increment); - } - - public Iterable iterable(UpdatePeriod updatePeriod, int increment) { - if (increment == 0) { - throw new UnsupportedOperationException("Can't iterate if iteration increment is zero"); - } - long numIters = DateUtil.getTimeDiff(fromDate, toDate, updatePeriod) / increment; - return new Iterable(updatePeriod, numIters, increment); - } - - /** Iterable so that foreach is supported */ - public class Iterable implements java.lang.Iterable<Date> { - private UpdatePeriod updatePeriod; - private long numIters; - private int increment; - - public Iterable(UpdatePeriod updatePeriod, long numIters, int increment) { - this.updatePeriod = updatePeriod; - this.numIters = numIters; - if (this.numIters < 0) { - this.numIters = 0; - } - this.increment = increment; - } - - @Override - public Iterator iterator() { - return new Iterator(); - } - - public class Iterator implements java.util.Iterator<Date> { - Calendar calendar; - // Tracks the index of the item returned after the last next() call. - // Index here refers to the index if the iterator were iterated and converted into a list. - @Getter - int counter = -1; - - public Iterator() { - calendar = Calendar.getInstance(); - calendar.setTime(fromDate); - } - - @Override - public boolean hasNext() { - return counter < numIters - 1; - } - - @Override - public Date next() { - Date cur = calendar.getTime(); - updatePeriod.increment(calendar, increment); - counter++; - return cur; - } - - public Date peekNext() { - return calendar.getTime(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("remove from timerange iterator"); - } - - public long getNumIters() { - return numIters; - } - } - } -} http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/main/java/org/apache/lens/cube/parse/TimerangeResolver.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/TimerangeResolver.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/TimerangeResolver.java index f772279..1a83d09 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/parse/TimerangeResolver.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/TimerangeResolver.java @@ -27,10 +27,7 @@ import java.util.*; import org.apache.lens.cube.error.ColUnAvailableInTimeRange; import org.apache.lens.cube.error.ColUnAvailableInTimeRangeException; import org.apache.lens.cube.error.LensCubeErrorCode; -import org.apache.lens.cube.metadata.AbstractCubeTable; -import org.apache.lens.cube.metadata.CubeColumn; -import org.apache.lens.cube.metadata.Dimension; -import org.apache.lens.cube.metadata.SchemaGraph; +import org.apache.lens.cube.metadata.*; import org.apache.lens.cube.parse.DenormalizationResolver.ReferencedQueriedColumn; import org.apache.lens.server.api.error.LensException; http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/test/java/org/apache/lens/cube/metadata/CubeFactTableTest.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/test/java/org/apache/lens/cube/metadata/CubeFactTableTest.java b/lens-cube/src/test/java/org/apache/lens/cube/metadata/CubeFactTableTest.java index 2abc6d0..25eaaef 100644 --- a/lens-cube/src/test/java/org/apache/lens/cube/metadata/CubeFactTableTest.java +++ b/lens-cube/src/test/java/org/apache/lens/cube/metadata/CubeFactTableTest.java @@ -26,7 +26,6 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; -import org.apache.lens.cube.parse.DateUtil; import org.apache.lens.server.api.error.LensException; import org.testng.annotations.DataProvider; http://git-wip-us.apache.org/repos/asf/lens/blob/7c7c86da/lens-cube/src/test/java/org/apache/lens/cube/metadata/DateFactory.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/test/java/org/apache/lens/cube/metadata/DateFactory.java b/lens-cube/src/test/java/org/apache/lens/cube/metadata/DateFactory.java new file mode 100644 index 0000000..87e4ce3 --- /dev/null +++ b/lens-cube/src/test/java/org/apache/lens/cube/metadata/DateFactory.java @@ -0,0 +1,196 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.lens.cube.metadata; + + +import static org.apache.lens.cube.metadata.UpdatePeriod.*; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; + +public class DateFactory { + private DateFactory() { + + } + + public static class DateOffsetProvider extends HashMap<Integer, Date> { + private final UpdatePeriod updatePeriod; + Calendar calendar = Calendar.getInstance(); + + public DateOffsetProvider(UpdatePeriod updatePeriod) { + this(updatePeriod, false); + } + + public DateOffsetProvider(UpdatePeriod updatePeriod, boolean truncate) { + this.updatePeriod = updatePeriod; + Date date = calendar.getTime(); + if (truncate) { + date = updatePeriod.truncate(date); + calendar.setTime(date); + } + put(0, date); + } + + @Override + public Date get(Object key) { + if (!containsKey(key) && key instanceof Integer) { + calendar.setTime(super.get(0)); + calendar.add(updatePeriod.calendarField(), (Integer) key); + put((Integer) key, calendar.getTime()); + } + return super.get(key); + } + } + + public static class GeneralDateOffsetProvider extends HashMap<UpdatePeriod, DateOffsetProvider> { + @Override + public DateOffsetProvider get(Object key) { + if (!containsKey(key) && key instanceof UpdatePeriod) { + UpdatePeriod up = (UpdatePeriod) key; + put(up, new DateOffsetProvider(up)); + } + return super.get(key); + } + + public Date get(UpdatePeriod updatePeriod, int offset) { + return get(updatePeriod).get(offset); + } + } + + public static final GeneralDateOffsetProvider GENERAL_DATE_OFFSET_PROVIDER = new GeneralDateOffsetProvider(); + + + public static Date getDateWithOffset(UpdatePeriod up, int offset) { + return GENERAL_DATE_OFFSET_PROVIDER.get(up, offset); + } + + public static String getDateStringWithOffset(UpdatePeriod up, int offset) { + return getDateStringWithOffset(up, offset, up); + } + + public static String getDateStringWithOffset(UpdatePeriod up, int offset, UpdatePeriod formatWith) { + return formatWith.format(GENERAL_DATE_OFFSET_PROVIDER.get(up, offset)); + } + + public static String getTimeRangeString(final String timeDim, final String startDate, final String endDate) { + return "time_range_in(" + timeDim + ", '" + startDate + "','" + endDate + "')"; + } + + public static String getTimeRangeString(final String timeDim, final UpdatePeriod updatePeriod, + final int startOffset, final int endOffset) { + return getTimeRangeString(timeDim, + getDateStringWithOffset(updatePeriod, startOffset), getDateStringWithOffset(updatePeriod, endOffset)); + } + + public static String getTimeRangeString(final String startDate, final String endDate) { + return getTimeRangeString("d_time", startDate, endDate); + } + + public static String getTimeRangeString(final UpdatePeriod updatePeriod, + final int startOffset, final int endOffset) { + return getTimeRangeString("d_time", updatePeriod, startOffset, endOffset); + } + + public static String getTimeRangeString(String partCol, UpdatePeriod updatePeriod, int startOffset, int endOffset, + UpdatePeriod formatWith) { + return getTimeRangeString(partCol, + formatWith.format(getDateWithOffset(updatePeriod, startOffset)), + formatWith.format(getDateWithOffset(updatePeriod, endOffset))); + } + + public static String getTimeRangeString(String partCol, UpdatePeriod updatePeriod, int startOffset, int endOffset, + DateFormat formatWith) { + return getTimeRangeString(partCol, + formatWith.format(getDateWithOffset(updatePeriod, startOffset)), + formatWith.format(getDateWithOffset(updatePeriod, endOffset))); + } + + public static String getTimeRangeString(UpdatePeriod updatePeriod, int startOffset, int endOffset, + UpdatePeriod formatWith) { + return getTimeRangeString("d_time", updatePeriod, startOffset, endOffset, formatWith); + } + + public static String getTimeRangeString(UpdatePeriod updatePeriod, int startOffset, int endOffset, + DateFormat formatWith) { + return getTimeRangeString("d_time", updatePeriod, startOffset, endOffset, formatWith); + } + + // Time Instances as Date Type + public static final Date NOW; + public static final Date TWODAYS_BACK; + public static final Date TWO_MONTHS_BACK; + public static final Date BEFORE_6_DAYS; + public static final Date BEFORE_4_DAYS; + + // Time Ranges + public static final String LAST_HOUR_TIME_RANGE; + public static final String TWO_DAYS_RANGE; + public static final String TWO_DAYS_RANGE_TTD; + public static final String TWO_DAYS_RANGE_TTD_BEFORE_4_DAYS; + public static final String TWO_DAYS_RANGE_TTD2; + public static final String TWO_DAYS_RANGE_TTD2_BEFORE_4_DAYS; + public static final String TWO_DAYS_RANGE_IT; + public static final String THIS_YEAR_RANGE; + public static final String LAST_YEAR_RANGE; + public static final String TWO_MONTHS_RANGE_UPTO_MONTH; + public static final String TWO_MONTHS_RANGE_UPTO_HOURS; + public static final String TWO_DAYS_RANGE_BEFORE_4_DAYS; + private static boolean zerothHour; + + + public static boolean isZerothHour() { + return zerothHour; + } + + static { + NOW = getDateWithOffset(HOURLY, 0); + + // Figure out if current hour is 0th hour + zerothHour = getDateStringWithOffset(HOURLY, 0).endsWith("-00"); + + TWODAYS_BACK = getDateWithOffset(DAILY, -2); + System.out.println("Test TWODAYS_BACK:" + TWODAYS_BACK); + + // two months back + TWO_MONTHS_BACK = getDateWithOffset(MONTHLY, -2); + System.out.println("Test TWO_MONTHS_BACK:" + TWO_MONTHS_BACK); + + // Before 4days + BEFORE_4_DAYS = getDateWithOffset(DAILY, -4); + BEFORE_6_DAYS = getDateWithOffset(DAILY, -6); + + TWO_DAYS_RANGE_BEFORE_4_DAYS = getTimeRangeString(DAILY, -6, -4, HOURLY); + + TWO_DAYS_RANGE = getTimeRangeString(HOURLY, -48, 0); + TWO_DAYS_RANGE_TTD = getTimeRangeString("test_time_dim", DAILY, -2, 0, HOURLY); + TWO_DAYS_RANGE_TTD_BEFORE_4_DAYS = getTimeRangeString("test_time_dim", DAILY, -6, -4, HOURLY); + TWO_DAYS_RANGE_TTD2 = getTimeRangeString("test_time_dim2", DAILY, -2, 0, HOURLY); + TWO_DAYS_RANGE_TTD2_BEFORE_4_DAYS = getTimeRangeString("test_time_dim2", DAILY, -6, -4, HOURLY); + TWO_DAYS_RANGE_IT = getTimeRangeString("it", DAILY, -2, 0, HOURLY); + THIS_YEAR_RANGE = getTimeRangeString(YEARLY, 0, 1); + LAST_YEAR_RANGE = getTimeRangeString(YEARLY, -1, 0); + TWO_MONTHS_RANGE_UPTO_MONTH = getTimeRangeString(MONTHLY, -2, 0); + TWO_MONTHS_RANGE_UPTO_HOURS = getTimeRangeString(MONTHLY, -2, 0, HOURLY); + + // calculate LAST_HOUR_TIME_RANGE + LAST_HOUR_TIME_RANGE = getTimeRangeString(HOURLY, -1, 0); + } +}