@dbsp/core 1.0.3 → 1.0.5
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 +47 -7
- package/dist/index.js +145 -52
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1726,6 +1726,36 @@ declare class ModelIRImpl implements ModelIR {
|
|
|
1726
1726
|
private detectCircularRelations;
|
|
1727
1727
|
}
|
|
1728
1728
|
|
|
1729
|
+
/**
|
|
1730
|
+
* Validate a PostgreSQL type name for use in CAST($N AS type[]).
|
|
1731
|
+
*
|
|
1732
|
+
* Accepted grammar (structured, not a broad character-class):
|
|
1733
|
+
* typeName = trim(base [modifier] [arraySuffix])
|
|
1734
|
+
*
|
|
1735
|
+
* arraySuffix = "[]" — at most ONE level; "int4[][]" is rejected as a
|
|
1736
|
+
* raw type-name input (array-ness is handled by the
|
|
1737
|
+
* batch-values layer which appends its own "[]").
|
|
1738
|
+
* modifier = "(" digits ")" | "(" digits "," digits ")"
|
|
1739
|
+
* — e.g. "(255)", "(10,2)"; any other parenthesised
|
|
1740
|
+
* content is rejected.
|
|
1741
|
+
* base = multiWordType | ident | ident "." ident
|
|
1742
|
+
* multiWordType = one of: "timestamp with time zone",
|
|
1743
|
+
* "timestamp without time zone",
|
|
1744
|
+
* "time with time zone",
|
|
1745
|
+
* "time without time zone",
|
|
1746
|
+
* "double precision",
|
|
1747
|
+
* "character varying",
|
|
1748
|
+
* "bit varying"
|
|
1749
|
+
* ident = [A-Za-z_][A-Za-z0-9_]*
|
|
1750
|
+
*
|
|
1751
|
+
* Examples that pass: int4, text, uuid, numeric(10,2), varchar(255),
|
|
1752
|
+
* timestamp with time zone, myschema.myenum, int4[].
|
|
1753
|
+
* Examples that throw: empty string, int4[][], int4) ; DROP TABLE x; --,
|
|
1754
|
+
* foo'bar, int4)); JOIN users ON true.
|
|
1755
|
+
*
|
|
1756
|
+
* @internal — exported for adapter compile-time revalidation only.
|
|
1757
|
+
*/
|
|
1758
|
+
declare function validateTypeName(typeName: string): void;
|
|
1729
1759
|
/**
|
|
1730
1760
|
* BatchValuesRef — a virtual batch data source backed by unnest($1::type[], ...).
|
|
1731
1761
|
*
|
|
@@ -2573,15 +2603,20 @@ declare function notExists(relation: string, options?: {
|
|
|
2573
2603
|
* Accepts a SubqueryBuilder (must have `.build()`) or any builder
|
|
2574
2604
|
* exposing `buildIntent(): QueryIntent` (e.g. QueryBuilder).
|
|
2575
2605
|
*
|
|
2606
|
+
* **Limitation:** correlated subqueries (using `outerRef()` inside the inner WHERE)
|
|
2607
|
+
* are NOT supported and will throw at compile time. For correlated EXISTS over an
|
|
2608
|
+
* FK-declared relation, use `exists('relation', { where: ... outerRef(...) })` instead.
|
|
2609
|
+
*
|
|
2576
2610
|
* @param subquery - A SubqueryBuilder or any object with buildIntent()
|
|
2577
2611
|
*
|
|
2578
2612
|
* @example
|
|
2579
|
-
* // EXISTS (SELECT 1 FROM audit_log WHERE audit_log.
|
|
2580
|
-
*
|
|
2613
|
+
* // EXISTS (SELECT 1 FROM audit_log WHERE audit_log.entity_type = 'login')
|
|
2614
|
+
* // Uncorrelated: no reference to the outer row — inner filter is a plain value.
|
|
2615
|
+
* rawExists(subquery('audit_log').select('id').where(eq('entityType', 'login')))
|
|
2581
2616
|
*
|
|
2582
2617
|
* @example
|
|
2583
|
-
* // EXISTS with a full query builder
|
|
2584
|
-
* rawExists(orm.select('sessions').where(and(eq('
|
|
2618
|
+
* // EXISTS with a full query builder — polymorphic table, no FK to source
|
|
2619
|
+
* rawExists(orm.select('sessions').where(and(eq('status', 'active'), gt('expiresAt', new Date()))))
|
|
2585
2620
|
*/
|
|
2586
2621
|
declare function rawExists(sq: SubqueryBuilder | {
|
|
2587
2622
|
buildIntent(): QueryIntent;
|
|
@@ -2593,11 +2628,16 @@ declare function rawExists(sq: SubqueryBuilder | {
|
|
|
2593
2628
|
* Accepts a SubqueryBuilder (must have `.build()`) or any builder
|
|
2594
2629
|
* exposing `buildIntent(): QueryIntent` (e.g. QueryBuilder).
|
|
2595
2630
|
*
|
|
2631
|
+
* **Limitation:** correlated subqueries (using `outerRef()` inside the inner WHERE)
|
|
2632
|
+
* are NOT supported and will throw at compile time. For correlated NOT EXISTS over an
|
|
2633
|
+
* FK-declared relation, use `notExists('relation', { where: ... outerRef(...) })` instead.
|
|
2634
|
+
*
|
|
2596
2635
|
* @param subquery - A SubqueryBuilder or any object with buildIntent()
|
|
2597
2636
|
*
|
|
2598
2637
|
* @example
|
|
2599
|
-
* // NOT EXISTS (SELECT 1 FROM bans WHERE bans.
|
|
2600
|
-
*
|
|
2638
|
+
* // NOT EXISTS (SELECT 1 FROM bans WHERE bans.reason = 'spam')
|
|
2639
|
+
* // Uncorrelated: inner filter is a plain value, no reference to the outer row.
|
|
2640
|
+
* rawNotExists(subquery('bans').select('id').where(eq('reason', 'spam')))
|
|
2601
2641
|
*/
|
|
2602
2642
|
declare function rawNotExists(sq: SubqueryBuilder | {
|
|
2603
2643
|
buildIntent(): QueryIntent;
|
|
@@ -8144,4 +8184,4 @@ declare const assertIntentHasOrderBy: (result: AssertionQueryResult, expected: b
|
|
|
8144
8184
|
*/
|
|
8145
8185
|
declare function runAssertions(blocks: AssertionBlock[], results: AssertionQueryResult[], queries: string[], hasDb?: boolean): AssertionSummary;
|
|
8146
8186
|
|
|
8147
|
-
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, wAvg, wCount, wMax, wMin, wSum, withPriority };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -2146,6 +2146,7 @@ function plan(intent, model, options = {}) {
|
|
|
2146
2146
|
throw new Error(`Unknown table: ${intent.from}`);
|
|
2147
2147
|
}
|
|
2148
2148
|
const optimizedWhere = intent.where ? optimizeInToExists(intent.where, intent.from, model) : void 0;
|
|
2149
|
+
const plannedIntent = optimizedWhere !== void 0 && optimizedWhere !== intent.where ? { ...intent, where: optimizedWhere } : intent;
|
|
2149
2150
|
if (optimizedWhere) {
|
|
2150
2151
|
processWhere(optimizedWhere, intent.from, model, state, opts, "where");
|
|
2151
2152
|
}
|
|
@@ -2188,7 +2189,13 @@ function plan(intent, model, options = {}) {
|
|
|
2188
2189
|
decisions: Object.freeze(state.decisions.slice()),
|
|
2189
2190
|
warnings: Object.freeze(state.warnings.slice()),
|
|
2190
2191
|
ctes: Object.freeze(state.ctes.slice()),
|
|
2192
|
+
// intent is ALWAYS the original submitted intent (contract: observable via dump()).
|
|
2193
|
+
// executableIntent carries the optimized WHERE (e.g. IN→EXISTS) when the
|
|
2194
|
+
// optimizer rewrote it, so the adapter compiles the correct SQL from that field.
|
|
2195
|
+
// When no optimization applies, executableIntent is left undefined and the
|
|
2196
|
+
// adapter falls back to intent.
|
|
2191
2197
|
intent,
|
|
2198
|
+
...plannedIntent !== intent && { executableIntent: plannedIntent },
|
|
2192
2199
|
metadata
|
|
2193
2200
|
};
|
|
2194
2201
|
return Object.freeze(report);
|
|
@@ -2326,11 +2333,15 @@ function isSubquerySelectedColumnNonNullable(existsIntent, sourceTable, model) {
|
|
|
2326
2333
|
if (!column) return false;
|
|
2327
2334
|
return !column.nullable;
|
|
2328
2335
|
}
|
|
2329
|
-
function optimizeInToExists(where, sourceTable, model) {
|
|
2336
|
+
function optimizeInToExists(where, sourceTable, model, negated = false) {
|
|
2330
2337
|
switch (where.kind) {
|
|
2331
2338
|
case "in": {
|
|
2332
2339
|
const inWhere = where;
|
|
2333
2340
|
if (!inWhere.subquery) return where;
|
|
2341
|
+
const sq = inWhere.subquery;
|
|
2342
|
+
if (sq.limit != null || sq.orderBy?.length || sq.offset != null || sq.groupBy?.length || sq.having != null || sq.distinct || sq.distinctOn?.length || sq.joins?.length || sq.include?.length || sq.batchValuesSource != null || sq.existsWrap || sq.lock != null || sq.select != null && sq.select.type !== "fields") {
|
|
2343
|
+
return where;
|
|
2344
|
+
}
|
|
2334
2345
|
const subSelect = inWhere.subquery.select;
|
|
2335
2346
|
if (!subSelect || subSelect.type !== "fields") return where;
|
|
2336
2347
|
const fields = "fields" in subSelect ? subSelect.fields : void 0;
|
|
@@ -2350,18 +2361,24 @@ function optimizeInToExists(where, sourceTable, model) {
|
|
|
2350
2361
|
}
|
|
2351
2362
|
}
|
|
2352
2363
|
if (!matchedRelation) return where;
|
|
2353
|
-
const
|
|
2354
|
-
|
|
2364
|
+
const effectiveNegated = negated !== Boolean(inWhere.not);
|
|
2365
|
+
if (effectiveNegated) {
|
|
2366
|
+
const existsIntent = { relation: matchedRelation };
|
|
2367
|
+
if (!isSubquerySelectedColumnNonNullable(existsIntent, sourceTable, model)) {
|
|
2368
|
+
return where;
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
const targetKind = inWhere.not ? "notExists" : "exists";
|
|
2372
|
+
return {
|
|
2373
|
+
kind: targetKind,
|
|
2355
2374
|
relation: matchedRelation,
|
|
2356
|
-
// Forward the subquery's inner WHERE conditions
|
|
2357
2375
|
...inWhere.subquery.where && { where: inWhere.subquery.where }
|
|
2358
2376
|
};
|
|
2359
|
-
return existsWhere;
|
|
2360
2377
|
}
|
|
2361
2378
|
case "and": {
|
|
2362
2379
|
const andWhere = where;
|
|
2363
2380
|
const optimized = andWhere.conditions.map(
|
|
2364
|
-
(c) => optimizeInToExists(c, sourceTable, model)
|
|
2381
|
+
(c) => optimizeInToExists(c, sourceTable, model, negated)
|
|
2365
2382
|
);
|
|
2366
2383
|
if (optimized.every((c, i) => c === andWhere.conditions[i])) return where;
|
|
2367
2384
|
return { kind: "and", conditions: optimized };
|
|
@@ -2369,7 +2386,7 @@ function optimizeInToExists(where, sourceTable, model) {
|
|
|
2369
2386
|
case "or": {
|
|
2370
2387
|
const orWhere = where;
|
|
2371
2388
|
const optimized = orWhere.conditions.map(
|
|
2372
|
-
(c) => optimizeInToExists(c, sourceTable, model)
|
|
2389
|
+
(c) => optimizeInToExists(c, sourceTable, model, negated)
|
|
2373
2390
|
);
|
|
2374
2391
|
if (optimized.every((c, i) => c === orWhere.conditions[i])) return where;
|
|
2375
2392
|
return { kind: "or", conditions: optimized };
|
|
@@ -2379,22 +2396,10 @@ function optimizeInToExists(where, sourceTable, model) {
|
|
|
2379
2396
|
const optimized = optimizeInToExists(
|
|
2380
2397
|
notWhere.condition,
|
|
2381
2398
|
sourceTable,
|
|
2382
|
-
model
|
|
2399
|
+
model,
|
|
2400
|
+
!negated
|
|
2383
2401
|
);
|
|
2384
2402
|
if (optimized === notWhere.condition) return where;
|
|
2385
|
-
if (optimized.kind === "exists") {
|
|
2386
|
-
if (!isSubquerySelectedColumnNonNullable(
|
|
2387
|
-
optimized,
|
|
2388
|
-
sourceTable,
|
|
2389
|
-
model
|
|
2390
|
-
)) {
|
|
2391
|
-
return where;
|
|
2392
|
-
}
|
|
2393
|
-
return {
|
|
2394
|
-
...optimized,
|
|
2395
|
-
kind: "notExists"
|
|
2396
|
-
};
|
|
2397
|
-
}
|
|
2398
2403
|
return { kind: "not", condition: optimized };
|
|
2399
2404
|
}
|
|
2400
2405
|
default:
|
|
@@ -2476,6 +2481,26 @@ function processWhere(where, sourceTable, model, state, opts, intentPath) {
|
|
|
2476
2481
|
break;
|
|
2477
2482
|
case "expression":
|
|
2478
2483
|
break;
|
|
2484
|
+
// Custom expression — no relation analysis, pass through
|
|
2485
|
+
// Adapter-only kinds: planner records no decisions; the adapter compiles
|
|
2486
|
+
// them directly from the intent. Explicit cases here prevent silent
|
|
2487
|
+
// fallthrough and keep the switch exhaustive.
|
|
2488
|
+
case "rawExists":
|
|
2489
|
+
case "rawNotExists":
|
|
2490
|
+
break;
|
|
2491
|
+
case "subquery":
|
|
2492
|
+
break;
|
|
2493
|
+
case "range":
|
|
2494
|
+
break;
|
|
2495
|
+
case "jsonContains":
|
|
2496
|
+
case "jsonExists":
|
|
2497
|
+
break;
|
|
2498
|
+
default: {
|
|
2499
|
+
const _exhaustive = where;
|
|
2500
|
+
throw new Error(
|
|
2501
|
+
`processWhere: unhandled WhereIntent kind '${_exhaustive.kind}'`
|
|
2502
|
+
);
|
|
2503
|
+
}
|
|
2479
2504
|
}
|
|
2480
2505
|
}
|
|
2481
2506
|
function processRelationFilter(relationPath, sourceTable, model, state, opts, intentPath, nestedWhere, mode) {
|
|
@@ -3012,6 +3037,60 @@ function assertCapability(adapter, capability, operation) {
|
|
|
3012
3037
|
}
|
|
3013
3038
|
|
|
3014
3039
|
// src/dx/batch-values.ts
|
|
3040
|
+
var MULTIWORD_BASE_TYPES = [
|
|
3041
|
+
"timestamp with time zone",
|
|
3042
|
+
"timestamp without time zone",
|
|
3043
|
+
"time with time zone",
|
|
3044
|
+
"time without time zone",
|
|
3045
|
+
"double precision",
|
|
3046
|
+
"character varying",
|
|
3047
|
+
"bit varying"
|
|
3048
|
+
];
|
|
3049
|
+
var IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3050
|
+
function validateTypeName(typeName) {
|
|
3051
|
+
const raw2 = typeName.trim();
|
|
3052
|
+
if (raw2.length === 0) {
|
|
3053
|
+
throw new Error(
|
|
3054
|
+
`batchValues: invalid type name '${typeName}'. Type names must not be empty.`
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
3057
|
+
let rest = raw2;
|
|
3058
|
+
if (rest.endsWith("[]")) {
|
|
3059
|
+
rest = rest.slice(0, -2);
|
|
3060
|
+
if (rest.endsWith("[]")) {
|
|
3061
|
+
throw new Error(
|
|
3062
|
+
`batchValues: invalid type name '${typeName}'. At most one array suffix "[]" is allowed as a raw type-name input. Use "int4[]" not "int4[][]".`
|
|
3063
|
+
);
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
const modifierMatch = rest.match(/\(([^)]*)\)$/);
|
|
3067
|
+
if (modifierMatch) {
|
|
3068
|
+
const inner = modifierMatch[1] ?? "";
|
|
3069
|
+
if (!/^\d+(?:,\d+)?$/.test(inner)) {
|
|
3070
|
+
throw new Error(
|
|
3071
|
+
`batchValues: invalid type name '${typeName}'. Type modifier must be "(N)" or "(N,M)" with digits only; got "(${inner})".`
|
|
3072
|
+
);
|
|
3073
|
+
}
|
|
3074
|
+
rest = rest.slice(0, rest.length - modifierMatch[0].length).trimEnd();
|
|
3075
|
+
}
|
|
3076
|
+
const baseLower = rest.toLowerCase();
|
|
3077
|
+
if (MULTIWORD_BASE_TYPES.includes(baseLower)) {
|
|
3078
|
+
return;
|
|
3079
|
+
}
|
|
3080
|
+
const parts = rest.split(".");
|
|
3081
|
+
if (parts.length > 2) {
|
|
3082
|
+
throw new Error(
|
|
3083
|
+
`batchValues: invalid type name '${typeName}'. Schema-qualified types allow at most one dot (schema.type).`
|
|
3084
|
+
);
|
|
3085
|
+
}
|
|
3086
|
+
for (const part of parts) {
|
|
3087
|
+
if (!IDENT_RE.test(part)) {
|
|
3088
|
+
throw new Error(
|
|
3089
|
+
`batchValues: invalid type name '${typeName}'. Base type "${part}" is not a valid SQL identifier ([A-Za-z_][A-Za-z0-9_]*) and is not in the multi-word type allowlist.`
|
|
3090
|
+
);
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3015
3094
|
function isBatchValuesRef(value) {
|
|
3016
3095
|
return typeof value === "object" && value !== null && "__kind" in value && value.__kind === "batchValues";
|
|
3017
3096
|
}
|
|
@@ -3024,20 +3103,28 @@ function batchValues(data, columns, types, opts) {
|
|
|
3024
3103
|
if (columns.length === 0) {
|
|
3025
3104
|
throw new Error("batchValues: at least one column is required");
|
|
3026
3105
|
}
|
|
3027
|
-
const
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
`batchValues: invalid type name '${invalidType}'. Type names must contain only letters, digits, and underscores.`
|
|
3031
|
-
);
|
|
3106
|
+
const normalizedTypes = types.map((t) => t.trim());
|
|
3107
|
+
for (const t of normalizedTypes) {
|
|
3108
|
+
validateTypeName(t);
|
|
3032
3109
|
}
|
|
3033
|
-
|
|
3110
|
+
const alias = opts?.alias ?? "batch";
|
|
3111
|
+
validateIdentifier(alias, "alias");
|
|
3112
|
+
for (const col2 of columns) {
|
|
3113
|
+
validateIdentifier(col2, "column");
|
|
3114
|
+
}
|
|
3115
|
+
const frozenData = Object.freeze(
|
|
3116
|
+
data.map((row) => Object.freeze([...row]))
|
|
3117
|
+
);
|
|
3118
|
+
const frozenColumns = Object.freeze([...columns]);
|
|
3119
|
+
const frozenTypes = Object.freeze([...normalizedTypes]);
|
|
3120
|
+
return Object.freeze({
|
|
3034
3121
|
__kind: "batchValues",
|
|
3035
|
-
data,
|
|
3036
|
-
columns,
|
|
3037
|
-
types,
|
|
3038
|
-
alias
|
|
3122
|
+
data: frozenData,
|
|
3123
|
+
columns: frozenColumns,
|
|
3124
|
+
types: frozenTypes,
|
|
3125
|
+
alias,
|
|
3039
3126
|
ordinality: opts?.ordinality ?? false
|
|
3040
|
-
};
|
|
3127
|
+
});
|
|
3041
3128
|
}
|
|
3042
3129
|
|
|
3043
3130
|
// src/dx/expressions.ts
|
|
@@ -4018,36 +4105,31 @@ function coalesce(fields, as) {
|
|
|
4018
4105
|
};
|
|
4019
4106
|
}
|
|
4020
4107
|
function raw(sqlFragment, as) {
|
|
4021
|
-
|
|
4022
|
-
throw new Error("raw() requires a non-empty alias");
|
|
4023
|
-
}
|
|
4108
|
+
validateIdentifier(as, "column");
|
|
4024
4109
|
return {
|
|
4025
4110
|
__expr: true,
|
|
4026
4111
|
intent: { kind: "raw", sql: sqlFragment, as }
|
|
4027
4112
|
};
|
|
4028
4113
|
}
|
|
4029
4114
|
function col(column, alias) {
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
}
|
|
4033
|
-
if (!alias || alias.trim() === "") {
|
|
4034
|
-
throw new Error("col() requires a non-empty alias");
|
|
4035
|
-
}
|
|
4115
|
+
validateIdentifier(column, "column");
|
|
4116
|
+
validateIdentifier(alias, "column");
|
|
4036
4117
|
return {
|
|
4037
4118
|
__expr: true,
|
|
4038
4119
|
intent: { kind: "columnAlias", column, alias }
|
|
4039
4120
|
};
|
|
4040
4121
|
}
|
|
4041
4122
|
function relationColumn(relation, column, as) {
|
|
4042
|
-
if (!relation
|
|
4123
|
+
if (!relation) {
|
|
4043
4124
|
throw new Error("relationColumn() requires a non-empty relation path");
|
|
4044
4125
|
}
|
|
4045
|
-
|
|
4046
|
-
|
|
4126
|
+
for (const segment of relation.split(".")) {
|
|
4127
|
+
validateIdentifier(segment, "relation");
|
|
4047
4128
|
}
|
|
4048
|
-
if (
|
|
4049
|
-
|
|
4129
|
+
if (column !== "*") {
|
|
4130
|
+
validateIdentifier(column, "column");
|
|
4050
4131
|
}
|
|
4132
|
+
validateIdentifier(as, "column");
|
|
4051
4133
|
return {
|
|
4052
4134
|
__expr: true,
|
|
4053
4135
|
intent: { kind: "relationColumn", relation, column, as }
|
|
@@ -4406,7 +4488,7 @@ async function runAfterMutationHooks(hooks, ctx, result, onHookError) {
|
|
|
4406
4488
|
error,
|
|
4407
4489
|
hook.name || "anonymous",
|
|
4408
4490
|
frozen,
|
|
4409
|
-
"
|
|
4491
|
+
"afterMutation"
|
|
4410
4492
|
);
|
|
4411
4493
|
if (action === "continue") continue;
|
|
4412
4494
|
}
|
|
@@ -4619,7 +4701,8 @@ function subquery(table) {
|
|
|
4619
4701
|
function outerRef(column) {
|
|
4620
4702
|
return {
|
|
4621
4703
|
kind: "ref",
|
|
4622
|
-
column
|
|
4704
|
+
column,
|
|
4705
|
+
outer: true
|
|
4623
4706
|
};
|
|
4624
4707
|
}
|
|
4625
4708
|
function isSubqueryExpression(value) {
|
|
@@ -7056,10 +7139,9 @@ function stream(builder, options) {
|
|
|
7056
7139
|
};
|
|
7057
7140
|
let hookIntent = rawIntent;
|
|
7058
7141
|
try {
|
|
7059
|
-
const afterHookCtx = await
|
|
7060
|
-
hookStore
|
|
7061
|
-
ctx,
|
|
7062
|
-
onHookError
|
|
7142
|
+
const afterHookCtx = await withReentrancyGuard(
|
|
7143
|
+
hookStore,
|
|
7144
|
+
(s) => runBeforeQueryHooks(s.beforeQuery, ctx, onHookError)
|
|
7063
7145
|
);
|
|
7064
7146
|
hookIntent = afterHookCtx.intent;
|
|
7065
7147
|
} catch (error) {
|
|
@@ -7515,6 +7597,13 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
|
|
|
7515
7597
|
"join(batchValuesRef): an `on` condition is required for BatchValues joins"
|
|
7516
7598
|
);
|
|
7517
7599
|
}
|
|
7600
|
+
validateIdentifier(bv.alias, "alias");
|
|
7601
|
+
for (const col2 of bv.columns) {
|
|
7602
|
+
validateIdentifier(col2, "column");
|
|
7603
|
+
}
|
|
7604
|
+
if (opts.as !== void 0) {
|
|
7605
|
+
validateIdentifier(opts.as, "alias");
|
|
7606
|
+
}
|
|
7518
7607
|
const joinIntent = {
|
|
7519
7608
|
batchValues: {
|
|
7520
7609
|
data: bv.data,
|
|
@@ -7530,6 +7619,9 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
|
|
|
7530
7619
|
builder.joinIntents.push(joinIntent);
|
|
7531
7620
|
} else {
|
|
7532
7621
|
validateIdentifier(relationOrTableOrBatch, "table");
|
|
7622
|
+
if (opts?.as !== void 0) {
|
|
7623
|
+
validateIdentifier(opts.as, "alias");
|
|
7624
|
+
}
|
|
7533
7625
|
const joinIntent = opts?.on ? {
|
|
7534
7626
|
table: relationOrTableOrBatch,
|
|
7535
7627
|
on: opts.on,
|
|
@@ -11550,6 +11642,7 @@ export {
|
|
|
11550
11642
|
validateAssertionBlocks,
|
|
11551
11643
|
validateRecursiveInclude,
|
|
11552
11644
|
validateRecursiveShape,
|
|
11645
|
+
validateTypeName,
|
|
11553
11646
|
wAvg,
|
|
11554
11647
|
wCount,
|
|
11555
11648
|
wMax,
|