Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-43118: [JS] Add interval for unit MONTH_DAY_NANO #43117

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
6 changes: 2 additions & 4 deletions dev/archery/archery/integration/datagen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1887,11 +1887,9 @@ def _temp_path():

generate_duration_case(),

generate_interval_case()
.skip_tester('JS'), # TODO(ARROW-5239): Intervals + JS
generate_interval_case(),

generate_month_day_nano_interval_case()
.skip_tester('JS'),
generate_month_day_nano_interval_case(),

generate_map_case(),

Expand Down
4 changes: 2 additions & 2 deletions js/src/Arrow.dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export {
Struct, StructRow,
Union, DenseUnion, SparseUnion,
Dictionary,
Interval, IntervalDayTime, IntervalYearMonth,
Interval, IntervalDayTime, IntervalYearMonth, IntervalMonthDayNano,
Duration, DurationSecond, DurationMillisecond, DurationMicrosecond, DurationNanosecond,
FixedSizeList,
Map_, MapRow,
Expand Down Expand Up @@ -86,7 +86,7 @@ export {
FixedSizeBinaryBuilder,
FixedSizeListBuilder,
FloatBuilder, Float16Builder, Float32Builder, Float64Builder,
IntervalBuilder, IntervalDayTimeBuilder, IntervalYearMonthBuilder,
IntervalBuilder, IntervalDayTimeBuilder, IntervalYearMonthBuilder, IntervalMonthDayNanoBuilder,
DurationBuilder, DurationSecondBuilder, DurationMillisecondBuilder, DurationMicrosecondBuilder, DurationNanosecondBuilder,
IntBuilder, Int8Builder, Int16Builder, Int32Builder, Int64Builder, Uint8Builder, Uint16Builder, Uint32Builder, Uint64Builder,
ListBuilder,
Expand Down
4 changes: 2 additions & 2 deletions js/src/Arrow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export {
Struct,
Union, DenseUnion, SparseUnion,
Dictionary,
Interval, IntervalDayTime, IntervalYearMonth,
Interval, IntervalDayTime, IntervalYearMonth, IntervalMonthDayNano,
Duration, DurationSecond, DurationMillisecond, DurationMicrosecond, DurationNanosecond,
FixedSizeList,
Map_
Expand Down Expand Up @@ -75,7 +75,7 @@ export { FloatBuilder, Float16Builder, Float32Builder, Float64Builder } from './
export { IntBuilder, Int8Builder, Int16Builder, Int32Builder, Int64Builder, Uint8Builder, Uint16Builder, Uint32Builder, Uint64Builder } from './builder/int.js';
export { TimeBuilder, TimeSecondBuilder, TimeMillisecondBuilder, TimeMicrosecondBuilder, TimeNanosecondBuilder } from './builder/time.js';
export { TimestampBuilder, TimestampSecondBuilder, TimestampMillisecondBuilder, TimestampMicrosecondBuilder, TimestampNanosecondBuilder } from './builder/timestamp.js';
export { IntervalBuilder, IntervalDayTimeBuilder, IntervalYearMonthBuilder } from './builder/interval.js';
export { IntervalBuilder, IntervalDayTimeBuilder, IntervalYearMonthBuilder, IntervalMonthDayNanoBuilder } from './builder/interval.js';
export { DurationBuilder, DurationSecondBuilder, DurationMillisecondBuilder, DurationMicrosecondBuilder, DurationNanosecondBuilder } from './builder/duration.js';
export { Utf8Builder } from './builder/utf8.js';
export { LargeUtf8Builder } from './builder/largeutf8.js';
Expand Down
9 changes: 7 additions & 2 deletions js/src/builder/interval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
// under the License.

import { FixedWidthBuilder } from '../builder.js';
import { Interval, IntervalDayTime, IntervalYearMonth } from '../type.js';
import { setIntervalValue, setIntervalDayTime, setIntervalYearMonth } from '../visitor/set.js';
import { Interval, IntervalDayTime, IntervalYearMonth, IntervalMonthDayNano } from '../type.js';
import { setIntervalValue, setIntervalDayTime, setIntervalYearMonth, setIntervalMonthDaynano } from '../visitor/set.js';

/** @ignore */
export class IntervalBuilder<T extends Interval = Interval, TNull = any> extends FixedWidthBuilder<T, TNull> { }
Expand All @@ -33,3 +33,8 @@ export class IntervalDayTimeBuilder<TNull = any> extends IntervalBuilder<Interva
export class IntervalYearMonthBuilder<TNull = any> extends IntervalBuilder<IntervalYearMonth, TNull> { }

(IntervalYearMonthBuilder.prototype as any)._setValue = setIntervalYearMonth;

/** @ignore */
export class IntervalMonthDayNanoBuilder<TNull = any> extends IntervalBuilder<IntervalMonthDayNano, TNull> { }

(IntervalMonthDayNanoBuilder.prototype as any)._setValue = setIntervalMonthDaynano;
1 change: 1 addition & 0 deletions js/src/enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export enum Type {
DurationMillisecond = -28,
DurationMicrosecond = -29,
DurationNanosecond = -30,
IntervalMonthDayNano = -31,
}

export enum BufferType {
Expand Down
5 changes: 4 additions & 1 deletion js/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import type { FloatBuilder, Float16Builder, Float32Builder, Float64Builder } fro
import type { IntBuilder, Int8Builder, Int16Builder, Int32Builder, Int64Builder, Uint8Builder, Uint16Builder, Uint32Builder, Uint64Builder } from './builder/int.js';
import type { TimeBuilder, TimeSecondBuilder, TimeMillisecondBuilder, TimeMicrosecondBuilder, TimeNanosecondBuilder } from './builder/time.js';
import type { TimestampBuilder, TimestampSecondBuilder, TimestampMillisecondBuilder, TimestampMicrosecondBuilder, TimestampNanosecondBuilder } from './builder/timestamp.js';
import type { IntervalBuilder, IntervalDayTimeBuilder, IntervalYearMonthBuilder } from './builder/interval.js';
import type { IntervalBuilder, IntervalDayTimeBuilder, IntervalMonthDayNanoBuilder, IntervalYearMonthBuilder } from './builder/interval.js';
import type { DurationBuilder, DurationSecondBuilder, DurationMillisecondBuilder, DurationMicrosecondBuilder, DurationNanosecondBuilder } from './builder/duration.js';
import type { Utf8Builder } from './builder/utf8.js';
import type { LargeUtf8Builder } from './builder/largeutf8.js';
Expand Down Expand Up @@ -233,6 +233,7 @@ export type TypeToDataType<T extends Type> = {
[Type.Interval]: type.Interval;
[Type.IntervalDayTime]: type.IntervalDayTime;
[Type.IntervalYearMonth]: type.IntervalYearMonth;
[Type.IntervalMonthDayNano]: type.IntervalMonthDayNano;
[Type.Duration]: type.Duration;
[Type.DurationSecond]: type.DurationSecond;
[Type.DurationMillisecond]: type.DurationMillisecond;
Expand Down Expand Up @@ -288,6 +289,7 @@ type TypeToBuilder<T extends Type = any, TNull = any> = {
[Type.Interval]: IntervalBuilder<any, TNull>;
[Type.IntervalDayTime]: IntervalDayTimeBuilder<TNull>;
[Type.IntervalYearMonth]: IntervalYearMonthBuilder<TNull>;
[Type.IntervalMonthDayNano]: IntervalMonthDayNanoBuilder<TNull>;
[Type.Duration]: DurationBuilder<any, TNull>;
[Type.DurationSecond]: DurationBuilder<any, TNull>;
[Type.DurationMillisecond]: DurationMillisecondBuilder<TNull>;
Expand Down Expand Up @@ -343,6 +345,7 @@ type DataTypeToBuilder<T extends DataType = any, TNull = any> = {
[Type.Interval]: T extends type.Interval ? IntervalBuilder<T, TNull> : never;
[Type.IntervalDayTime]: T extends type.IntervalDayTime ? IntervalDayTimeBuilder<TNull> : never;
[Type.IntervalYearMonth]: T extends type.IntervalYearMonth ? IntervalYearMonthBuilder<TNull> : never;
[Type.IntervalMonthDayNano]: T extends type.IntervalMonthDayNano ? IntervalMonthDayNanoBuilder<TNull> : never;
[Type.Duration]: T extends type.Duration ? DurationBuilder<T, TNull> : never;
[Type.DurationSecond]: T extends type.DurationSecond ? DurationSecondBuilder<TNull> : never;
[Type.DurationMillisecond]: T extends type.DurationMillisecond ? DurationMillisecondBuilder<TNull> : never;
Expand Down
11 changes: 9 additions & 2 deletions js/src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ export class TimestampMicrosecond extends Timestamp_<Type.TimestampMicrosecond>
export class TimestampNanosecond extends Timestamp_<Type.TimestampNanosecond> { constructor(timezone?: string | null) { super(TimeUnit.NANOSECOND, timezone); } }

/** @ignore */
type Intervals = Type.Interval | Type.IntervalDayTime | Type.IntervalYearMonth;
type Intervals = Type.Interval | Type.IntervalDayTime | Type.IntervalYearMonth | Type.IntervalMonthDayNano;
/** @ignore */
interface Interval_<T extends Intervals = Intervals> extends DataType<T> {
TArray: Int32Array;
Expand All @@ -488,6 +488,8 @@ export { Interval_ as Interval };
export class IntervalDayTime extends Interval_<Type.IntervalDayTime> { constructor() { super(IntervalUnit.DAY_TIME); } }
/** @ignore */
export class IntervalYearMonth extends Interval_<Type.IntervalYearMonth> { constructor() { super(IntervalUnit.YEAR_MONTH); } }
/** @ignore */
export class IntervalMonthDayNano extends Interval_<Type.IntervalMonthDayNano> { constructor() { super(IntervalUnit.MONTH_DAY_NANO); } }

/** @ignore */
type Durations = Type.Duration | Type.DurationSecond | Type.DurationMillisecond | Type.DurationMicrosecond | Type.DurationNanosecond;
Expand Down Expand Up @@ -749,7 +751,12 @@ export function strideForType(type: DataType) {
const t: any = type;
switch (type.typeId) {
case Type.Decimal: return (type as Decimal).bitWidth / 32;
case Type.Interval: return 1 + (t as Interval_).unit;
case Type.Interval: {
if ((t as Interval_).unit === IntervalUnit.MONTH_DAY_NANO) {
return 4;
}
return 1 + (t as Interval_).unit;
}
// case Type.Int: return 1 + +((t as Int_).bitWidth > 32);
// case Type.Time: return 1 + +((t as Time_).bitWidth > 32);
case Type.FixedSizeList: return (t as FixedSizeList).listSize;
Expand Down
58 changes: 58 additions & 0 deletions js/src/util/interval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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.

type EncodedIntervalMonthDayNano = {
months: number;
days: number;
nanoseconds: bigint | number;
};

type EncodedIntervalDayTime = {
days: number;
milliseconds: number;
};

export function encodeIntervalDayTime(intervalArray: EncodedIntervalDayTime[]) {
const length = intervalArray.length;
const data = new Int32Array(length * 2);
const intervalLength = intervalArray.length;
for (let i = 0, ptr = 0; i < intervalLength; i++) {
const interval = intervalArray[i];
data[ptr++] = interval.days ?? 0;
data[ptr++] = interval.milliseconds ?? 0;
}
return data;
}

export function encodeIntervalMonthDayNano(
intervalArray: EncodedIntervalMonthDayNano[]
) {
const length = intervalArray.length;
const data = new Int32Array(length * 4);
const intervalLength = intervalArray.length;
for (let i = 0, ptr = 0; i < intervalLength; i++) {
const interval = intervalArray[i];
const nanoseconds = new Int32Array(
new BigInt64Array([BigInt(interval.nanoseconds ?? 0)]).buffer
);
data[ptr++] = interval.months ?? 0;
data[ptr++] = interval.days ?? 0;
data[ptr++] = nanoseconds[0];
data[ptr++] = nanoseconds[1];
}
return data;
}
4 changes: 4 additions & 0 deletions js/src/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ function getVisitFnByTypeId(visitor: Visitor, dtype: Type, throwIfNotFound = tru
case Type.Interval: fn = visitor.visitInterval; break;
case Type.IntervalDayTime: fn = visitor.visitIntervalDayTime || visitor.visitInterval; break;
case Type.IntervalYearMonth: fn = visitor.visitIntervalYearMonth || visitor.visitInterval; break;
case Type.IntervalMonthDayNano: fn = visitor.visitIntervalMonthDayNano || visitor.visitInterval; break;
case Type.Duration: fn = visitor.visitDuration; break;
case Type.DurationSecond: fn = visitor.visitDurationSecond || visitor.visitDuration; break;
case Type.DurationMillisecond: fn = visitor.visitDurationMillisecond || visitor.visitDuration; break;
Expand Down Expand Up @@ -189,6 +190,7 @@ function inferDType<T extends DataType>(type: T): Type {
switch ((type as any as Interval).unit) {
case IntervalUnit.DAY_TIME: return Type.IntervalDayTime;
case IntervalUnit.YEAR_MONTH: return Type.IntervalYearMonth;
case IntervalUnit.MONTH_DAY_NANO: return Type.IntervalMonthDayNano;
}
// @ts-ignore
return Type.Interval;
Expand Down Expand Up @@ -262,6 +264,7 @@ export interface Visitor {
visitInterval(node: any, ...args: any[]): any;
visitIntervalDayTime?(node: any, ...args: any[]): any;
visitIntervalYearMonth?(node: any, ...args: any[]): any;
visitIntervalMonthDayNano?(node: any, ...args: any[]): any;
visitDuration(node: any, ...args: any[]): any;
visitDurationSecond(node: any, ...args: any[]): any;
visitDurationMillisecond(node: any, ...args: any[]): any;
Expand Down Expand Up @@ -298,6 +301,7 @@ export interface Visitor {
(Visitor.prototype as any).visitSparseUnion = null;
(Visitor.prototype as any).visitIntervalDayTime = null;
(Visitor.prototype as any).visitIntervalYearMonth = null;
(Visitor.prototype as any).visitIntervalMonthDayNano = null;
(Visitor.prototype as any).visitDuration = null;
(Visitor.prototype as any).visitDurationSecond = null;
(Visitor.prototype as any).visitDurationMillisecond = null;
Expand Down
3 changes: 2 additions & 1 deletion js/src/visitor/builderctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { DictionaryBuilder } from '../builder/dictionary.js';
import { FixedSizeBinaryBuilder } from '../builder/fixedsizebinary.js';
import { FixedSizeListBuilder } from '../builder/fixedsizelist.js';
import { FloatBuilder, Float16Builder, Float32Builder, Float64Builder } from '../builder/float.js';
import { IntervalBuilder, IntervalDayTimeBuilder, IntervalYearMonthBuilder } from '../builder/interval.js';
import { IntervalBuilder, IntervalDayTimeBuilder, IntervalMonthDayNanoBuilder, IntervalYearMonthBuilder } from '../builder/interval.js';
import { DurationBuilder, DurationSecondBuilder, DurationMillisecondBuilder, DurationMicrosecondBuilder, DurationNanosecondBuilder } from '../builder/duration.js';
import { IntBuilder, Int8Builder, Int16Builder, Int32Builder, Int64Builder, Uint8Builder, Uint16Builder, Uint32Builder, Uint64Builder } from '../builder/int.js';
import { ListBuilder } from '../builder/list.js';
Expand Down Expand Up @@ -96,6 +96,7 @@ export class GetBuilderCtor extends Visitor {
public visitInterval() { return IntervalBuilder; }
public visitIntervalDayTime() { return IntervalDayTimeBuilder; }
public visitIntervalYearMonth() { return IntervalYearMonthBuilder; }
public visitIntervalMonthDayNano() { return IntervalMonthDayNanoBuilder; }
public visitDuration() { return DurationBuilder; }
public visitDurationSecond() { return DurationSecondBuilder; }
public visitDurationMillisecond() { return DurationMillisecondBuilder; }
Expand Down
14 changes: 11 additions & 3 deletions js/src/visitor/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
Timestamp, TimestampSecond, TimestampMillisecond, TimestampMicrosecond, TimestampNanosecond,
Duration, DurationSecond, DurationMillisecond, DurationMicrosecond, DurationNanosecond,
Union, DenseUnion, SparseUnion,
IntervalMonthDayNano,
} from '../type.js';

/** @ignore */
Expand Down Expand Up @@ -88,6 +89,7 @@ export interface GetVisitor extends Visitor {
visitInterval<T extends Interval>(data: Data<T>, index: number): T['TValue'] | null;
visitIntervalDayTime<T extends IntervalDayTime>(data: Data<T>, index: number): T['TValue'] | null;
visitIntervalYearMonth<T extends IntervalYearMonth>(data: Data<T>, index: number): T['TValue'] | null;
visitIntervalMonthDayNano<T extends IntervalMonthDayNano>(data: Data<T>, index: number): T['TValue'] | null;
visitDuration<T extends Duration>(data: Data<T>, index: number): T['TValue'] | null;
visitDurationSecond<T extends DurationSecond>(data: Data<T>, index: number): T['TValue'] | null;
visitDurationMillisecond<T extends DurationMillisecond>(data: Data<T>, index: number): T['TValue'] | null;
Expand Down Expand Up @@ -265,9 +267,11 @@ const getDictionary = <T extends Dictionary>(data: Data<T>, index: number): T['T
/* istanbul ignore next */
/** @ignore */
const getInterval = <T extends Interval>(data: Data<T>, index: number): T['TValue'] =>
(data.type.unit === IntervalUnit.DAY_TIME)
? getIntervalDayTime(data as Data<IntervalDayTime>, index)
: getIntervalYearMonth(data as Data<IntervalYearMonth>, index);
(data.type.unit === IntervalUnit.MONTH_DAY_NANO)
? getIntervalMonthDayNano(data as Data<IntervalMonthDayNano>, index)
: (data.type.unit === IntervalUnit.DAY_TIME)
? getIntervalDayTime(data as Data<IntervalDayTime>, index)
: getIntervalYearMonth(data as Data<IntervalYearMonth>, index);
Comment on lines +270 to +274
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we split these conditions out into separate methods and assign them to the GetVisitor prototype separately?


/** @ignore */
const getIntervalDayTime = <T extends IntervalDayTime>({ values }: Data<T>, index: number): T['TValue'] => values.subarray(2 * index, 2 * (index + 1));
Expand All @@ -281,6 +285,9 @@ const getIntervalYearMonth = <T extends IntervalYearMonth>({ values }: Data<T>,
return int32s;
};

/** @ignore */
const getIntervalMonthDayNano = <T extends IntervalMonthDayNano>({ values }: Data<T>, index: number): T['TValue'] => values.subarray(4 * index, 4 * (index + 1));

/** @ignore */
const getDurationSecond = <T extends DurationSecond>({ values }: Data<T>, index: number): T['TValue'] => values[index];
/** @ignore */
Expand Down Expand Up @@ -351,6 +358,7 @@ GetVisitor.prototype.visitDictionary = wrapGet(getDictionary);
GetVisitor.prototype.visitInterval = wrapGet(getInterval);
GetVisitor.prototype.visitIntervalDayTime = wrapGet(getIntervalDayTime);
GetVisitor.prototype.visitIntervalYearMonth = wrapGet(getIntervalYearMonth);
GetVisitor.prototype.visitIntervalMonthDayNano = wrapGet(getIntervalMonthDayNano);
GetVisitor.prototype.visitDuration = wrapGet(getDuration);
GetVisitor.prototype.visitDurationSecond = wrapGet(getDurationSecond);
GetVisitor.prototype.visitDurationMillisecond = wrapGet(getDurationMillisecond);
Expand Down
3 changes: 3 additions & 0 deletions js/src/visitor/indexof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
Timestamp, TimestampSecond, TimestampMillisecond, TimestampMicrosecond, TimestampNanosecond,
Duration, DurationSecond, DurationMillisecond, DurationMicrosecond, DurationNanosecond,
Union, DenseUnion, SparseUnion,
IntervalMonthDayNano,
} from '../type.js';

/** @ignore */
Expand Down Expand Up @@ -84,6 +85,7 @@ export interface IndexOfVisitor extends Visitor {
visitInterval<T extends Interval>(data: Data<T>, value: T['TValue'] | null, index?: number): number;
visitIntervalDayTime<T extends IntervalDayTime>(data: Data<T>, value: T['TValue'] | null, index?: number): number;
visitIntervalYearMonth<T extends IntervalYearMonth>(data: Data<T>, value: T['TValue'] | null, index?: number): number;
visitIntervalMonthDayNano<T extends IntervalMonthDayNano>(data: Data<T>, value: T['TValue'] | null, index?: number): number;
visitDuration<T extends Duration>(data: Data<T>, value: T['TValue'] | null, index?: number): number;
visitDurationSecond<T extends DurationSecond>(data: Data<T>, value: T['TValue'] | null, index?: number): number;
visitDurationMillisecond<T extends DurationMillisecond>(data: Data<T>, value: T['TValue'] | null, index?: number): number;
Expand Down Expand Up @@ -201,6 +203,7 @@ IndexOfVisitor.prototype.visitDictionary = indexOfValue;
IndexOfVisitor.prototype.visitInterval = indexOfValue;
IndexOfVisitor.prototype.visitIntervalDayTime = indexOfValue;
IndexOfVisitor.prototype.visitIntervalYearMonth = indexOfValue;
IndexOfVisitor.prototype.visitIntervalMonthDayNano = indexOfValue;
IndexOfVisitor.prototype.visitDuration = indexOfValue;
IndexOfVisitor.prototype.visitDurationSecond = indexOfValue;
IndexOfVisitor.prototype.visitDurationMillisecond = indexOfValue;
Expand Down
Loading
Loading