diff --git a/packages/malloy-db-trino/src/trino_connection.ts b/packages/malloy-db-trino/src/trino_connection.ts index f58b0f58b..1ea18a6b5 100644 --- a/packages/malloy-db-trino/src/trino_connection.ts +++ b/packages/malloy-db-trino/src/trino_connection.ts @@ -38,15 +38,14 @@ import { SQLSourceDef, AtomicTypeDef, mkFieldDef, - isScalarArrayType, + isScalarArray, RepeatedRecordTypeDef, RecordTypeDef, - isRepeatedRecord, Dialect, ArrayTypeDef, FieldDef, TinyParser, - isRepeatedRecordType, + isRepeatedRecord, } from '@malloydata/malloy'; import {BaseConnection} from '@malloydata/malloy/connection'; @@ -298,9 +297,9 @@ export abstract class TrinoPrestoConnection private resultRow(colSchema: AtomicTypeDef, rawRow: unknown) { if (colSchema.type === 'record') { return this.convertRow(colSchema.fields, rawRow); - } else if (isRepeatedRecordType(colSchema)) { + } else if (isRepeatedRecord(colSchema)) { return this.convertNest(colSchema.fields, rawRow) as QueryValue; - } else if (isScalarArrayType(colSchema)) { + } else if (isScalarArray(colSchema)) { const elType = colSchema.elementTypeDef; let theArray = this.unpackArray(rawRow); if (elType.type === 'array') { diff --git a/packages/malloy/src/dialect/snowflake/snowflake.ts b/packages/malloy/src/dialect/snowflake/snowflake.ts index a1168935e..c0328c36e 100644 --- a/packages/malloy/src/dialect/snowflake/snowflake.ts +++ b/packages/malloy/src/dialect/snowflake/snowflake.ts @@ -40,8 +40,8 @@ import { ArrayLiteralNode, RecordLiteralNode, isAtomic, - isRepeatedRecordType, - isScalarArrayType, + isRepeatedRecord, + isScalarArray, } from '../../model/malloy_types'; import { DialectFunctionOverloadDef, @@ -488,10 +488,7 @@ ${indent(sql)} } else { return 'DOUBLE'; } - } else if ( - malloyType.type === 'record' || - isRepeatedRecordType(malloyType) - ) { + } else if (malloyType.type === 'record' || isRepeatedRecord(malloyType)) { const sqlFields = malloyType.fields.reduce((ret, f) => { if (isAtomic(f)) { const name = f.as ?? f.name; @@ -506,7 +503,7 @@ ${indent(sql)} return malloyType.type === 'record' ? recordScehma : `ARRAY(${recordScehma})`; - } else if (isScalarArrayType(malloyType)) { + } else if (isScalarArray(malloyType)) { return `ARRAY(${this.malloyTypeToSQLType(malloyType.elementTypeDef)})`; } return malloyType.type; diff --git a/packages/malloy/src/doc/fielddef.md b/packages/malloy/src/doc/fielddef.md index adde0595e..49b4c7a46 100644 --- a/packages/malloy/src/doc/fielddef.md +++ b/packages/malloy/src/doc/fielddef.md @@ -72,8 +72,7 @@ interface JoinBase { } ``` -MTOY TODO FIX THE DISCRIMINATORS AGAIN -* `isJoined(fd)` which will return true and grant typed access to the `JoinBase` properties of the `FieldDef`, and because all joined fields are structs, also the `StructDef` properties as well. +* `isJoined(def)` which will return true and grant typed access to the `JoinBase` properties of the object, and because all joined fields are structs, also the `StructDef` properties as well. ## Views @@ -92,11 +91,11 @@ are an array of ... * `join_XXX:` always on query joins -## Descriminators +## Discriminators -* `isTemporalField` -- `date` or `timestamp` type -* `isAtomicFieldType` -- Does the data in this field fit in one column of a table -* `isRepeatedRecord(FieldDef)` -- In some databases this is a type, in other this is an array of record -* `isScalarArray(FieldDef|SrtuctDef)` -- Is a ".each" array -* `isAtomic(FieldDef)` -- Like `isAtomicFieldType` for `FieldDef` instead -* `isLeafAtomic(FieldDef | QueryFieldDef)` -- an Atomic field can be stored in a column, a LeafAtomic is one which isn't a join \ No newline at end of file +* `isTemporalType` -- `date` or `timestamp` type +* `isAtomicFieldType` -- Does type string match the type of one of the atomiv types +* `isRepeatedRecord` -- In some databases this is a type, in other this is an array of record +* `isScalarArray` -- Is a ".each" array +* `isAtomic` -- Like `isAtomicFieldType` for `FieldDef` instead +* `isLeafAtomic` -- an Atomic field can be stored in a column, a LeafAtomic is one which isn't a join \ No newline at end of file diff --git a/packages/malloy/src/index.ts b/packages/malloy/src/index.ts index 773a543a7..034139fad 100644 --- a/packages/malloy/src/index.ts +++ b/packages/malloy/src/index.ts @@ -117,18 +117,16 @@ export type { ArrayLiteralNode, } from './model'; export { - isRepeatedRecord, isSourceDef, // Used in Composer Demo Segment, isLeafAtomic, - isJoinedField, + isJoined, isJoinedSource, isSamplingEnable, isSamplingPercent, isSamplingRows, - isRepeatedRecordType, - isScalarArrayType, + isRepeatedRecord, isScalarArray, mkArrayDef, mkFieldDef, diff --git a/packages/malloy/src/lang/ast/expressions/expr-aggregate-function.ts b/packages/malloy/src/lang/ast/expressions/expr-aggregate-function.ts index 49b475b8d..a76da7147 100644 --- a/packages/malloy/src/lang/ast/expressions/expr-aggregate-function.ts +++ b/packages/malloy/src/lang/ast/expressions/expr-aggregate-function.ts @@ -29,8 +29,8 @@ import { AggregateExpr, Expr, hasExpression, - isJoinedField, isAtomic, + isJoined, } from '../../../model/malloy_types'; import {exprWalk} from '../../../model/utils'; @@ -267,7 +267,7 @@ function getJoinUsage(fs: FieldSpace, expr: Expr): JoinPath[] { if (frag.node === 'field') { const def = lookupWithPath(fs, frag.path); const field = def.def; - if (isAtomic(field) && !isJoinedField(field)) { + if (isAtomic(field) && !isJoined(field)) { if (hasExpression(field)) { const defUsage = getJoinUsage(def.fs, field.e); result.push(...defUsage.map(r => [...def.joinPath, ...r])); diff --git a/packages/malloy/src/lang/ast/expressions/expr-time-extract.ts b/packages/malloy/src/lang/ast/expressions/expr-time-extract.ts index 0ad933293..947c3183c 100644 --- a/packages/malloy/src/lang/ast/expressions/expr-time-extract.ts +++ b/packages/malloy/src/lang/ast/expressions/expr-time-extract.ts @@ -24,7 +24,7 @@ import { ExtractUnit, isExtractUnit, - isTemporalField, + isTemporalType, isTimestampUnit, mkTemporal, TD, @@ -89,13 +89,13 @@ export class ExprTimeExtract extends ExpressionDef { from: [first, last], }); } - if (!isTemporalField(first.type)) { + if (!isTemporalType(first.type)) { return from.first.loggedErrorExpr( 'invalid-type-for-time-extraction', `Can't extract ${extractTo} from '${first.type}'` ); } - if (!isTemporalField(last.type)) { + if (!isTemporalType(last.type)) { return from.last.loggedErrorExpr( 'invalid-type-for-time-extraction', `Cannot extract ${extractTo} from '${last.type}'` @@ -151,7 +151,7 @@ export class ExprTimeExtract extends ExpressionDef { }); } else { const argV = from.getExpression(fs); - if (isTemporalField(argV.type)) { + if (isTemporalType(argV.type)) { return computedExprValue({ dataType: {type: 'number', numberType: 'integer'}, value: { diff --git a/packages/malloy/src/lang/ast/expressions/expr-time.ts b/packages/malloy/src/lang/ast/expressions/expr-time.ts index a965bcc2a..78d305c57 100644 --- a/packages/malloy/src/lang/ast/expressions/expr-time.ts +++ b/packages/malloy/src/lang/ast/expressions/expr-time.ts @@ -25,7 +25,7 @@ import { Expr, TemporalFieldType, TypecastExpr, - isTemporalField, + isTemporalType, } from '../../../model/malloy_types'; import {FieldSpace} from '../types/field-space'; @@ -58,7 +58,7 @@ export class ExprTime extends ExpressionDef { dstType: {type: timeType}, e: expr.value, }; - if (isTemporalField(expr.type)) { + if (isTemporalType(expr.type)) { toTs.srcType = {type: expr.type}; } value = toTs; diff --git a/packages/malloy/src/lang/ast/expressions/time-literal.ts b/packages/malloy/src/lang/ast/expressions/time-literal.ts index d8711ceaa..a064819dc 100644 --- a/packages/malloy/src/lang/ast/expressions/time-literal.ts +++ b/packages/malloy/src/lang/ast/expressions/time-literal.ts @@ -26,7 +26,7 @@ import {DateTime as LuxonDateTime} from 'luxon'; import { TemporalFieldType, TimestampUnit, - isTemporalField, + isTemporalType, TimeLiteralNode, } from '../../../model/malloy_types'; @@ -206,7 +206,7 @@ class GranularLiteral extends TimeLiteral { } // Compiler is unsure about rangeEnd = newEnd for some reason - if (rangeEnd && isTemporalField(testValue.type)) { + if (rangeEnd && isTemporalType(testValue.type)) { const rangeType = testValue.type; const range = new Range( new ExprTime(rangeType, rangeStart.value), diff --git a/packages/malloy/src/lang/ast/field-space/static-space.ts b/packages/malloy/src/lang/ast/field-space/static-space.ts index 8c9cdc44a..7f45f3c83 100644 --- a/packages/malloy/src/lang/ast/field-space/static-space.ts +++ b/packages/malloy/src/lang/ast/field-space/static-space.ts @@ -27,8 +27,8 @@ import { FieldDef, StructDef, SourceDef, - isJoinedField, - isTurtleDef, + isJoined, + isTurtle, isSourceDef, JoinFieldDef, } from '../../../model/malloy_types'; @@ -71,9 +71,9 @@ export class StaticSpace implements FieldSpace { } defToSpaceField(from: FieldDef): SpaceField { - if (isJoinedField(from)) { + if (isJoined(from)) { return new StructSpaceField(from); - } else if (isTurtleDef(from)) { + } else if (isTurtle(from)) { return new IRViewField(this, from); } return new ColumnSpaceField(from); @@ -151,10 +151,7 @@ export class StaticSpace implements FieldSpace { if (found instanceof SpaceField) { const definition = found.fieldDef(); if (definition) { - if ( - !(found instanceof StructSpaceFieldBase) && - isJoinedField(definition) - ) { + if (!(found instanceof StructSpaceFieldBase) && isJoined(definition)) { // We have looked up a field which is a join, but not a StructSpaceField // because it is someting like "dimension: joinedArray is arrayComputation" // which wasn't known to be a join when the fieldspace was constructed. diff --git a/packages/malloy/src/lang/ast/query-builders/reduce-builder.ts b/packages/malloy/src/lang/ast/query-builders/reduce-builder.ts index 6441ef8c9..4b6cb5cf9 100644 --- a/packages/malloy/src/lang/ast/query-builders/reduce-builder.ts +++ b/packages/malloy/src/lang/ast/query-builders/reduce-builder.ts @@ -36,7 +36,7 @@ import { isPartialSegment, isQuerySegment, isReduceSegment, - isTemporalField, + isTemporalType, } from '../../../model/malloy_types'; import {ErrorFactory} from '../error-factory'; @@ -215,7 +215,7 @@ export class ReduceBuilder extends QuerySegmentBuilder implements QueryBuilder { fieldAnalytic = hasExpression(field) && expressionIsAnalytic(field.expressionType); } - if (isTemporalField(fieldType) || fieldAggregate) { + if (isTemporalType(fieldType) || fieldAggregate) { reduceSegment.defaultOrderBy = true; reduceSegment.orderBy = [{field: fieldName, dir: 'desc'}]; usableDefaultOrderField = undefined; diff --git a/packages/malloy/src/lang/ast/typedesc-utils.ts b/packages/malloy/src/lang/ast/typedesc-utils.ts index 67e28c30a..75f789068 100644 --- a/packages/malloy/src/lang/ast/typedesc-utils.ts +++ b/packages/malloy/src/lang/ast/typedesc-utils.ts @@ -27,7 +27,7 @@ import { expressionIsScalar, ExpressionType, ExpressionValueType, - isRepeatedRecordType, + isRepeatedRecord, TD, TypeDesc, } from '../../model'; @@ -145,7 +145,7 @@ export function atomicDef(td: AtomicTypeDef | TypeDesc): AtomicTypeDef { if (TD.isAtomic(td)) { switch (td.type) { case 'array': { - return isRepeatedRecordType(td) + return isRepeatedRecord(td) ? { type: 'array', elementTypeDef: td.elementTypeDef, diff --git a/packages/malloy/src/lang/ast/types/expression-def.ts b/packages/malloy/src/lang/ast/types/expression-def.ts index e76f4853a..89346a553 100644 --- a/packages/malloy/src/lang/ast/types/expression-def.ts +++ b/packages/malloy/src/lang/ast/types/expression-def.ts @@ -25,7 +25,7 @@ import { Expr, TimestampUnit, isDateUnit, - isTemporalField, + isTemporalType, expressionIsAggregate, TD, LeafExpressionType, @@ -181,7 +181,7 @@ export class ExprDuration extends ExpressionDef { ): ExprValue { const lhs = left.getExpression(fs); this.typeCheck(this, lhs); - if (isTemporalField(lhs.type) && (op === '+' || op === '-')) { + if (isTemporalType(lhs.type) && (op === '+' || op === '-')) { const num = this.n.getExpression(fs); if (!TDU.typeEq(num, TDU.numberT)) { this.logError( @@ -253,8 +253,8 @@ function timeCompare( op: CompareMalloyOperator, rhs: ExprValue ): Expr | undefined { - const leftIsTime = isTemporalField(lhs.type); - const rightIsTime = isTemporalField(rhs.type); + const leftIsTime = isTemporalType(lhs.type); + const rightIsTime = isTemporalType(rhs.type); const node = getExprNode(op); if (leftIsTime && rightIsTime) { if (lhs.type !== rhs.type) { @@ -459,7 +459,7 @@ function delta( return noGo; } - const timeLHS = isTemporalField(lhs.type); + const timeLHS = isTemporalType(lhs.type); const err = errorCascade(timeLHS ? 'error' : 'number', lhs, rhs); if (err) return err; diff --git a/packages/malloy/src/lang/ast/view-elements/reference-view.ts b/packages/malloy/src/lang/ast/view-elements/reference-view.ts index 6f0cf4b25..c69f98260 100644 --- a/packages/malloy/src/lang/ast/view-elements/reference-view.ts +++ b/packages/malloy/src/lang/ast/view-elements/reference-view.ts @@ -25,7 +25,7 @@ import { PipeSegment, SourceDef, isAtomic, - isTurtleDef, + isTurtle, sourceBase, } from '../../../model/malloy_types'; import {ErrorFactory} from '../error-factory'; @@ -107,7 +107,7 @@ export class ReferenceView extends View { name, outputStruct, }; - } else if (isTurtleDef(fieldDef)) { + } else if (isTurtle(fieldDef)) { if (this.reference.list.length > 1) { if (forRefinement) { this.logError( diff --git a/packages/malloy/src/lang/test/field-symbols.spec.ts b/packages/malloy/src/lang/test/field-symbols.spec.ts index 99d6edee4..83e184947 100644 --- a/packages/malloy/src/lang/test/field-symbols.spec.ts +++ b/packages/malloy/src/lang/test/field-symbols.spec.ts @@ -112,7 +112,7 @@ describe('structdef comprehension', () => { }); test('import repeated record', () => { - const field: model.LeafArrayDef = { + const field: model.ScalarArrayDef = { name: 't', type: 'array', dialect: 'standardsql', diff --git a/packages/malloy/src/lang/test/imports.spec.ts b/packages/malloy/src/lang/test/imports.spec.ts index ced1e1368..920b9e479 100644 --- a/packages/malloy/src/lang/test/imports.spec.ts +++ b/packages/malloy/src/lang/test/imports.spec.ts @@ -20,7 +20,7 @@ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import {isJoinedField} from '../../model'; +import {isJoined} from '../../model'; import './parse-expects'; import {TestTranslator, errorMessage, model} from './test-translator'; import escapeRegEx from 'lodash/escapeRegExp'; @@ -91,8 +91,8 @@ source: botProjQSrc is botProjQ const maybeField = newSrc?.fields.find(f => f.name === 'b'); expect(maybeField).toBeDefined(); const f = maybeField!; - expect(isJoinedField(f)).toBeTruthy(); - if (isJoinedField(f)) { + expect(isJoined(f)).toBeTruthy(); + if (isJoined(f)) { expect(f.type).toBe('query_source'); if (f.type === 'query_source') { expect(typeof f.query.structRef).not.toBe('string'); diff --git a/packages/malloy/src/lang/test/query.spec.ts b/packages/malloy/src/lang/test/query.spec.ts index a28c9fe16..bce500906 100644 --- a/packages/malloy/src/lang/test/query.spec.ts +++ b/packages/malloy/src/lang/test/query.spec.ts @@ -36,7 +36,7 @@ import { QueryFieldDef, QuerySegment, expressionIsCalculation, - isJoinedField, + isJoined, isQuerySegment, isAtomic, } from '../../model'; @@ -1327,7 +1327,7 @@ describe('query:', () => { const q = t.translated.queryList[0].pipeline[0]; if (q.type === 'reduce' && q.extendSource) { expect(q.extendSource.length).toBe(1); - expect(isJoinedField(q.extendSource[0])).toBeTruthy(); + expect(isJoined(q.extendSource[0])).toBeTruthy(); expect(q.extendSource[0].type).toBe('table'); } else { fail('Did not generate extendSource'); diff --git a/packages/malloy/src/malloy.ts b/packages/malloy/src/malloy.ts index 513c4c456..a22ef1bfb 100644 --- a/packages/malloy/src/malloy.ts +++ b/packages/malloy/src/malloy.ts @@ -68,7 +68,6 @@ import { SourceDef, isSourceDef, QueryToMaterialize, - isJoinedField, isJoined, } from './model'; import { @@ -1573,7 +1572,7 @@ export class Explore extends Entity implements Taggable { this.structDef.fields.map(fieldDef => { const name = fieldDef.as || fieldDef.name; const sourceField = sourceFields.get(fieldDef.name); - if (isJoinedField(fieldDef)) { + if (isJoined(fieldDef)) { return [name, new ExploreField(fieldDef, this, sourceField)]; } else if (fieldDef.type === 'turtle') { return [name, new QueryField(fieldDef, this, sourceField)]; diff --git a/packages/malloy/src/model/composite_source_utils.ts b/packages/malloy/src/model/composite_source_utils.ts index a87fe0cff..00af6de5c 100644 --- a/packages/malloy/src/model/composite_source_utils.ts +++ b/packages/malloy/src/model/composite_source_utils.ts @@ -10,7 +10,7 @@ import { CompositeFieldUsage, FieldDef, isJoinable, - isJoinedField, + isJoined, isSourceDef, SourceDef, } from './malloy_types'; @@ -147,7 +147,7 @@ function _resolveCompositeSources( error: {code: 'composite_source_not_defined', data: {path: newPath}}, }; } - if (!isJoinedField(join) || !isSourceDef(join)) { + if (!isJoined(join) || !isSourceDef(join)) { return { error: {code: 'composite_source_not_a_join', data: {path: newPath}}, }; diff --git a/packages/malloy/src/model/malloy_query.ts b/packages/malloy/src/model/malloy_query.ts index 1253b3365..3ef9fe2e8 100644 --- a/packages/malloy/src/model/malloy_query.ts +++ b/packages/malloy/src/model/malloy_query.ts @@ -42,7 +42,6 @@ import { FunctionOverloadDef, FunctionParameterDef, getIdentifier, - getAtomicFields, hasExpression, IndexFieldDef, IndexSegment, @@ -98,7 +97,7 @@ import { isBaseTable, NestSourceDef, TimestampFieldDef, - isJoinedField, + isJoined, isJoinedSource, QueryResultDef, isScalarArray, @@ -113,7 +112,6 @@ import { JoinFieldDef, LeafAtomicDef, Expression, - isJoined, } from './malloy_types'; import {Connection} from '../connection/types'; @@ -1881,7 +1879,7 @@ class FieldInstanceResult implements FieldInstance { if (fi.fieldUsage.type === 'result') { if ( fi.f.fieldDef.type === 'turtle' || - isJoinedField(fi.f.fieldDef) || + isJoined(fi.f.fieldDef) || expressionIsAnalytic(fi.f.fieldDef.expressionType) ) { continue; @@ -4958,9 +4956,16 @@ export class QueryModel { // for (const f of ret.outputStruct.fields) { // fieldNames.push(getIdentifier(f)); // } - const fieldNames = getAtomicFields(ret.outputStruct).map(fieldDef => - q.parent.dialect.sqlMaybeQuoteIdentifier(fieldDef.name) - ); + const fieldNames: string[] = []; + for (const f of ret.outputStruct.fields) { + if (isAtomic(f)) { + const quoted = q.parent.dialect.sqlMaybeQuoteIdentifier(f.name); + fieldNames.push(quoted); + } + } + // const fieldNames = getAtomicFields(ret.outputStruct).map(fieldDef => + // q.parent.dialect.sqlMaybeQuoteIdentifier(fieldDef.name) + // ); ret.lastStageName = stageWriter.addStage( q.parent.dialect.sqlFinalStage(ret.lastStageName, fieldNames) ); diff --git a/packages/malloy/src/model/malloy_types.ts b/packages/malloy/src/model/malloy_types.ts index 03bcaf9b5..5a71455c7 100644 --- a/packages/malloy/src/model/malloy_types.ts +++ b/packages/malloy/src/model/malloy_types.ts @@ -603,7 +603,7 @@ export function hasExpression( } export type TemporalFieldType = 'date' | 'timestamp'; -export function isTemporalField(s: string): s is TemporalFieldType { +export function isTemporalType(s: string): s is TemporalFieldType { return s === 'date' || s === 'timestamp'; } export type CastType = @@ -689,12 +689,12 @@ export interface NativeUnsupportedTypeDef { export type NativeUnsupportedFieldDef = NativeUnsupportedTypeDef & AtomicFieldDef; -export interface LeafArrayTypeDef { +export interface ScalarArrayTypeDef { type: 'array'; elementTypeDef: Exclude; } -export interface LeafArrayDef - extends LeafArrayTypeDef, +export interface ScalarArrayDef + extends ScalarArrayTypeDef, StructDefBase, JoinBase, FieldBase { @@ -707,10 +707,10 @@ export function mkFieldDef( name: string, dialect: string ): AtomicFieldDef { - if (isScalarArrayType(atd)) { + if (isScalarArray(atd)) { return mkArrayDef(atd.elementTypeDef, name, dialect); } - if (isRepeatedRecordType(atd)) { + if (isRepeatedRecord(atd)) { const {type, fields, elementTypeDef} = atd; return {type, fields, elementTypeDef, join: 'many', name, dialect}; } @@ -793,29 +793,19 @@ export interface RepeatedRecordDef type: 'array'; join: 'many'; } -export type ArrayTypeDef = LeafArrayTypeDef | RepeatedRecordTypeDef; -export type ArrayDef = LeafArrayDef | RepeatedRecordDef; - -export function isRepeatedRecordType( - td: AtomicTypeDef -): td is RepeatedRecordTypeDef { - return td.type === 'array' && td.elementTypeDef.type === 'record_element'; -} +export type ArrayTypeDef = ScalarArrayTypeDef | RepeatedRecordTypeDef; +export type ArrayDef = ScalarArrayDef | RepeatedRecordDef; export function isRepeatedRecord( - fd: FieldDef | QueryFieldDef | StructDef -): fd is RepeatedRecordDef { + fd: FieldDef | QueryFieldDef | StructDef | AtomicTypeDef +): fd is RepeatedRecordTypeDef { return fd.type === 'array' && fd.elementTypeDef.type === 'record_element'; } -export function isScalarArrayType(td: AtomicTypeDef): td is LeafArrayTypeDef { - return td.type === 'array' && td.elementTypeDef.type !== 'record_element'; -} - export function isScalarArray( - fd: FieldDef | QueryFieldDef | StructDef -): fd is LeafArrayDef { - return fd.type === 'array' && fd.elementTypeDef.type !== 'record_element'; + td: AtomicTypeDef | FieldDef | QueryFieldDef | StructDef +): td is ScalarArrayTypeDef { + return td.type === 'array' && td.elementTypeDef.type !== 'record_element'; } export interface ErrorTypeDef { @@ -873,16 +863,12 @@ export function isJoinable(sd: StructDef): sd is Joinable { ].includes(sd.type); } -export function isJoinedField(fd: FieldDef): fd is JoinFieldDef { - return 'join' in fd; -} - -export function isJoined(sd: StructDef): sd is JoinFieldDef { +export function isJoined(sd: TypedDef): sd is JoinFieldDef { return 'join' in sd; } export function isJoinedSource(sd: StructDef): sd is SourceDef & JoinBase { - return isSourceDef(sd) && 'join' in sd; + return isSourceDef(sd) && isJoined(sd); } export type DateUnit = 'day' | 'week' | 'month' | 'quarter' | 'year'; @@ -1341,12 +1327,12 @@ export type LeafAtomicDef = LeafAtomicTypeDef & FieldBase; export type AtomicTypeDef = | LeafAtomicTypeDef - | LeafArrayTypeDef + | ScalarArrayTypeDef | RecordTypeDef | RepeatedRecordTypeDef; export type AtomicFieldDef = | LeafAtomicDef - | LeafArrayDef + | ScalarArrayDef | RecordDef | RepeatedRecordDef; @@ -1355,7 +1341,7 @@ export function isLeafAtomic( ): fd is LeafAtomicDef { return ( fd.type === 'string' || - isTemporalField(fd.type) || + isTemporalType(fd.type) || fd.type === 'number' || fd.type === 'boolean' || fd.type === 'json' || @@ -1377,6 +1363,14 @@ export interface RefToField { } export type QueryFieldDef = AtomicFieldDef | TurtleDef | RefToField; +// All these share the same "type" space +export type TypedDef = + | AtomicTypeDef + | JoinFieldDef + | TurtleDef + | RefToField + | StructDef; + /** Get the output name for a NamedObject */ export function getIdentifier(n: AliasedName): string { if (n.as !== undefined) { @@ -1477,11 +1471,11 @@ export interface QueryResult extends CompiledQuery { profilingUrl?: string; } -export function isTurtleDef(def: FieldDef): def is TurtleDef { +export function isTurtle(def: TypedDef): def is TurtleDef { return def.type === 'turtle'; } -export function isAtomic(def: FieldDef | StructDef): def is AtomicFieldDef { +export function isAtomic(def: TypedDef): def is AtomicTypeDef { return isAtomicFieldType(def.type); } @@ -1493,10 +1487,6 @@ export interface SearchResultRow { export type SearchResult = SearchResultRow[]; -export function getAtomicFields(structDef: StructDef): AtomicFieldDef[] { - return structDef.fields.filter(isAtomic); -} - export function isValueString( value: QueryValue, field: FieldDef @@ -1570,7 +1560,8 @@ export const TD = { isDate: (td: UTD): td is DateTypeDef => td?.type === 'date', isTimestamp: (td: UTD): td is TimestampTypeDef => td?.type === 'timestamp', isTemporal(td: UTD): td is TimestampTypeDef { - return td?.type === 'timestamp' || td?.type === 'date'; + const typ = td?.type ?? ''; + return isTemporalType(typ); }, isError: (td: UTD): td is ErrorTypeDef => td?.type === 'error', eq(x: UTD, y: UTD): boolean {