77 UpdateResult ,
88 type Compilable ,
99 type IsolationLevel ,
10+ type QueryResult ,
1011 type SelectQueryBuilder ,
1112} from 'kysely' ;
1213import { nanoid } from 'nanoid' ;
@@ -248,6 +249,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
248249 data : any ,
249250 fromRelation ?: FromRelationContext < Schema > ,
250251 creatingForDelegate = false ,
252+ returnFields ?: string [ ] ,
251253 ) : Promise < unknown > {
252254 const modelDef = this . requireModel ( model ) ;
253255
@@ -339,12 +341,15 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
339341 }
340342
341343 const updatedData = this . fillGeneratedAndDefaultValues ( modelDef , createFields ) ;
342- const idFields = requireIdFields ( this . schema , model ) ;
344+
345+ // return id fields if no returnFields specified
346+ returnFields = returnFields ?? requireIdFields ( this . schema , model ) ;
347+
343348 const query = kysely
344349 . insertInto ( model )
345350 . $if ( Object . keys ( updatedData ) . length === 0 , ( qb ) => qb . defaultValues ( ) )
346351 . $if ( Object . keys ( updatedData ) . length > 0 , ( qb ) => qb . values ( updatedData ) )
347- . returning ( idFields as any )
352+ . returning ( returnFields as any )
348353 . modifyEnd (
349354 this . makeContextComment ( {
350355 model,
@@ -661,6 +666,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
661666 input : { data : any ; skipDuplicates ?: boolean } ,
662667 returnData : ReturnData ,
663668 fromRelation ?: FromRelationContext < Schema > ,
669+ fieldsToReturn ?: string [ ] ,
664670 ) : Promise < Result > {
665671 if ( ! input . data || ( Array . isArray ( input . data ) && input . data . length === 0 ) ) {
666672 // nothing todo
@@ -763,8 +769,8 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
763769 const result = await this . executeQuery ( kysely , query , 'createMany' ) ;
764770 return { count : Number ( result . numAffectedRows ) } as Result ;
765771 } else {
766- const idFields = requireIdFields ( this . schema , model ) ;
767- const result = await query . returning ( idFields as any ) . execute ( ) ;
772+ fieldsToReturn = fieldsToReturn ?? requireIdFields ( this . schema , model ) ;
773+ const result = await query . returning ( fieldsToReturn as any ) . execute ( ) ;
768774 return result as Result ;
769775 }
770776 }
@@ -899,6 +905,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
899905 fromRelation ?: FromRelationContext < Schema > ,
900906 allowRelationUpdate = true ,
901907 throwIfNotFound = true ,
908+ fieldsToReturn ?: string [ ] ,
902909 ) : Promise < unknown > {
903910 if ( ! data || typeof data !== 'object' ) {
904911 throw new InternalError ( 'data must be an object' ) ;
@@ -1044,12 +1051,12 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
10441051 // nothing to update, return the filter so that the caller can identify the entity
10451052 return combinedWhere ;
10461053 } else {
1047- const idFields = requireIdFields ( this . schema , model ) ;
1054+ fieldsToReturn = fieldsToReturn ?? requireIdFields ( this . schema , model ) ;
10481055 const query = kysely
10491056 . updateTable ( model )
10501057 . where ( ( ) => this . dialect . buildFilter ( model , model , combinedWhere ) )
10511058 . set ( updateFields )
1052- . returning ( idFields as any )
1059+ . returning ( fieldsToReturn as any )
10531060 . modifyEnd (
10541061 this . makeContextComment ( {
10551062 model,
@@ -1058,16 +1065,6 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
10581065 ) ;
10591066
10601067 const updatedEntity = await this . executeQueryTakeFirst ( kysely , query , 'update' ) ;
1061-
1062- // try {
1063- // updatedEntity = await this.executeQueryTakeFirst(kysely, query, 'update');
1064- // } catch (err) {
1065- // const { sql, parameters } = query.compile();
1066- // throw new QueryError(
1067- // `Error during update: ${err}, sql: ${sql}, parameters: ${parameters}`
1068- // );
1069- // }
1070-
10711068 if ( ! updatedEntity ) {
10721069 if ( throwIfNotFound ) {
10731070 throw new NotFoundError ( model ) ;
@@ -1214,6 +1211,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
12141211 limit : number | undefined ,
12151212 returnData : ReturnData ,
12161213 filterModel ?: GetModels < Schema > ,
1214+ fieldsToReturn ?: string [ ] ,
12171215 ) : Promise < Result > {
12181216 if ( typeof data !== 'object' ) {
12191217 throw new InternalError ( 'data must be an object' ) ;
@@ -1302,8 +1300,8 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
13021300 const result = await this . executeQuery ( kysely , query , 'update' ) ;
13031301 return { count : Number ( result . numAffectedRows ) } as Result ;
13041302 } else {
1305- const idFields = requireIdFields ( this . schema , model ) ;
1306- const finalQuery = query . returning ( idFields as any ) ;
1303+ fieldsToReturn = fieldsToReturn ?? requireIdFields ( this . schema , model ) ;
1304+ const finalQuery = query . returning ( fieldsToReturn as any ) ;
13071305 const result = await this . executeQuery ( kysely , finalQuery , 'update' ) ;
13081306 return result . rows as Result ;
13091307 }
@@ -1861,7 +1859,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
18611859 expectedDeleteCount = deleteConditions . length ;
18621860 }
18631861
1864- let deleteResult : { count : number } ;
1862+ let deleteResult : QueryResult < unknown > ;
18651863 let deleteFromModel : GetModels < Schema > ;
18661864 const m2m = getManyToManyRelation ( this . schema , fromRelation . model , fromRelation . field ) ;
18671865
@@ -1926,7 +1924,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
19261924 }
19271925
19281926 // validate result
1929- if ( throwForNotFound && expectedDeleteCount > deleteResult . count ) {
1927+ if ( throwForNotFound && expectedDeleteCount > deleteResult . rows . length ) {
19301928 // some entities were not deleted
19311929 throw new NotFoundError ( deleteFromModel ) ;
19321930 }
@@ -1944,7 +1942,8 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
19441942 where : any ,
19451943 limit ?: number ,
19461944 filterModel ?: GetModels < Schema > ,
1947- ) : Promise < { count : number } > {
1945+ fieldsToReturn ?: string [ ] ,
1946+ ) : Promise < QueryResult < unknown > > {
19481947 filterModel ??= model ;
19491948
19501949 const modelDef = this . requireModel ( model ) ;
@@ -1957,7 +1956,9 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
19571956 return this . processBaseModelDelete ( kysely , modelDef . baseModel , where , limit , filterModel ) ;
19581957 }
19591958
1960- let query = kysely . deleteFrom ( model ) ;
1959+ fieldsToReturn = fieldsToReturn ?? requireIdFields ( this . schema , model ) ;
1960+ let query = kysely . deleteFrom ( model ) . returning ( fieldsToReturn as any ) ;
1961+
19611962 let needIdFilter = false ;
19621963
19631964 if ( limit !== undefined && ! this . dialect . supportsDeleteWithLimit ) {
@@ -1999,8 +2000,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
19992000 await this . processDelegateRelationDelete ( kysely , modelDef , where , limit ) ;
20002001
20012002 query = query . modifyEnd ( this . makeContextComment ( { model, operation : 'delete' } ) ) ;
2002- const result = await this . executeQuery ( kysely , query , 'delete' ) ;
2003- return { count : Number ( result . numAffectedRows ) } ;
2003+ return this . executeQuery ( kysely , query , 'delete' ) ;
20042004 }
20052005
20062006 private async processDelegateRelationDelete (
@@ -2140,4 +2140,56 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
21402140 }
21412141 return result . rows [ 0 ] ;
21422142 }
2143+
2144+ protected mutationNeedsReadBack ( model : string , args : any ) {
2145+ if ( this . hasPolicyEnabled ) {
2146+ // TODO: refactor this check
2147+ // policy enforcement always requires read back
2148+ return { needReadBack : true , selectedFields : undefined } ;
2149+ }
2150+
2151+ if ( args . include && typeof args . include === 'object' && Object . keys ( args . include ) . length > 0 ) {
2152+ // includes present, need read back to fetch relations
2153+ return { needReadBack : true , selectedFields : undefined } ;
2154+ }
2155+
2156+ const modelDef = this . requireModel ( model ) ;
2157+
2158+ if ( modelDef . baseModel || modelDef . isDelegate ) {
2159+ // polymorphic model, need read back
2160+ return { needReadBack : true , selectedFields : undefined } ;
2161+ }
2162+
2163+ const allFields = Object . keys ( modelDef . fields ) ;
2164+ const relationFields = Object . values ( modelDef . fields )
2165+ . filter ( ( f ) => f . relation )
2166+ . map ( ( f ) => f . name ) ;
2167+ const computedFields = Object . values ( modelDef . fields )
2168+ . filter ( ( f ) => f . computed )
2169+ . map ( ( f ) => f . name ) ;
2170+ const omit = Object . entries ( args . omit ?? { } )
2171+ . filter ( ( [ , v ] ) => v )
2172+ . map ( ( [ k ] ) => k ) ;
2173+
2174+ const allFieldsSelected : string [ ] = [ ] ;
2175+
2176+ if ( ! args . select || typeof args . select !== 'object' ) {
2177+ // all non-relation fields selected
2178+ allFieldsSelected . push ( ...allFields . filter ( ( f ) => ! relationFields . includes ( f ) && ! omit . includes ( f ) ) ) ;
2179+ } else {
2180+ // explicit select
2181+ allFieldsSelected . push (
2182+ ...Object . entries ( args . select )
2183+ . filter ( ( [ k , v ] ) => v && ! omit . includes ( k ) )
2184+ . map ( ( [ k ] ) => k ) ,
2185+ ) ;
2186+ }
2187+
2188+ if ( allFieldsSelected . some ( ( f ) => relationFields . includes ( f ) || computedFields . includes ( f ) ) ) {
2189+ // relation or computed field selected, need read back
2190+ return { needReadBack : true , selectedFields : undefined } ;
2191+ } else {
2192+ return { needReadBack : false , selectedFields : allFieldsSelected } ;
2193+ }
2194+ }
21432195}
0 commit comments