/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.types.mcompat.mfuncs; import com.foundationdb.server.types.LazyList; import com.foundationdb.server.types.TExecutionContext; import com.foundationdb.server.types.TScalar; import com.foundationdb.server.types.TOverloadResult; import com.foundationdb.server.types.mcompat.mtypes.MNumeric; import com.foundationdb.server.types.value.ValueSource; import com.foundationdb.server.types.value.ValueTarget; import com.foundationdb.server.types.texpressions.TInputSetBuilder; import com.foundationdb.server.types.texpressions.TScalarBase; public abstract class MPeriodArith extends TScalarBase { private final String name; public static final TScalar[] INSTANCES = { new MPeriodArith("PERIOD_ADD") { @Override protected void doEvaluate(TExecutionContext context, LazyList<? extends ValueSource> inputs, ValueTarget output) { long period = inputs.get(0).getInt64(); long offsetMonths = inputs.get(1).getInt64(); // COMPATIBILITY: MySQL currently has undefined behavior for negative numbers // Our behavior follows our B.C. year numbering (-199402 + 1 = -199401) // Java's mod returns negative numbers: -1994 % 100 = -94 long periodInMonths = fromPeriod(period); long totalMonths = periodInMonths + offsetMonths; // Handle the case where the period changes sign (e.g. -YYMM to YYMM) // as a result of adding. Since 0000 months is not really a date, // this leads to an off by one error if (Long.signum(periodInMonths) * Long.signum(totalMonths) == -1) { totalMonths -= Long.signum(totalMonths) * 2; } long result = toPeriod(totalMonths); output.putInt64(result); } }, new MPeriodArith("PERIOD_DIFF") { @Override protected void doEvaluate(TExecutionContext context, LazyList<? extends ValueSource> inputs, ValueTarget output) { // COMPATIBILITY: MySQL currently has undefined behavior for negative numbers // Our behavior follows our B.C. year numbering (-199402 + 1 = -199401) long periodLeft = inputs.get(0).getInt64(); long periodRight = inputs.get(1).getInt64(); long result = fromPeriod(periodLeft) - fromPeriod(periodRight); output.putInt64(result); } } }; private MPeriodArith(String name) { this.name = name; } @Override protected void buildInputSets(TInputSetBuilder builder) { builder.covers(MNumeric.BIGINT, 0, 1); } @Override public String displayName() { return name; } @Override public TOverloadResult resultType() { return TOverloadResult.fixed(MNumeric.BIGINT); } // Helper functions // Takes a period and returns the number of months from year 0 protected static long fromPeriod(long period) { int periodSign = Long.signum(period); long rawMonth = period % 100; long rawYear = period / 100; long absValYear = Math.abs(rawYear); if (absValYear < 70) rawYear += periodSign * 2000; else if (absValYear < 100) rawYear += periodSign * 1900; return (rawYear * 12 + rawMonth - (1 * periodSign)); } // Create a YYYYMM format from a number of months protected static long toPeriod(long monthCount) { long year = monthCount / 12; long month = (monthCount % 12) + 1 * Long.signum(monthCount); return (year * 100) + month; } // End helper functions *************************************************** }