Skip to content

Commit a403fce

Browse files
authored
Merge pull request #509 from supabase/null_default
bugfix: arguments with null default were required
2 parents 36c1dab + 24bf313 commit a403fce

File tree

6 files changed

+785
-21
lines changed

6 files changed

+785
-21
lines changed

docs/changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,4 @@
6969
- feature: improved descriptions for all internal error states
7070

7171
## master
72+
- bugfix: function arguments with a null default value were required instead of optional

docs/functions.md

+61-1
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ Functions accepting or returning arrays of non-composite types are also supporte
371371

372372
## Default Arguments
373373

374-
Functions with default arguments can have their default arguments omitted.
374+
Arguments without a default value are required in the GraphQL schema, to make them optional they should have a default value.
375375

376376
=== "Function"
377377

@@ -410,6 +410,66 @@ Functions with default arguments can have their default arguments omitted.
410410
}
411411
```
412412

413+
If there is no sensible default, and you still want to make the argument optional, consider using the default value null.
414+
415+
416+
=== "Function"
417+
418+
```sql
419+
create function "addNums"(a int default null, b int default null)
420+
returns int
421+
immutable
422+
language plpgsql
423+
as $$
424+
begin
425+
426+
if a is null and b is null then
427+
raise exception 'a and b both can''t be null';
428+
end if;
429+
430+
if a is null then
431+
return b;
432+
end if;
433+
434+
if b is null then
435+
return a;
436+
end if;
437+
438+
return a + b;
439+
end;
440+
$$;
441+
```
442+
443+
=== "QueryType"
444+
445+
```graphql
446+
type Query {
447+
addNums(a: Int, b: Int): Int
448+
}
449+
```
450+
451+
452+
=== "Query"
453+
454+
```graphql
455+
query {
456+
addNums(a: 42)
457+
}
458+
```
459+
460+
=== "Response"
461+
462+
```json
463+
{
464+
"data": {
465+
"addNums": 42
466+
}
467+
}
468+
```
469+
470+
Currently, null defaults are only supported as simple expressions, as shown in the previous example.
471+
472+
413473
## Limitations
414474

415475
The following features are not yet supported. Any function using these features is not exposed in the API:

src/graphql.rs

+16-6
Original file line numberDiff line numberDiff line change
@@ -1373,12 +1373,22 @@ fn function_args(schema: &Arc<__Schema>, func: &Arc<Function>) -> Vec<__InputVal
13731373
(t, arg_name, arg_default)
13741374
})
13751375
})
1376-
.map(|(arg_type, arg_name, arg_default)| __InputValue {
1377-
name_: schema.graphql_function_arg_name(func, arg_name),
1378-
type_: arg_type,
1379-
description: None,
1380-
default_value: arg_default,
1381-
sql_type: None,
1376+
.map(|(arg_type, arg_name, arg_default)| {
1377+
let default_value = if let Some(default_value) = arg_default {
1378+
match default_value {
1379+
DefaultValue::NonNull(value) => Some(value),
1380+
DefaultValue::Null => None,
1381+
}
1382+
} else {
1383+
None
1384+
};
1385+
__InputValue {
1386+
name_: schema.graphql_function_arg_name(func, arg_name),
1387+
type_: arg_type,
1388+
description: None,
1389+
default_value,
1390+
sql_type: None,
1391+
}
13821392
})
13831393
.collect()
13841394
}

src/sql_types.rs

+30-14
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ pub struct Function {
8888
}
8989

9090
impl Function {
91-
pub fn args(&self) -> impl Iterator<Item = (u32, &str, Option<&str>, Option<String>)> {
91+
pub fn args(&self) -> impl Iterator<Item = (u32, &str, Option<&str>, Option<DefaultValue>)> {
9292
ArgsIterator::new(
9393
&self.arg_types,
9494
&self.arg_type_names,
@@ -200,7 +200,13 @@ struct ArgsIterator<'a> {
200200
arg_types: &'a [u32],
201201
arg_type_names: &'a Vec<String>,
202202
arg_names: &'a Option<Vec<String>>,
203-
arg_defaults: Vec<Option<String>>,
203+
arg_defaults: Vec<Option<DefaultValue>>,
204+
}
205+
206+
#[derive(Clone)]
207+
pub(crate) enum DefaultValue {
208+
NonNull(String),
209+
Null,
204210
}
205211

206212
impl<'a> ArgsIterator<'a> {
@@ -230,7 +236,7 @@ impl<'a> ArgsIterator<'a> {
230236
arg_defaults: &'a Option<String>,
231237
num_default_args: usize,
232238
num_total_args: usize,
233-
) -> Vec<Option<String>> {
239+
) -> Vec<Option<DefaultValue>> {
234240
let mut defaults = vec![None; num_total_args];
235241
let Some(arg_defaults) = arg_defaults else {
236242
return defaults;
@@ -249,23 +255,33 @@ impl<'a> ArgsIterator<'a> {
249255
debug_assert!(num_default_args <= num_total_args);
250256
let start_idx = num_total_args - num_default_args;
251257
for i in start_idx..num_total_args {
252-
defaults[i] =
253-
Self::sql_to_graphql_default(default_strs[i - start_idx], arg_types[i])
258+
defaults[i] = Self::sql_to_graphql_default(default_strs[i - start_idx], arg_types[i])
254259
}
255260

256261
defaults
257262
}
258263

259-
fn sql_to_graphql_default(default_str: &str, type_oid: u32) -> Option<String> {
264+
fn sql_to_graphql_default(default_str: &str, type_oid: u32) -> Option<DefaultValue> {
260265
let trimmed = default_str.trim();
266+
if trimmed.starts_with("NULL::") {
267+
return Some(DefaultValue::Null);
268+
}
261269
match type_oid {
262-
21 | 23 => trimmed.parse::<i32>().ok().map(|i| i.to_string()),
263-
16 => trimmed.parse::<bool>().ok().map(|i| i.to_string()),
264-
700 | 701 => trimmed.parse::<f64>().ok().map(|i| i.to_string()),
265-
25 => trimmed
266-
.strip_suffix("::text")
267-
.to_owned()
268-
.map(|i| format!("\"{}\"", i.trim_matches(',').trim_matches('\''))),
270+
21 | 23 => trimmed
271+
.parse::<i32>()
272+
.ok()
273+
.map(|i| DefaultValue::NonNull(i.to_string())),
274+
16 => trimmed
275+
.parse::<bool>()
276+
.ok()
277+
.map(|i| DefaultValue::NonNull(i.to_string())),
278+
700 | 701 => trimmed
279+
.parse::<f64>()
280+
.ok()
281+
.map(|i| DefaultValue::NonNull(i.to_string())),
282+
25 => trimmed.strip_suffix("::text").map(|i| {
283+
DefaultValue::NonNull(format!("\"{}\"", i.trim_matches(',').trim_matches('\'')))
284+
}),
269285
_ => None,
270286
}
271287
}
@@ -276,7 +292,7 @@ lazy_static! {
276292
}
277293

278294
impl<'a> Iterator for ArgsIterator<'a> {
279-
type Item = (u32, &'a str, Option<&'a str>, Option<String>);
295+
type Item = (u32, &'a str, Option<&'a str>, Option<DefaultValue>);
280296

281297
fn next(&mut self) -> Option<Self::Item> {
282298
if self.index < self.arg_types.len() {

0 commit comments

Comments
 (0)