@mikro-orm/sql
Advanced tools
@@ -39,2 +39,4 @@ import { type RawQueryFragment, type Constructor, type EntityManager, type EntityRepository, type IDatabaseDriver, type IsolationLevel, type MikroORM, Platform } from '@mikro-orm/core'; | ||
| supportsUnionWhere(): boolean; | ||
| /** Whether the platform supports `count(distinct col1, col2)` with multiple columns. If false, a subquery wrapper is used instead. */ | ||
| supportsMultiColumnCountDistinct(): boolean; | ||
| supportsSchemas(): boolean; | ||
@@ -41,0 +43,0 @@ /** @inheritDoc */ |
@@ -94,2 +94,6 @@ import { isRaw, JsonProperty, Platform, raw, Utils, } from '@mikro-orm/core'; | ||
| } | ||
| /** Whether the platform supports `count(distinct col1, col2)` with multiple columns. If false, a subquery wrapper is used instead. */ | ||
| supportsMultiColumnCountDistinct() { | ||
| return false; | ||
| } | ||
| supportsSchemas() { | ||
@@ -96,0 +100,0 @@ return false; |
@@ -140,4 +140,9 @@ import { LockMode, QueryFlag, RawQueryFragment, Utils } from '@mikro-orm/core'; | ||
| compileSelect() { | ||
| const wrapCountSubquery = this.needsCountSubquery(); | ||
| if (wrapCountSubquery) { | ||
| this.parts.push(`select count(*) as ${this.quote('count')} from (`); | ||
| } | ||
| this.parts.push('select'); | ||
| if (this.options.limit != null && this.options.offset == null) { | ||
| // skip top(?) inside the count subquery — it would limit the distinct rows before counting | ||
| if (this.options.limit != null && this.options.offset == null && !wrapCountSubquery) { | ||
| this.parts.push(`top (?)`); | ||
@@ -147,3 +152,3 @@ this.params.push(this.options.limit); | ||
| this.addHintComment(); | ||
| this.parts.push(`${this.getFields()} from ${this.getTableName()}`); | ||
| this.parts.push(`${this.getFields(wrapCountSubquery)} from ${this.getTableName()}`); | ||
| this.addLockClause(); | ||
@@ -168,17 +173,23 @@ if (this.options.joins) { | ||
| } | ||
| if (this.options.orderBy) { | ||
| this.parts.push(`order by ${this.options.orderBy}`); | ||
| } | ||
| if (this.options.offset != null) { | ||
| /* v8 ignore next */ | ||
| if (!this.options.orderBy) { | ||
| throw new Error('Order by clause is required for pagination'); | ||
| if (!wrapCountSubquery) { | ||
| if (this.options.orderBy) { | ||
| this.parts.push(`order by ${this.options.orderBy}`); | ||
| } | ||
| this.parts.push(`offset ? rows`); | ||
| this.params.push(this.options.offset); | ||
| if (this.options.limit != null) { | ||
| this.parts.push(`fetch next ? rows only`); | ||
| this.params.push(this.options.limit); | ||
| if (this.options.offset != null) { | ||
| /* v8 ignore next */ | ||
| if (!this.options.orderBy) { | ||
| throw new Error('Order by clause is required for pagination'); | ||
| } | ||
| this.parts.push(`offset ? rows`); | ||
| this.params.push(this.options.offset); | ||
| if (this.options.limit != null) { | ||
| this.parts.push(`fetch next ? rows only`); | ||
| this.params.push(this.options.limit); | ||
| } | ||
| } | ||
| } | ||
| if (wrapCountSubquery) { | ||
| const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' '; | ||
| this.parts.push(`)${asKeyword}${this.quote('dcnt')}`); | ||
| } | ||
| } | ||
@@ -185,0 +196,0 @@ addLockClause() { |
@@ -17,2 +17,3 @@ import { type SimpleColumnMeta, type Type, type TransformContext, type MikroORM, type IsolationLevel } from '@mikro-orm/core'; | ||
| }; | ||
| supportsMultiColumnCountDistinct(): boolean; | ||
| /** @internal */ | ||
@@ -19,0 +20,0 @@ createNativeQueryBuilder(): MySqlNativeQueryBuilder; |
@@ -21,2 +21,5 @@ import { Utils, QueryOrder, DecimalType, DoubleType, } from '@mikro-orm/core'; | ||
| }; | ||
| supportsMultiColumnCountDistinct() { | ||
| return true; | ||
| } | ||
| /** @internal */ | ||
@@ -23,0 +26,0 @@ createNativeQueryBuilder() { |
@@ -216,5 +216,9 @@ import { raw, RawQueryFragment, Utils } from '@mikro-orm/core'; | ||
| compileSelect() { | ||
| const wrapCountSubquery = this.needsCountSubquery(); | ||
| if (wrapCountSubquery) { | ||
| this.parts.push(`select count(*) as ${this.quote('count')} from (`); | ||
| } | ||
| this.parts.push('select'); | ||
| this.addHintComment(); | ||
| this.parts.push(`${this.getFields()} from ${this.getTableName()}`); | ||
| this.parts.push(`${this.getFields(wrapCountSubquery)} from ${this.getTableName()}`); | ||
| if (this.options.joins) { | ||
@@ -238,14 +242,20 @@ for (const join of this.options.joins) { | ||
| } | ||
| if (this.options.orderBy) { | ||
| this.parts.push(`order by ${this.options.orderBy}`); | ||
| if (!wrapCountSubquery) { | ||
| if (this.options.orderBy) { | ||
| this.parts.push(`order by ${this.options.orderBy}`); | ||
| } | ||
| if (this.options.offset != null) { | ||
| this.parts.push(`offset ? rows`); | ||
| this.params.push(this.options.offset); | ||
| } | ||
| if (this.options.limit != null) { | ||
| this.parts.push(`fetch next ? rows only`); | ||
| this.params.push(this.options.limit); | ||
| } | ||
| } | ||
| if (this.options.offset != null) { | ||
| this.parts.push(`offset ? rows`); | ||
| this.params.push(this.options.offset); | ||
| if (wrapCountSubquery) { | ||
| const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' '; | ||
| this.parts.push(`)${asKeyword}${this.quote('dcnt')}`); | ||
| } | ||
| if (this.options.limit != null) { | ||
| this.parts.push(`fetch next ? rows only`); | ||
| this.params.push(this.options.limit); | ||
| } | ||
| } | ||
| } |
+2
-2
| { | ||
| "name": "@mikro-orm/sql", | ||
| "version": "7.0.7-dev.6", | ||
| "version": "7.0.7-dev.7", | ||
| "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.", | ||
@@ -56,3 +56,3 @@ "keywords": [ | ||
| "peerDependencies": { | ||
| "@mikro-orm/core": "7.0.7-dev.6" | ||
| "@mikro-orm/core": "7.0.7-dev.7" | ||
| }, | ||
@@ -59,0 +59,0 @@ "engines": { |
@@ -124,3 +124,5 @@ import { type Dictionary, LockMode, type QueryFlag, RawQueryFragment, type Subquery } from '@mikro-orm/core'; | ||
| protected compileSelect(): void; | ||
| protected getFields(): string; | ||
| /** Whether this COUNT query needs a subquery wrapper for multi-column distinct. */ | ||
| protected needsCountSubquery(): boolean; | ||
| protected getFields(countSubquery?: boolean): string; | ||
| protected compileInsert(): void; | ||
@@ -127,0 +129,0 @@ protected addOutputClause(type: 'inserted' | 'deleted'): void; |
@@ -284,5 +284,9 @@ import { LockMode, raw, RawQueryFragment, Utils, } from '@mikro-orm/core'; | ||
| compileSelect() { | ||
| const wrapCountSubquery = this.needsCountSubquery(); | ||
| if (wrapCountSubquery) { | ||
| this.parts.push(`select count(*) as ${this.quote('count')} from (`); | ||
| } | ||
| this.parts.push('select'); | ||
| this.addHintComment(); | ||
| this.parts.push(`${this.getFields()} from ${this.getTableName()}`); | ||
| this.parts.push(`${this.getFields(wrapCountSubquery)} from ${this.getTableName()}`); | ||
| if (this.options.joins) { | ||
@@ -306,15 +310,28 @@ for (const join of this.options.joins) { | ||
| } | ||
| if (this.options.orderBy) { | ||
| this.parts.push(`order by ${this.options.orderBy}`); | ||
| if (!wrapCountSubquery) { | ||
| if (this.options.orderBy) { | ||
| this.parts.push(`order by ${this.options.orderBy}`); | ||
| } | ||
| if (this.options.limit != null) { | ||
| this.parts.push(`limit ?`); | ||
| this.params.push(this.options.limit); | ||
| } | ||
| if (this.options.offset != null) { | ||
| this.parts.push(`offset ?`); | ||
| this.params.push(this.options.offset); | ||
| } | ||
| } | ||
| if (this.options.limit != null) { | ||
| this.parts.push(`limit ?`); | ||
| this.params.push(this.options.limit); | ||
| if (wrapCountSubquery) { | ||
| const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' '; | ||
| this.parts.push(`)${asKeyword}${this.quote('dcnt')}`); | ||
| } | ||
| if (this.options.offset != null) { | ||
| this.parts.push(`offset ?`); | ||
| this.params.push(this.options.offset); | ||
| } | ||
| } | ||
| getFields() { | ||
| /** Whether this COUNT query needs a subquery wrapper for multi-column distinct. */ | ||
| needsCountSubquery() { | ||
| return (this.type === QueryType.COUNT && | ||
| !!this.options.distinct && | ||
| this.options.select.length > 1 && | ||
| !this.platform.supportsMultiColumnCountDistinct()); | ||
| } | ||
| getFields(countSubquery) { | ||
| if (!this.options.select || this.options.select.length === 0) { | ||
@@ -324,2 +341,6 @@ throw new Error('No fields selected'); | ||
| let fields = this.options.select.map(field => this.quote(field)).join(', '); | ||
| // count subquery emits just `distinct col1, col2` — the outer wrapper adds count(*) | ||
| if (countSubquery) { | ||
| return `distinct ${fields}`; | ||
| } | ||
| if (this.options.distinct) { | ||
@@ -326,0 +347,0 @@ fields = `distinct ${fields}`; |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
854539
0.32%18210
0.3%