Skip to content

Commit 4e549c3

Browse files
committed
feat: query parser: handle empty embedded resources ()
Closes #445.
1 parent bcbd0f4 commit 4e549c3

File tree

2 files changed

+21
-5
lines changed

2 files changed

+21
-5
lines changed

src/select-query-parser.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ type ConstructFieldDefinition<
105105
Field
106106
> = Field extends { star: true }
107107
? Row
108+
: Field extends { children: [] }
109+
? {}
108110
: Field extends { spread: true; original: string; children: unknown[] }
109111
? GetResultHelper<
110112
Schema,
@@ -289,6 +291,8 @@ type ParseField<Input extends string> = Input extends ''
289291
* - a field, as defined above
290292
* - a renamed field, `renamed_field:field`
291293
* - a spread field, `...field`
294+
*
295+
* Returns a tuple of ["Parsed node", "Remainder of text"] or an error.
292296
*/
293297
type ParseNode<Input extends string> = Input extends ''
294298
? ParserError<'Empty string'>
@@ -339,19 +343,22 @@ type ParseJsonAccessor<Input extends string> = Input extends `->${infer Remainde
339343

340344
/**
341345
* Parses an embedded resource, which is an opening `(`, followed by a sequence of
342-
* nodes, separated by `,`, then a closing `)`.
346+
* 0 or more nodes separated by `,`, then a closing `)`.
343347
*
344348
* Returns a tuple of ["Parsed fields", "Remainder of text"], an error,
345349
* or the original string input indicating that no opening `(` was found.
346350
*/
347351
type ParseEmbeddedResource<Input extends string> = Input extends `(${infer Remainder}`
348352
? ParseNodes<EatWhitespace<Remainder>> extends [infer Fields, `${infer Remainder}`]
349353
? EatWhitespace<Remainder> extends `)${infer Remainder}`
350-
? Fields extends []
351-
? ParserError<'Expected fields after `(`'>
352-
: [Fields, EatWhitespace<Remainder>]
354+
? [Fields, EatWhitespace<Remainder>]
353355
: ParserError<`Expected ")"`>
354-
: ParseNodes<EatWhitespace<Remainder>>
356+
: // If no nodes were detected, check for `)` for empty embedded resources `()`.
357+
ParseNodes<EatWhitespace<Remainder>> extends ParserError<string>
358+
? EatWhitespace<Remainder> extends `)${infer Remainder}`
359+
? [[], EatWhitespace<Remainder>]
360+
: ParseNodes<EatWhitespace<Remainder>>
361+
: ParserError<'Expected embedded resource fields or `)`'>
355362
: Input
356363

357364
/**

test/index.test-d.ts

+9
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ const postgrest = new PostgrestClient<Database>(REST_URL)
5454
expectError(postgrest.from('updatable_view').update({ non_updatable_column: 0 }))
5555
}
5656

57+
// embedded resource with no fields
58+
{
59+
const { data, error } = await postgrest.from('messages').select('message, users()').single()
60+
if (error) {
61+
throw new Error(error.message)
62+
}
63+
expectType<{ message: string | null }>(data)
64+
}
65+
5766
// spread resource with single column in select query
5867
{
5968
const { data, error } = await postgrest

0 commit comments

Comments
 (0)