@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.
Files changed (53) hide show
  1. package/dist/api/core.js +2 -0
  2. package/dist/api/index.d.ts +1 -0
  3. package/dist/api/index.js +1 -0
  4. package/dist/api/row_data_utils.d.ts +30 -0
  5. package/dist/api/row_data_utils.js +87 -0
  6. package/dist/api/util.js +20 -38
  7. package/dist/connection/base_connection.js +5 -3
  8. package/dist/dialect/dialect.d.ts +36 -0
  9. package/dist/dialect/dialect.js +28 -1
  10. package/dist/dialect/duckdb/duckdb.d.ts +2 -1
  11. package/dist/dialect/duckdb/duckdb.js +12 -3
  12. package/dist/dialect/mysql/mysql.js +18 -4
  13. package/dist/dialect/pg_impl.d.ts +1 -0
  14. package/dist/dialect/pg_impl.js +2 -0
  15. package/dist/dialect/postgres/postgres.js +4 -1
  16. package/dist/dialect/snowflake/snowflake.d.ts +2 -1
  17. package/dist/dialect/snowflake/snowflake.js +37 -15
  18. package/dist/dialect/standardsql/standardsql.d.ts +2 -1
  19. package/dist/dialect/standardsql/standardsql.js +9 -3
  20. package/dist/dialect/trino/trino.js +10 -2
  21. package/dist/lang/ast/expressions/expr-avg.d.ts +5 -0
  22. package/dist/lang/ast/expressions/expr-avg.js +9 -0
  23. package/dist/lang/ast/expressions/expr-coalesce.js +6 -0
  24. package/dist/lang/ast/expressions/expr-number.d.ts +14 -1
  25. package/dist/lang/ast/expressions/expr-number.js +71 -6
  26. package/dist/lang/ast/expressions/expr-props.d.ts +1 -1
  27. package/dist/lang/ast/expressions/pick-when.js +10 -3
  28. package/dist/lang/ast/source-properties/renames.js +5 -0
  29. package/dist/lang/ast/types/expression-def.js +36 -2
  30. package/dist/lang/parse-log.d.ts +1 -0
  31. package/dist/lang/test/test-translator.js +2 -0
  32. package/dist/malloy.d.ts +23 -2
  33. package/dist/malloy.js +204 -41
  34. package/dist/model/malloy_types.d.ts +2 -2
  35. package/dist/model/query_model_impl.js +5 -1
  36. package/dist/test/cellsToObject.d.ts +6 -0
  37. package/dist/test/cellsToObject.js +111 -0
  38. package/dist/test/index.d.ts +7 -0
  39. package/dist/test/index.js +16 -20
  40. package/dist/test/matchers.d.ts +10 -0
  41. package/dist/test/matchers.js +17 -0
  42. package/dist/test/resultMatchers.d.ts +42 -0
  43. package/dist/test/resultMatchers.js +722 -0
  44. package/dist/test/runQuery.d.ts +31 -0
  45. package/dist/test/runQuery.js +67 -0
  46. package/dist/test/test-models.d.ts +77 -0
  47. package/dist/test/test-models.js +319 -0
  48. package/dist/test/test-values.d.ts +66 -0
  49. package/dist/test/test-values.js +208 -0
  50. package/dist/to_stable.js +3 -1
  51. package/dist/version.d.ts +1 -1
  52. package/dist/version.js +1 -1
  53. package/package.json +9 -5
package/dist/api/core.js CHANGED
@@ -89,6 +89,8 @@ function convertNumberSubtype(subtype) {
89
89
  return undefined;
90
90
  if (subtype === 'decimal')
91
91
  return 'float';
92
+ if (subtype === 'bigint')
93
+ return 'bigint';
92
94
  return 'integer';
93
95
  }
94
96
  function typeDefFromField(type) {
@@ -3,3 +3,4 @@ export * as stateless from './stateless';
3
3
  export * as asynchronous from './asynchronous';
4
4
  export * from './connection';
5
5
  export * as util from './util';
6
+ export * from './row_data_utils';
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 luxon_1 = require("luxon");
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 = valueToDate(value).toISOString();
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
- if (typeof value !== 'number') {
115
- throw new Error(`Invalid number ${value}`);
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 { kind: 'number_cell', number_value: value };
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.toObject(), schema),
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
- cached = { error: cacheResponse };
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
- cached = { error: uncaught.message };
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).
@@ -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: 'integer' },
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: 'integer' },
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: 'integer' },
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: 'integer' },
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: 'integer' },
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
- return malloyType.numberType === 'integer' ? 'INT' : 'DOUBLE';
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
- return malloyType.numberType === 'integer' ? 'SIGNED' : 'DOUBLE';
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;
@@ -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: 'integer' },
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
- 'number': { type: 'number', numberType: 'integer' },
50
- 'numeric': { type: 'number', numberType: 'integer' },
51
- 'decimal': { type: 'number', numberType: 'integer' },
52
- 'dec': { type: 'number', numberType: 'integer' },
53
- 'integer': { type: 'number', numberType: 'integer' },
54
- 'int': { type: 'number', numberType: 'integer' },
55
- 'bigint': { type: 'number', numberType: 'integer' },
56
- 'smallint': { type: 'number', numberType: 'integer' },
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
- return 'INTEGER';
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
- return (snowflakeToMalloyTypes[baseSqlType.trim().toLowerCase()] || {
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;