@malloydata/malloy 0.0.325 → 0.0.327
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/connection/base_connection.js +5 -3
- package/dist/dialect/dialect.d.ts +36 -0
- package/dist/dialect/dialect.js +28 -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 +37 -15
- package/dist/dialect/standardsql/standardsql.d.ts +2 -1
- package/dist/dialect/standardsql/standardsql.js +9 -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 +14 -1
- package/dist/lang/ast/expressions/expr-number.js +71 -6
- 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/source-properties/renames.js +5 -0
- 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 +7 -0
- package/dist/test/index.js +16 -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,
|
|
@@ -19,19 +19,21 @@ class BaseConnection {
|
|
|
19
19
|
try {
|
|
20
20
|
const cacheResponse = await fillCache();
|
|
21
21
|
if (typeof cacheResponse === 'string') {
|
|
22
|
-
|
|
22
|
+
// Don't cache errors - just return them
|
|
23
|
+
return { error: cacheResponse };
|
|
23
24
|
}
|
|
24
25
|
else {
|
|
25
26
|
cached = {
|
|
26
27
|
schema: cacheResponse,
|
|
27
28
|
timestamp: refreshTimestamp !== null && refreshTimestamp !== void 0 ? refreshTimestamp : Date.now(),
|
|
28
29
|
};
|
|
30
|
+
this.schemaCache[schemaKey] = cached;
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
catch (uncaught) {
|
|
32
|
-
|
|
34
|
+
// Don't cache errors - just return them
|
|
35
|
+
return { error: uncaught.message };
|
|
33
36
|
}
|
|
34
|
-
this.schemaCache[schemaKey] = cached;
|
|
35
37
|
}
|
|
36
38
|
if (cached.error) {
|
|
37
39
|
return cached;
|
|
@@ -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,28 @@ 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
|
+
* Min/max range for an integer type.
|
|
38
|
+
* - null: type is not supported by this dialect
|
|
39
|
+
* - {min, max}: the range of values (use the exported constants like MIN_INT64, MAX_INT64)
|
|
40
|
+
*/
|
|
41
|
+
export type IntegerTypeRange = {
|
|
42
|
+
min: number | bigint;
|
|
43
|
+
max: number | bigint;
|
|
44
|
+
} | null;
|
|
45
|
+
/**
|
|
46
|
+
* Configuration for integer literal type selection.
|
|
47
|
+
*
|
|
48
|
+
* When translating an integer literal, the translator walks down this list
|
|
49
|
+
* (integer → bigint) and selects the first type that can hold the value.
|
|
50
|
+
* If no type can hold the value (or all are null), an error is raised.
|
|
51
|
+
*/
|
|
52
|
+
export interface IntegerTypeLimits {
|
|
53
|
+
/** Range for 'integer' type (-2^53 to 2^53-1, safe for JS Number) */
|
|
54
|
+
integer: IntegerTypeRange;
|
|
55
|
+
/** Range for 'bigint' type (64-bit or larger integers, varies by dialect) */
|
|
56
|
+
bigint: IntegerTypeRange;
|
|
57
|
+
}
|
|
28
58
|
export declare abstract class Dialect {
|
|
29
59
|
abstract name: string;
|
|
30
60
|
abstract defaultNumberType: string;
|
|
@@ -51,6 +81,7 @@ export declare abstract class Dialect {
|
|
|
51
81
|
orderByClause: OrderByClauseType;
|
|
52
82
|
nullMatchesFunctionSignature: boolean;
|
|
53
83
|
supportsSelectReplace: boolean;
|
|
84
|
+
supportsBigIntPrecision: boolean;
|
|
54
85
|
supportsComplexFilteredSources: boolean;
|
|
55
86
|
supportsTempTables: boolean;
|
|
56
87
|
hasModOperator: boolean;
|
|
@@ -62,6 +93,11 @@ export declare abstract class Dialect {
|
|
|
62
93
|
compoundObjectInSchema: boolean;
|
|
63
94
|
booleanType: BooleanTypeSupport;
|
|
64
95
|
likeEscape: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Ranges for integer types in this dialect.
|
|
98
|
+
* Default supports integer (JS safe) and bigint (64-bit signed).
|
|
99
|
+
*/
|
|
100
|
+
integerTypeLimits: IntegerTypeLimits;
|
|
65
101
|
/**
|
|
66
102
|
* Create the appropriate time literal IR node based on dialect support.
|
|
67
103
|
* 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 integerTypeLimits 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,14 @@ class Dialect {
|
|
|
90
109
|
this.booleanType = 'supported';
|
|
91
110
|
// Like characters are escaped with ESCAPE clause
|
|
92
111
|
this.likeEscape = true;
|
|
112
|
+
/**
|
|
113
|
+
* Ranges for integer types in this dialect.
|
|
114
|
+
* Default supports integer (JS safe) and bigint (64-bit signed).
|
|
115
|
+
*/
|
|
116
|
+
this.integerTypeLimits = {
|
|
117
|
+
integer: { min: Number.MIN_SAFE_INTEGER, max: Number.MAX_SAFE_INTEGER },
|
|
118
|
+
bigint: { min: exports.MIN_INT64, max: exports.MAX_INT64 },
|
|
119
|
+
};
|
|
93
120
|
}
|
|
94
121
|
/**
|
|
95
122
|
* 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, IntegerTypeLimits } 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
|
+
integerTypeLimits: IntegerTypeLimits;
|
|
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 supports HUGEINT (128-bit signed integer)
|
|
77
|
+
this.integerTypeLimits = {
|
|
78
|
+
integer: { min: dialect_1.MIN_INT32, max: dialect_1.MAX_INT32 },
|
|
79
|
+
bigint: { min: dialect_1.MIN_INT128, max: dialect_1.MAX_INT128 },
|
|
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, IntegerTypeLimits, 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
|
+
integerTypeLimits: IntegerTypeLimits;
|
|
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,11 @@ 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 - only one integer type
|
|
102
|
+
this.integerTypeLimits = {
|
|
103
|
+
integer: { min: dialect_1.MIN_DECIMAL38, max: dialect_1.MAX_DECIMAL38 },
|
|
104
|
+
bigint: null,
|
|
105
|
+
};
|
|
103
106
|
}
|
|
104
107
|
// don't mess with the table pathing.
|
|
105
108
|
quoteTablePath(tablePath) {
|
|
@@ -434,8 +437,9 @@ ${(0, utils_1.indent)(sql)}
|
|
|
434
437
|
return 'VARCHAR';
|
|
435
438
|
}
|
|
436
439
|
else if (malloyType.type === 'number') {
|
|
437
|
-
if (malloyType.numberType === 'integer'
|
|
438
|
-
|
|
440
|
+
if (malloyType.numberType === 'integer' ||
|
|
441
|
+
malloyType.numberType === 'bigint') {
|
|
442
|
+
return 'NUMBER';
|
|
439
443
|
}
|
|
440
444
|
else {
|
|
441
445
|
return 'DOUBLE';
|
|
@@ -468,10 +472,28 @@ ${(0, utils_1.indent)(sql)}
|
|
|
468
472
|
var _a, _b;
|
|
469
473
|
// Remove trailing params
|
|
470
474
|
const baseSqlType = (_b = (_a = sqlType.match(/^([\w\s]+)/)) === null || _a === void 0 ? void 0 : _a.at(0)) !== null && _b !== void 0 ? _b : sqlType;
|
|
471
|
-
|
|
475
|
+
const lowerType = baseSqlType.trim().toLowerCase();
|
|
476
|
+
const mapped = snowflakeToMalloyTypes[lowerType];
|
|
477
|
+
if (mapped) {
|
|
478
|
+
return mapped;
|
|
479
|
+
}
|
|
480
|
+
// Handle NUMBER/NUMERIC/DECIMAL with scale
|
|
481
|
+
// If scale > 0, it's a float (decimal). If scale == 0 or omitted, it's a bigint (integer).
|
|
482
|
+
if (['number', 'numeric', 'decimal', 'dec'].includes(lowerType)) {
|
|
483
|
+
const match = sqlType.match(/\(\s*\d+\s*,\s*(\d+)\s*\)/);
|
|
484
|
+
if (match) {
|
|
485
|
+
const scale = parseInt(match[1], 10);
|
|
486
|
+
if (scale > 0) {
|
|
487
|
+
return { type: 'number', numberType: 'float' };
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// Default to bigint if scale is 0 or not specified (Snowflake defaults to NUMBER(38,0))
|
|
491
|
+
return { type: 'number', numberType: 'bigint' };
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
472
494
|
type: 'sql native',
|
|
473
495
|
rawType: sqlType,
|
|
474
|
-
}
|
|
496
|
+
};
|
|
475
497
|
}
|
|
476
498
|
castToString(expression) {
|
|
477
499
|
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, IntegerTypeLimits, 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
|
+
integerTypeLimits: IntegerTypeLimits;
|
|
30
31
|
quoteTablePath(tablePath: string): string;
|
|
31
32
|
needsCivilTimeComputation(typeDef: AtomicTypeDef, truncateTo: TimestampUnit | undefined, offsetUnit: TimestampUnit | undefined, qi: QueryInfo): {
|
|
32
33
|
needed: boolean;
|