metal-orm 1.0.83 → 1.0.86

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.
@@ -68,7 +68,7 @@ import { SelectCTEFacet } from './select/cte-facet.js';
68
68
  import { SelectSetOpFacet } from './select/setop-facet.js';
69
69
  import { SelectRelationFacet } from './select/relation-facet.js';
70
70
  import { buildFilterParameters, extractSchema, SchemaOptions, OpenApiSchemaBundle } from '../openapi/index.js';
71
- import { hasParamOperandsInQuery } from '../core/ast/ast-validation.js';
71
+ import { findFirstParamOperandName } from '../core/ast/ast-validation.js';
72
72
 
73
73
  type ColumnSelectionValue =
74
74
  | ColumnDef
@@ -82,10 +82,14 @@ type SelectionValueType<TValue> =
82
82
  TValue extends ColumnDef ? ColumnToTs<TValue> :
83
83
  unknown;
84
84
 
85
- type SelectionResult<TSelection extends Record<string, ColumnSelectionValue>> = {
86
- [K in keyof TSelection]: SelectionValueType<TSelection[K]>;
87
- };
88
-
85
+ type SelectionResult<TSelection extends Record<string, ColumnSelectionValue>> = {
86
+ [K in keyof TSelection]: SelectionValueType<TSelection[K]>;
87
+ };
88
+
89
+ type ParamOperandOptions = {
90
+ allowParamOperands?: boolean;
91
+ };
92
+
89
93
  type SelectionFromKeys<
90
94
  TTable extends TableDef,
91
95
  K extends keyof TTable['columns'] & string
@@ -730,11 +734,12 @@ export class SelectQueryBuilder<T = EntityInstance<TableDef>, TTable extends Tab
730
734
  * Validates that the query does not contain Param operands.
731
735
  * Param proxies are only for schema generation, not execution.
732
736
  */
733
- private validateNoParamOperands(): void {
737
+ private validateNoParamOperands(options?: ParamOperandOptions): void {
738
+ if (options?.allowParamOperands) return;
734
739
  const ast = this.context.hydration.applyToAst(this.context.state.ast);
735
- const hasParams = hasParamOperandsInQuery(ast);
736
- if (hasParams) {
737
- throw new Error('Cannot execute query containing Param operands. Param proxies are only for schema generation (getSchema()). If you need real parameters, use literal values.');
740
+ const paramName = findFirstParamOperandName(ast);
741
+ if (paramName) {
742
+ throw new Error(`Cannot execute query containing Param operand "${paramName}". Param proxies are only for schema generation (getSchema()). If you need real parameters, use literal values.`);
738
743
  }
739
744
  }
740
745
 
@@ -750,13 +755,13 @@ export class SelectQueryBuilder<T = EntityInstance<TableDef>, TTable extends Tab
750
755
  * // users is User[]
751
756
  * users[0] instanceof User; // true
752
757
  */
753
- async execute(ctx: OrmSession): Promise<T[]> {
754
- this.validateNoParamOperands();
758
+ async execute(ctx: OrmSession, options?: ParamOperandOptions): Promise<T[]> {
759
+ this.validateNoParamOperands(options);
755
760
  if (this.entityConstructor) {
756
- return this.executeAs(this.entityConstructor, ctx) as unknown as T[];
761
+ return this.executeAs(this.entityConstructor, ctx, options) as unknown as T[];
757
762
  }
758
763
  const builder = this.ensureDefaultSelection();
759
- return executeHydrated(ctx, builder) as unknown as T[];
764
+ return executeHydrated(ctx, builder, options) as unknown as T[];
760
765
  }
761
766
 
762
767
  /**
@@ -770,10 +775,10 @@ export class SelectQueryBuilder<T = EntityInstance<TableDef>, TTable extends Tab
770
775
  * // rows is EntityInstance<UserTable>[] (plain objects)
771
776
  * rows[0] instanceof User; // false
772
777
  */
773
- async executePlain(ctx: OrmSession): Promise<EntityInstance<TTable>[]> {
774
- this.validateNoParamOperands();
778
+ async executePlain(ctx: OrmSession, options?: ParamOperandOptions): Promise<EntityInstance<TTable>[]> {
779
+ this.validateNoParamOperands(options);
775
780
  const builder = this.ensureDefaultSelection();
776
- const rows = await executeHydratedPlain(ctx, builder);
781
+ const rows = await executeHydratedPlain(ctx, builder, options);
777
782
  return rows as EntityInstance<TTable>[];
778
783
  }
779
784
 
@@ -793,11 +798,12 @@ export class SelectQueryBuilder<T = EntityInstance<TableDef>, TTable extends Tab
793
798
  */
794
799
  async executeAs<TEntity extends object>(
795
800
  entityClass: EntityConstructor<TEntity>,
796
- ctx: OrmSession
801
+ ctx: OrmSession,
802
+ options?: ParamOperandOptions
797
803
  ): Promise<TEntity[]> {
798
- this.validateNoParamOperands();
804
+ this.validateNoParamOperands(options);
799
805
  const builder = this.ensureDefaultSelection();
800
- const results = await executeHydrated(ctx, builder);
806
+ const results = await executeHydrated(ctx, builder, options);
801
807
  return materializeAs(entityClass, results as unknown as Record<string, unknown>[]);
802
808
  }
803
809
 
@@ -807,9 +813,9 @@ export class SelectQueryBuilder<T = EntityInstance<TableDef>, TTable extends Tab
807
813
  * @example
808
814
  * const total = await qb.count(session);
809
815
  */
810
- async count(session: OrmSession): Promise<number> {
811
- this.validateNoParamOperands();
812
- return executeCount(this.context, this.env, session);
816
+ async count(session: OrmSession, options?: ParamOperandOptions): Promise<number> {
817
+ this.validateNoParamOperands(options);
818
+ return executeCount(this.context, this.env, session, options);
813
819
  }
814
820
 
815
821
  /**
@@ -820,11 +826,11 @@ export class SelectQueryBuilder<T = EntityInstance<TableDef>, TTable extends Tab
820
826
  */
821
827
  async executePaged(
822
828
  session: OrmSession,
823
- options: { page: number; pageSize: number }
829
+ options: { page: number; pageSize: number } & ParamOperandOptions
824
830
  ): Promise<PaginatedResult<T>> {
825
- this.validateNoParamOperands();
831
+ this.validateNoParamOperands(options);
826
832
  const builder = this.ensureDefaultSelection();
827
- return executePagedQuery(builder, session, options, sess => builder.count(sess));
833
+ return executePagedQuery(builder, session, options, sess => builder.count(sess, options), options);
828
834
  }
829
835
 
830
836
  /**
@@ -837,10 +843,10 @@ export class SelectQueryBuilder<T = EntityInstance<TableDef>, TTable extends Tab
837
843
  * const hydCtx = new HydrationContext();
838
844
  * const users = await qb.executeWithContexts(execCtx, hydCtx);
839
845
  */
840
- async executeWithContexts(execCtx: ExecutionContext, hydCtx: HydrationContext): Promise<T[]> {
841
- this.validateNoParamOperands();
846
+ async executeWithContexts(execCtx: ExecutionContext, hydCtx: HydrationContext, options?: ParamOperandOptions): Promise<T[]> {
847
+ this.validateNoParamOperands(options);
842
848
  const builder = this.ensureDefaultSelection();
843
- const results = await executeHydratedWithContexts(execCtx, hydCtx, builder);
849
+ const results = await executeHydratedWithContexts(execCtx, hydCtx, builder, options);
844
850
  if (this.entityConstructor) {
845
851
  return materializeAs(this.entityConstructor, results as unknown as Record<string, unknown>[]) as unknown as T[];
846
852
  }
@@ -1109,10 +1115,18 @@ export class SelectQueryBuilder<T = EntityInstance<TableDef>, TTable extends Tab
1109
1115
  * .compile('postgres');
1110
1116
  * console.log(compiled.sql); // SELECT "id", "name" FROM "users" WHERE "active" = true
1111
1117
  */
1112
- compile(dialect: SelectDialectInput): CompiledQuery {
1113
- const resolved = resolveDialectInput(dialect);
1114
- return resolved.compileSelect(this.getAST());
1115
- }
1118
+ compile(dialect: SelectDialectInput, options?: ParamOperandOptions): CompiledQuery {
1119
+ const resolved = resolveDialectInput(dialect);
1120
+ const ast = this.getAST();
1121
+ if (!options?.allowParamOperands) {
1122
+ const paramName = findFirstParamOperandName(ast);
1123
+ if (paramName) {
1124
+ throw new Error(`Cannot compile query containing Param operand "${paramName}". Param proxies are only for schema generation (getSchema()). If you need real parameters, use literal values.`);
1125
+ }
1126
+ return resolved.compileSelect(ast);
1127
+ }
1128
+ return resolved.compileSelectWithOptions(ast, { allowParams: true });
1129
+ }
1116
1130
 
1117
1131
  /**
1118
1132
  * Converts the query to SQL string for a specific dialect
@@ -1124,9 +1138,9 @@ export class SelectQueryBuilder<T = EntityInstance<TableDef>, TTable extends Tab
1124
1138
  * .toSql('postgres');
1125
1139
  * console.log(sql); // SELECT "id", "name" FROM "users" WHERE "active" = true
1126
1140
  */
1127
- toSql(dialect: SelectDialectInput): string {
1128
- return this.compile(dialect).sql;
1129
- }
1141
+ toSql(dialect: SelectDialectInput, options?: ParamOperandOptions): string {
1142
+ return this.compile(dialect, options).sql;
1143
+ }
1130
1144
 
1131
1145
  /**
1132
1146
  * Gets hydration plan for query