add duckdb-ui-client & other ts pkgs (#10)

* add duckdb-ui-client & other ts pkgs

* workflow fixes

* fix working dir

* no sparse checkout; specify package.json path

* path to pnpm-lock.yaml

* add check & build test

* workflow step descriptions

* use comments & names

* one more naming tweak
This commit is contained in:
Jeff Raymakers
2025-06-13 09:06:55 -07:00
parent d6cc9eeea4
commit 0edb52054a
133 changed files with 11112 additions and 4 deletions

View File

@@ -0,0 +1,37 @@
{
"name": "@duckdb/data-types",
"version": "0.0.1",
"description": "Utilities for representing DuckDB types",
"type": "module",
"main": "./out/index.js",
"module": "./out/index.js",
"types": "./out/index.d.ts",
"scripts": {
"preinstall": "pnpm build:src",
"build": "tsc -b src test",
"build:src": "tsc -b src",
"build:test": "tsc -b test",
"build:watch": "tsc -b src test --watch",
"check": "pnpm format:check && pnpm lint",
"clean": "rimraf out",
"format:check": "prettier . --ignore-path $(find-up .prettierignore) --check",
"format:write": "prettier . --ignore-path $(find-up .prettierignore) --write",
"lint": "pnpm eslint src test",
"test": "vitest run",
"test:watch": "vitest"
},
"dependencies": {
"@duckdb/data-values": "workspace:*"
},
"devDependencies": {
"@eslint/js": "^9.24.0",
"eslint": "^9.24.0",
"find-up-cli": "^6.0.0",
"prettier": "^3.5.3",
"rimraf": "^6.0.1",
"typescript": "^5.8.3",
"typescript-eslint": "^8.30.1",
"vite": "^6.2.6",
"vitest": "^3.1.1"
}
}

View File

@@ -0,0 +1,989 @@
import { Json } from '@duckdb/data-values';
import { DuckDBTypeId } from './DuckDBTypeId.js';
import { quotedIdentifier, quotedString } from './sql.js';
export interface DuckDBTypeToStringOptions {
short?: boolean;
}
export abstract class BaseDuckDBType<T extends DuckDBTypeId> {
public readonly typeId: T;
public readonly alias?: string;
protected constructor(typeId: T, alias?: string) {
this.typeId = typeId;
this.alias = alias;
}
public toString(_options?: DuckDBTypeToStringOptions): string {
return this.alias ?? DuckDBTypeId[this.typeId];
}
public toJson(): Json {
return {
typeId: this.typeId,
...(this.alias ? { alias: this.alias } : {}),
};
}
}
export class DuckDBBooleanType extends BaseDuckDBType<DuckDBTypeId.BOOLEAN> {
public constructor(alias?: string) {
super(DuckDBTypeId.BOOLEAN, alias);
}
public static readonly instance = new DuckDBBooleanType();
public static create(alias?: string): DuckDBBooleanType {
return alias ? new DuckDBBooleanType(alias) : DuckDBBooleanType.instance;
}
}
export const BOOLEAN = DuckDBBooleanType.instance;
export class DuckDBTinyIntType extends BaseDuckDBType<DuckDBTypeId.TINYINT> {
public constructor(alias?: string) {
super(DuckDBTypeId.TINYINT, alias);
}
public static readonly instance = new DuckDBTinyIntType();
public static create(alias?: string): DuckDBTinyIntType {
return alias ? new DuckDBTinyIntType(alias) : DuckDBTinyIntType.instance;
}
public static readonly Max = 2 ** 7 - 1;
public static readonly Min = -(2 ** 7);
public get max() {
return DuckDBTinyIntType.Max;
}
public get min() {
return DuckDBTinyIntType.Min;
}
}
export const TINYINT = DuckDBTinyIntType.instance;
export class DuckDBSmallIntType extends BaseDuckDBType<DuckDBTypeId.SMALLINT> {
public constructor(alias?: string) {
super(DuckDBTypeId.SMALLINT, alias);
}
public static readonly instance = new DuckDBSmallIntType();
public static create(alias?: string): DuckDBSmallIntType {
return alias ? new DuckDBSmallIntType(alias) : DuckDBSmallIntType.instance;
}
public static readonly Max = 2 ** 15 - 1;
public static readonly Min = -(2 ** 15);
public get max() {
return DuckDBSmallIntType.Max;
}
public get min() {
return DuckDBSmallIntType.Min;
}
}
export const SMALLINT = DuckDBSmallIntType.instance;
export class DuckDBIntegerType extends BaseDuckDBType<DuckDBTypeId.INTEGER> {
public constructor(alias?: string) {
super(DuckDBTypeId.INTEGER, alias);
}
public static readonly instance = new DuckDBIntegerType();
public static create(alias?: string): DuckDBIntegerType {
return alias ? new DuckDBIntegerType(alias) : DuckDBIntegerType.instance;
}
public static readonly Max = 2 ** 31 - 1;
public static readonly Min = -(2 ** 31);
public get max() {
return DuckDBIntegerType.Max;
}
public get min() {
return DuckDBIntegerType.Min;
}
}
export const INTEGER = DuckDBIntegerType.instance;
export class DuckDBBigIntType extends BaseDuckDBType<DuckDBTypeId.BIGINT> {
public constructor(alias?: string) {
super(DuckDBTypeId.BIGINT, alias);
}
public static readonly instance = new DuckDBBigIntType();
public static create(alias?: string): DuckDBBigIntType {
return alias ? new DuckDBBigIntType(alias) : DuckDBBigIntType.instance;
}
public static readonly Max: bigint = 2n ** 63n - 1n;
public static readonly Min: bigint = -(2n ** 63n);
public get max() {
return DuckDBBigIntType.Max;
}
public get min() {
return DuckDBBigIntType.Min;
}
}
export const BIGINT = DuckDBBigIntType.instance;
export class DuckDBUTinyIntType extends BaseDuckDBType<DuckDBTypeId.UTINYINT> {
public constructor(alias?: string) {
super(DuckDBTypeId.UTINYINT, alias);
}
public static readonly instance = new DuckDBUTinyIntType();
public static create(alias?: string): DuckDBUTinyIntType {
return alias ? new DuckDBUTinyIntType(alias) : DuckDBUTinyIntType.instance;
}
public static readonly Max = 2 ** 8 - 1;
public static readonly Min = 0;
public get max() {
return DuckDBUTinyIntType.Max;
}
public get min() {
return DuckDBUTinyIntType.Min;
}
}
export const UTINYINT = DuckDBUTinyIntType.instance;
export class DuckDBUSmallIntType extends BaseDuckDBType<DuckDBTypeId.USMALLINT> {
public constructor(alias?: string) {
super(DuckDBTypeId.USMALLINT, alias);
}
public static readonly instance = new DuckDBUSmallIntType();
public static create(alias?: string): DuckDBUSmallIntType {
return alias
? new DuckDBUSmallIntType(alias)
: DuckDBUSmallIntType.instance;
}
public static readonly Max = 2 ** 16 - 1;
public static readonly Min = 0;
public get max() {
return DuckDBUSmallIntType.Max;
}
public get min() {
return DuckDBUSmallIntType.Min;
}
}
export const USMALLINT = DuckDBUSmallIntType.instance;
export class DuckDBUIntegerType extends BaseDuckDBType<DuckDBTypeId.UINTEGER> {
public constructor(alias?: string) {
super(DuckDBTypeId.UINTEGER, alias);
}
public static readonly instance = new DuckDBUIntegerType();
public static create(alias?: string): DuckDBUIntegerType {
return alias ? new DuckDBUIntegerType(alias) : DuckDBUIntegerType.instance;
}
public static readonly Max = 2 ** 32 - 1;
public static readonly Min = 0;
public get max() {
return DuckDBUIntegerType.Max;
}
public get min() {
return DuckDBUIntegerType.Min;
}
}
export const UINTEGER = DuckDBUIntegerType.instance;
export class DuckDBUBigIntType extends BaseDuckDBType<DuckDBTypeId.UBIGINT> {
public constructor(alias?: string) {
super(DuckDBTypeId.UBIGINT, alias);
}
public static readonly instance = new DuckDBUBigIntType();
public static create(alias?: string): DuckDBUBigIntType {
return alias ? new DuckDBUBigIntType(alias) : DuckDBUBigIntType.instance;
}
public static readonly Max: bigint = 2n ** 64n - 1n;
public static readonly Min: bigint = 0n;
public get max() {
return DuckDBUBigIntType.Max;
}
public get min() {
return DuckDBUBigIntType.Min;
}
}
export const UBIGINT = DuckDBUBigIntType.instance;
export class DuckDBFloatType extends BaseDuckDBType<DuckDBTypeId.FLOAT> {
public constructor(alias?: string) {
super(DuckDBTypeId.FLOAT, alias);
}
public static readonly instance = new DuckDBFloatType();
public static create(alias?: string): DuckDBFloatType {
return alias ? new DuckDBFloatType(alias) : DuckDBFloatType.instance;
}
public static readonly Max = Math.fround(3.4028235e38);
public static readonly Min = Math.fround(-3.4028235e38);
public get max() {
return DuckDBFloatType.Max;
}
public get min() {
return DuckDBFloatType.Min;
}
}
export const FLOAT = DuckDBFloatType.instance;
export class DuckDBDoubleType extends BaseDuckDBType<DuckDBTypeId.DOUBLE> {
public constructor(alias?: string) {
super(DuckDBTypeId.DOUBLE, alias);
}
public static readonly instance = new DuckDBDoubleType();
public static create(alias?: string): DuckDBDoubleType {
return alias ? new DuckDBDoubleType(alias) : DuckDBDoubleType.instance;
}
public static readonly Max = Number.MAX_VALUE;
public static readonly Min = -Number.MAX_VALUE;
public get max() {
return DuckDBDoubleType.Max;
}
public get min() {
return DuckDBDoubleType.Min;
}
}
export const DOUBLE = DuckDBDoubleType.instance;
export class DuckDBTimestampType extends BaseDuckDBType<DuckDBTypeId.TIMESTAMP> {
public constructor(alias?: string) {
super(DuckDBTypeId.TIMESTAMP, alias);
}
public static readonly instance = new DuckDBTimestampType();
public static create(alias?: string): DuckDBTimestampType {
return alias
? new DuckDBTimestampType(alias)
: DuckDBTimestampType.instance;
}
// TODO: common DuckDBValues on type objects
// public get epoch() {
// return DuckDBTimestampValue.Epoch;
// }
// public get max() {
// return DuckDBTimestampValue.Max;
// }
// public get min() {
// return DuckDBTimestampValue.Min;
// }
// public get posInf() {
// return DuckDBTimestampValue.PosInf;
// }
// public get negInf() {
// return DuckDBTimestampValue.NegInf;
// }
}
export const TIMESTAMP = DuckDBTimestampType.instance;
export type DuckDBTimestampMicrosecondsType = DuckDBTimestampType;
export const DuckDBTimestampMicrosecondsType = DuckDBTimestampType;
export class DuckDBDateType extends BaseDuckDBType<DuckDBTypeId.DATE> {
public constructor(alias?: string) {
super(DuckDBTypeId.DATE, alias);
}
public static readonly instance = new DuckDBDateType();
public static create(alias?: string): DuckDBDateType {
return alias ? new DuckDBDateType(alias) : DuckDBDateType.instance;
}
// TODO: common DuckDBValues on type objects
// public get epoch() {
// return DuckDBDateValue.Epoch;
// }
// public get max() {
// return DuckDBDateValue.Max;
// }
// public get min() {
// return DuckDBDateValue.Min;
// }
// public get posInf() {
// return DuckDBDateValue.PosInf;
// }
// public get negInf() {
// return DuckDBDateValue.NegInf;
// }
}
export const DATE = DuckDBDateType.instance;
export class DuckDBTimeType extends BaseDuckDBType<DuckDBTypeId.TIME> {
public constructor(alias?: string) {
super(DuckDBTypeId.TIME, alias);
}
public static readonly instance = new DuckDBTimeType();
public static create(alias?: string): DuckDBTimeType {
return alias ? new DuckDBTimeType(alias) : DuckDBTimeType.instance;
}
// TODO: common DuckDBValues on type objects
// public get max() {
// return DuckDBTimeValue.Max;
// }
// public get min() {
// return DuckDBTimeValue.Min;
// }
}
export const TIME = DuckDBTimeType.instance;
export class DuckDBIntervalType extends BaseDuckDBType<DuckDBTypeId.INTERVAL> {
public constructor(alias?: string) {
super(DuckDBTypeId.INTERVAL, alias);
}
public static readonly instance = new DuckDBIntervalType();
public static create(alias?: string): DuckDBIntervalType {
return alias ? new DuckDBIntervalType(alias) : DuckDBIntervalType.instance;
}
}
export const INTERVAL = DuckDBIntervalType.instance;
export class DuckDBHugeIntType extends BaseDuckDBType<DuckDBTypeId.HUGEINT> {
public constructor(alias?: string) {
super(DuckDBTypeId.HUGEINT, alias);
}
public static readonly instance = new DuckDBHugeIntType();
public static create(alias?: string): DuckDBHugeIntType {
return alias ? new DuckDBHugeIntType(alias) : DuckDBHugeIntType.instance;
}
public static readonly Max: bigint = 2n ** 127n - 1n;
public static readonly Min: bigint = -(2n ** 127n);
public get max() {
return DuckDBHugeIntType.Max;
}
public get min() {
return DuckDBHugeIntType.Min;
}
}
export const HUGEINT = DuckDBHugeIntType.instance;
export class DuckDBUHugeIntType extends BaseDuckDBType<DuckDBTypeId.UHUGEINT> {
public constructor(alias?: string) {
super(DuckDBTypeId.UHUGEINT, alias);
}
public static readonly instance = new DuckDBUHugeIntType();
public static create(alias?: string): DuckDBUHugeIntType {
return alias ? new DuckDBUHugeIntType(alias) : DuckDBUHugeIntType.instance;
}
public static readonly Max: bigint = 2n ** 128n - 1n;
public static readonly Min: bigint = 0n;
public get max() {
return DuckDBUHugeIntType.Max;
}
public get min() {
return DuckDBUHugeIntType.Min;
}
}
export const UHUGEINT = DuckDBUHugeIntType.instance;
export class DuckDBVarCharType extends BaseDuckDBType<DuckDBTypeId.VARCHAR> {
public constructor(alias?: string) {
super(DuckDBTypeId.VARCHAR, alias);
}
public static readonly instance = new DuckDBVarCharType();
public static create(alias?: string): DuckDBVarCharType {
return alias ? new DuckDBVarCharType(alias) : DuckDBVarCharType.instance;
}
}
export const VARCHAR = DuckDBVarCharType.instance;
export class DuckDBBlobType extends BaseDuckDBType<DuckDBTypeId.BLOB> {
public constructor(alias?: string) {
super(DuckDBTypeId.BLOB, alias);
}
public static readonly instance = new DuckDBBlobType();
public static create(alias?: string): DuckDBBlobType {
return alias ? new DuckDBBlobType(alias) : DuckDBBlobType.instance;
}
}
export const BLOB = DuckDBBlobType.instance;
export class DuckDBDecimalType extends BaseDuckDBType<DuckDBTypeId.DECIMAL> {
public readonly width: number;
public readonly scale: number;
public constructor(width: number, scale: number, alias?: string) {
super(DuckDBTypeId.DECIMAL, alias);
this.width = width;
this.scale = scale;
}
public toString(_options?: DuckDBTypeToStringOptions): string {
return this.alias ?? `DECIMAL(${this.width},${this.scale})`;
}
public override toJson(): Json {
return {
typeId: this.typeId,
width: this.width,
scale: this.scale,
...(this.alias ? { alias: this.alias } : {}),
};
}
public static readonly default = new DuckDBDecimalType(18, 3);
}
export function DECIMAL(
width?: number,
scale?: number,
alias?: string,
): DuckDBDecimalType {
if (width === undefined) {
return DuckDBDecimalType.default;
}
if (scale === undefined) {
return new DuckDBDecimalType(width, 0);
}
return new DuckDBDecimalType(width, scale, alias);
}
export class DuckDBTimestampSecondsType extends BaseDuckDBType<DuckDBTypeId.TIMESTAMP_S> {
public constructor(alias?: string) {
super(DuckDBTypeId.TIMESTAMP_S, alias);
}
public static readonly instance = new DuckDBTimestampSecondsType();
public static create(alias?: string): DuckDBTimestampSecondsType {
return alias
? new DuckDBTimestampSecondsType(alias)
: DuckDBTimestampSecondsType.instance;
}
// TODO: common DuckDBValues on type objects
// public get epoch() {
// return DuckDBTimestampSecondsValue.Epoch;
// }
// public get max() {
// return DuckDBTimestampSecondsValue.Max;
// }
// public get min() {
// return DuckDBTimestampSecondsValue.Min;
// }
// public get posInf() {
// return DuckDBTimestampSecondsValue.PosInf;
// }
// public get negInf() {
// return DuckDBTimestampSecondsValue.NegInf;
// }
}
export const TIMESTAMP_S = DuckDBTimestampSecondsType.instance;
export class DuckDBTimestampMillisecondsType extends BaseDuckDBType<DuckDBTypeId.TIMESTAMP_MS> {
public constructor(alias?: string) {
super(DuckDBTypeId.TIMESTAMP_MS, alias);
}
public static readonly instance = new DuckDBTimestampMillisecondsType();
public static create(alias?: string): DuckDBTimestampMillisecondsType {
return alias
? new DuckDBTimestampMillisecondsType(alias)
: DuckDBTimestampMillisecondsType.instance;
}
// TODO: common DuckDBValues on type objects
// public get epoch() {
// return DuckDBTimestampMillisecondsValue.Epoch;
// }
// public get max() {
// return DuckDBTimestampMillisecondsValue.Max;
// }
// public get min() {
// return DuckDBTimestampMillisecondsValue.Min;
// }
// public get posInf() {
// return DuckDBTimestampMillisecondsValue.PosInf;
// }
// public get negInf() {
// return DuckDBTimestampMillisecondsValue.NegInf;
// }
}
export const TIMESTAMP_MS = DuckDBTimestampMillisecondsType.instance;
export class DuckDBTimestampNanosecondsType extends BaseDuckDBType<DuckDBTypeId.TIMESTAMP_NS> {
public constructor(alias?: string) {
super(DuckDBTypeId.TIMESTAMP_NS, alias);
}
public static readonly instance = new DuckDBTimestampNanosecondsType();
public static create(alias?: string): DuckDBTimestampNanosecondsType {
return alias
? new DuckDBTimestampNanosecondsType(alias)
: DuckDBTimestampNanosecondsType.instance;
}
// TODO: common DuckDBValues on type objects
// public get epoch() {
// return DuckDBTimestampNanosecondsValue.Epoch;
// }
// public get max() {
// return DuckDBTimestampNanosecondsValue.Max;
// }
// public get min() {
// return DuckDBTimestampNanosecondsValue.Min;
// }
// public get posInf() {
// return DuckDBTimestampNanosecondsValue.PosInf;
// }
// public get negInf() {
// return DuckDBTimestampNanosecondsValue.NegInf;
// }
}
export const TIMESTAMP_NS = DuckDBTimestampNanosecondsType.instance;
export class DuckDBEnumType extends BaseDuckDBType<DuckDBTypeId.ENUM> {
public readonly values: readonly string[];
public readonly valueIndexes: Readonly<Record<string, number>>;
public readonly internalTypeId: DuckDBTypeId;
public constructor(
values: readonly string[],
internalTypeId: DuckDBTypeId,
alias?: string,
) {
super(DuckDBTypeId.ENUM, alias);
this.values = values;
const valueIndexes: Record<string, number> = {};
for (let i = 0; i < values.length; i++) {
valueIndexes[values[i]] = i;
}
this.valueIndexes = valueIndexes;
this.internalTypeId = internalTypeId;
}
public indexForValue(value: string): number {
return this.valueIndexes[value];
}
public toString(options?: DuckDBTypeToStringOptions): string {
if (this.alias) {
return this.alias;
}
if (options?.short) {
return `ENUM(…)`;
}
return `ENUM(${this.values.map(quotedString).join(', ')})`;
}
public override toJson(): Json {
return {
typeId: this.typeId,
values: [...this.values],
internalTypeId: this.internalTypeId,
...(this.alias ? { alias: this.alias } : {}),
};
}
}
export function ENUM8(
values: readonly string[],
alias?: string,
): DuckDBEnumType {
return new DuckDBEnumType(values, DuckDBTypeId.UTINYINT, alias);
}
export function ENUM16(
values: readonly string[],
alias?: string,
): DuckDBEnumType {
return new DuckDBEnumType(values, DuckDBTypeId.USMALLINT, alias);
}
export function ENUM32(
values: readonly string[],
alias?: string,
): DuckDBEnumType {
return new DuckDBEnumType(values, DuckDBTypeId.UINTEGER, alias);
}
export function ENUM(
values: readonly string[],
alias?: string,
): DuckDBEnumType {
if (values.length < 256) {
return ENUM8(values, alias);
} else if (values.length < 65536) {
return ENUM16(values, alias);
} else if (values.length < 4294967296) {
return ENUM32(values, alias);
} else {
throw new Error(
`ENUM types cannot have more than 4294967295 values; received ${values.length}`,
);
}
}
export class DuckDBListType extends BaseDuckDBType<DuckDBTypeId.LIST> {
public readonly valueType: DuckDBType;
public constructor(valueType: DuckDBType, alias?: string) {
super(DuckDBTypeId.LIST, alias);
this.valueType = valueType;
}
public toString(options?: DuckDBTypeToStringOptions): string {
return this.alias ?? `${this.valueType.toString(options)}[]`;
}
public override toJson(): Json {
return {
typeId: this.typeId,
valueType: this.valueType.toJson(),
...(this.alias ? { alias: this.alias } : {}),
};
}
}
export function LIST(valueType: DuckDBType, alias?: string): DuckDBListType {
return new DuckDBListType(valueType, alias);
}
export class DuckDBStructType extends BaseDuckDBType<DuckDBTypeId.STRUCT> {
public readonly entryNames: readonly string[];
public readonly entryTypes: readonly DuckDBType[];
public readonly entryIndexes: Readonly<Record<string, number>>;
public constructor(
entryNames: readonly string[],
entryTypes: readonly DuckDBType[],
alias?: string,
) {
super(DuckDBTypeId.STRUCT, alias);
if (entryNames.length !== entryTypes.length) {
throw new Error(`Could not create DuckDBStructType: \
entryNames length (${entryNames.length}) does not match entryTypes length (${entryTypes.length})`);
}
this.entryNames = entryNames;
this.entryTypes = entryTypes;
const entryIndexes: Record<string, number> = {};
for (let i = 0; i < entryNames.length; i++) {
entryIndexes[entryNames[i]] = i;
}
this.entryIndexes = entryIndexes;
}
public get entryCount() {
return this.entryNames.length;
}
public indexForEntry(entryName: string): number {
return this.entryIndexes[entryName];
}
public typeForEntry(entryName: string): DuckDBType {
return this.entryTypes[this.entryIndexes[entryName]];
}
public toString(options?: DuckDBTypeToStringOptions): string {
if (this.alias) {
return this.alias;
}
if (options?.short) {
return `STRUCT(…)`;
}
const parts: string[] = [];
for (let i = 0; i < this.entryNames.length; i++) {
parts.push(
`${quotedIdentifier(this.entryNames[i])} ${this.entryTypes[i]}`,
);
}
return `STRUCT(${parts.join(', ')})`;
}
public override toJson(): Json {
return {
typeId: this.typeId,
entryNames: [...this.entryNames],
entryTypes: this.entryTypes.map((t) => t.toJson()),
...(this.alias ? { alias: this.alias } : {}),
};
}
}
export function STRUCT(
entries: Record<string, DuckDBType>,
alias?: string,
): DuckDBStructType {
const entryNames = Object.keys(entries);
const entryTypes = Object.values(entries);
return new DuckDBStructType(entryNames, entryTypes, alias);
}
export class DuckDBMapType extends BaseDuckDBType<DuckDBTypeId.MAP> {
public readonly keyType: DuckDBType;
public readonly valueType: DuckDBType;
public constructor(
keyType: DuckDBType,
valueType: DuckDBType,
alias?: string,
) {
super(DuckDBTypeId.MAP, alias);
this.keyType = keyType;
this.valueType = valueType;
}
public toString(options?: DuckDBTypeToStringOptions): string {
if (this.alias) {
return this.alias;
}
if (options?.short) {
return `MAP(…)`;
}
return `MAP(${this.keyType}, ${this.valueType})`;
}
public override toJson(): Json {
return {
typeId: this.typeId,
keyType: this.keyType.toJson(),
valueType: this.valueType.toJson(),
...(this.alias ? { alias: this.alias } : {}),
};
}
}
export function MAP(
keyType: DuckDBType,
valueType: DuckDBType,
alias?: string,
): DuckDBMapType {
return new DuckDBMapType(keyType, valueType, alias);
}
export class DuckDBArrayType extends BaseDuckDBType<DuckDBTypeId.ARRAY> {
public readonly valueType: DuckDBType;
public readonly length: number;
public constructor(valueType: DuckDBType, length: number, alias?: string) {
super(DuckDBTypeId.ARRAY, alias);
this.valueType = valueType;
this.length = length;
}
public toString(options?: DuckDBTypeToStringOptions): string {
return this.alias ?? `${this.valueType.toString(options)}[${this.length}]`;
}
public override toJson(): Json {
return {
typeId: this.typeId,
valueType: this.valueType.toJson(),
length: this.length,
...(this.alias ? { alias: this.alias } : {}),
};
}
}
export function ARRAY(
valueType: DuckDBType,
length: number,
alias?: string,
): DuckDBArrayType {
return new DuckDBArrayType(valueType, length, alias);
}
export class DuckDBUUIDType extends BaseDuckDBType<DuckDBTypeId.UUID> {
public constructor(alias?: string) {
super(DuckDBTypeId.UUID, alias);
}
public static readonly instance = new DuckDBUUIDType();
public static create(alias?: string): DuckDBUUIDType {
return alias ? new DuckDBUUIDType(alias) : DuckDBUUIDType.instance;
}
// TODO: common DuckDBValues on type objects
// public get max() {
// return DuckDBUUIDValue.Max;
// }
// public get min() {
// return DuckDBUUIDValue.Min;
// }
}
export const UUID = DuckDBUUIDType.instance;
export class DuckDBUnionType extends BaseDuckDBType<DuckDBTypeId.UNION> {
public readonly memberTags: readonly string[];
public readonly tagMemberIndexes: Readonly<Record<string, number>>;
public readonly memberTypes: readonly DuckDBType[];
public constructor(
memberTags: readonly string[],
memberTypes: readonly DuckDBType[],
alias?: string,
) {
super(DuckDBTypeId.UNION, alias);
if (memberTags.length !== memberTypes.length) {
throw new Error(`Could not create DuckDBUnionType: \
tags length (${memberTags.length}) does not match valueTypes length (${memberTypes.length})`);
}
this.memberTags = memberTags;
const tagMemberIndexes: Record<string, number> = {};
for (let i = 0; i < memberTags.length; i++) {
tagMemberIndexes[memberTags[i]] = i;
}
this.tagMemberIndexes = tagMemberIndexes;
this.memberTypes = memberTypes;
}
public memberIndexForTag(tag: string): number {
return this.tagMemberIndexes[tag];
}
public memberTypeForTag(tag: string): DuckDBType {
return this.memberTypes[this.tagMemberIndexes[tag]];
}
public get memberCount() {
return this.memberTags.length;
}
public toString(options?: DuckDBTypeToStringOptions): string {
if (this.alias) {
return this.alias;
}
if (options?.short) {
return `UNION(…)`;
}
const parts: string[] = [];
for (let i = 0; i < this.memberTags.length; i++) {
parts.push(
`${quotedIdentifier(this.memberTags[i])} ${this.memberTypes[i]}`,
);
}
return `UNION(${parts.join(', ')})`;
}
public override toJson(): Json {
return {
typeId: this.typeId,
memberTags: [...this.memberTags],
memberTypes: this.memberTypes.map((t) => t.toJson()),
...(this.alias ? { alias: this.alias } : {}),
};
}
}
export function UNION(
members: Record<string, DuckDBType>,
alias?: string,
): DuckDBUnionType {
const memberTags = Object.keys(members);
const memberTypes = Object.values(members);
return new DuckDBUnionType(memberTags, memberTypes, alias);
}
export class DuckDBBitType extends BaseDuckDBType<DuckDBTypeId.BIT> {
public constructor(alias?: string) {
super(DuckDBTypeId.BIT, alias);
}
public static readonly instance = new DuckDBBitType();
public static create(alias?: string): DuckDBBitType {
return alias ? new DuckDBBitType(alias) : DuckDBBitType.instance;
}
}
export const BIT = DuckDBBitType.instance;
export class DuckDBTimeTZType extends BaseDuckDBType<DuckDBTypeId.TIME_TZ> {
public constructor(alias?: string) {
super(DuckDBTypeId.TIME_TZ, alias);
}
public toString(options?: DuckDBTypeToStringOptions): string {
if (this.alias) {
return this.alias;
}
if (options?.short) {
return 'TIMETZ';
}
return 'TIME WITH TIME ZONE';
}
public static readonly instance = new DuckDBTimeTZType();
public static create(alias?: string): DuckDBTimeTZType {
return alias ? new DuckDBTimeTZType(alias) : DuckDBTimeTZType.instance;
}
// TODO: common DuckDBValues on type objects
// public get max() {
// return DuckDBTimeTZValue.Max;
// }
// public get min() {
// return DuckDBTimeTZValue.Min;
// }
}
export const TIMETZ = DuckDBTimeTZType.instance;
export class DuckDBTimestampTZType extends BaseDuckDBType<DuckDBTypeId.TIMESTAMP_TZ> {
public constructor(alias?: string) {
super(DuckDBTypeId.TIMESTAMP_TZ, alias);
}
public toString(options?: DuckDBTypeToStringOptions): string {
if (this.alias) {
return this.alias;
}
if (options?.short) {
return 'TIMESTAMPTZ';
}
return 'TIMESTAMP WITH TIME ZONE';
}
public static readonly instance = new DuckDBTimestampTZType();
public static create(alias?: string): DuckDBTimestampTZType {
return alias
? new DuckDBTimestampTZType(alias)
: DuckDBTimestampTZType.instance;
}
// TODO: common DuckDBValues on type objects
// public get epoch() {
// return DuckDBTimestampTZValue.Epoch;
// }
// public get max() {
// return DuckDBTimestampTZValue.Max;
// }
// public get min() {
// return DuckDBTimestampTZValue.Min;
// }
// public get posInf() {
// return DuckDBTimestampTZValue.PosInf;
// }
// public get negInf() {
// return DuckDBTimestampTZValue.NegInf;
// }
}
export const TIMESTAMPTZ = DuckDBTimestampTZType.instance;
export class DuckDBAnyType extends BaseDuckDBType<DuckDBTypeId.ANY> {
public constructor(alias?: string) {
super(DuckDBTypeId.ANY, alias);
}
public static readonly instance = new DuckDBAnyType();
public static create(alias?: string): DuckDBAnyType {
return alias ? new DuckDBAnyType(alias) : DuckDBAnyType.instance;
}
}
export const ANY = DuckDBAnyType.instance;
export class DuckDBVarIntType extends BaseDuckDBType<DuckDBTypeId.VARINT> {
public constructor(alias?: string) {
super(DuckDBTypeId.VARINT, alias);
}
public static readonly instance = new DuckDBVarIntType();
public static create(alias?: string): DuckDBVarIntType {
return alias ? new DuckDBVarIntType(alias) : DuckDBVarIntType.instance;
}
public static readonly Max: bigint =
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368n;
public static readonly Min: bigint =
-179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368n;
public get max() {
return DuckDBVarIntType.Max;
}
public get min() {
return DuckDBVarIntType.Min;
}
}
export const VARINT = DuckDBVarIntType.instance;
export class DuckDBSQLNullType extends BaseDuckDBType<DuckDBTypeId.SQLNULL> {
public constructor(alias?: string) {
super(DuckDBTypeId.SQLNULL, alias);
}
public static readonly instance = new DuckDBSQLNullType();
public static create(alias?: string): DuckDBSQLNullType {
return alias ? new DuckDBSQLNullType(alias) : DuckDBSQLNullType.instance;
}
}
export const SQLNULL = DuckDBSQLNullType.instance;
export class DuckDBStringLiteralType extends BaseDuckDBType<DuckDBTypeId.STRING_LITERAL> {
public constructor(alias?: string) {
super(DuckDBTypeId.STRING_LITERAL, alias);
}
public static readonly instance = new DuckDBStringLiteralType();
public static create(alias?: string): DuckDBStringLiteralType {
return alias
? new DuckDBStringLiteralType(alias)
: DuckDBStringLiteralType.instance;
}
}
export const STRING_LITERAL = DuckDBStringLiteralType.instance;
export class DuckDBIntegerLiteralType extends BaseDuckDBType<DuckDBTypeId.INTEGER_LITERAL> {
public constructor(alias?: string) {
super(DuckDBTypeId.INTEGER_LITERAL, alias);
}
public static readonly instance = new DuckDBIntegerLiteralType();
public static create(alias?: string): DuckDBIntegerLiteralType {
return alias
? new DuckDBIntegerLiteralType(alias)
: DuckDBIntegerLiteralType.instance;
}
}
export const INTEGER_LITERAL = DuckDBIntegerLiteralType.instance;
export type DuckDBType =
| DuckDBBooleanType
| DuckDBTinyIntType
| DuckDBSmallIntType
| DuckDBIntegerType
| DuckDBBigIntType
| DuckDBUTinyIntType
| DuckDBUSmallIntType
| DuckDBUIntegerType
| DuckDBUBigIntType
| DuckDBFloatType
| DuckDBDoubleType
| DuckDBTimestampType
| DuckDBDateType
| DuckDBTimeType
| DuckDBIntervalType
| DuckDBHugeIntType
| DuckDBUHugeIntType
| DuckDBVarCharType
| DuckDBBlobType
| DuckDBDecimalType
| DuckDBTimestampSecondsType
| DuckDBTimestampMillisecondsType
| DuckDBTimestampNanosecondsType
| DuckDBEnumType
| DuckDBListType
| DuckDBStructType
| DuckDBMapType
| DuckDBArrayType
| DuckDBUUIDType
| DuckDBUnionType
| DuckDBBitType
| DuckDBTimeTZType
| DuckDBTimestampTZType
| DuckDBAnyType
| DuckDBVarIntType
| DuckDBSQLNullType
| DuckDBStringLiteralType
| DuckDBIntegerLiteralType;

View File

@@ -0,0 +1,42 @@
// copy of DUCKDB_TYPE from the C API, with names shortened
export enum DuckDBTypeId {
INVALID = 0,
BOOLEAN = 1,
TINYINT = 2,
SMALLINT = 3,
INTEGER = 4,
BIGINT = 5,
UTINYINT = 6,
USMALLINT = 7,
UINTEGER = 8,
UBIGINT = 9,
FLOAT = 10,
DOUBLE = 11,
TIMESTAMP = 12,
DATE = 13,
TIME = 14,
INTERVAL = 15,
HUGEINT = 16,
UHUGEINT = 32,
VARCHAR = 17,
BLOB = 18,
DECIMAL = 19,
TIMESTAMP_S = 20,
TIMESTAMP_MS = 21,
TIMESTAMP_NS = 22,
ENUM = 23,
LIST = 24,
STRUCT = 25,
MAP = 26,
ARRAY = 33,
UUID = 27,
UNION = 28,
BIT = 29,
TIME_TZ = 30,
TIMESTAMP_TZ = 31,
ANY = 34,
VARINT = 35,
SQLNULL = 36,
STRING_LITERAL = 37,
INTEGER_LITERAL = 38,
}

View File

@@ -0,0 +1,46 @@
import {
DOUBLE,
DuckDBBlobType,
DuckDBVarCharType,
FLOAT,
HUGEINT,
LIST,
STRUCT,
USMALLINT,
UTINYINT,
} from './DuckDBType.js';
// see https://github.com/duckdb/duckdb-inet/blob/main/src/inet_extension.cpp
export const INET = STRUCT(
{ ip_type: UTINYINT, address: HUGEINT, mask: USMALLINT },
'INET',
);
// see LogicalType::JSON() in https://github.com/duckdb/duckdb/blob/main/src/common/types.cpp
export const JSONType = DuckDBVarCharType.create('JSON');
// see https://github.com/duckdb/duckdb-spatial/blob/main/src/spatial/spatial_types.cpp
export const BOX_2D = STRUCT(
{ min_x: DOUBLE, min_y: DOUBLE, max_x: DOUBLE, max_y: DOUBLE },
'BOX_2D',
);
export const BOX_2DF = STRUCT(
{ min_x: FLOAT, min_y: FLOAT, max_x: FLOAT, max_y: FLOAT },
'BOX_2DF',
);
export const GEOMETRY = DuckDBBlobType.create('GEOMETRY');
export const LINESTRING_2D = LIST(
STRUCT({ x: DOUBLE, y: DOUBLE }),
'LINESTRING_2D',
);
export const POINT_2D = STRUCT({ x: DOUBLE, y: DOUBLE }, 'POINT_2D');
export const POINT_3D = STRUCT({ x: DOUBLE, y: DOUBLE, z: DOUBLE }, 'POINT_3D');
export const POINT_4D = STRUCT(
{ x: DOUBLE, y: DOUBLE, z: DOUBLE, m: DOUBLE },
'POINT_4D',
);
export const POLYGON_2D = LIST(
LIST(STRUCT({ x: DOUBLE, y: DOUBLE })),
'POLYGON_2D',
);
export const WKB_BLOB = DuckDBBlobType.create('WKB_BLOB');

View File

@@ -0,0 +1,4 @@
export * from './DuckDBType.js';
export * from './DuckDBTypeId.js';
export * from './extensionTypes.js';
export * from './parseLogicalTypeString.js';

View File

@@ -0,0 +1,286 @@
import {
ARRAY,
BIGINT,
BIT,
BLOB,
BOOLEAN,
DATE,
DECIMAL,
DOUBLE,
DuckDBMapType,
DuckDBStructType,
DuckDBType,
DuckDBUnionType,
ENUM,
FLOAT,
HUGEINT,
INTEGER,
INTERVAL,
LIST,
MAP,
SMALLINT,
SQLNULL,
STRUCT,
TIME,
TIMESTAMP,
TIMESTAMP_MS,
TIMESTAMP_NS,
TIMESTAMP_S,
TIMESTAMPTZ,
TIMETZ,
TINYINT,
UBIGINT,
UHUGEINT,
UINTEGER,
UNION,
USMALLINT,
UTINYINT,
UUID,
VARCHAR,
VARINT,
} from './DuckDBType.js';
import {
BOX_2D,
BOX_2DF,
GEOMETRY,
INET,
JSONType,
LINESTRING_2D,
POINT_2D,
POINT_3D,
POINT_4D,
POLYGON_2D,
WKB_BLOB,
} from './extensionTypes.js';
const simpleTypeMap: Record<string, DuckDBType> = {
BIGINT: BIGINT,
BIT: BIT,
BOOLEAN: BOOLEAN,
BLOB: BLOB,
BOX_2D: BOX_2D,
BOX_2DF: BOX_2DF,
DATE: DATE,
DOUBLE: DOUBLE,
FLOAT: FLOAT,
GEOMETRY: GEOMETRY,
HUGEINT: HUGEINT,
INET: INET,
INTEGER: INTEGER,
INTERVAL: INTERVAL,
JSON: JSONType,
LINESTRING_2D: LINESTRING_2D,
POINT_2D: POINT_2D,
POINT_3D: POINT_3D,
POINT_4D: POINT_4D,
POLYGON_2D: POLYGON_2D,
SMALLINT: SMALLINT,
SQLNULL: SQLNULL,
TIME: TIME,
'TIME WITH TIME ZONE': TIMETZ,
TIMESTAMP: TIMESTAMP,
'TIMESTAMP WITH TIME ZONE': TIMESTAMPTZ,
TIMESTAMP_S: TIMESTAMP_S,
TIMESTAMP_MS: TIMESTAMP_MS,
TIMESTAMP_NS: TIMESTAMP_NS,
TINYINT: TINYINT,
UBIGINT: UBIGINT,
UHUGEINT: UHUGEINT,
UINTEGER: UINTEGER,
USMALLINT: USMALLINT,
UTINYINT: UTINYINT,
UUID: UUID,
VARCHAR: VARCHAR,
VARINT: VARINT,
WKB_BLOB: WKB_BLOB,
};
function matchStructMapOrUnion(
typeString: string,
): DuckDBStructType | DuckDBMapType | DuckDBUnionType | undefined {
typeString = typeString.trim();
const fields = parseStructLike(typeString);
if (!fields) {
return undefined;
}
if (typeString.startsWith('STRUCT')) {
const entries: Record<string, DuckDBType> = {};
for (const field of fields) {
if (field.key && field.type) {
entries[field.key] = field.type;
}
}
return STRUCT(entries);
}
if (typeString.startsWith('MAP')) {
const keyType = fields[0]?.type;
const valueType = fields[1]?.type;
if (keyType && valueType) {
return MAP(keyType, valueType);
}
}
if (typeString.startsWith('UNION')) {
const members: Record<string, DuckDBType> = {};
for (const field of fields) {
if (field.key && field.type) {
members[field.key] = field.type;
}
}
return UNION(members);
}
return undefined;
}
function parseStructLike(typeString: string): ParsedField[] | undefined {
const structPattern = /^(STRUCT|MAP|UNION)\s*\((.*)\)$/;
const match = structPattern.exec(typeString);
if (!match) {
return undefined;
}
const fieldsString = match[2];
return parseFields(fieldsString);
}
/** Parse the fields substring. We do this by counting parens and double quotes.
* When checking for double-quotes, we only need to count an even number of them
* to count brackets, since in cases where there escaped double quotes inside
* a double-quoted string, the double quotes appear adjacent to each other,
* always incrementing the count by 2 before there could theoretically be another
* paren.
*/
function parseFields(fieldsString: string): ParsedField[] {
const fields: ParsedField[] = [];
let currentFieldStartIndex: number | null = null;
let parenCount = 0;
let quoteCount = 0;
for (let i = 0; i < fieldsString.length; i++) {
const char = fieldsString[i];
if (
currentFieldStartIndex === null &&
char !== '(' &&
char !== ')' &&
char !== ','
) {
currentFieldStartIndex = i;
}
if (char === '"') {
quoteCount++;
}
if (
char === ',' &&
parenCount === 0 &&
quoteCount % 2 === 0 &&
currentFieldStartIndex !== null
) {
const field = fieldsString.slice(currentFieldStartIndex, i);
fields.push(parseField(field.trim()));
currentFieldStartIndex = null;
} else {
if (char === '(' && quoteCount % 2 === 0) parenCount++;
if (char === ')' && quoteCount % 2 === 0) parenCount--;
}
}
if (currentFieldStartIndex !== null) {
const lastField = fieldsString.slice(currentFieldStartIndex);
fields.push(parseField(lastField.trim()));
}
return fields;
}
interface ParsedField {
key?: string;
type?: DuckDBType;
}
function parseField(fieldString: string): ParsedField {
const fieldPattern = /^(".*?"|\w+)\s+(.+)$/;
const match = fieldPattern.exec(fieldString);
if (match) {
const key = match[1];
const type = parseLogicalTypeString(match[2].trim());
return { key, type };
} else {
const type = parseLogicalTypeString(fieldString);
return { type };
}
}
function matchDecimal(typeString: string) {
const match = typeString.match(/^DECIMAL\((\d+),(\d+)\)$/);
if (match) {
return DECIMAL(Number(match[1]), Number(match[2]));
}
return undefined;
}
function matchEnum(typeString: string) {
const match = /ENUM\(([^)]*)\)/i.exec(typeString);
if (match) {
const matches = match[1].matchAll(/'((?:[^']|'')*)'/g);
const values: string[] = [];
for (const match of matches) {
values.push(match[1].replace(/''/, `'`));
}
return ENUM(values);
}
return undefined;
}
function matchList(typeString: string) {
if (typeString.endsWith('[]')) {
const innerType = typeString.slice(0, -2);
return LIST(parseLogicalTypeString(innerType));
}
return undefined;
}
function matchArray(typeString: string) {
const match = typeString.match(/\[(\d+)\]$/);
if (match) {
const innerType = typeString.slice(0, -match[0].length);
const length = match[1];
return ARRAY(parseLogicalTypeString(innerType), Number(length));
}
return undefined;
}
export function parseLogicalTypeString(typeString: string): DuckDBType {
if (typeString in simpleTypeMap) {
return simpleTypeMap[typeString];
}
const listType = matchList(typeString);
if (listType) {
return listType;
}
const arrayType = matchArray(typeString);
if (arrayType) {
return arrayType;
}
const decimalType = matchDecimal(typeString);
if (decimalType) {
return decimalType;
}
const enumType = matchEnum(typeString);
if (enumType) {
return enumType;
}
const structMapOrUnionType = matchStructMapOrUnion(typeString);
if (structMapOrUnionType) {
return structMapOrUnionType;
}
throw Error(`unimplemented type match: ${typeString}`);
}

View File

@@ -0,0 +1,7 @@
export function quotedString(input: string): string {
return `'${input.replace(`'`, `''`)}'`;
}
export function quotedIdentifier(input: string): string {
return `"${input.replace(`"`, `""`)}"`;
}

View File

@@ -0,0 +1,6 @@
{
"extends": "../../../tsconfig.library.json",
"compilerOptions": {
"outDir": "../out"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,326 @@
import { expect, suite, test } from 'vitest';
import {
ARRAY,
BIGINT,
BIT,
BLOB,
BOOLEAN,
DATE,
DECIMAL,
DOUBLE,
ENUM,
FLOAT,
HUGEINT,
INTEGER,
INTERVAL,
LIST,
MAP,
SMALLINT,
STRUCT,
TIME,
TIMESTAMP,
TIMESTAMP_MS,
TIMESTAMP_NS,
TIMESTAMP_S,
TIMESTAMPTZ,
TIMETZ,
TINYINT,
UBIGINT,
UHUGEINT,
UINTEGER,
UNION,
USMALLINT,
UTINYINT,
UUID,
VARCHAR,
VARINT,
} from '../src/DuckDBType';
import {
BOX_2D,
BOX_2DF,
GEOMETRY,
INET,
JSONType,
LINESTRING_2D,
POINT_2D,
POINT_3D,
POINT_4D,
POLYGON_2D,
WKB_BLOB,
} from '../src/extensionTypes';
import { parseLogicalTypeString } from '../src/parseLogicalTypeString';
suite('parseLogicalTypeString', () => {
test('BOOLEAN', () => {
expect(parseLogicalTypeString('BOOLEAN')).toStrictEqual(BOOLEAN);
});
test('TINYINT', () => {
expect(parseLogicalTypeString('TINYINT')).toStrictEqual(TINYINT);
});
test('GEOMETRY', () => {
expect(parseLogicalTypeString('GEOMETRY')).toStrictEqual(GEOMETRY);
});
test('LINESTRING_2D', () => {
expect(parseLogicalTypeString('LINESTRING_2D')).toStrictEqual(
LINESTRING_2D,
);
});
test('BOX_2D', () => {
expect(parseLogicalTypeString('BOX_2D')).toStrictEqual(BOX_2D);
});
test('BOX_2DF', () => {
expect(parseLogicalTypeString('BOX_2DF')).toStrictEqual(BOX_2DF);
});
test('POINT_2D', () => {
expect(parseLogicalTypeString('POINT_2D')).toStrictEqual(POINT_2D);
});
test('POINT_3D', () => {
expect(parseLogicalTypeString('POINT_3D')).toStrictEqual(POINT_3D);
});
test('POINT_4D', () => {
expect(parseLogicalTypeString('POINT_4D')).toStrictEqual(POINT_4D);
});
test('POLYGON_2D', () => {
expect(parseLogicalTypeString('POLYGON_2D')).toStrictEqual(POLYGON_2D);
});
test('INET', () => {
expect(parseLogicalTypeString('INET')).toStrictEqual(INET);
});
test('JSON', () => {
expect(parseLogicalTypeString('JSON')).toStrictEqual(JSONType);
});
test('WKB_BLOB', () => {
expect(parseLogicalTypeString('WKB_BLOB')).toStrictEqual(WKB_BLOB);
});
test('SMALLINT', () => {
expect(parseLogicalTypeString('SMALLINT')).toStrictEqual(SMALLINT);
});
test('INTEGER', () => {
expect(parseLogicalTypeString('INTEGER')).toStrictEqual(INTEGER);
});
test('BIGINT', () => {
expect(parseLogicalTypeString('BIGINT')).toStrictEqual(BIGINT);
});
test('HUGEINT', () => {
expect(parseLogicalTypeString('HUGEINT')).toStrictEqual(HUGEINT);
});
test('UTINYINT', () => {
expect(parseLogicalTypeString('UTINYINT')).toStrictEqual(UTINYINT);
});
test('UHUGEINT', () => {
expect(parseLogicalTypeString('UHUGEINT')).toStrictEqual(UHUGEINT);
});
test('USMALLINT', () => {
expect(parseLogicalTypeString('USMALLINT')).toStrictEqual(USMALLINT);
});
test('UINTEGER', () => {
expect(parseLogicalTypeString('UINTEGER')).toStrictEqual(UINTEGER);
});
test('UBIGINT', () => {
expect(parseLogicalTypeString('UBIGINT')).toStrictEqual(UBIGINT);
});
test('DATE', () => {
expect(parseLogicalTypeString('DATE')).toStrictEqual(DATE);
});
test('TIME', () => {
expect(parseLogicalTypeString('TIME')).toStrictEqual(TIME);
});
test('TIMESTAMP', () => {
expect(parseLogicalTypeString('TIMESTAMP')).toStrictEqual(TIMESTAMP);
});
test('TIMESTAMP_S', () => {
expect(parseLogicalTypeString('TIMESTAMP_S')).toStrictEqual(TIMESTAMP_S);
});
test('TIMESTAMP_MS', () => {
expect(parseLogicalTypeString('TIMESTAMP_MS')).toStrictEqual(TIMESTAMP_MS);
});
test('TIMESTAMP_NS', () => {
expect(parseLogicalTypeString('TIMESTAMP_NS')).toStrictEqual(TIMESTAMP_NS);
});
test('TIME WITH TIME ZONE', () => {
expect(parseLogicalTypeString('TIME WITH TIME ZONE')).toStrictEqual(TIMETZ);
});
test('TIMESTAMP WITH TIME ZONE', () => {
expect(parseLogicalTypeString('TIMESTAMP WITH TIME ZONE')).toStrictEqual(
TIMESTAMPTZ,
);
});
test('FLOAT', () => {
expect(parseLogicalTypeString('FLOAT')).toStrictEqual(FLOAT);
});
test('DOUBLE', () => {
expect(parseLogicalTypeString('DOUBLE')).toStrictEqual(DOUBLE);
});
test('DECIMAL(18,6)', () => {
expect(parseLogicalTypeString('DECIMAL(18,6)')).toStrictEqual(
DECIMAL(18, 6),
);
});
test(`ENUM('DUCK_DUCK_ENUM', 'GOOSE')`, () => {
expect(
parseLogicalTypeString(`ENUM('DUCK_DUCK_ENUM', 'GOOSE')`),
).toStrictEqual(ENUM(['DUCK_DUCK_ENUM', 'GOOSE']));
});
test('DOUBLE[]', () => {
expect(parseLogicalTypeString('DOUBLE[]')).toStrictEqual(LIST(DOUBLE));
});
test('STRUCT(a INTEGER, b VARCHAR)', () => {
expect(
parseLogicalTypeString('STRUCT(a INTEGER, b VARCHAR)'),
).toStrictEqual(
STRUCT({
a: INTEGER,
b: VARCHAR,
}),
);
});
test('STRUCT(a INTEGER[], b VARCHAR[])', () => {
expect(
parseLogicalTypeString('STRUCT(a INTEGER[], b VARCHAR[])'),
).toStrictEqual(
STRUCT({
a: LIST(INTEGER),
b: LIST(VARCHAR),
}),
);
});
test('STRUCT(a INTEGER, b VARCHAR)[]', () => {
expect(
parseLogicalTypeString('STRUCT(a INTEGER, b VARCHAR)[]'),
).toStrictEqual(
LIST(
STRUCT({
a: INTEGER,
b: VARCHAR,
}),
),
);
});
// addition: nested struct
test('STRUCT(a STRUCT(b INTEGER), b VARCHAR)', () => {
expect(
parseLogicalTypeString('STRUCT(a STRUCT(b INTEGER), b VARCHAR)'),
).toStrictEqual(
STRUCT({
a: STRUCT({ b: INTEGER }),
b: VARCHAR,
}),
);
});
test('STRUCT("my weird ""key" INTEGER, b VARCHAR)', () => {
expect(
parseLogicalTypeString('STRUCT("my weird ""key" INTEGER, b VARCHAR)'),
).toStrictEqual(
STRUCT({
'"my weird ""key"': INTEGER,
b: VARCHAR,
}),
);
});
test('STRUCT("my weird ""key" STRUCT("my other ""weird key" INTEGER), b VARCHAR)', () => {
expect(
parseLogicalTypeString(
'STRUCT("my weird ""key" STRUCT("my other ""weird key" INTEGER), b VARCHAR)',
),
).toStrictEqual(
STRUCT({
'"my weird ""key"': STRUCT({
'"my other ""weird key"': INTEGER,
}),
b: VARCHAR,
}),
);
});
test('MAP(INTEGER, VARCHAR)', () => {
expect(parseLogicalTypeString('MAP(INTEGER, VARCHAR)')).toStrictEqual(
MAP(INTEGER, VARCHAR),
);
});
test('MAP(VARCHAR, STRUCT(b INTEGER))', () => {
expect(
parseLogicalTypeString('MAP(VARCHAR, STRUCT(b INTEGER))'),
).toStrictEqual(MAP(VARCHAR, STRUCT({ b: INTEGER })));
});
test('UNION("name" VARCHAR, age SMALLINT)', () => {
expect(
parseLogicalTypeString('UNION("name" VARCHAR, age SMALLINT)'),
).toStrictEqual(
UNION({
'"name"': VARCHAR,
age: SMALLINT,
}),
);
});
test('INTEGER[3]', () => {
expect(parseLogicalTypeString('INTEGER[3]')).toStrictEqual(
ARRAY(INTEGER, 3),
);
});
test('STRUCT(a INTEGER, b VARCHAR)[3]', () => {
expect(
parseLogicalTypeString('STRUCT(a INTEGER, b VARCHAR)[3]'),
).toStrictEqual(
ARRAY(
STRUCT({
a: INTEGER,
b: VARCHAR,
}),
3,
),
);
});
test('STRUCT(a INTEGER[3], b VARCHAR[3])', () => {
expect(
parseLogicalTypeString('STRUCT(a INTEGER[3], b VARCHAR[3])'),
).toStrictEqual(
STRUCT({
a: ARRAY(INTEGER, 3),
b: ARRAY(VARCHAR, 3),
}),
);
});
test('INTEGER[][3]', () => {
expect(parseLogicalTypeString('INTEGER[][3]')).toStrictEqual(
ARRAY(LIST(INTEGER), 3),
);
});
test('INTEGER[3][]', () => {
expect(parseLogicalTypeString('INTEGER[3][]')).toStrictEqual(
LIST(ARRAY(INTEGER, 3)),
);
});
test('UUID', () => {
expect(parseLogicalTypeString('UUID')).toStrictEqual(UUID);
});
test('INTERVAL', () => {
expect(parseLogicalTypeString('INTERVAL')).toStrictEqual(INTERVAL);
});
test('VARCHAR', () => {
expect(parseLogicalTypeString('VARCHAR')).toStrictEqual(VARCHAR);
});
test('VARINT', () => {
expect(parseLogicalTypeString('VARINT')).toStrictEqual(VARINT);
});
test('BLOB', () => {
expect(parseLogicalTypeString('BLOB')).toStrictEqual(BLOB);
});
test('BIT', () => {
expect(parseLogicalTypeString('BIT')).toStrictEqual(BIT);
});
});

View File

@@ -0,0 +1,6 @@
{
"extends": "../../../tsconfig.test.json",
"references": [
{ "path": "../src" }
]
}