@@ -34,30 +34,23 @@ export async function executeQuery(queryRequest: sdk.QueryRequest, functionsSche
34
34
const parallelLimit = pLimit ( functionDefinition . parallelDegree ?? DEFAULT_PARALLEL_DEGREE ) ;
35
35
const functionInvocations : Promise < sdk . RowSet > [ ] = functionInvocationPreparedArgs . map ( invocationPreparedArgs => parallelLimit ( async ( ) => {
36
36
const result = await invokeFunction ( runtimeFunction , invocationPreparedArgs , functionName ) ;
37
- const prunedResult = reshapeResultToNdcResponseValue ( result , functionDefinition . resultType , [ ] , queryRequest . query . fields ?? { } , functionsSchema . objectTypes ) ;
38
- return {
39
- aggregates : { } ,
40
- rows : [
41
- {
42
- __value : prunedResult
43
- }
44
- ]
45
- } ;
37
+ return reshapeResultUsingFunctionCallingConvention ( result , functionDefinition . resultType , queryRequest . query , functionsSchema . objectTypes ) ;
46
38
} ) ) ;
47
39
48
40
return await Promise . all ( functionInvocations ) ;
49
41
}
50
42
51
43
export async function executeMutation ( mutationRequest : sdk . MutationRequest , functionsSchema : schema . FunctionsSchema , runtimeFunctions : RuntimeFunctions ) : Promise < sdk . MutationResponse > {
52
- const operationResults : sdk . MutationOperationResults [ ] = [ ] ;
44
+ if ( mutationRequest . operations . length > 1 )
45
+ throw new sdk . NotSupported ( "Transactional mutations (multiple operations) are not supported" ) ;
46
+ if ( mutationRequest . operations . length <= 0 )
47
+ throw new sdk . BadRequest ( "One mutation operation must be provided" )
53
48
54
- for ( const mutationOperation of mutationRequest . operations ) {
55
- const result = await executeMutationOperation ( mutationOperation , functionsSchema , runtimeFunctions ) ;
56
- operationResults . push ( result ) ;
57
- }
49
+ const mutationOperation = mutationRequest . operations [ 0 ] ! ;
50
+ const result = await executeMutationOperation ( mutationOperation , functionsSchema , runtimeFunctions ) ;
58
51
59
52
return {
60
- operation_results : operationResults
53
+ operation_results : [ result ]
61
54
} ;
62
55
}
63
56
@@ -77,13 +70,11 @@ async function executeMutationOperation(mutationOperation: sdk.MutationOperation
77
70
78
71
const preparedArgs = prepareArguments ( mutationOperation . arguments , functionDefinition , functionsSchema . objectTypes ) ;
79
72
const result = await invokeFunction ( runtimeFunction , preparedArgs , functionName ) ;
80
- const prunedResult = reshapeResultToNdcResponseValue ( result , functionDefinition . resultType , [ ] , mutationOperation . fields ?? { } , functionsSchema . objectTypes ) ;
73
+ const reshapedResult = reshapeResultUsingFieldSelection ( result , functionDefinition . resultType , [ ] , mutationOperation . fields ?? { type : "scalar" } , functionsSchema . objectTypes ) ;
81
74
82
75
return {
83
- affected_rows : 1 ,
84
- returning : [ {
85
- __value : prunedResult
86
- } ]
76
+ type : "procedure" ,
77
+ result : reshapedResult
87
78
}
88
79
}
89
80
@@ -211,20 +202,76 @@ function buildCausalStackTrace(error: Error): string {
211
202
return stackTrace ;
212
203
}
213
204
214
- export function reshapeResultToNdcResponseValue ( value : unknown , type : schema . TypeReference , valuePath : string [ ] , fields : Record < string , sdk . Field > | "AllColumns" , objectTypes : schema . ObjectTypeDefinitions ) : unknown {
205
+ // Represents either selecting a scalar (ie. the whole value, opaquely), an object (selecting properties), or an array (select whole array)
206
+ export type FieldSelection = sdk . NestedField | { type : "scalar" }
207
+
208
+ function reshapeResultUsingFunctionCallingConvention ( functionResultValue : unknown , functionResultType : schema . TypeReference , query : sdk . Query , objectTypes : schema . ObjectTypeDefinitions ) : sdk . RowSet {
209
+ if ( query . aggregates ) throw new sdk . NotSupported ( "Query aggregates are not supported" ) ;
210
+ if ( query . order_by ) throw new sdk . NotSupported ( "Query order_by is not supported" ) ;
211
+ if ( query . predicate ) throw new sdk . NotSupported ( "Query predicate is not supported" ) ;
212
+ if ( ! query . fields ) {
213
+ return {
214
+ aggregates : null ,
215
+ rows : null ,
216
+ }
217
+ }
218
+ // There's one virtual row in the function calling convention, so if the query (pointlessly) usees
219
+ // pagination to skip it, just do what it says
220
+ if ( query . limit !== undefined && query . limit !== null && query . limit <= 0
221
+ || query . offset !== undefined && query . offset !== null && query . offset >= 1 ) {
222
+ return {
223
+ aggregates : null ,
224
+ rows : [ ] ,
225
+ }
226
+ }
227
+
228
+ const rowValue = mapObjectValues ( query . fields , ( field : sdk . Field , fieldName : string ) => {
229
+ switch ( field . type ) {
230
+ case "column" :
231
+ if ( field . column === "__value" ) {
232
+ return reshapeResultUsingFieldSelection ( functionResultValue , functionResultType , [ fieldName ] , field . fields ?? { type : "scalar" } , objectTypes ) ;
233
+ } else {
234
+ throw new sdk . BadRequest ( `Unknown column '${ field . column } ' used in root query field` )
235
+ }
236
+
237
+ case "relationship" :
238
+ throw new sdk . NotSupported ( `Field '${ fieldName } ' is a relationship field, which is unsupported.'` )
239
+
240
+ default :
241
+ return unreachable ( field [ "type" ] ) ;
242
+ }
243
+ } ) ;
244
+
245
+ return {
246
+ aggregates : null ,
247
+ rows : [ rowValue ]
248
+ }
249
+ }
250
+
251
+ export function reshapeResultUsingFieldSelection ( value : unknown , type : schema . TypeReference , valuePath : string [ ] , fieldSelection : FieldSelection , objectTypes : schema . ObjectTypeDefinitions ) : unknown {
215
252
switch ( type . type ) {
216
253
case "array" :
217
- if ( isArray ( value ) ) {
218
- return value . map ( ( elementValue , index ) => reshapeResultToNdcResponseValue ( elementValue , type . elementType , [ ...valuePath , `[${ index } ]` ] , fields , objectTypes ) )
219
- }
220
- break ;
254
+ if ( ! isArray ( value ) )
255
+ throw new sdk . InternalServerError ( `Expected an array, but received '${ value === null ? "null" : null ?? typeof value } '` ) ;
256
+
257
+ const elementFieldSelection = ( ( ) => {
258
+ switch ( fieldSelection . type ) {
259
+ case "scalar" : return fieldSelection ;
260
+ case "array" : return fieldSelection . fields ;
261
+ case "object" : throw new sdk . BadRequest ( `Trying to perform an object selection on an array type at '${ valuePath . join ( "." ) } '` )
262
+ default : return unreachable ( fieldSelection [ "type" ] ) ;
263
+ }
264
+ } ) ( ) ;
265
+
266
+ return value . map ( ( elementValue , index ) => reshapeResultUsingFieldSelection ( elementValue , type . elementType , [ ...valuePath , `[${ index } ]` ] , elementFieldSelection , objectTypes ) )
267
+
221
268
222
269
case "nullable" :
223
270
// Selected fields must always return a value, so they cannot be undefined. So all
224
271
// undefineds are coerced to nulls so that the field is included with a null value.
225
272
return value === null || value === undefined
226
273
? null
227
- : reshapeResultToNdcResponseValue ( value , type . underlyingType , valuePath , fields , objectTypes ) ;
274
+ : reshapeResultUsingFieldSelection ( value , type . underlyingType , valuePath , fieldSelection , objectTypes ) ;
228
275
229
276
case "named" :
230
277
switch ( type . kind ) {
@@ -240,23 +287,30 @@ export function reshapeResultToNdcResponseValue(value: unknown, type: schema.Typ
240
287
if ( value === null || Array . isArray ( value ) || typeof value !== "object" )
241
288
throw new sdk . InternalServerError ( `Expected an object, but received '${ value === null ? "null" : null ?? Array . isArray ( value ) ? "array" : null ?? typeof value } '` ) ;
242
289
243
- const selectedFields : Record < string , sdk . Field > =
244
- fields === "AllColumns"
245
- ? Object . fromEntries ( objectType . properties . map ( propDef => [ propDef . propertyName , { type : "column" , column : propDef . propertyName } ] ) )
246
- : fields ;
290
+ const selectedFields : Record < string , sdk . Field > = ( ( ) => {
291
+ switch ( fieldSelection . type ) {
292
+ case "scalar" : return Object . fromEntries ( objectType . properties . map ( propDef => [ propDef . propertyName , { type : "column" , column : propDef . propertyName } ] ) ) ;
293
+ case "array" : throw new sdk . BadRequest ( `Trying to perform an array selection on an object type at '${ valuePath . join ( "." ) } '` ) ;
294
+ case "object" : return fieldSelection . fields ;
295
+ default : return unreachable ( fieldSelection [ "type" ] ) ;
296
+ }
297
+ } ) ( ) ;
247
298
248
299
return mapObjectValues ( selectedFields , ( field , fieldName ) => {
249
300
switch ( field . type ) {
250
301
case "column" :
251
302
const objPropDef = objectType . properties . find ( prop => prop . propertyName === field . column ) ;
252
303
if ( objPropDef === undefined )
253
- throw new sdk . InternalServerError ( `Unable to find property definition '${ field . column } ' on object type '${ type . name } '` ) ;
304
+ throw new sdk . BadRequest ( `Unable to find property definition '${ field . column } ' on object type '${ type . name } ' at '${ valuePath . join ( "." ) } '` ) ;
305
+
306
+ const columnFieldSelection = field . fields ?? { type : "scalar" } ;
307
+ return reshapeResultUsingFieldSelection ( ( value as Record < string , unknown > ) [ field . column ] , objPropDef . type , [ ...valuePath , fieldName ] , columnFieldSelection , objectTypes )
254
308
255
- // We pass "AllColumns" as the fields because we don't yet support nested field selections, so we just include all columns by default for now
256
- return reshapeResultToNdcResponseValue ( ( value as Record < string , unknown > ) [ field . column ] , objPropDef . type , [ ... valuePath , field . column ] , "AllColumns" , objectTypes )
309
+ case "relationship" :
310
+ throw new sdk . NotSupported ( `Field ' ${ fieldName } ' is a relationship field, which is unsupported.'` )
257
311
258
312
default :
259
- throw new sdk . NotSupported ( `Field ' ${ fieldName } ' uses an unsupported field type: ' ${ field . type } '` )
313
+ return unreachable ( field [ " type" ] ) ;
260
314
}
261
315
} )
262
316
0 commit comments