@dbsp/core 1.0.5 → 1.1.0
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/index.d.ts +49 -12
- package/dist/index.js +325 -85
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -4915,11 +4915,11 @@ declare class UpsertBuilder<T = void> extends MutationBuilderBase<T, UpsertInten
|
|
|
4915
4915
|
* const users = await orm.nql<{ name: string; email: string }>`users | select name, email`.all();
|
|
4916
4916
|
* ```
|
|
4917
4917
|
*
|
|
4918
|
-
* Each `${value}` interpolation is
|
|
4919
|
-
*
|
|
4920
|
-
*
|
|
4921
|
-
*
|
|
4922
|
-
*
|
|
4918
|
+
* Each non-raw `${value}` interpolation is converted into a generated NQL named
|
|
4919
|
+
* parameter (`:__p0`, `:__p1`, ...) and forwarded to the compiler through its
|
|
4920
|
+
* params map. Values never touch NQL source text. Use `nqlRaw()` only for trusted
|
|
4921
|
+
* dynamic NQL fragments that must participate in parsing, such as an ORDER BY
|
|
4922
|
+
* clause assembled from application-controlled choices.
|
|
4923
4923
|
*
|
|
4924
4924
|
* @module nql
|
|
4925
4925
|
* @since DX-040
|
|
@@ -4951,15 +4951,32 @@ interface NqlBuilder<T> {
|
|
|
4951
4951
|
* ```
|
|
4952
4952
|
*/
|
|
4953
4953
|
type NqlTag = <T>(strings: TemplateStringsArray, ...values: unknown[]) => NqlBuilder<T>;
|
|
4954
|
+
declare const NQL_RAW_FRAGMENT_TYPE: unique symbol;
|
|
4955
|
+
/**
|
|
4956
|
+
* Trusted NQL source fragment for `nql` template interpolation.
|
|
4957
|
+
*
|
|
4958
|
+
* Never wrap untrusted input in `nqlRaw()`: raw fragments are spliced verbatim
|
|
4959
|
+
* into NQL source and parsed as query structure.
|
|
4960
|
+
*
|
|
4961
|
+
* @public
|
|
4962
|
+
*/
|
|
4963
|
+
interface NqlRawFragment {
|
|
4964
|
+
readonly fragment: string;
|
|
4965
|
+
readonly [NQL_RAW_FRAGMENT_TYPE]: true;
|
|
4966
|
+
}
|
|
4967
|
+
/**
|
|
4968
|
+
* Mark a trusted fragment to splice verbatim into an NQL template.
|
|
4969
|
+
*
|
|
4970
|
+
* Never pass untrusted input to this function.
|
|
4971
|
+
*
|
|
4972
|
+
* @public
|
|
4973
|
+
*/
|
|
4974
|
+
declare function nqlRaw(fragment: string): NqlRawFragment;
|
|
4954
4975
|
/**
|
|
4955
4976
|
* Create an NQL template tag function.
|
|
4956
4977
|
*
|
|
4957
|
-
* Each `${value}` in the template is
|
|
4958
|
-
*
|
|
4959
|
-
* Untrusted string input is therefore safe from NQL-syntax injection — it is always
|
|
4960
|
-
* wrapped in single-quotes with embedded quotes doubled (`''`).
|
|
4961
|
-
* For dynamic structural fragments (table names, column names, ORDER BY direction)
|
|
4962
|
-
* use the builder API (`orm.select()`). See issue #134 for upcoming `:param` binding.
|
|
4978
|
+
* Each non-raw `${value}` in the template is bound as a generated named param.
|
|
4979
|
+
* Use `nqlRaw()` only for trusted dynamic NQL structure.
|
|
4963
4980
|
*
|
|
4964
4981
|
* @param schemaDefinition - Schema definition for validation
|
|
4965
4982
|
* @param model - ModelIR for plan execution
|
|
@@ -7806,6 +7823,26 @@ type SelectField = string | {
|
|
|
7806
7823
|
*/
|
|
7807
7824
|
type TraversalDirection = AdjacencyDirection | EdgeTableDirection;
|
|
7808
7825
|
|
|
7826
|
+
/**
|
|
7827
|
+
* Shared helpers for relation-dotted include path identity.
|
|
7828
|
+
*
|
|
7829
|
+
* Planner intent paths are positional (`include[0].include[1]`). Join identity
|
|
7830
|
+
* and hydration keys use the relation chain actually traversed
|
|
7831
|
+
* (`definition.file`, `manager.manager`), so every consumer must derive that
|
|
7832
|
+
* string the same way.
|
|
7833
|
+
*/
|
|
7834
|
+
type RelationPathIncludeNode = {
|
|
7835
|
+
readonly relation?: unknown;
|
|
7836
|
+
readonly via?: unknown;
|
|
7837
|
+
readonly include?: readonly unknown[];
|
|
7838
|
+
};
|
|
7839
|
+
type RelationPathUsage = {
|
|
7840
|
+
readonly relationName?: string | null | undefined;
|
|
7841
|
+
readonly relationPath?: string | null | undefined;
|
|
7842
|
+
};
|
|
7843
|
+
declare function deriveRelationPathFromIntentPath(includes: readonly unknown[] | undefined, intentPath: string | undefined, fallbackRelation: string | undefined): string | undefined;
|
|
7844
|
+
declare function countDistinctRelationPathsByName(usages: readonly RelationPathUsage[]): Map<string, number>;
|
|
7845
|
+
|
|
7809
7846
|
/**
|
|
7810
7847
|
* ResultHydrator - Handles result hydration and recursive include processing.
|
|
7811
7848
|
*
|
|
@@ -8184,4 +8221,4 @@ declare const assertIntentHasOrderBy: (result: AssertionQueryResult, expected: b
|
|
|
8184
8221
|
*/
|
|
8185
8222
|
declare function runAssertions(blocks: AssertionBlock[], results: AssertionQueryResult[], queries: string[], hasDb?: boolean): AssertionSummary;
|
|
8186
8223
|
|
|
8187
|
-
export { ASSERTION_TYPES, AdapterRequiredError, type AdjacencyOptions, type AfterMutationHook, type AfterQueryHook, type AggregateOptions, type AliasedColumn, type AliasedExprColumn, type AllColumns, AmbiguousPlanError, AmbiguousRelationError, type Assertion, type AssertionBlock, type AssertionOutcome, type AssertionQueryResult, type AssertionSummary, type AssertionType, BRAND, type BatchValuesOptions, type BatchValuesRef, type BeforeMutationHook, type BeforeQueryHook, type BrandKey, COLUMN_META, type CardinalityShorthand, CaseBuilder, type CaseValue, type ColumnDef, type ColumnMetaKey, ColumnNotFoundError, type ColumnRef, type ColumnSpec, type ColumnTypeToTS, CteBuilder, type CteDump, CteQueryBuilder, type CursorPaginateOptions, type CursorPaginatedResult, DEFAULT_CONVENTIONS, DEFAULT_FEATURE_CHECKERS, DUCKDB_CAPABILITIES, type DefaultFilters, type DefineModelOptions, DeleteBuilder, type DistinctField, type DumpMetaInput, type EdgeTableOptions, ErrorCode, type ErrorHookContext, Errors, ExecutionError, type ExprInput, ExpressionRef, type ExpressionSpec, type FeatureChecker, type FeatureUsage, type FilterOperators, type FilterValue, type FullTextSearchField, type FullTextSearchOptions, type GeneratedBelongsTo, type GeneratedColumn, type GeneratedColumnType, type GeneratedConventions, type GeneratedHasMany, type GeneratedHint, type GeneratedManyToMany, type GeneratedRelation, type GeneratedRelationKind, type GeneratedSchema, type GeneratedTable, type GetSchemaFromDbOptions, type HierarchyOptions, type HookErrorHandler, type HookManager, type HookPriority, type HydrateOptions, IRREGULAR_PLURALS, type IncludeOptions, type IncludeOptionsWithRecursive, type InferColumn, type InferColumnType, type InferColumnTypes, type InferDB, type InferDBFromSchema, type InferRefColumn, type InferRow, type InferRowType, type InferSchemaDB, type InferTableRow, type InferTables, type InferredRangeValue, InsertBuilder, IntentBuilder, type IntentBuilderState, type IntentSummary, InvalidOperationError, InvalidRelationDefinitionError, type JoinOptions, type JsonValue, type LightweightRelationsDef, type ListHierarchyOptions, type ListIndexOptions, type Logger, MSSQL_CAPABILITIES, MYSQL_CAPABILITIES, ModelIRImpl, type MutationDump, type MutationHookContext, type MutationOperation, NamingConventionMismatchError, type NegotiationResult, type NestedInclude, type SchemaColumnType$1 as NewSchemaColumnType, NotFoundError, type NqlBuilder, type NqlTag, type OnErrorHook, type OrderByInput, type OrderByRecord, type OrderBySpec, type OrmInstance, type OrmOf, type OrmOptions, type OrmOptionsWithAdapter, type OrmOptionsWithModel, type OrmOptionsWithSchema, POSTGRESQL_CAPABILITIES, type PaginateOptions, type PaginatedResult, type ParseError, type ParseResult, type ParsedRelationDef, type ParsedRelationKey, type PathOptions, type PrioritizedHook, type QueryAssertionResult, type QueryBuilder, type QueryHookContext, type QueryResultType, RELATION_META, type RangeType, RawCteQueryBuilder, type RecursiveDump, type RecursiveIncludeConfig, type RecursiveOptions, RecursiveShapeMismatchError, type RefDefinition, type RefOptions, type RelationHints, type RelationKey, type RelationMetaKey, RelationNotFoundError, type RelationObjectDef, type RelationRef, type RelationShorthand, type RelationTupleDef, type ResolvedSchema, ResolvedSchemaValidation, ResultHydrator, SQLITE_CAPABILITIES, SQL_RAW_MARKER, type Schema, type SchemaBelongsToRelation, type SchemaCardinality, type SchemaColumnDefinition, type SchemaColumnType, type SchemaConfigInput, type SchemaConstraints, type SchemaConventionsDefinition, type SchemaConversionResult, type SchemaDefinition, type SchemaDefinitionInput, SchemaValidationError$1 as SchemaError, type SchemaExtras, type SchemaFilterStrategy, type SchemaForeignKeyReference, type SchemaHasManyRelation, type SchemaHintDefinition, type SchemaHintsDefinition, type SchemaIndexDefinition, type SchemaIndexOptions, type SchemaIndexesDefinition, type SchemaManyToManyRelation, type SchemaOnDeleteAction, type SchemaOptions, type SchemaRelationDefinition, type SchemaRelationKind, type SchemaRelationsDefinition, type SchemaTableDefinition, type SchemaTableOptions, type SchemaTablesDefinition, SchemaValidationError, type SelectField, type SelfRefRoles, type SetOperationBuilder, type SimplifiedOrmOptions, type SqlRawExpression, type StreamOptions, SubqueryBuilder, SubqueryExpression, TABLE_META, type TableAssertionData, type TableDDL, type TableDef, type TableIndexes, type TableMetaKey, TableNotFoundError, type TableRef, type TraversalDirection, UnhandledTypeInDialect, UnknownDialectError, UnsafeOperationError, UnsupportedCapabilityError, UnsupportedFeatureError, UnsupportedStrategyError, UpdateBuilder, UpsertBuilder, type ValidatedResolvedSchema, type WhereFilter, WindowBuilder, aggOrderBy, and, any, array, arrayAgg, assertCapability, assertContains, assertDbColumnExists, assertDbOutput, assertDbRowsEquals, assertDbRowsMax, assertDbRowsMin, assertDbValueEquals, assertEquals, assertIntentHasGroupBy, assertIntentHasOrderBy, assertIntentHasWhere, assertIntentTable, assertIntentType, assertIntentWith, assertMatches, assertParamsEquals, assertParamsLength, assertParamsType, assertParamsValue, assertResolvedSchemaToGeneratedSchema, assertSQLColumn, assertSQLEquals, assertSQLJoin, assertSQLTable, assertSuccess, assertTypeSupported, batchValues, buildModelFromResolvedSchema, buildModelFromSchema, capitalize, caseWhen, cast, coalesce, col, composeAfterMutationHooks, composeAfterQueryHooks, composeBeforeMutationHooks, composeBeforeQueryHooks, composeOnErrorHooks, createDialectCapabilities, createHookManager, createNqlTag, createOrm, createPseudoColumnMetadata, createRawCteBuilder, createRecursiveMetadata, decapitalize, defaultLogger, defineModel, defineSchema, denseRank, detectForeignKeys, detectManyToMany, distinct, eq, every, exists, ref as exprRef, extendDialect, extractPseudoColumnKeywords, findClosestMatch, fn, fullTextSearch, getAvailableDialects, getDialectCapabilities, getLogger, getRelationKind, getSchemaFromDb, gt, gte, inArray, inSubquery, inferForeignKey, inferRelationsFromSchema, isAliasedColumn, isAllColumns, isBatchValuesRef, isBelongsTo, isCardinalityShorthand, isColumnRef, isDistinctField, isDistinctFrom, isExpressionSpec, isGeneratedSchema, isHasMany, isKnownDialect, isManyToMany, isNotNull, isNull, isOverallSuccess, isRecursiveIncludeOptions, isRecursiveRelation, isRef, isRelationObjectDef, isRelationRef, isRelationTupleDef, isResolvedSchema, isSelfReferential, isSqlRaw, isSubqueryExpression, isTableRef, isWhereIntent, lag, lead, like, literal, lt, lte, namedArg, negotiateFeatures, neq, none, normalizeSQL, normalizeSchema, not, notExists, objectToWhereIntent, op, or, outerRef, param, parseAssertionFile, parseRelationDef, parseRelationKey, pipeAfterMutationHooks, pipeAfterQueryHooks, pipeBeforeMutationHooks, pipeBeforeQueryHooks, pipeOnErrorHooks, plan, planRecursive, pluralize, rangeContainedBy, rangeContains, rangeOverlaps, rank, raw, rawExists, rawNotExists, ref$1 as ref, registerDialect, relationColumn, requiresDatabase, resetLogger, resolveQueryIndex, resolvedSchemaToGeneratedSchema, rowNumber, runAssertions, schema, schemaToModelIR, setLogger, silentLogger, singularize, some, sortByPriority, sql, star, stringAgg, subquery, supportsDDLGeneration, supportsExecution, supportsIntrospection, supportsRawSql, supportsStreaming, supportsTransactions, textScore, unary, validateAssertionBlocks, validateRecursiveInclude, validateRecursiveShape, validateTypeName, wAvg, wCount, wMax, wMin, wSum, withPriority };
|
|
8224
|
+
export { ASSERTION_TYPES, AdapterRequiredError, type AdjacencyOptions, type AfterMutationHook, type AfterQueryHook, type AggregateOptions, type AliasedColumn, type AliasedExprColumn, type AllColumns, AmbiguousPlanError, AmbiguousRelationError, type Assertion, type AssertionBlock, type AssertionOutcome, type AssertionQueryResult, type AssertionSummary, type AssertionType, BRAND, type BatchValuesOptions, type BatchValuesRef, type BeforeMutationHook, type BeforeQueryHook, type BrandKey, COLUMN_META, type CardinalityShorthand, CaseBuilder, type CaseValue, type ColumnDef, type ColumnMetaKey, ColumnNotFoundError, type ColumnRef, type ColumnSpec, type ColumnTypeToTS, CteBuilder, type CteDump, CteQueryBuilder, type CursorPaginateOptions, type CursorPaginatedResult, DEFAULT_CONVENTIONS, DEFAULT_FEATURE_CHECKERS, DUCKDB_CAPABILITIES, type DefaultFilters, type DefineModelOptions, DeleteBuilder, type DistinctField, type DumpMetaInput, type EdgeTableOptions, ErrorCode, type ErrorHookContext, Errors, ExecutionError, type ExprInput, ExpressionRef, type ExpressionSpec, type FeatureChecker, type FeatureUsage, type FilterOperators, type FilterValue, type FullTextSearchField, type FullTextSearchOptions, type GeneratedBelongsTo, type GeneratedColumn, type GeneratedColumnType, type GeneratedConventions, type GeneratedHasMany, type GeneratedHint, type GeneratedManyToMany, type GeneratedRelation, type GeneratedRelationKind, type GeneratedSchema, type GeneratedTable, type GetSchemaFromDbOptions, type HierarchyOptions, type HookErrorHandler, type HookManager, type HookPriority, type HydrateOptions, IRREGULAR_PLURALS, type IncludeOptions, type IncludeOptionsWithRecursive, type InferColumn, type InferColumnType, type InferColumnTypes, type InferDB, type InferDBFromSchema, type InferRefColumn, type InferRow, type InferRowType, type InferSchemaDB, type InferTableRow, type InferTables, type InferredRangeValue, InsertBuilder, IntentBuilder, type IntentBuilderState, type IntentSummary, InvalidOperationError, InvalidRelationDefinitionError, type JoinOptions, type JsonValue, type LightweightRelationsDef, type ListHierarchyOptions, type ListIndexOptions, type Logger, MSSQL_CAPABILITIES, MYSQL_CAPABILITIES, ModelIRImpl, type MutationDump, type MutationHookContext, type MutationOperation, NamingConventionMismatchError, type NegotiationResult, type NestedInclude, type SchemaColumnType$1 as NewSchemaColumnType, NotFoundError, type NqlBuilder, type NqlRawFragment, type NqlTag, type OnErrorHook, type OrderByInput, type OrderByRecord, type OrderBySpec, type OrmInstance, type OrmOf, type OrmOptions, type OrmOptionsWithAdapter, type OrmOptionsWithModel, type OrmOptionsWithSchema, POSTGRESQL_CAPABILITIES, type PaginateOptions, type PaginatedResult, type ParseError, type ParseResult, type ParsedRelationDef, type ParsedRelationKey, type PathOptions, type PrioritizedHook, type QueryAssertionResult, type QueryBuilder, type QueryHookContext, type QueryResultType, RELATION_META, type RangeType, RawCteQueryBuilder, type RecursiveDump, type RecursiveIncludeConfig, type RecursiveOptions, RecursiveShapeMismatchError, type RefDefinition, type RefOptions, type RelationHints, type RelationKey, type RelationMetaKey, RelationNotFoundError, type RelationObjectDef, type RelationPathIncludeNode, type RelationPathUsage, type RelationRef, type RelationShorthand, type RelationTupleDef, type ResolvedSchema, ResolvedSchemaValidation, ResultHydrator, SQLITE_CAPABILITIES, SQL_RAW_MARKER, type Schema, type SchemaBelongsToRelation, type SchemaCardinality, type SchemaColumnDefinition, type SchemaColumnType, type SchemaConfigInput, type SchemaConstraints, type SchemaConventionsDefinition, type SchemaConversionResult, type SchemaDefinition, type SchemaDefinitionInput, SchemaValidationError$1 as SchemaError, type SchemaExtras, type SchemaFilterStrategy, type SchemaForeignKeyReference, type SchemaHasManyRelation, type SchemaHintDefinition, type SchemaHintsDefinition, type SchemaIndexDefinition, type SchemaIndexOptions, type SchemaIndexesDefinition, type SchemaManyToManyRelation, type SchemaOnDeleteAction, type SchemaOptions, type SchemaRelationDefinition, type SchemaRelationKind, type SchemaRelationsDefinition, type SchemaTableDefinition, type SchemaTableOptions, type SchemaTablesDefinition, SchemaValidationError, type SelectField, type SelfRefRoles, type SetOperationBuilder, type SimplifiedOrmOptions, type SqlRawExpression, type StreamOptions, SubqueryBuilder, SubqueryExpression, TABLE_META, type TableAssertionData, type TableDDL, type TableDef, type TableIndexes, type TableMetaKey, TableNotFoundError, type TableRef, type TraversalDirection, UnhandledTypeInDialect, UnknownDialectError, UnsafeOperationError, UnsupportedCapabilityError, UnsupportedFeatureError, UnsupportedStrategyError, UpdateBuilder, UpsertBuilder, type ValidatedResolvedSchema, type WhereFilter, WindowBuilder, aggOrderBy, and, any, array, arrayAgg, assertCapability, assertContains, assertDbColumnExists, assertDbOutput, assertDbRowsEquals, assertDbRowsMax, assertDbRowsMin, assertDbValueEquals, assertEquals, assertIntentHasGroupBy, assertIntentHasOrderBy, assertIntentHasWhere, assertIntentTable, assertIntentType, assertIntentWith, assertMatches, assertParamsEquals, assertParamsLength, assertParamsType, assertParamsValue, assertResolvedSchemaToGeneratedSchema, assertSQLColumn, assertSQLEquals, assertSQLJoin, assertSQLTable, assertSuccess, assertTypeSupported, batchValues, buildModelFromResolvedSchema, buildModelFromSchema, capitalize, caseWhen, cast, coalesce, col, composeAfterMutationHooks, composeAfterQueryHooks, composeBeforeMutationHooks, composeBeforeQueryHooks, composeOnErrorHooks, countDistinctRelationPathsByName, createDialectCapabilities, createHookManager, createNqlTag, createOrm, createPseudoColumnMetadata, createRawCteBuilder, createRecursiveMetadata, decapitalize, defaultLogger, defineModel, defineSchema, denseRank, deriveRelationPathFromIntentPath, detectForeignKeys, detectManyToMany, distinct, eq, every, exists, ref as exprRef, extendDialect, extractPseudoColumnKeywords, findClosestMatch, fn, fullTextSearch, getAvailableDialects, getDialectCapabilities, getLogger, getRelationKind, getSchemaFromDb, gt, gte, inArray, inSubquery, inferForeignKey, inferRelationsFromSchema, isAliasedColumn, isAllColumns, isBatchValuesRef, isBelongsTo, isCardinalityShorthand, isColumnRef, isDistinctField, isDistinctFrom, isExpressionSpec, isGeneratedSchema, isHasMany, isKnownDialect, isManyToMany, isNotNull, isNull, isOverallSuccess, isRecursiveIncludeOptions, isRecursiveRelation, isRef, isRelationObjectDef, isRelationRef, isRelationTupleDef, isResolvedSchema, isSelfReferential, isSqlRaw, isSubqueryExpression, isTableRef, isWhereIntent, lag, lead, like, literal, lt, lte, namedArg, negotiateFeatures, neq, none, normalizeSQL, normalizeSchema, not, notExists, nqlRaw, objectToWhereIntent, op, or, outerRef, param, parseAssertionFile, parseRelationDef, parseRelationKey, pipeAfterMutationHooks, pipeAfterQueryHooks, pipeBeforeMutationHooks, pipeBeforeQueryHooks, pipeOnErrorHooks, plan, planRecursive, pluralize, rangeContainedBy, rangeContains, rangeOverlaps, rank, raw, rawExists, rawNotExists, ref$1 as ref, registerDialect, relationColumn, requiresDatabase, resetLogger, resolveQueryIndex, resolvedSchemaToGeneratedSchema, rowNumber, runAssertions, schema, schemaToModelIR, setLogger, silentLogger, singularize, some, sortByPriority, sql, star, stringAgg, subquery, supportsDDLGeneration, supportsExecution, supportsIntrospection, supportsRawSql, supportsStreaming, supportsTransactions, textScore, unary, validateAssertionBlocks, validateRecursiveInclude, validateRecursiveShape, validateTypeName, wAvg, wCount, wMax, wMin, wSum, withPriority };
|
package/dist/index.js
CHANGED
|
@@ -2601,7 +2601,8 @@ function processInclude(include, sourceTable, model, state, opts, intentPath, de
|
|
|
2601
2601
|
return;
|
|
2602
2602
|
}
|
|
2603
2603
|
const includePath = `${sourceTable}.${relation.name}`;
|
|
2604
|
-
|
|
2604
|
+
const isSelfReferentialRelation = relation.source === relation.target;
|
|
2605
|
+
if (!isSelfReferentialRelation && state.visitedIncludes.has(includePath)) {
|
|
2605
2606
|
state.warnings.push({
|
|
2606
2607
|
code: "CIRCULAR_INCLUDE",
|
|
2607
2608
|
message: `Circular include detected: ${includePath}`,
|
|
@@ -2609,12 +2610,12 @@ function processInclude(include, sourceTable, model, state, opts, intentPath, de
|
|
|
2609
2610
|
});
|
|
2610
2611
|
return;
|
|
2611
2612
|
}
|
|
2612
|
-
state.visitedIncludes.add(includePath);
|
|
2613
|
+
if (!isSelfReferentialRelation) state.visitedIncludes.add(includePath);
|
|
2613
2614
|
const relationPath = `${sourceTable}.${relation.name}`;
|
|
2614
2615
|
const paths = state.relationAccessCounts.get(relationPath) ?? [];
|
|
2615
2616
|
paths.push(intentPath);
|
|
2616
2617
|
state.relationAccessCounts.set(relationPath, paths);
|
|
2617
|
-
const isRecursiveInclude = (!!include.recursive || !!relation.recursive) &&
|
|
2618
|
+
const isRecursiveInclude = (!!include.recursive || !!relation.recursive) && isSelfReferentialRelation;
|
|
2618
2619
|
let includeStrategy;
|
|
2619
2620
|
if (isRecursiveInclude) {
|
|
2620
2621
|
if (opts.dialectCapabilities?.supportsRecursiveCTE === false) {
|
|
@@ -2749,7 +2750,7 @@ function processInclude(include, sourceTable, model, state, opts, intentPath, de
|
|
|
2749
2750
|
}
|
|
2750
2751
|
}
|
|
2751
2752
|
}
|
|
2752
|
-
state.visitedIncludes.delete(includePath);
|
|
2753
|
+
if (!isSelfReferentialRelation) state.visitedIncludes.delete(includePath);
|
|
2753
2754
|
}
|
|
2754
2755
|
function disambiguateRelation(relationName, sourceTable, model, state, opts, _intentPath, viaHint) {
|
|
2755
2756
|
const directRelation = model.getRelation(`${sourceTable}.${relationName}`);
|
|
@@ -4105,6 +4106,12 @@ function coalesce(fields, as) {
|
|
|
4105
4106
|
};
|
|
4106
4107
|
}
|
|
4107
4108
|
function raw(sqlFragment, as) {
|
|
4109
|
+
if (typeof as !== "string" || as.trim().length === 0) {
|
|
4110
|
+
throw new InvalidOperationError(
|
|
4111
|
+
"raw",
|
|
4112
|
+
"raw() requires an alias as second argument, e.g. raw('COUNT(*)', 'count')"
|
|
4113
|
+
);
|
|
4114
|
+
}
|
|
4108
4115
|
validateIdentifier(as, "column");
|
|
4109
4116
|
return {
|
|
4110
4117
|
__expr: true,
|
|
@@ -6126,53 +6133,131 @@ function negotiateFeatures(model, capabilities, behavior = "warning", checkers =
|
|
|
6126
6133
|
}
|
|
6127
6134
|
|
|
6128
6135
|
// src/dx/nql.ts
|
|
6129
|
-
import {
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6136
|
+
import {
|
|
6137
|
+
NqlLexer,
|
|
6138
|
+
compile as nqlCompile
|
|
6139
|
+
} from "@dbsp/nql";
|
|
6140
|
+
import { NQL_INTERNAL_COMPILER_OPTIONS } from "@dbsp/types/internal";
|
|
6141
|
+
var NQL_RAW_FRAGMENT = /* @__PURE__ */ Symbol("NqlRawFragment");
|
|
6142
|
+
function nqlRaw(fragment) {
|
|
6143
|
+
if (typeof fragment !== "string") {
|
|
6144
|
+
throw new TypeError("nqlRaw() expects a string fragment");
|
|
6145
|
+
}
|
|
6146
|
+
const raw2 = { fragment };
|
|
6147
|
+
Object.defineProperty(raw2, NQL_RAW_FRAGMENT, {
|
|
6148
|
+
value: true,
|
|
6149
|
+
enumerable: false
|
|
6150
|
+
});
|
|
6151
|
+
return Object.freeze(raw2);
|
|
6152
|
+
}
|
|
6153
|
+
function isNqlRawFragment(value) {
|
|
6154
|
+
if (typeof value !== "object" || value === null) {
|
|
6155
|
+
return false;
|
|
6133
6156
|
}
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6157
|
+
if (!Object.hasOwn(value, NQL_RAW_FRAGMENT)) {
|
|
6158
|
+
return false;
|
|
6159
|
+
}
|
|
6160
|
+
return value[NQL_RAW_FRAGMENT] === true;
|
|
6161
|
+
}
|
|
6162
|
+
function isInsideGeneratedRange(start, end, generatedRanges) {
|
|
6163
|
+
return generatedRanges.some(
|
|
6164
|
+
(range) => start >= range.start && end <= range.end
|
|
6165
|
+
);
|
|
6166
|
+
}
|
|
6167
|
+
function findOverlappingGeneratedRange(start, end, generatedRanges) {
|
|
6168
|
+
return generatedRanges.find(
|
|
6169
|
+
(range) => start < range.end && end > range.start
|
|
6170
|
+
);
|
|
6171
|
+
}
|
|
6172
|
+
function findExactGeneratedRange(name, start, end, generatedRanges) {
|
|
6173
|
+
return generatedRanges.find(
|
|
6174
|
+
(range) => range.name === name && range.start === start && range.end === end
|
|
6175
|
+
);
|
|
6176
|
+
}
|
|
6177
|
+
function generatedParamLexError(name) {
|
|
6178
|
+
return `Generated NQL parameter :${name} was not recognized as exactly one NamedParam token after raw-fragment assembly; check adjacent nqlRaw() fragments for quotes, comments, or identifier text that can swallow the placeholder.`;
|
|
6179
|
+
}
|
|
6180
|
+
function findInternalParamSourceError(query, generatedRanges) {
|
|
6181
|
+
const lexResult = NqlLexer.tokenize(query);
|
|
6182
|
+
const generatedTokenCounts = new Map(
|
|
6183
|
+
generatedRanges.map((range) => [range.name, 0])
|
|
6184
|
+
);
|
|
6185
|
+
for (const token of lexResult.tokens) {
|
|
6186
|
+
if (token.tokenType.name !== "NamedParam") {
|
|
6187
|
+
continue;
|
|
6150
6188
|
}
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
`nql\`...\`: cannot interpolate a string containing a newline at position ${index}. NQL string literals do not support raw newline characters. For dynamic NQL structure use the builder API (orm.select()). See issue #134.`
|
|
6155
|
-
);
|
|
6156
|
-
}
|
|
6157
|
-
const escaped = value.replaceAll("'", "''");
|
|
6158
|
-
return `'${escaped}'`;
|
|
6189
|
+
const name = token.image.slice(1);
|
|
6190
|
+
if (!name.startsWith("__p")) {
|
|
6191
|
+
continue;
|
|
6159
6192
|
}
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6193
|
+
const start = token.startOffset;
|
|
6194
|
+
const end = (token.endOffset ?? start + token.image.length - 1) + 1;
|
|
6195
|
+
const exactGeneratedRange = findExactGeneratedRange(
|
|
6196
|
+
name,
|
|
6197
|
+
start,
|
|
6198
|
+
end,
|
|
6199
|
+
generatedRanges
|
|
6200
|
+
);
|
|
6201
|
+
if (exactGeneratedRange) {
|
|
6202
|
+
generatedTokenCounts.set(
|
|
6203
|
+
exactGeneratedRange.name,
|
|
6204
|
+
(generatedTokenCounts.get(exactGeneratedRange.name) ?? 0) + 1
|
|
6164
6205
|
);
|
|
6206
|
+
continue;
|
|
6207
|
+
}
|
|
6208
|
+
const overlappingGeneratedRange = findOverlappingGeneratedRange(
|
|
6209
|
+
start,
|
|
6210
|
+
end,
|
|
6211
|
+
generatedRanges
|
|
6212
|
+
);
|
|
6213
|
+
if (overlappingGeneratedRange) {
|
|
6214
|
+
return generatedParamLexError(overlappingGeneratedRange.name);
|
|
6165
6215
|
}
|
|
6216
|
+
if (!isInsideGeneratedRange(start, end, generatedRanges)) {
|
|
6217
|
+
return `Reserved NQL parameter namespace "__p" cannot be referenced by user source (${token.image}).`;
|
|
6218
|
+
}
|
|
6219
|
+
}
|
|
6220
|
+
for (const range of generatedRanges) {
|
|
6221
|
+
if (generatedTokenCounts.get(range.name) !== 1) {
|
|
6222
|
+
return generatedParamLexError(range.name);
|
|
6223
|
+
}
|
|
6224
|
+
}
|
|
6225
|
+
return void 0;
|
|
6226
|
+
}
|
|
6227
|
+
function assembleNqlTemplate(strings, values) {
|
|
6228
|
+
const params = /* @__PURE__ */ Object.create(null);
|
|
6229
|
+
const generatedRanges = [];
|
|
6230
|
+
let query = strings[0] ?? "";
|
|
6231
|
+
let boundIndex = 0;
|
|
6232
|
+
for (let i = 0; i < values.length; i++) {
|
|
6233
|
+
const value = values[i];
|
|
6234
|
+
if (isNqlRawFragment(value)) {
|
|
6235
|
+
query += value.fragment;
|
|
6236
|
+
} else {
|
|
6237
|
+
const name = `__p${boundIndex++}`;
|
|
6238
|
+
const placeholder = `:${name}`;
|
|
6239
|
+
const start = query.length;
|
|
6240
|
+
query += placeholder;
|
|
6241
|
+
generatedRanges.push({ name, start, end: start + placeholder.length });
|
|
6242
|
+
params[name] = value;
|
|
6243
|
+
}
|
|
6244
|
+
query += strings[i + 1] ?? "";
|
|
6166
6245
|
}
|
|
6246
|
+
return {
|
|
6247
|
+
query,
|
|
6248
|
+
params,
|
|
6249
|
+
hasBoundParams: boundIndex > 0,
|
|
6250
|
+
sourceError: findInternalParamSourceError(query, generatedRanges)
|
|
6251
|
+
};
|
|
6167
6252
|
}
|
|
6168
6253
|
function createNqlTag(schemaDefinition, model, adapter, schemaName) {
|
|
6169
6254
|
return function nql(strings, ...values) {
|
|
6170
|
-
|
|
6171
|
-
for (let i = 0; i < values.length; i++) {
|
|
6172
|
-
query += toNqlLiteral(values[i], i) + (strings[i + 1] ?? "");
|
|
6173
|
-
}
|
|
6255
|
+
const assembled = assembleNqlTemplate(strings, values);
|
|
6174
6256
|
return new NqlBuilderImpl(
|
|
6175
|
-
query,
|
|
6257
|
+
assembled.query,
|
|
6258
|
+
assembled.params,
|
|
6259
|
+
assembled.hasBoundParams,
|
|
6260
|
+
assembled.sourceError,
|
|
6176
6261
|
schemaDefinition,
|
|
6177
6262
|
model,
|
|
6178
6263
|
adapter,
|
|
@@ -6183,13 +6268,19 @@ function createNqlTag(schemaDefinition, model, adapter, schemaName) {
|
|
|
6183
6268
|
var NqlBuilderImpl = class {
|
|
6184
6269
|
_intent;
|
|
6185
6270
|
query;
|
|
6271
|
+
params;
|
|
6272
|
+
hasBoundParams;
|
|
6273
|
+
sourceError;
|
|
6186
6274
|
schemaDefinition;
|
|
6187
6275
|
model;
|
|
6188
6276
|
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: Reserved for future schema-scoping support
|
|
6189
6277
|
_schemaName;
|
|
6190
6278
|
adapter;
|
|
6191
|
-
constructor(query, schemaDefinition, model, adapter, schemaName) {
|
|
6279
|
+
constructor(query, params, hasBoundParams, sourceError, schemaDefinition, model, adapter, schemaName) {
|
|
6192
6280
|
this.query = query;
|
|
6281
|
+
this.params = params;
|
|
6282
|
+
this.hasBoundParams = hasBoundParams;
|
|
6283
|
+
this.sourceError = sourceError;
|
|
6193
6284
|
this.schemaDefinition = schemaDefinition;
|
|
6194
6285
|
this.model = model;
|
|
6195
6286
|
this.adapter = adapter;
|
|
@@ -6199,7 +6290,14 @@ var NqlBuilderImpl = class {
|
|
|
6199
6290
|
if (this._intent) {
|
|
6200
6291
|
return this._intent;
|
|
6201
6292
|
}
|
|
6202
|
-
|
|
6293
|
+
if (this.sourceError !== void 0) {
|
|
6294
|
+
throw new Error(`NQL compilation failed: ${this.sourceError}`);
|
|
6295
|
+
}
|
|
6296
|
+
const compilerOptions = {
|
|
6297
|
+
...extractPseudoColumnKeywords(this.model) ?? {},
|
|
6298
|
+
params: this.params,
|
|
6299
|
+
[NQL_INTERNAL_COMPILER_OPTIONS]: { allowInternalParams: true }
|
|
6300
|
+
};
|
|
6203
6301
|
const result = nqlCompile(
|
|
6204
6302
|
this.query,
|
|
6205
6303
|
this.schemaDefinition,
|
|
@@ -6208,7 +6306,8 @@ var NqlBuilderImpl = class {
|
|
|
6208
6306
|
);
|
|
6209
6307
|
if (!result.success) {
|
|
6210
6308
|
const errors = result.errors?.map((e) => e.message).join(", ") ?? "Unknown error";
|
|
6211
|
-
|
|
6309
|
+
const rawHint = this.hasBoundParams ? " If an interpolation was intended as NQL structure, wrap a trusted fragment with nqlRaw()." : "";
|
|
6310
|
+
throw new Error(`NQL compilation failed: ${errors}${rawHint}`);
|
|
6212
6311
|
}
|
|
6213
6312
|
if (result.ast?.mutation && !result.ast?.query) {
|
|
6214
6313
|
throw new Error(
|
|
@@ -6224,12 +6323,15 @@ var NqlBuilderImpl = class {
|
|
|
6224
6323
|
toIntentIR() {
|
|
6225
6324
|
return this.compile();
|
|
6226
6325
|
}
|
|
6227
|
-
|
|
6326
|
+
planInternal() {
|
|
6228
6327
|
const intent = this.compile();
|
|
6229
6328
|
return plan(intent, this.model);
|
|
6230
6329
|
}
|
|
6330
|
+
plan() {
|
|
6331
|
+
return this.planInternal();
|
|
6332
|
+
}
|
|
6231
6333
|
dump(meta) {
|
|
6232
|
-
const planReport = this.
|
|
6334
|
+
const planReport = this.planInternal();
|
|
6233
6335
|
if (!this.adapter) {
|
|
6234
6336
|
return {
|
|
6235
6337
|
plan: planReport,
|
|
@@ -6272,7 +6374,7 @@ var NqlBuilderImpl = class {
|
|
|
6272
6374
|
"Cannot execute query: no adapter configured. Pass an adapter to createOrm() or use .toIntentIR() / .plan() for debugging."
|
|
6273
6375
|
);
|
|
6274
6376
|
}
|
|
6275
|
-
const planReport = this.
|
|
6377
|
+
const planReport = this.planInternal();
|
|
6276
6378
|
const compiled = this.adapter.compile(planReport);
|
|
6277
6379
|
return this.adapter.execute(compiled);
|
|
6278
6380
|
}
|
|
@@ -6661,8 +6763,152 @@ function hydrateJsonAggIncludes(results, planReport) {
|
|
|
6661
6763
|
}
|
|
6662
6764
|
}
|
|
6663
6765
|
|
|
6766
|
+
// src/dx/relation-paths.ts
|
|
6767
|
+
function nonEmptyString(value) {
|
|
6768
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
6769
|
+
}
|
|
6770
|
+
function includeNode(value) {
|
|
6771
|
+
return value !== null && typeof value === "object" ? value : void 0;
|
|
6772
|
+
}
|
|
6773
|
+
function parseIntentPathIndexes(intentPath) {
|
|
6774
|
+
const indexes = [];
|
|
6775
|
+
const indexPattern = /include\[(\d+)\]/g;
|
|
6776
|
+
let execResult = indexPattern.exec(intentPath);
|
|
6777
|
+
while (execResult !== null) {
|
|
6778
|
+
const rawIndex = execResult[1];
|
|
6779
|
+
if (rawIndex === void 0) break;
|
|
6780
|
+
indexes.push(parseInt(rawIndex, 10));
|
|
6781
|
+
execResult = indexPattern.exec(intentPath);
|
|
6782
|
+
}
|
|
6783
|
+
return indexes;
|
|
6784
|
+
}
|
|
6785
|
+
function deriveRelationPathFromIntentPath(includes, intentPath, fallbackRelation) {
|
|
6786
|
+
if (includes && intentPath) {
|
|
6787
|
+
let current = includes;
|
|
6788
|
+
const path = [];
|
|
6789
|
+
for (const index of parseIntentPathIndexes(intentPath)) {
|
|
6790
|
+
const item = includeNode(current[index]);
|
|
6791
|
+
if (!item) break;
|
|
6792
|
+
const segment = nonEmptyString(item.via) ?? nonEmptyString(item.relation);
|
|
6793
|
+
if (!segment) break;
|
|
6794
|
+
path.push(segment);
|
|
6795
|
+
current = Array.isArray(item.include) ? item.include : [];
|
|
6796
|
+
}
|
|
6797
|
+
if (path.length > 0) return path.join(".");
|
|
6798
|
+
}
|
|
6799
|
+
return nonEmptyString(fallbackRelation);
|
|
6800
|
+
}
|
|
6801
|
+
function countDistinctRelationPathsByName(usages) {
|
|
6802
|
+
const pathsByRelationName = /* @__PURE__ */ new Map();
|
|
6803
|
+
for (const usage of usages) {
|
|
6804
|
+
const relationName = nonEmptyString(usage.relationName);
|
|
6805
|
+
if (!relationName) continue;
|
|
6806
|
+
const relationPath = nonEmptyString(usage.relationPath) ?? relationName;
|
|
6807
|
+
const paths = pathsByRelationName.get(relationName) ?? /* @__PURE__ */ new Set();
|
|
6808
|
+
paths.add(relationPath);
|
|
6809
|
+
pathsByRelationName.set(relationName, paths);
|
|
6810
|
+
}
|
|
6811
|
+
const counts = /* @__PURE__ */ new Map();
|
|
6812
|
+
for (const [relationName, paths] of pathsByRelationName) {
|
|
6813
|
+
counts.set(relationName, paths.size);
|
|
6814
|
+
}
|
|
6815
|
+
return counts;
|
|
6816
|
+
}
|
|
6817
|
+
|
|
6664
6818
|
// src/dx/result-hydrator.ts
|
|
6665
6819
|
var COMPOSITE_KEY_SEP = "\0";
|
|
6820
|
+
function stringProp(source, key) {
|
|
6821
|
+
const value = source?.[key];
|
|
6822
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
6823
|
+
}
|
|
6824
|
+
function resolveJoinIncludePath(planReport, decision) {
|
|
6825
|
+
const context = decision.context;
|
|
6826
|
+
const decisionRecord = decision;
|
|
6827
|
+
const contextRecord = context;
|
|
6828
|
+
const explicitRelationPath = stringProp(decisionRecord, "relationPath") ?? stringProp(contextRecord, "relationPath");
|
|
6829
|
+
if (explicitRelationPath) return explicitRelationPath;
|
|
6830
|
+
const fallbackRelation = stringProp(contextRecord, "relation") ?? stringProp(contextRecord, "includeAlias") ?? stringProp(decisionRecord, "relationName");
|
|
6831
|
+
const intentPath = stringProp(contextRecord, "intentPath");
|
|
6832
|
+
return deriveRelationPathFromIntentPath(
|
|
6833
|
+
Array.isArray(planReport.intent?.include) ? planReport.intent.include : void 0,
|
|
6834
|
+
intentPath,
|
|
6835
|
+
fallbackRelation
|
|
6836
|
+
);
|
|
6837
|
+
}
|
|
6838
|
+
function lastRelationSegment(path) {
|
|
6839
|
+
const segments = path.split(".");
|
|
6840
|
+
return segments[segments.length - 1] ?? path;
|
|
6841
|
+
}
|
|
6842
|
+
function resolveJoinRelationName(decision, relationPath) {
|
|
6843
|
+
const context = decision.context;
|
|
6844
|
+
const decisionRecord = decision;
|
|
6845
|
+
return stringProp(context, "relation") ?? stringProp(context, "includeAlias") ?? stringProp(decisionRecord, "relationName") ?? lastRelationSegment(relationPath);
|
|
6846
|
+
}
|
|
6847
|
+
function collectJoinHydrationInfos(planReport) {
|
|
6848
|
+
const candidates = [];
|
|
6849
|
+
for (const decision of planReport.decisions) {
|
|
6850
|
+
if (decision.type !== "include-strategy" || decision.choice !== "join") {
|
|
6851
|
+
continue;
|
|
6852
|
+
}
|
|
6853
|
+
const relationPath = resolveJoinIncludePath(planReport, decision);
|
|
6854
|
+
if (!relationPath) continue;
|
|
6855
|
+
const relationName = resolveJoinRelationName(decision, relationPath);
|
|
6856
|
+
const decisionRecord = decision;
|
|
6857
|
+
const contextRecord = decision.context;
|
|
6858
|
+
const hydrationPrefix = stringProp(decisionRecord, "hydrationPrefix") ?? stringProp(contextRecord, "hydrationPrefix");
|
|
6859
|
+
candidates.push({
|
|
6860
|
+
relationName,
|
|
6861
|
+
relationPath,
|
|
6862
|
+
...hydrationPrefix && { hydrationPrefix }
|
|
6863
|
+
});
|
|
6864
|
+
}
|
|
6865
|
+
const pathCountsByRelationName = countDistinctRelationPathsByName(candidates);
|
|
6866
|
+
const infosByPath = /* @__PURE__ */ new Map();
|
|
6867
|
+
for (const candidate of candidates) {
|
|
6868
|
+
if (infosByPath.has(candidate.relationPath)) continue;
|
|
6869
|
+
const usesFullPath = (pathCountsByRelationName.get(candidate.relationName) ?? 0) > 1;
|
|
6870
|
+
const keyPrefix = candidate.hydrationPrefix ?? (usesFullPath ? candidate.relationPath : candidate.relationName);
|
|
6871
|
+
if (!keyPrefix) continue;
|
|
6872
|
+
infosByPath.set(candidate.relationPath, {
|
|
6873
|
+
relationPath: candidate.relationPath,
|
|
6874
|
+
segments: candidate.relationPath.split("."),
|
|
6875
|
+
keyPrefix,
|
|
6876
|
+
keyPrefixWithDot: `${keyPrefix}.`
|
|
6877
|
+
});
|
|
6878
|
+
}
|
|
6879
|
+
return [...infosByPath.values()];
|
|
6880
|
+
}
|
|
6881
|
+
function findLongestJoinInfo(key, infos) {
|
|
6882
|
+
let best;
|
|
6883
|
+
for (const info of infos) {
|
|
6884
|
+
if (!key.startsWith(info.keyPrefixWithDot)) continue;
|
|
6885
|
+
if (!best || info.keyPrefixWithDot.length > best.keyPrefixWithDot.length) {
|
|
6886
|
+
best = info;
|
|
6887
|
+
}
|
|
6888
|
+
}
|
|
6889
|
+
return best;
|
|
6890
|
+
}
|
|
6891
|
+
function assignNestedValue(target, path, value) {
|
|
6892
|
+
let cursor = target;
|
|
6893
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
6894
|
+
const segment = path[i];
|
|
6895
|
+
if (!segment) return;
|
|
6896
|
+
const existing = cursor[segment];
|
|
6897
|
+
if (existing === null) {
|
|
6898
|
+
return;
|
|
6899
|
+
} else if (typeof existing !== "object" || Array.isArray(existing)) {
|
|
6900
|
+
cursor[segment] = {};
|
|
6901
|
+
}
|
|
6902
|
+
cursor = cursor[segment];
|
|
6903
|
+
}
|
|
6904
|
+
const leaf = path[path.length - 1];
|
|
6905
|
+
if (leaf) cursor[leaf] = value;
|
|
6906
|
+
}
|
|
6907
|
+
function assignColumnValue(target, columnPath, value) {
|
|
6908
|
+
const segments = columnPath.split(".").filter(Boolean);
|
|
6909
|
+
if (segments.length === 0) return;
|
|
6910
|
+
assignNestedValue(target, segments, value);
|
|
6911
|
+
}
|
|
6666
6912
|
var ResultHydrator = class {
|
|
6667
6913
|
model;
|
|
6668
6914
|
from;
|
|
@@ -6739,51 +6985,42 @@ var ResultHydrator = class {
|
|
|
6739
6985
|
* E2E-004: JOIN strategy for to-one relations returns columns like "author.id", "author.name".
|
|
6740
6986
|
*/
|
|
6741
6987
|
hydrateJoinIncludes(results, planReport) {
|
|
6742
|
-
const
|
|
6743
|
-
|
|
6988
|
+
const joinInfos = collectJoinHydrationInfos(planReport);
|
|
6989
|
+
if (joinInfos.length === 0) return;
|
|
6990
|
+
const infosByDepth = [...joinInfos].sort(
|
|
6991
|
+
(a, b) => a.segments.length - b.segments.length
|
|
6744
6992
|
);
|
|
6745
|
-
if (joinDecisions.length === 0) {
|
|
6746
|
-
return;
|
|
6747
|
-
}
|
|
6748
|
-
const joinInfos = joinDecisions.map((d) => d.context?.relation).filter((r) => typeof r === "string").map((relation) => ({ relation, prefix: `${relation}.` }));
|
|
6749
|
-
if (joinInfos.length === 0) {
|
|
6750
|
-
return;
|
|
6751
|
-
}
|
|
6752
|
-
const joinInfosWithCache = joinInfos.map((info) => ({
|
|
6753
|
-
...info,
|
|
6754
|
-
cachedKeys: null
|
|
6755
|
-
}));
|
|
6756
|
-
if (results.length > 0) {
|
|
6757
|
-
const firstRow = results.find(
|
|
6758
|
-
(r) => typeof r === "object" && r !== null
|
|
6759
|
-
);
|
|
6760
|
-
if (firstRow) {
|
|
6761
|
-
const firstRecord = firstRow;
|
|
6762
|
-
const allKeys = Object.keys(firstRecord);
|
|
6763
|
-
for (const info of joinInfosWithCache) {
|
|
6764
|
-
info.cachedKeys = allKeys.filter((k) => k.startsWith(info.prefix));
|
|
6765
|
-
}
|
|
6766
|
-
}
|
|
6767
|
-
}
|
|
6768
6993
|
for (const row of results) {
|
|
6769
6994
|
if (typeof row !== "object" || row === null) {
|
|
6770
6995
|
continue;
|
|
6771
6996
|
}
|
|
6772
6997
|
const record2 = row;
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
delete record2[key];
|
|
6783
|
-
if (val !== null) allNull = false;
|
|
6784
|
-
}
|
|
6998
|
+
const valuesByPath = /* @__PURE__ */ new Map();
|
|
6999
|
+
for (const key of Object.keys(record2)) {
|
|
7000
|
+
const info = findLongestJoinInfo(key, joinInfos);
|
|
7001
|
+
if (!info) continue;
|
|
7002
|
+
const value = record2[key];
|
|
7003
|
+
let entry = valuesByPath.get(info.relationPath);
|
|
7004
|
+
if (!entry) {
|
|
7005
|
+
entry = { values: {}, allNull: true };
|
|
7006
|
+
valuesByPath.set(info.relationPath, entry);
|
|
6785
7007
|
}
|
|
6786
|
-
|
|
7008
|
+
assignColumnValue(
|
|
7009
|
+
entry.values,
|
|
7010
|
+
key.slice(info.keyPrefixWithDot.length),
|
|
7011
|
+
value
|
|
7012
|
+
);
|
|
7013
|
+
if (value !== null && value !== void 0) entry.allNull = false;
|
|
7014
|
+
delete record2[key];
|
|
7015
|
+
}
|
|
7016
|
+
for (const info of infosByDepth) {
|
|
7017
|
+
const entry = valuesByPath.get(info.relationPath);
|
|
7018
|
+
if (!entry) continue;
|
|
7019
|
+
assignNestedValue(
|
|
7020
|
+
record2,
|
|
7021
|
+
info.segments,
|
|
7022
|
+
entry.allNull ? null : entry.values
|
|
7023
|
+
);
|
|
6787
7024
|
}
|
|
6788
7025
|
}
|
|
6789
7026
|
}
|
|
@@ -11474,6 +11711,7 @@ export {
|
|
|
11474
11711
|
composeBeforeMutationHooks,
|
|
11475
11712
|
composeBeforeQueryHooks,
|
|
11476
11713
|
composeOnErrorHooks,
|
|
11714
|
+
countDistinctRelationPathsByName,
|
|
11477
11715
|
createDialectCapabilities,
|
|
11478
11716
|
createHookManager,
|
|
11479
11717
|
createNqlTag,
|
|
@@ -11486,6 +11724,7 @@ export {
|
|
|
11486
11724
|
defineModel,
|
|
11487
11725
|
defineSchema,
|
|
11488
11726
|
denseRank,
|
|
11727
|
+
deriveRelationPathFromIntentPath,
|
|
11489
11728
|
detectForeignKeys,
|
|
11490
11729
|
detectManyToMany,
|
|
11491
11730
|
distinct,
|
|
@@ -11588,6 +11827,7 @@ export {
|
|
|
11588
11827
|
normalizeSchema,
|
|
11589
11828
|
not,
|
|
11590
11829
|
notExists,
|
|
11830
|
+
nqlRaw,
|
|
11591
11831
|
objectToWhereIntent,
|
|
11592
11832
|
op,
|
|
11593
11833
|
or,
|