@malloydata/malloy 0.0.326 → 0.0.328
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/core.js +2 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/row_data_utils.d.ts +30 -0
- package/dist/api/row_data_utils.js +87 -0
- package/dist/api/util.js +20 -38
- package/dist/dialect/dialect.d.ts +35 -0
- package/dist/dialect/dialect.js +47 -1
- package/dist/dialect/duckdb/duckdb.d.ts +2 -1
- package/dist/dialect/duckdb/duckdb.js +12 -3
- package/dist/dialect/mysql/mysql.js +18 -4
- package/dist/dialect/pg_impl.d.ts +1 -0
- package/dist/dialect/pg_impl.js +2 -0
- package/dist/dialect/postgres/postgres.js +4 -1
- package/dist/dialect/snowflake/snowflake.d.ts +2 -1
- package/dist/dialect/snowflake/snowflake.js +36 -15
- package/dist/dialect/standardsql/standardsql.d.ts +2 -1
- package/dist/dialect/standardsql/standardsql.js +8 -3
- package/dist/dialect/trino/trino.js +10 -2
- package/dist/lang/ast/expressions/expr-avg.d.ts +5 -0
- package/dist/lang/ast/expressions/expr-avg.js +9 -0
- package/dist/lang/ast/expressions/expr-coalesce.js +6 -0
- package/dist/lang/ast/expressions/expr-number.d.ts +10 -1
- package/dist/lang/ast/expressions/expr-number.js +23 -7
- package/dist/lang/ast/expressions/expr-props.d.ts +1 -1
- package/dist/lang/ast/expressions/pick-when.js +10 -3
- package/dist/lang/ast/types/expression-def.js +36 -2
- package/dist/lang/parse-log.d.ts +1 -0
- package/dist/lang/test/test-translator.js +2 -0
- package/dist/malloy.d.ts +23 -2
- package/dist/malloy.js +204 -41
- package/dist/model/malloy_types.d.ts +2 -2
- package/dist/model/query_model_impl.js +5 -1
- package/dist/test/cellsToObject.d.ts +6 -0
- package/dist/test/cellsToObject.js +111 -0
- package/dist/test/index.d.ts +47 -0
- package/dist/test/index.js +95 -20
- package/dist/test/matchers.d.ts +10 -0
- package/dist/test/matchers.js +17 -0
- package/dist/test/resultMatchers.d.ts +42 -0
- package/dist/test/resultMatchers.js +722 -0
- package/dist/test/runQuery.d.ts +31 -0
- package/dist/test/runQuery.js +67 -0
- package/dist/test/test-models.d.ts +77 -0
- package/dist/test/test-models.js +319 -0
- package/dist/test/test-values.d.ts +66 -0
- package/dist/test/test-values.js +208 -0
- package/dist/to_stable.js +3 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -5
package/dist/api/core.js
CHANGED
package/dist/api/index.d.ts
CHANGED
package/dist/api/index.js
CHANGED
|
@@ -48,4 +48,5 @@ exports.stateless = __importStar(require("./stateless"));
|
|
|
48
48
|
exports.asynchronous = __importStar(require("./asynchronous"));
|
|
49
49
|
__exportStar(require("./connection"), exports);
|
|
50
50
|
exports.util = __importStar(require("./util"));
|
|
51
|
+
__exportStar(require("./row_data_utils"), exports);
|
|
51
52
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database connections return numeric data in various forms depending on
|
|
3
|
+
* the database driver and configuration. Values may arrive as:
|
|
4
|
+
* - Native JavaScript numbers
|
|
5
|
+
* - BigInt (when jsTreatIntegerAsBigInt is enabled)
|
|
6
|
+
* - Strings (when bigNumberStrings is enabled, or for precise decimals)
|
|
7
|
+
* - Wrapper objects (e.g., Snowflake's Integer class)
|
|
8
|
+
*
|
|
9
|
+
* These utilities normalize row data into consistent JavaScript types.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Converts a numeric value from database row data to a JavaScript number.
|
|
13
|
+
*/
|
|
14
|
+
export declare function rowDataToNumber(value: unknown): number;
|
|
15
|
+
/**
|
|
16
|
+
* Converts a numeric value from database row data to a string representation.
|
|
17
|
+
* Used for bigint fields where precision must be preserved - JavaScript numbers
|
|
18
|
+
* lose precision beyond Number.MAX_SAFE_INTEGER.
|
|
19
|
+
*/
|
|
20
|
+
export declare function rowDataToSerializedBigint(value: unknown): string;
|
|
21
|
+
/**
|
|
22
|
+
* Converts a date/timestamp value from database row data to a JavaScript Date.
|
|
23
|
+
* Database drivers return dates in various forms:
|
|
24
|
+
* - Native Date objects
|
|
25
|
+
* - Objects that look like Date but fail instanceof (DuckDB TSTZ)
|
|
26
|
+
* - Numbers (timestamps)
|
|
27
|
+
* - Objects with a .value property (some wrappers)
|
|
28
|
+
* - Strings (Postgres timestamps)
|
|
29
|
+
*/
|
|
30
|
+
export declare function rowDataToDate(value: unknown): Date;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Contributors to the Malloy project
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.rowDataToNumber = rowDataToNumber;
|
|
8
|
+
exports.rowDataToSerializedBigint = rowDataToSerializedBigint;
|
|
9
|
+
exports.rowDataToDate = rowDataToDate;
|
|
10
|
+
const luxon_1 = require("luxon");
|
|
11
|
+
/**
|
|
12
|
+
* Database connections return numeric data in various forms depending on
|
|
13
|
+
* the database driver and configuration. Values may arrive as:
|
|
14
|
+
* - Native JavaScript numbers
|
|
15
|
+
* - BigInt (when jsTreatIntegerAsBigInt is enabled)
|
|
16
|
+
* - Strings (when bigNumberStrings is enabled, or for precise decimals)
|
|
17
|
+
* - Wrapper objects (e.g., Snowflake's Integer class)
|
|
18
|
+
*
|
|
19
|
+
* These utilities normalize row data into consistent JavaScript types.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Converts a numeric value from database row data to a JavaScript number.
|
|
23
|
+
*/
|
|
24
|
+
function rowDataToNumber(value) {
|
|
25
|
+
if (typeof value === 'number')
|
|
26
|
+
return value;
|
|
27
|
+
if (typeof value === 'bigint')
|
|
28
|
+
return Number(value);
|
|
29
|
+
if (typeof value === 'string')
|
|
30
|
+
return Number(value);
|
|
31
|
+
if (typeof value === 'object' && value !== null)
|
|
32
|
+
return Number(value);
|
|
33
|
+
throw new Error(`Cannot convert ${typeof value} to number: ${value}`);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Converts a numeric value from database row data to a string representation.
|
|
37
|
+
* Used for bigint fields where precision must be preserved - JavaScript numbers
|
|
38
|
+
* lose precision beyond Number.MAX_SAFE_INTEGER.
|
|
39
|
+
*/
|
|
40
|
+
function rowDataToSerializedBigint(value) {
|
|
41
|
+
if (typeof value === 'string')
|
|
42
|
+
return value;
|
|
43
|
+
if (typeof value === 'bigint')
|
|
44
|
+
return value.toString();
|
|
45
|
+
return String(value !== null && value !== void 0 ? value : '');
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Converts a date/timestamp value from database row data to a JavaScript Date.
|
|
49
|
+
* Database drivers return dates in various forms:
|
|
50
|
+
* - Native Date objects
|
|
51
|
+
* - Objects that look like Date but fail instanceof (DuckDB TSTZ)
|
|
52
|
+
* - Numbers (timestamps)
|
|
53
|
+
* - Objects with a .value property (some wrappers)
|
|
54
|
+
* - Strings (Postgres timestamps)
|
|
55
|
+
*/
|
|
56
|
+
function rowDataToDate(value) {
|
|
57
|
+
var _a;
|
|
58
|
+
if (value instanceof Date) {
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
62
|
+
const valAsAny = value;
|
|
63
|
+
if (((_a = valAsAny === null || valAsAny === void 0 ? void 0 : valAsAny.constructor) === null || _a === void 0 ? void 0 : _a.name) === 'Date') {
|
|
64
|
+
// For some reason duckdb TSTZ values come back as objects which do not
|
|
65
|
+
// pass "instanceof" but do seem date like.
|
|
66
|
+
return new Date(value);
|
|
67
|
+
}
|
|
68
|
+
else if (typeof value === 'number') {
|
|
69
|
+
return new Date(value);
|
|
70
|
+
}
|
|
71
|
+
else if (typeof value !== 'string') {
|
|
72
|
+
return new Date(value.value);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Postgres timestamps end up here, and ideally we would know the system
|
|
76
|
+
// timezone of the postgres instance to correctly create a Date() object
|
|
77
|
+
// which represents the same instant in time, but we don't have the data
|
|
78
|
+
// flow to implement that. This may be a problem at some future day,
|
|
79
|
+
// so here is a comment, for that day.
|
|
80
|
+
let parsed = luxon_1.DateTime.fromISO(value, { zone: 'UTC' });
|
|
81
|
+
if (!parsed.isValid) {
|
|
82
|
+
parsed = luxon_1.DateTime.fromSQL(value, { zone: 'UTC' });
|
|
83
|
+
}
|
|
84
|
+
return parsed.toJSDate();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=row_data_utils.js.map
|
package/dist/api/util.js
CHANGED
|
@@ -16,7 +16,7 @@ exports.mapLogs = mapLogs;
|
|
|
16
16
|
const malloy_tag_1 = require("@malloydata/malloy-tag");
|
|
17
17
|
const annotation_1 = require("../annotation");
|
|
18
18
|
const to_stable_1 = require("../to_stable");
|
|
19
|
-
const
|
|
19
|
+
const row_data_utils_1 = require("./row_data_utils");
|
|
20
20
|
function wrapLegacyInfoConnection(connection) {
|
|
21
21
|
return {
|
|
22
22
|
get dialectName() {
|
|
@@ -54,37 +54,6 @@ function wrapLegacyConnection(connection) {
|
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
function valueToDate(value) {
|
|
58
|
-
// TODO properly map the data from BQ/Postgres types
|
|
59
|
-
if (value instanceof Date) {
|
|
60
|
-
return value;
|
|
61
|
-
}
|
|
62
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
-
const valAsAny = value;
|
|
64
|
-
if (valAsAny.constructor.name === 'Date') {
|
|
65
|
-
// For some reason duckdb TSTZ values come back as objects which do not
|
|
66
|
-
// pass "instance of" but do seem date like.
|
|
67
|
-
return new Date(value);
|
|
68
|
-
}
|
|
69
|
-
else if (typeof value === 'number') {
|
|
70
|
-
return new Date(value);
|
|
71
|
-
}
|
|
72
|
-
else if (typeof value !== 'string') {
|
|
73
|
-
return new Date(value.value);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
// Postgres timestamps end up here, and ideally we would know the system
|
|
77
|
-
// timezone of the postgres instance to correctly create a Date() object
|
|
78
|
-
// which represents the same instant in time, but we don't have the data
|
|
79
|
-
// flow to implement that. This may be a problem at some future day,
|
|
80
|
-
// so here is a comment, for that day.
|
|
81
|
-
let parsed = luxon_1.DateTime.fromISO(value, { zone: 'UTC' });
|
|
82
|
-
if (!parsed.isValid) {
|
|
83
|
-
parsed = luxon_1.DateTime.fromSQL(value, { zone: 'UTC' });
|
|
84
|
-
}
|
|
85
|
-
return parsed.toJSDate();
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
57
|
function mapData(data, schema) {
|
|
89
58
|
function mapValue(value, field) {
|
|
90
59
|
if (value === null) {
|
|
@@ -93,7 +62,7 @@ function mapData(data, schema) {
|
|
|
93
62
|
else if (field.type.kind === 'date_type' ||
|
|
94
63
|
field.type.kind === 'timestamp_type' ||
|
|
95
64
|
field.type.kind === 'timestamptz_type') {
|
|
96
|
-
const time_value =
|
|
65
|
+
const time_value = (0, row_data_utils_1.rowDataToDate)(value).toISOString();
|
|
97
66
|
if (field.type.kind === 'date_type') {
|
|
98
67
|
return { kind: 'date_cell', date_value: time_value };
|
|
99
68
|
}
|
|
@@ -102,19 +71,32 @@ function mapData(data, schema) {
|
|
|
102
71
|
}
|
|
103
72
|
}
|
|
104
73
|
else if (field.type.kind === 'boolean_type') {
|
|
105
|
-
if (typeof value === 'number') {
|
|
74
|
+
if (typeof value === 'number' || typeof value === 'bigint') {
|
|
106
75
|
return { kind: 'boolean_cell', boolean_value: value !== 0 };
|
|
107
76
|
}
|
|
77
|
+
if (typeof value === 'string') {
|
|
78
|
+
// MySQL with bigNumberStrings returns "0" or "1"
|
|
79
|
+
return {
|
|
80
|
+
kind: 'boolean_cell',
|
|
81
|
+
boolean_value: value !== '0' && value !== '',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
108
84
|
if (typeof value !== 'boolean') {
|
|
109
85
|
throw new Error(`Invalid boolean ${value}`);
|
|
110
86
|
}
|
|
111
87
|
return { kind: 'boolean_cell', boolean_value: value };
|
|
112
88
|
}
|
|
113
89
|
else if (field.type.kind === 'number_type') {
|
|
114
|
-
|
|
115
|
-
|
|
90
|
+
const subtype = field.type.subtype;
|
|
91
|
+
if (subtype === 'bigint') {
|
|
92
|
+
const stringValue = (0, row_data_utils_1.rowDataToSerializedBigint)(value);
|
|
93
|
+
return { kind: 'big_number_cell', number_value: stringValue, subtype };
|
|
116
94
|
}
|
|
117
|
-
return {
|
|
95
|
+
return {
|
|
96
|
+
kind: 'number_cell',
|
|
97
|
+
number_value: (0, row_data_utils_1.rowDataToNumber)(value),
|
|
98
|
+
subtype,
|
|
99
|
+
};
|
|
118
100
|
}
|
|
119
101
|
else if (field.type.kind === 'string_type') {
|
|
120
102
|
if (typeof value !== 'string') {
|
|
@@ -221,7 +203,7 @@ function wrapResult(result) {
|
|
|
221
203
|
}));
|
|
222
204
|
return {
|
|
223
205
|
schema,
|
|
224
|
-
data: mapData(result.data.
|
|
206
|
+
data: mapData(result.data.rawData, schema),
|
|
225
207
|
connection_name: result.connectionName,
|
|
226
208
|
annotations: annotations.length > 0 ? annotations : undefined,
|
|
227
209
|
model_annotations: modelAnnotations.length > 0 ? modelAnnotations : undefined,
|
|
@@ -7,6 +7,14 @@ interface DialectField {
|
|
|
7
7
|
sqlOutputName: string;
|
|
8
8
|
}
|
|
9
9
|
export type DialectFieldList = DialectField[];
|
|
10
|
+
export declare const MIN_INT32 = -2147483648;
|
|
11
|
+
export declare const MAX_INT32 = 2147483647;
|
|
12
|
+
export declare const MIN_INT64: bigint;
|
|
13
|
+
export declare const MAX_INT64: bigint;
|
|
14
|
+
export declare const MIN_INT128: bigint;
|
|
15
|
+
export declare const MAX_INT128: bigint;
|
|
16
|
+
export declare const MIN_DECIMAL38: bigint;
|
|
17
|
+
export declare const MAX_DECIMAL38: bigint;
|
|
10
18
|
/**
|
|
11
19
|
* Data which dialect methods need in order to correctly generate SQL.
|
|
12
20
|
* Initially this is just timezone related, but I made this an interface
|
|
@@ -25,6 +33,18 @@ export declare function qtz(qi: QueryInfo): string | undefined;
|
|
|
25
33
|
export type OrderByClauseType = 'output_name' | 'ordinal' | 'expression';
|
|
26
34
|
export type OrderByRequest = 'query' | 'turtle' | 'analytical';
|
|
27
35
|
export type BooleanTypeSupport = 'supported' | 'simulated' | 'none';
|
|
36
|
+
/**
|
|
37
|
+
* Maps a range of integer values to a Malloy number type.
|
|
38
|
+
*
|
|
39
|
+
* Dialects define an array of these mappings to describe how integer literals
|
|
40
|
+
* should be typed. The array is searched in order, and the first matching
|
|
41
|
+
* range determines the type.
|
|
42
|
+
*/
|
|
43
|
+
export interface IntegerTypeMapping {
|
|
44
|
+
min: bigint;
|
|
45
|
+
max: bigint;
|
|
46
|
+
numberType: 'integer' | 'bigint';
|
|
47
|
+
}
|
|
28
48
|
export declare abstract class Dialect {
|
|
29
49
|
abstract name: string;
|
|
30
50
|
abstract defaultNumberType: string;
|
|
@@ -51,6 +71,7 @@ export declare abstract class Dialect {
|
|
|
51
71
|
orderByClause: OrderByClauseType;
|
|
52
72
|
nullMatchesFunctionSignature: boolean;
|
|
53
73
|
supportsSelectReplace: boolean;
|
|
74
|
+
supportsBigIntPrecision: boolean;
|
|
54
75
|
supportsComplexFilteredSources: boolean;
|
|
55
76
|
supportsTempTables: boolean;
|
|
56
77
|
hasModOperator: boolean;
|
|
@@ -62,6 +83,20 @@ export declare abstract class Dialect {
|
|
|
62
83
|
compoundObjectInSchema: boolean;
|
|
63
84
|
booleanType: BooleanTypeSupport;
|
|
64
85
|
likeEscape: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Mappings from integer value ranges to Malloy number types.
|
|
88
|
+
*
|
|
89
|
+
* The array is searched in order; the first matching range determines the type.
|
|
90
|
+
* Default: small integers (≤32-bit) → 'integer', larger → 'bigint'.
|
|
91
|
+
*/
|
|
92
|
+
integerTypeMappings: IntegerTypeMapping[];
|
|
93
|
+
/**
|
|
94
|
+
* Determine the Malloy number type for a numeric literal.
|
|
95
|
+
*/
|
|
96
|
+
literalNumberType(value: string): {
|
|
97
|
+
type: 'number';
|
|
98
|
+
numberType: 'integer' | 'float' | 'bigint';
|
|
99
|
+
};
|
|
65
100
|
/**
|
|
66
101
|
* Create the appropriate time literal IR node based on dialect support.
|
|
67
102
|
* Static method so it can be called with undefined dialect (e.g., ConstantFieldSpace).
|
package/dist/dialect/dialect.js
CHANGED
|
@@ -22,10 +22,26 @@
|
|
|
22
22
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.Dialect = exports.dayIndex = void 0;
|
|
25
|
+
exports.Dialect = exports.dayIndex = exports.MAX_DECIMAL38 = exports.MIN_DECIMAL38 = exports.MAX_INT128 = exports.MIN_INT128 = exports.MAX_INT64 = exports.MIN_INT64 = exports.MAX_INT32 = exports.MIN_INT32 = void 0;
|
|
26
26
|
exports.inDays = inDays;
|
|
27
27
|
exports.qtz = qtz;
|
|
28
28
|
const malloy_types_1 = require("../model/malloy_types");
|
|
29
|
+
/*
|
|
30
|
+
* Standard integer type limits.
|
|
31
|
+
* Use these in dialect integerTypeMappings definitions.
|
|
32
|
+
*/
|
|
33
|
+
// 32-bit signed integer limits (for databases with 32-bit INTEGER type)
|
|
34
|
+
exports.MIN_INT32 = -2147483648; // -2^31
|
|
35
|
+
exports.MAX_INT32 = 2147483647; // 2^31 - 1
|
|
36
|
+
// 64-bit signed integer limits
|
|
37
|
+
exports.MIN_INT64 = BigInt('-9223372036854775808'); // -2^63
|
|
38
|
+
exports.MAX_INT64 = BigInt('9223372036854775807'); // 2^63 - 1
|
|
39
|
+
// 128-bit signed integer limits (for DuckDB HUGEINT)
|
|
40
|
+
exports.MIN_INT128 = BigInt('-170141183460469231731687303715884105728'); // -2^127
|
|
41
|
+
exports.MAX_INT128 = BigInt('170141183460469231731687303715884105727'); // 2^127 - 1
|
|
42
|
+
// Decimal(38,0) limits (for Snowflake NUMBER(38,0))
|
|
43
|
+
exports.MIN_DECIMAL38 = BigInt('-99999999999999999999999999999999999999'); // -(10^38 - 1)
|
|
44
|
+
exports.MAX_DECIMAL38 = BigInt('99999999999999999999999999999999999999'); // 10^38 - 1
|
|
29
45
|
const allUnits = [
|
|
30
46
|
'microsecond',
|
|
31
47
|
'millisecond',
|
|
@@ -72,6 +88,9 @@ class Dialect {
|
|
|
72
88
|
this.nullMatchesFunctionSignature = true;
|
|
73
89
|
// support select * replace(...)
|
|
74
90
|
this.supportsSelectReplace = true;
|
|
91
|
+
// Does the data path preserve bigint precision? False for dialects that
|
|
92
|
+
// serialize results through JSON (postgres, presto, trino)
|
|
93
|
+
this.supportsBigIntPrecision = true;
|
|
75
94
|
// ability to join source with a filter on a joined source.
|
|
76
95
|
this.supportsComplexFilteredSources = true;
|
|
77
96
|
// can create temp tables
|
|
@@ -90,6 +109,33 @@ class Dialect {
|
|
|
90
109
|
this.booleanType = 'supported';
|
|
91
110
|
// Like characters are escaped with ESCAPE clause
|
|
92
111
|
this.likeEscape = true;
|
|
112
|
+
/**
|
|
113
|
+
* Mappings from integer value ranges to Malloy number types.
|
|
114
|
+
*
|
|
115
|
+
* The array is searched in order; the first matching range determines the type.
|
|
116
|
+
* Default: small integers (≤32-bit) → 'integer', larger → 'bigint'.
|
|
117
|
+
*/
|
|
118
|
+
this.integerTypeMappings = [
|
|
119
|
+
{ min: BigInt(exports.MIN_INT32), max: BigInt(exports.MAX_INT32), numberType: 'integer' },
|
|
120
|
+
{ min: exports.MIN_INT64, max: exports.MAX_INT64, numberType: 'bigint' },
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Determine the Malloy number type for a numeric literal.
|
|
125
|
+
*/
|
|
126
|
+
literalNumberType(value) {
|
|
127
|
+
const isInteger = /^-?\d+$/.test(value);
|
|
128
|
+
if (!isInteger) {
|
|
129
|
+
return { type: 'number', numberType: 'float' };
|
|
130
|
+
}
|
|
131
|
+
const bigValue = BigInt(value);
|
|
132
|
+
for (const mapping of this.integerTypeMappings) {
|
|
133
|
+
if (bigValue >= mapping.min && bigValue <= mapping.max) {
|
|
134
|
+
return { type: 'number', numberType: mapping.numberType };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Value exceeds all supported ranges - let SQL fail at runtime
|
|
138
|
+
return { type: 'number', numberType: 'bigint' };
|
|
93
139
|
}
|
|
94
140
|
/**
|
|
95
141
|
* Create the appropriate time literal IR node based on dialect support.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Sampling, AtomicTypeDef, RegexMatchExpr, MeasureTimeExpr, BasicAtomicTypeDef, RecordLiteralNode, OrderBy, TimestampUnit } from '../../model/malloy_types';
|
|
2
2
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
3
|
-
import type { DialectFieldList, FieldReferenceType } from '../dialect';
|
|
3
|
+
import type { DialectFieldList, FieldReferenceType, IntegerTypeMapping } from '../dialect';
|
|
4
4
|
import { PostgresBase } from '../pg_impl';
|
|
5
5
|
export declare class DuckDBDialect extends PostgresBase {
|
|
6
6
|
name: string;
|
|
@@ -22,6 +22,7 @@ export declare class DuckDBDialect extends PostgresBase {
|
|
|
22
22
|
supportsSafeCast: boolean;
|
|
23
23
|
supportsNesting: boolean;
|
|
24
24
|
supportsCountApprox: boolean;
|
|
25
|
+
integerTypeMappings: IntegerTypeMapping[];
|
|
25
26
|
get udfPrefix(): string;
|
|
26
27
|
quoteTablePath(tableName: string): string;
|
|
27
28
|
sqlGroupSetTable(groupSetCount: number): string;
|
|
@@ -34,15 +34,16 @@ const tiny_parser_1 = require("../tiny_parser");
|
|
|
34
34
|
// need to refactor runSQL to take a SQLBlock instead of just a sql string.
|
|
35
35
|
const hackSplitComment = '-- hack: split on this';
|
|
36
36
|
const duckDBToMalloyTypes = {
|
|
37
|
-
'BIGINT': { type: 'number', numberType: '
|
|
37
|
+
'BIGINT': { type: 'number', numberType: 'bigint' },
|
|
38
38
|
'INTEGER': { type: 'number', numberType: 'integer' },
|
|
39
39
|
'TINYINT': { type: 'number', numberType: 'integer' },
|
|
40
40
|
'SMALLINT': { type: 'number', numberType: 'integer' },
|
|
41
|
-
'UBIGINT': { type: 'number', numberType: '
|
|
41
|
+
'UBIGINT': { type: 'number', numberType: 'bigint' },
|
|
42
42
|
'UINTEGER': { type: 'number', numberType: 'integer' },
|
|
43
43
|
'UTINYINT': { type: 'number', numberType: 'integer' },
|
|
44
44
|
'USMALLINT': { type: 'number', numberType: 'integer' },
|
|
45
|
-
'HUGEINT': { type: 'number', numberType: '
|
|
45
|
+
'HUGEINT': { type: 'number', numberType: 'bigint' },
|
|
46
|
+
'UHUGEINT': { type: 'number', numberType: 'bigint' },
|
|
46
47
|
'DOUBLE': { type: 'number', numberType: 'float' },
|
|
47
48
|
'FLOAT': { type: 'number', numberType: 'float' },
|
|
48
49
|
'VARCHAR': { type: 'string' },
|
|
@@ -72,6 +73,11 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
72
73
|
this.supportsSafeCast = true;
|
|
73
74
|
this.supportsNesting = true;
|
|
74
75
|
this.supportsCountApprox = true;
|
|
76
|
+
// DuckDB: 32-bit INTEGER is safe, larger integers need bigint
|
|
77
|
+
this.integerTypeMappings = [
|
|
78
|
+
{ min: BigInt(dialect_1.MIN_INT32), max: BigInt(dialect_1.MAX_INT32), numberType: 'integer' },
|
|
79
|
+
{ min: dialect_1.MIN_INT128, max: dialect_1.MAX_INT128, numberType: 'bigint' },
|
|
80
|
+
];
|
|
75
81
|
}
|
|
76
82
|
// hack until they support temporary macros.
|
|
77
83
|
get udfPrefix() {
|
|
@@ -275,6 +281,9 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
275
281
|
if (malloyType.numberType === 'integer') {
|
|
276
282
|
return 'integer';
|
|
277
283
|
}
|
|
284
|
+
else if (malloyType.numberType === 'bigint') {
|
|
285
|
+
return 'hugeint';
|
|
286
|
+
}
|
|
278
287
|
else {
|
|
279
288
|
return 'double precision';
|
|
280
289
|
}
|
|
@@ -46,12 +46,12 @@ const mysqlToMalloyTypes = {
|
|
|
46
46
|
'smallint': { type: 'number', numberType: 'integer' },
|
|
47
47
|
'mediumint': { type: 'number', numberType: 'integer' },
|
|
48
48
|
'int': { type: 'number', numberType: 'integer' },
|
|
49
|
-
'bigint': { type: 'number', numberType: '
|
|
49
|
+
'bigint': { type: 'number', numberType: 'bigint' },
|
|
50
50
|
'tinyint unsigned': { type: 'number', numberType: 'integer' },
|
|
51
51
|
'smallint unsigned': { type: 'number', numberType: 'integer' },
|
|
52
52
|
'mediumint unsigned': { type: 'number', numberType: 'integer' },
|
|
53
53
|
'int unsigned': { type: 'number', numberType: 'integer' },
|
|
54
|
-
'bigint unsigned': { type: 'number', numberType: '
|
|
54
|
+
'bigint unsigned': { type: 'number', numberType: 'bigint' },
|
|
55
55
|
'double': { type: 'number', numberType: 'float' },
|
|
56
56
|
'varchar': { type: 'string' },
|
|
57
57
|
'varbinary': { type: 'string' },
|
|
@@ -68,7 +68,15 @@ const mysqlToMalloyTypes = {
|
|
|
68
68
|
function malloyTypeToJSONTableType(malloyType) {
|
|
69
69
|
switch (malloyType.type) {
|
|
70
70
|
case 'number':
|
|
71
|
-
|
|
71
|
+
if (malloyType.numberType === 'integer') {
|
|
72
|
+
return 'INT';
|
|
73
|
+
}
|
|
74
|
+
else if (malloyType.numberType === 'bigint') {
|
|
75
|
+
return 'BIGINT';
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
return 'DOUBLE';
|
|
79
|
+
}
|
|
72
80
|
case 'string':
|
|
73
81
|
return 'CHAR(255)'; // JSON_TABLE needs a length
|
|
74
82
|
case 'boolean':
|
|
@@ -116,7 +124,13 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
116
124
|
malloyTypeToSQLType(malloyType) {
|
|
117
125
|
switch (malloyType.type) {
|
|
118
126
|
case 'number':
|
|
119
|
-
|
|
127
|
+
if (malloyType.numberType === 'integer' ||
|
|
128
|
+
malloyType.numberType === 'bigint') {
|
|
129
|
+
return 'SIGNED';
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
return 'DOUBLE';
|
|
133
|
+
}
|
|
120
134
|
case 'string':
|
|
121
135
|
return 'CHAR';
|
|
122
136
|
case 'boolean':
|
|
@@ -8,6 +8,7 @@ export declare const timeExtractMap: Record<string, string>;
|
|
|
8
8
|
*/
|
|
9
9
|
export declare abstract class PostgresBase extends Dialect {
|
|
10
10
|
hasTimestamptz: boolean;
|
|
11
|
+
supportsBigIntPrecision: boolean;
|
|
11
12
|
sqlNowExpr(): string;
|
|
12
13
|
sqlTimeExtractExpr(qi: QueryInfo, from: TimeExtractExpr): string;
|
|
13
14
|
sqlCast(qi: QueryInfo, cast: TypecastExpr): string;
|
package/dist/dialect/pg_impl.js
CHANGED
|
@@ -21,6 +21,8 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
21
21
|
constructor() {
|
|
22
22
|
super(...arguments);
|
|
23
23
|
this.hasTimestamptz = true;
|
|
24
|
+
// Postgres-family dialects use JSON serialization which loses bigint precision
|
|
25
|
+
this.supportsBigIntPrecision = false;
|
|
24
26
|
}
|
|
25
27
|
sqlNowExpr() {
|
|
26
28
|
return 'LOCALTIMESTAMP';
|
|
@@ -53,7 +53,7 @@ const postgresToMalloyTypes = {
|
|
|
53
53
|
'text': { type: 'string' },
|
|
54
54
|
'date': { type: 'date' },
|
|
55
55
|
'integer': { type: 'number', numberType: 'integer' },
|
|
56
|
-
'bigint': { type: 'number', numberType: '
|
|
56
|
+
'bigint': { type: 'number', numberType: 'bigint' },
|
|
57
57
|
'double precision': { type: 'number', numberType: 'float' },
|
|
58
58
|
'timestamp without time zone': { type: 'timestamp' },
|
|
59
59
|
'timestamp with time zone': { type: 'timestamptz' },
|
|
@@ -321,6 +321,9 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
|
|
|
321
321
|
if (malloyType.numberType === 'integer') {
|
|
322
322
|
return 'integer';
|
|
323
323
|
}
|
|
324
|
+
else if (malloyType.numberType === 'bigint') {
|
|
325
|
+
return 'bigint';
|
|
326
|
+
}
|
|
324
327
|
else {
|
|
325
328
|
return 'double precision';
|
|
326
329
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Sampling, AtomicTypeDef, TimeExtractExpr, TypecastExpr, MeasureTimeExpr, RegexMatchExpr, BasicAtomicTypeDef, TimestampTypeDef, ArrayLiteralNode, RecordLiteralNode } from '../../model/malloy_types';
|
|
2
2
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
3
|
-
import type { DialectFieldList, FieldReferenceType, QueryInfo } from '../dialect';
|
|
3
|
+
import type { DialectFieldList, FieldReferenceType, IntegerTypeMapping, QueryInfo } from '../dialect';
|
|
4
4
|
import { Dialect } from '../dialect';
|
|
5
5
|
export declare class SnowflakeDialect extends Dialect {
|
|
6
6
|
name: string;
|
|
@@ -26,6 +26,7 @@ export declare class SnowflakeDialect extends Dialect {
|
|
|
26
26
|
supportsQualify: boolean;
|
|
27
27
|
supportsPipelinesInViews: boolean;
|
|
28
28
|
supportsComplexFilteredSources: boolean;
|
|
29
|
+
integerTypeMappings: IntegerTypeMapping[];
|
|
29
30
|
quoteTablePath(tablePath: string): string;
|
|
30
31
|
sqlGroupSetTable(groupSetCount: number): string;
|
|
31
32
|
sqlAnyValue(groupSet: number, fieldName: string): string;
|
|
@@ -45,17 +45,15 @@ const snowflakeToMalloyTypes = {
|
|
|
45
45
|
'nvarchar2': { type: 'string' },
|
|
46
46
|
'char varying': { type: 'string' },
|
|
47
47
|
'nchar varying': { type: 'string' },
|
|
48
|
-
// numbers
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'tinyint': { type: 'number', numberType: 'integer' },
|
|
58
|
-
'byteint': { type: 'number', numberType: 'integer' },
|
|
48
|
+
// numbers - Snowflake uses NUMBER(38,0) for all integers, which exceeds 64-bit
|
|
49
|
+
// NUMBER, NUMERIC, DECIMAL, DEC are handled dynamically in sqlTypeToMalloyType
|
|
50
|
+
// because they can be integers or floats depending on scale.
|
|
51
|
+
'integer': { type: 'number', numberType: 'bigint' },
|
|
52
|
+
'int': { type: 'number', numberType: 'bigint' },
|
|
53
|
+
'bigint': { type: 'number', numberType: 'bigint' },
|
|
54
|
+
'smallint': { type: 'number', numberType: 'bigint' },
|
|
55
|
+
'tinyint': { type: 'number', numberType: 'bigint' },
|
|
56
|
+
'byteint': { type: 'number', numberType: 'bigint' },
|
|
59
57
|
'float': { type: 'number', numberType: 'float' },
|
|
60
58
|
'float4': { type: 'number', numberType: 'float' },
|
|
61
59
|
'float8': { type: 'number', numberType: 'float' },
|
|
@@ -100,6 +98,10 @@ class SnowflakeDialect extends dialect_1.Dialect {
|
|
|
100
98
|
this.supportsQualify = false;
|
|
101
99
|
this.supportsPipelinesInViews = false;
|
|
102
100
|
this.supportsComplexFilteredSources = false;
|
|
101
|
+
// Snowflake uses NUMBER(38,0) for all integers - can exceed JS Number precision
|
|
102
|
+
this.integerTypeMappings = [
|
|
103
|
+
{ min: dialect_1.MIN_DECIMAL38, max: dialect_1.MAX_DECIMAL38, numberType: 'bigint' },
|
|
104
|
+
];
|
|
103
105
|
}
|
|
104
106
|
// don't mess with the table pathing.
|
|
105
107
|
quoteTablePath(tablePath) {
|
|
@@ -434,8 +436,9 @@ ${(0, utils_1.indent)(sql)}
|
|
|
434
436
|
return 'VARCHAR';
|
|
435
437
|
}
|
|
436
438
|
else if (malloyType.type === 'number') {
|
|
437
|
-
if (malloyType.numberType === 'integer'
|
|
438
|
-
|
|
439
|
+
if (malloyType.numberType === 'integer' ||
|
|
440
|
+
malloyType.numberType === 'bigint') {
|
|
441
|
+
return 'NUMBER';
|
|
439
442
|
}
|
|
440
443
|
else {
|
|
441
444
|
return 'DOUBLE';
|
|
@@ -468,10 +471,28 @@ ${(0, utils_1.indent)(sql)}
|
|
|
468
471
|
var _a, _b;
|
|
469
472
|
// Remove trailing params
|
|
470
473
|
const baseSqlType = (_b = (_a = sqlType.match(/^([\w\s]+)/)) === null || _a === void 0 ? void 0 : _a.at(0)) !== null && _b !== void 0 ? _b : sqlType;
|
|
471
|
-
|
|
474
|
+
const lowerType = baseSqlType.trim().toLowerCase();
|
|
475
|
+
const mapped = snowflakeToMalloyTypes[lowerType];
|
|
476
|
+
if (mapped) {
|
|
477
|
+
return mapped;
|
|
478
|
+
}
|
|
479
|
+
// Handle NUMBER/NUMERIC/DECIMAL with scale
|
|
480
|
+
// If scale > 0, it's a float (decimal). If scale == 0 or omitted, it's a bigint (integer).
|
|
481
|
+
if (['number', 'numeric', 'decimal', 'dec'].includes(lowerType)) {
|
|
482
|
+
const match = sqlType.match(/\(\s*\d+\s*,\s*(\d+)\s*\)/);
|
|
483
|
+
if (match) {
|
|
484
|
+
const scale = parseInt(match[1], 10);
|
|
485
|
+
if (scale > 0) {
|
|
486
|
+
return { type: 'number', numberType: 'float' };
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Default to bigint if scale is 0 or not specified (Snowflake defaults to NUMBER(38,0))
|
|
490
|
+
return { type: 'number', numberType: 'bigint' };
|
|
491
|
+
}
|
|
492
|
+
return {
|
|
472
493
|
type: 'sql native',
|
|
473
494
|
rawType: sqlType,
|
|
474
|
-
}
|
|
495
|
+
};
|
|
475
496
|
}
|
|
476
497
|
castToString(expression) {
|
|
477
498
|
return `TO_VARCHAR(${expression})`;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Sampling, AtomicTypeDef, TimeExtractExpr, TypecastExpr, RegexMatchExpr, MeasureTimeExpr, BasicAtomicTypeDef, RecordLiteralNode, ArrayLiteralNode, TimestampUnit, TimestampTypeDef } from '../../model/malloy_types';
|
|
2
2
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
3
|
-
import type { DialectFieldList, OrderByRequest, QueryInfo } from '../dialect';
|
|
3
|
+
import type { DialectFieldList, IntegerTypeMapping, OrderByRequest, QueryInfo } from '../dialect';
|
|
4
4
|
import { Dialect } from '../dialect';
|
|
5
5
|
export declare class StandardSQLDialect extends Dialect {
|
|
6
6
|
name: string;
|
|
@@ -27,6 +27,7 @@ export declare class StandardSQLDialect extends Dialect {
|
|
|
27
27
|
nestedArrays: boolean;
|
|
28
28
|
supportsHyperLogLog: boolean;
|
|
29
29
|
likeEscape: boolean;
|
|
30
|
+
integerTypeMappings: IntegerTypeMapping[];
|
|
30
31
|
quoteTablePath(tablePath: string): string;
|
|
31
32
|
needsCivilTimeComputation(typeDef: AtomicTypeDef, truncateTo: TimestampUnit | undefined, offsetUnit: TimestampUnit | undefined, qi: QueryInfo): {
|
|
32
33
|
needed: boolean;
|