@@ -1325,6 +1325,11 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
13251325 return ( returnData ? [ ] : { count : 0 } ) as Result ;
13261326 }
13271327
1328+ const modelDef = this . requireModel ( model ) ;
1329+ if ( modelDef . baseModel && limit !== undefined ) {
1330+ throw new QueryError ( 'Updating with a limit is not supported for polymorphic models' ) ;
1331+ }
1332+
13281333 filterModel ??= model ;
13291334 let updateFields : any = { } ;
13301335
@@ -1335,7 +1340,6 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
13351340 updateFields [ field ] = this . processScalarFieldUpdateData ( model , field , data ) ;
13361341 }
13371342
1338- const modelDef = this . requireModel ( model ) ;
13391343 let shouldFallbackToIdFilter = false ;
13401344
13411345 if ( limit !== undefined && ! this . dialect . supportsUpdateWithLimit ) {
@@ -1358,7 +1362,6 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
13581362 modelDef . baseModel ,
13591363 where ,
13601364 updateFields ,
1361- limit ,
13621365 filterModel ,
13631366 ) ;
13641367 updateFields = baseResult . remainingFields ;
@@ -1412,7 +1415,6 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
14121415 model : string ,
14131416 where : any ,
14141417 updateFields : any ,
1415- limit : number | undefined ,
14161418 filterModel : GetModels < Schema > ,
14171419 ) {
14181420 const thisUpdateFields : any = { } ;
@@ -1433,7 +1435,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
14331435 model as GetModels < Schema > ,
14341436 where ,
14351437 thisUpdateFields ,
1436- limit ,
1438+ undefined ,
14371439 false ,
14381440 filterModel ,
14391441 ) ;
@@ -1983,24 +1985,18 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
19831985 const fieldDef = this . requireField ( fromRelation . model , fromRelation . field ) ;
19841986 invariant ( fieldDef . relation ?. opposite ) ;
19851987
1986- deleteResult = await this . delete (
1987- kysely ,
1988- model ,
1989- {
1990- AND : [
1991- {
1992- [ fieldDef . relation . opposite ] : {
1993- some : fromRelation . ids ,
1994- } ,
1995- } ,
1996- {
1997- OR : deleteConditions ,
1988+ deleteResult = await this . delete ( kysely , model , {
1989+ AND : [
1990+ {
1991+ [ fieldDef . relation . opposite ] : {
1992+ some : fromRelation . ids ,
19981993 } ,
1999- ] ,
2000- } ,
2001- undefined ,
2002- false ,
2003- ) ;
1994+ } ,
1995+ {
1996+ OR : deleteConditions ,
1997+ } ,
1998+ ] ,
1999+ } ) ;
20042000 } else {
20052001 const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs (
20062002 this . schema ,
@@ -2018,36 +2014,24 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
20182014
20192015 const fieldDef = this . requireField ( fromRelation . model , fromRelation . field ) ;
20202016 invariant ( fieldDef . relation ?. opposite ) ;
2021- deleteResult = await this . delete (
2022- kysely ,
2023- model ,
2024- {
2025- AND : [
2026- // filter for parent
2027- Object . fromEntries ( keyPairs . map ( ( { fk, pk } ) => [ pk , fromEntity [ fk ] ] ) ) ,
2028- {
2029- OR : deleteConditions ,
2030- } ,
2031- ] ,
2032- } ,
2033- undefined ,
2034- false ,
2035- ) ;
2017+ deleteResult = await this . delete ( kysely , model , {
2018+ AND : [
2019+ // filter for parent
2020+ Object . fromEntries ( keyPairs . map ( ( { fk, pk } ) => [ pk , fromEntity [ fk ] ] ) ) ,
2021+ {
2022+ OR : deleteConditions ,
2023+ } ,
2024+ ] ,
2025+ } ) ;
20362026 } else {
2037- deleteResult = await this . delete (
2038- kysely ,
2039- model ,
2040- {
2041- AND : [
2042- Object . fromEntries ( keyPairs . map ( ( { fk, pk } ) => [ fk , fromRelation . ids [ pk ] ] ) ) ,
2043- {
2044- OR : deleteConditions ,
2045- } ,
2046- ] ,
2047- } ,
2048- undefined ,
2049- false ,
2050- ) ;
2027+ deleteResult = await this . delete ( kysely , model , {
2028+ AND : [
2029+ Object . fromEntries ( keyPairs . map ( ( { fk, pk } ) => [ fk , fromRelation . ids [ pk ] ] ) ) ,
2030+ {
2031+ OR : deleteConditions ,
2032+ } ,
2033+ ] ,
2034+ } ) ;
20512035 }
20522036 }
20532037
@@ -2064,54 +2048,109 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
20642048
20652049 // #endregion
20662050
2067- protected async delete <
2068- ReturnData extends boolean ,
2069- Result = ReturnData extends true ? unknown [ ] : { count : number } ,
2070- > (
2051+ protected async delete (
20712052 kysely : ToKysely < Schema > ,
20722053 model : GetModels < Schema > ,
20732054 where : any ,
2074- limit : number | undefined ,
2075- returnData : ReturnData ,
2076- ) : Promise < Result > {
2055+ limit ?: number ,
2056+ filterModel ?: GetModels < Schema > ,
2057+ ) : Promise < { count : number } > {
2058+ filterModel ??= model ;
2059+
2060+ const modelDef = this . requireModel ( model ) ;
2061+
2062+ if ( modelDef . baseModel ) {
2063+ if ( limit !== undefined ) {
2064+ throw new QueryError ( 'Deleting with a limit is not supported for polymorphic models' ) ;
2065+ }
2066+ // just delete base and it'll cascade back to this model
2067+ return this . processBaseModelDelete ( kysely , modelDef . baseModel , where , limit , filterModel ) ;
2068+ }
2069+
20772070 let query = kysely . deleteFrom ( model ) ;
2071+ let needIdFilter = false ;
2072+
2073+ if ( limit !== undefined && ! this . dialect . supportsDeleteWithLimit ) {
2074+ // if the dialect doesn't support delete with limit natively, we'll
2075+ // simulate it by filtering by id with a limit
2076+ needIdFilter = true ;
2077+ }
20782078
2079- if ( limit === undefined ) {
2079+ if ( modelDef . isDelegate || modelDef . baseModel ) {
2080+ // if the model is in a delegate hierarchy, we'll need to filter by
2081+ // id because the filter may involve fields in different models in
2082+ // the hierarchy
2083+ needIdFilter = true ;
2084+ }
2085+
2086+ if ( ! needIdFilter ) {
20802087 query = query . where ( ( eb ) => this . dialect . buildFilter ( eb , model , model , where ) ) ;
20812088 } else {
2082- if ( this . dialect . supportsDeleteWithLimit ) {
2083- query = query . where ( ( eb ) => this . dialect . buildFilter ( eb , model , model , where ) ) . limit ( limit ! ) ;
2084- } else {
2085- query = query . where ( ( eb ) =>
2086- eb (
2087- eb . refTuple (
2088- // @ts -expect-error
2089- ...this . buildIdFieldRefs ( kysely , model ) ,
2090- ) ,
2091- 'in' ,
2092- kysely
2093- . selectFrom ( model )
2094- . where ( ( eb ) => this . dialect . buildFilter ( eb , model , model , where ) )
2095- . select ( this . buildIdFieldRefs ( kysely , model ) )
2096- . limit ( limit ! ) ,
2089+ query = query . where ( ( eb ) =>
2090+ eb (
2091+ eb . refTuple (
2092+ // @ts -expect-error
2093+ ...this . buildIdFieldRefs ( kysely , model ) ,
20972094 ) ,
2098- ) ;
2099- }
2095+ 'in' ,
2096+ this . dialect
2097+ . buildSelectModel ( eb , filterModel )
2098+ . where ( ( eb ) => this . dialect . buildFilter ( eb , filterModel , filterModel , where ) )
2099+ . select ( this . buildIdFieldRefs ( kysely , filterModel ) )
2100+ . $if ( limit !== undefined , ( qb ) => qb . limit ( limit ! ) ) ,
2101+ ) ,
2102+ ) ;
21002103 }
21012104
2105+ // if the model being deleted has a relation to a model that extends a delegate model, and if that
2106+ // relation is set to trigger a cascade delete from this model, the deletion will not automatically
2107+ // clean up the base hierarchy of the relation side (because polymorphic model's cascade deletion
2108+ // works downward not upward). We need to take care of the base deletions manually here.
2109+ await this . processDelegateRelationDelete ( kysely , modelDef , where , limit ) ;
2110+
21022111 query = query . modifyEnd ( this . makeContextComment ( { model, operation : 'delete' } ) ) ;
2112+ const result = await query . executeTakeFirstOrThrow ( ) ;
2113+ return { count : Number ( result . numDeletedRows ) } ;
2114+ }
21032115
2104- if ( returnData ) {
2105- const result = await query . execute ( ) ;
2106- return result as Result ;
2107- } else {
2108- const result = ( await query . executeTakeFirstOrThrow ( ) ) as DeleteResult ;
2109- return {
2110- count : Number ( result . numDeletedRows ) ,
2111- } as Result ;
2116+ private async processDelegateRelationDelete (
2117+ kysely : ToKysely < Schema > ,
2118+ modelDef : ModelDef ,
2119+ where : any ,
2120+ limit : number | undefined ,
2121+ ) {
2122+ for ( const fieldDef of Object . values ( modelDef . fields ) ) {
2123+ if ( fieldDef . relation && fieldDef . relation . opposite ) {
2124+ const oppositeModelDef = this . requireModel ( fieldDef . type ) ;
2125+ const oppositeRelation = this . requireField ( fieldDef . type , fieldDef . relation . opposite ) ;
2126+ if ( oppositeModelDef . baseModel && oppositeRelation . relation ?. onDelete === 'Cascade' ) {
2127+ if ( limit !== undefined ) {
2128+ throw new QueryError ( 'Deleting with a limit is not supported for polymorphic models' ) ;
2129+ }
2130+ // the deletion will propagate upward to the base model chain
2131+ await this . delete (
2132+ kysely ,
2133+ fieldDef . type as GetModels < Schema > ,
2134+ {
2135+ [ fieldDef . relation . opposite ] : where ,
2136+ } ,
2137+ undefined ,
2138+ ) ;
2139+ }
2140+ }
21122141 }
21132142 }
21142143
2144+ private async processBaseModelDelete (
2145+ kysely : ToKysely < Schema > ,
2146+ model : string ,
2147+ where : any ,
2148+ limit : number | undefined ,
2149+ filterModel : GetModels < Schema > ,
2150+ ) {
2151+ return this . delete ( kysely , model as GetModels < Schema > , where , limit , filterModel ) ;
2152+ }
2153+
21152154 protected makeIdSelect ( model : string ) {
21162155 const modelDef = this . requireModel ( model ) ;
21172156 return modelDef . idFields . reduce ( ( acc , f ) => {
@@ -2154,9 +2193,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
21542193 } else {
21552194 // otherwise, create a new transaction and execute the callback
21562195 let txBuilder = this . kysely . transaction ( ) ;
2157- if ( isolationLevel ) {
2158- txBuilder = txBuilder . setIsolationLevel ( isolationLevel ) ;
2159- }
2196+ txBuilder = txBuilder . setIsolationLevel ( isolationLevel ?? 'repeatable read' ) ;
21602197 return txBuilder . execute ( callback ) ;
21612198 }
21622199 }
0 commit comments