From cc4f263e5478fe0649dfaa4151209bd417d885d1 Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Wed, 22 Nov 2023 15:59:29 +0530 Subject: [PATCH 01/17] add support for array types in UDFs --- src/builder.rs | 8 +- src/sql_types.rs | 2 +- src/transpile.rs | 7 ++ test/expected/array_args.out | 0 test/sql/array_args.sql | 165 +++++++++++++++++++++++++++++++++++ 5 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 test/expected/array_args.out create mode 100644 test/sql/array_args.sql diff --git a/src/builder.rs b/src/builder.rs index e4ba939b..466080d0 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -3,6 +3,7 @@ use crate::gson; use crate::parser_util::*; use crate::sql_types::*; use graphql_parser::query::*; +use pgrx::notice; use serde::Serialize; use std::collections::HashMap; use std::ops::Deref; @@ -564,6 +565,7 @@ pub struct FunctionCallBuilder { pub enum FuncCallReturnTypeBuilder { Scalar, + List, Node(NodeBuilder), Connection(ConnectionBuilder), } @@ -600,6 +602,7 @@ where let return_type_builder = match func_call_resp_type.return_type.deref() { __Type::Scalar(_) => FuncCallReturnTypeBuilder::Scalar, + __Type::List(_) => FuncCallReturnTypeBuilder::List, __Type::Node(_) => { let node_builder = to_node_builder( field, @@ -620,7 +623,8 @@ where )?; FuncCallReturnTypeBuilder::Connection(connection_builder) } - _ => { + t => { + notice!("TYPE: {t:?}"); return Err(format!( "unsupported return type: {}", func_call_resp_type @@ -628,7 +632,7 @@ where .unmodified_type() .name() .ok_or("Encountered type without name in function call builder")? - )) + )); } }; diff --git a/src/sql_types.rs b/src/sql_types.rs index 3bab8087..8cc7732b 100644 --- a/src/sql_types.rs +++ b/src/sql_types.rs @@ -125,6 +125,7 @@ impl Function { self.args().all(|(arg_type, _, _, _)| { if let Some(return_type) = types.get(&arg_type) { return_type.category == TypeCategory::Other + || return_type.category == TypeCategory::Array } else { false } @@ -137,7 +138,6 @@ impl Function { && return_type.name != "record" && return_type.name != "trigger" && return_type.name != "event_trigger" - && !self.type_name.ends_with("[]") } else { false } diff --git a/src/transpile.rs b/src/transpile.rs index 92f4574d..57cd4f7d 100644 --- a/src/transpile.rs +++ b/src/transpile.rs @@ -564,6 +564,10 @@ impl FunctionCallBuilder { let type_adjustment_clause = apply_suffix_casts(self.function.type_oid); format!("select to_jsonb({func_schema}.{func_name}{args_clause}{type_adjustment_clause}) {block_name};") } + FuncCallReturnTypeBuilder::List => { + let type_adjustment_clause = apply_suffix_casts(self.function.type_oid); + format!("select to_jsonb({func_schema}.{func_name}{args_clause}{type_adjustment_clause}) {block_name};") + } FuncCallReturnTypeBuilder::Node(node_builder) => { let select_clause = node_builder.to_sql(block_name, param_context)?; let select_clause = if select_clause.is_empty() { @@ -1353,6 +1357,9 @@ fn apply_suffix_casts(type_oid: u32) -> String { 20 => "::text", // bigints as text 114 | 3802 => "#>> '{}'", // json/b as stringified 1700 => "::text", // numeric as text + 1016 => "::text[]", // bigint arrays as array of text + 199 | 3807 => "#>> '{}'", // json/b array as array of text + 1231 => "::text[]", // numeric array as array of text _ => "", } .to_string() diff --git a/test/expected/array_args.out b/test/expected/array_args.out new file mode 100644 index 00000000..e69de29b diff --git a/test/sql/array_args.sql b/test/sql/array_args.sql new file mode 100644 index 00000000..c8d07f66 --- /dev/null +++ b/test/sql/array_args.sql @@ -0,0 +1,165 @@ +begin; + + -- functions accepting arrays + create function get_smallint_array_item(arr smallint[], i int) + returns smallint language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getSmallintArrayItem(arr: [1, 2, 3], i: 1) + } + $$)); + + create function get_int_array_item(arr int[], i int) + returns int language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getIntArrayItem(arr: [1, 2, 3], i: 2) + } + $$)); + + create function get_bigint_array_item(arr bigint[], i int) + returns bigint language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getBigintArrayItem(arr: ["1", "2", "3"], i: 3) + } + $$)); + + create function get_real_array_item(arr real[], i int) + returns real language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getRealArrayItem(arr: [1.1, 2.2, 3.3], i: 1) + } + $$)); + + create function get_double_array_item(arr double precision[], i int) + returns double precision language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getDoubleArrayItem(arr: [1.1, 2.2, 3.3], i: 2) + } + $$)); + + create function get_numeric_array_item(arr numeric[], i int) + returns numeric language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getNumericArrayItem(arr: ["1.1", "2.2", "3.3"], i: 3) + } + $$)); + + create function get_bool_array_item(arr bool[], i int) + returns bool language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getBoolArrayItem(arr: [true, false], i: 2) + } + $$)); + + create function get_uuid_array_item(arr uuid[], i int) + returns uuid language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getUuidArrayItem(arr: ["e8dc3a9a-2c72-11ee-b094-776acede6790", "d3ef3a8c-2c72-11ee-b094-776acede7221"], i: 2) + } + $$)); + + -- functions returning arrays + create function returns_smallint_array() + returns smallint[] language sql stable + as $$ select '{1, 2, 3}'::smallint[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsSmallintArray + } + $$)); + + create function returns_int_array() + returns int[] language sql stable + as $$ select '{1, 2, 3}'::int[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsIntArray + } + $$)); + + create function returns_bigint_array() + returns bigint[] language sql stable + as $$ select '{1, 2, 3}'::bigint[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsBigintArray + } + $$)); + + create function returns_real_array() + returns real[] language sql stable + as $$ select '{1.1, 2.2, 3.3}'::real[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsRealArray + } + $$)); + + create function returns_double_array() + returns double precision[] language sql stable + as $$ select '{1.1, 2.2, 3.3}'::double precision[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsDoubleArray + } + $$)); + + create function returns_numeric_array() + returns numeric[] language sql stable + as $$ select '{1.1, 2.2, 3.3}'::numeric[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsNumericArray + } + $$)); + + create function returns_bool_array() + returns bool[] language sql stable + as $$ select '{true, false}'::bool[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsBoolArray + } + $$)); + + create function returns_uuid_array() + returns uuid[] language sql stable + as $$ select '{"e8dc3a9a-2c72-11ee-b094-776acede6790", "d3ef3a8c-2c72-11ee-b094-776acede7221"}'::uuid[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsUuidArray + } + $$)); + +rollback; From 7120a15ff4208157afaa791c159609821abdb79f Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Wed, 22 Nov 2023 16:27:10 +0530 Subject: [PATCH 02/17] add text, json and jsonb tests --- src/transpile.rs | 2 +- test/sql/array_args.sql | 62 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/transpile.rs b/src/transpile.rs index 57cd4f7d..921ac5c5 100644 --- a/src/transpile.rs +++ b/src/transpile.rs @@ -1358,7 +1358,7 @@ fn apply_suffix_casts(type_oid: u32) -> String { 114 | 3802 => "#>> '{}'", // json/b as stringified 1700 => "::text", // numeric as text 1016 => "::text[]", // bigint arrays as array of text - 199 | 3807 => "#>> '{}'", // json/b array as array of text + 199 | 3807 => "::text[]", // json/b array as array of text 1231 => "::text[]", // numeric array as array of text _ => "", } diff --git a/test/sql/array_args.sql b/test/sql/array_args.sql index c8d07f66..0096ae11 100644 --- a/test/sql/array_args.sql +++ b/test/sql/array_args.sql @@ -67,7 +67,7 @@ begin; select jsonb_pretty(graphql.resolve($$ query { - getBoolArrayItem(arr: [true, false], i: 2) + getBoolArrayItem(arr: [true, false], i: 1) } $$)); @@ -81,6 +81,36 @@ begin; } $$)); + create function get_text_array_item(arr text[], i int) + returns text language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getTextArrayItem(arr: ["hello", "world"], i: 1) + } + $$)); + + create function get_json_array_item(arr json[], i int) + returns json language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getJsonArrayItem(arr: ["{\"hello\": \"world\"}", "{\"bye\": \"world\"}"], i: 2) + } + $$)); + + create function get_jsonb_array_item(arr jsonb[], i int) + returns jsonb language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getJsonbArrayItem(arr: ["{\"hello\": \"world\"}", "{\"bye\": \"world\"}"], i: 1) + } + $$)); + -- functions returning arrays create function returns_smallint_array() returns smallint[] language sql stable @@ -162,4 +192,34 @@ begin; } $$)); + create function returns_text_array() + returns text[] language sql stable + as $$ select '{"hello", "world"}'::text[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsTextArray + } + $$)); + + create function returns_json_array() + returns json[] language sql stable + as $$ select array[json_build_object('hello', 'world'), json_build_object('bye', 'world')]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsJsonArray + } + $$)); + + create function returns_jsonb_array() + returns jsonb[] language sql stable + as $$ select array[jsonb_build_object('hello', 'world'), jsonb_build_object('bye', 'world')]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsJsonbArray + } + $$)); + rollback; From 9b51a544d5ebfd554497a48d8a15185efaf3f54b Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Wed, 22 Nov 2023 16:28:04 +0530 Subject: [PATCH 03/17] update out arg --- test/expected/array_args.out | 417 +++++++++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) diff --git a/test/expected/array_args.out b/test/expected/array_args.out index e69de29b..d0fbf31f 100644 --- a/test/expected/array_args.out +++ b/test/expected/array_args.out @@ -0,0 +1,417 @@ +begin; + -- functions accepting arrays + create function get_smallint_array_item(arr smallint[], i int) + returns smallint language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getSmallintArrayItem(arr: [1, 2, 3], i: 1) + } + $$)); + jsonb_pretty +----------------------------------- + { + + "data": { + + "getSmallintArrayItem": 1+ + } + + } +(1 row) + + create function get_int_array_item(arr int[], i int) + returns int language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getIntArrayItem(arr: [1, 2, 3], i: 2) + } + $$)); + jsonb_pretty +------------------------------ + { + + "data": { + + "getIntArrayItem": 2+ + } + + } +(1 row) + + create function get_bigint_array_item(arr bigint[], i int) + returns bigint language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getBigintArrayItem(arr: ["1", "2", "3"], i: 3) + } + $$)); + jsonb_pretty +----------------------------------- + { + + "data": { + + "getBigintArrayItem": "3"+ + } + + } +(1 row) + + create function get_real_array_item(arr real[], i int) + returns real language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getRealArrayItem(arr: [1.1, 2.2, 3.3], i: 1) + } + $$)); + jsonb_pretty +--------------------------------- + { + + "data": { + + "getRealArrayItem": 1.1+ + } + + } +(1 row) + + create function get_double_array_item(arr double precision[], i int) + returns double precision language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getDoubleArrayItem(arr: [1.1, 2.2, 3.3], i: 2) + } + $$)); + jsonb_pretty +----------------------------------- + { + + "data": { + + "getDoubleArrayItem": 2.2+ + } + + } +(1 row) + + create function get_numeric_array_item(arr numeric[], i int) + returns numeric language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getNumericArrayItem(arr: ["1.1", "2.2", "3.3"], i: 3) + } + $$)); + jsonb_pretty +-------------------------------------- + { + + "data": { + + "getNumericArrayItem": "3.3"+ + } + + } +(1 row) + + create function get_bool_array_item(arr bool[], i int) + returns bool language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getBoolArrayItem(arr: [true, false], i: 1) + } + $$)); + jsonb_pretty +---------------------------------- + { + + "data": { + + "getBoolArrayItem": true+ + } + + } +(1 row) + + create function get_uuid_array_item(arr uuid[], i int) + returns uuid language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getUuidArrayItem(arr: ["e8dc3a9a-2c72-11ee-b094-776acede6790", "d3ef3a8c-2c72-11ee-b094-776acede7221"], i: 2) + } + $$)); + jsonb_pretty +-------------------------------------------------------------------- + { + + "data": { + + "getUuidArrayItem": "d3ef3a8c-2c72-11ee-b094-776acede7221"+ + } + + } +(1 row) + + create function get_text_array_item(arr text[], i int) + returns text language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getTextArrayItem(arr: ["hello", "world"], i: 1) + } + $$)); + jsonb_pretty +------------------------------------- + { + + "data": { + + "getTextArrayItem": "hello"+ + } + + } +(1 row) + + create function get_json_array_item(arr json[], i int) + returns json language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getJsonArrayItem(arr: ["{\"hello\": \"world\"}", "{\"bye\": \"world\"}"], i: 2) + } + $$)); + jsonb_pretty +---------------------------------------------------- + { + + "data": { + + "getJsonArrayItem": "{\"bye\": \"world\"}"+ + } + + } +(1 row) + + create function get_jsonb_array_item(arr jsonb[], i int) + returns jsonb language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getJsonbArrayItem(arr: ["{\"hello\": \"world\"}", "{\"bye\": \"world\"}"], i: 1) + } + $$)); + jsonb_pretty +------------------------------------------------------- + { + + "data": { + + "getJsonbArrayItem": "{\"hello\": \"world\"}"+ + } + + } +(1 row) + + -- functions returning arrays + create function returns_smallint_array() + returns smallint[] language sql stable + as $$ select '{1, 2, 3}'::smallint[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsSmallintArray + } + $$)); + jsonb_pretty +----------------------------------- + { + + "data": { + + "returnsSmallintArray": [+ + 1, + + 2, + + 3 + + ] + + } + + } +(1 row) + + create function returns_int_array() + returns int[] language sql stable + as $$ select '{1, 2, 3}'::int[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsIntArray + } + $$)); + jsonb_pretty +------------------------------ + { + + "data": { + + "returnsIntArray": [+ + 1, + + 2, + + 3 + + ] + + } + + } +(1 row) + + create function returns_bigint_array() + returns bigint[] language sql stable + as $$ select '{1, 2, 3}'::bigint[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsBigintArray + } + $$)); + jsonb_pretty +--------------------------------- + { + + "data": { + + "returnsBigintArray": [+ + "1", + + "2", + + "3" + + ] + + } + + } +(1 row) + + create function returns_real_array() + returns real[] language sql stable + as $$ select '{1.1, 2.2, 3.3}'::real[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsRealArray + } + $$)); + jsonb_pretty +------------------------------- + { + + "data": { + + "returnsRealArray": [+ + 1.1, + + 2.2, + + 3.3 + + ] + + } + + } +(1 row) + + create function returns_double_array() + returns double precision[] language sql stable + as $$ select '{1.1, 2.2, 3.3}'::double precision[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsDoubleArray + } + $$)); + jsonb_pretty +--------------------------------- + { + + "data": { + + "returnsDoubleArray": [+ + 1.1, + + 2.2, + + 3.3 + + ] + + } + + } +(1 row) + + create function returns_numeric_array() + returns numeric[] language sql stable + as $$ select '{1.1, 2.2, 3.3}'::numeric[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsNumericArray + } + $$)); + jsonb_pretty +---------------------------------- + { + + "data": { + + "returnsNumericArray": [+ + "1.1", + + "2.2", + + "3.3" + + ] + + } + + } +(1 row) + + create function returns_bool_array() + returns bool[] language sql stable + as $$ select '{true, false}'::bool[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsBoolArray + } + $$)); + jsonb_pretty +------------------------------- + { + + "data": { + + "returnsBoolArray": [+ + true, + + false + + ] + + } + + } +(1 row) + + create function returns_uuid_array() + returns uuid[] language sql stable + as $$ select '{"e8dc3a9a-2c72-11ee-b094-776acede6790", "d3ef3a8c-2c72-11ee-b094-776acede7221"}'::uuid[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsUuidArray + } + $$)); + jsonb_pretty +----------------------------------------------------- + { + + "data": { + + "returnsUuidArray": [ + + "e8dc3a9a-2c72-11ee-b094-776acede6790",+ + "d3ef3a8c-2c72-11ee-b094-776acede7221" + + ] + + } + + } +(1 row) + + create function returns_text_array() + returns text[] language sql stable + as $$ select '{"hello", "world"}'::text[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsTextArray + } + $$)); + jsonb_pretty +------------------------------- + { + + "data": { + + "returnsTextArray": [+ + "hello", + + "world" + + ] + + } + + } +(1 row) + + create function returns_json_array() + returns json[] language sql stable + as $$ select array[json_build_object('hello', 'world'), json_build_object('bye', 'world')]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsJsonArray + } + $$)); + jsonb_pretty +---------------------------------------- + { + + "data": { + + "returnsJsonArray": [ + + "{\"hello\" : \"world\"}",+ + "{\"bye\" : \"world\"}" + + ] + + } + + } +(1 row) + + create function returns_jsonb_array() + returns jsonb[] language sql stable + as $$ select array[jsonb_build_object('hello', 'world'), jsonb_build_object('bye', 'world')]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsJsonbArray + } + $$)); + jsonb_pretty +--------------------------------------- + { + + "data": { + + "returnsJsonbArray": [ + + "{\"hello\": \"world\"}",+ + "{\"bye\": \"world\"}" + + ] + + } + + } +(1 row) + +rollback; From 31e01539c41e96ad33aa5cfe27557b41a30b8452 Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Wed, 22 Nov 2023 16:37:26 +0530 Subject: [PATCH 04/17] remove array funcs from fuction_calls_unpported tests --- test/expected/function_calls_unsupported.out | 40 -------------------- test/sql/function_calls_unsupported.sql | 20 ---------- 2 files changed, 60 deletions(-) diff --git a/test/expected/function_calls_unsupported.out b/test/expected/function_calls_unsupported.out index bb567df3..ea90810e 100644 --- a/test/expected/function_calls_unsupported.out +++ b/test/expected/function_calls_unsupported.out @@ -184,46 +184,6 @@ begin; } (1 row) - create function func_accepting_array(a int[]) - returns int language sql immutable - as $$ select 0; $$; - select jsonb_pretty(graphql.resolve($$ - query { - funcAcceptingArray(a: [1, 2, 3]) - } - $$)); - jsonb_pretty ------------------------------------------------------------------------------ - { + - "data": null, + - "errors": [ + - { + - "message": "Unknown field \"funcAcceptingArray\" on type Query"+ - } + - ] + - } -(1 row) - - create function func_returning_array() - returns int[] language sql immutable - as $$ select array[1, 2, 3]; $$; - select jsonb_pretty(graphql.resolve($$ - query { - funcReturningArray - } - $$)); - jsonb_pretty ------------------------------------------------------------------------------ - { + - "data": null, + - "errors": [ + - { + - "message": "Unknown field \"funcReturningArray\" on type Query"+ - } + - ] + - } -(1 row) - -- function returning type not on search path create schema dev; create table dev.book( diff --git a/test/sql/function_calls_unsupported.sql b/test/sql/function_calls_unsupported.sql index b78d4291..4adae612 100644 --- a/test/sql/function_calls_unsupported.sql +++ b/test/sql/function_calls_unsupported.sql @@ -107,26 +107,6 @@ begin; } $$)); - create function func_accepting_array(a int[]) - returns int language sql immutable - as $$ select 0; $$; - - select jsonb_pretty(graphql.resolve($$ - query { - funcAcceptingArray(a: [1, 2, 3]) - } - $$)); - - create function func_returning_array() - returns int[] language sql immutable - as $$ select array[1, 2, 3]; $$; - - select jsonb_pretty(graphql.resolve($$ - query { - funcReturningArray - } - $$)); - -- function returning type not on search path create schema dev; create table dev.book( From aea20f3b3beea1e8393fcc0b26485cdbc0ebce8c Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Wed, 22 Nov 2023 17:47:02 +0530 Subject: [PATCH 05/17] add more tests --- test/sql/array_args.sql | 98 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/test/sql/array_args.sql b/test/sql/array_args.sql index 0096ae11..d28055ef 100644 --- a/test/sql/array_args.sql +++ b/test/sql/array_args.sql @@ -111,6 +111,36 @@ begin; } $$)); + create function get_date_array_item(arr date[], i int) + returns date language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getDateArrayItem(arr: ["2023-11-22", "2023-11-23", "2023-11-24"], i: 3) + } + $$)); + + create function get_time_array_item(arr time[], i int) + returns time language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getTimeArrayItem(arr: ["5:05", "5:06", "5:07"], i: 1) + } + $$)); + + create function get_timestamp_array_item(arr timestamp[], i int) + returns timestamp language sql stable + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getTimestampArrayItem(arr: ["2023-07-28 12:39:05", "2023-08-28 12:39:05", "2023-09-28 12:39:05"], i: 2) + } + $$)); + -- functions returning arrays create function returns_smallint_array() returns smallint[] language sql stable @@ -222,4 +252,72 @@ begin; } $$)); + create function returns_date_array() + returns date[] language sql stable + as $$ select '{"2023-11-22", "2023-11-23", "2023-11-24"}'::date[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsDateArray + } + $$)); + + create function returns_time_array() + returns time[] language sql stable + as $$ select '{"5:05", "5:06", "5:07"}'::time[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsTimeArray + } + $$)); + + create function returns_timestamp_array() + returns timestamp[] language sql stable + as $$ select '{"2023-07-28 12:39:05", "2023-08-28 12:39:05", "2023-09-28 12:39:05"}'::timestamp[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsTimestampArray + } + $$)); + + select jsonb_pretty(graphql.resolve($$ + query IntrospectionQuery { + __schema { + queryType { + fields { + name + description + type { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + args { + name + type { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } + } $$)); rollback; From e0e24629313f9d31943cc9721906a149428a500b Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Wed, 22 Nov 2023 20:05:12 +0530 Subject: [PATCH 06/17] add date, time and schema tests --- src/builder.rs | 12 +- test/expected/array_args.out | 1029 ++++++++++++++++++++++++++++++++++ 2 files changed, 1038 insertions(+), 3 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 466080d0..18874412 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -3,7 +3,6 @@ use crate::gson; use crate::parser_util::*; use crate::sql_types::*; use graphql_parser::query::*; -use pgrx::notice; use serde::Serialize; use std::collections::HashMap; use std::ops::Deref; @@ -623,8 +622,7 @@ where )?; FuncCallReturnTypeBuilder::Connection(connection_builder) } - t => { - notice!("TYPE: {t:?}"); + _ => { return Err(format!( "unsupported return type: {}", func_call_resp_type @@ -2216,6 +2214,14 @@ impl __Schema { variables, )?) } + __Type::FuncCallResponse(func_call_resp_type) => { + Some(self.to_type_builder_from_type( + &func_call_resp_type.return_type, + selection_field, + fragment_definitions, + variables, + )?) + } _ => None, }; __TypeField::OfType(unwrapped_type_builder) diff --git a/test/expected/array_args.out b/test/expected/array_args.out index d0fbf31f..fe8924ba 100644 --- a/test/expected/array_args.out +++ b/test/expected/array_args.out @@ -187,6 +187,57 @@ begin; } (1 row) + create function get_date_array_item(arr date[], i int) + returns date language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getDateArrayItem(arr: ["2023-11-22", "2023-11-23", "2023-11-24"], i: 3) + } + $$)); + jsonb_pretty +------------------------------------------ + { + + "data": { + + "getDateArrayItem": "2023-11-24"+ + } + + } +(1 row) + + create function get_time_array_item(arr time[], i int) + returns time language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getTimeArrayItem(arr: ["5:05", "5:06", "5:07"], i: 1) + } + $$)); + jsonb_pretty +---------------------------------------- + { + + "data": { + + "getTimeArrayItem": "05:05:00"+ + } + + } +(1 row) + + create function get_timestamp_array_item(arr timestamp[], i int) + returns timestamp language sql stable + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getTimestampArrayItem(arr: ["2023-07-28 12:39:05", "2023-08-28 12:39:05", "2023-09-28 12:39:05"], i: 2) + } + $$)); + jsonb_pretty +-------------------------------------------------------- + { + + "data": { + + "getTimestampArrayItem": "2023-08-28T12:39:05"+ + } + + } +(1 row) + -- functions returning arrays create function returns_smallint_array() returns smallint[] language sql stable @@ -414,4 +465,982 @@ begin; } (1 row) + create function returns_date_array() + returns date[] language sql stable + as $$ select '{"2023-11-22", "2023-11-23", "2023-11-24"}'::date[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsDateArray + } + $$)); + jsonb_pretty +------------------------------- + { + + "data": { + + "returnsDateArray": [+ + "2023-11-22", + + "2023-11-23", + + "2023-11-24" + + ] + + } + + } +(1 row) + + create function returns_time_array() + returns time[] language sql stable + as $$ select '{"5:05", "5:06", "5:07"}'::time[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsTimeArray + } + $$)); + jsonb_pretty +------------------------------- + { + + "data": { + + "returnsTimeArray": [+ + "05:05:00", + + "05:06:00", + + "05:07:00" + + ] + + } + + } +(1 row) + + create function returns_timestamp_array() + returns timestamp[] language sql stable + as $$ select '{"2023-07-28 12:39:05", "2023-08-28 12:39:05", "2023-09-28 12:39:05"}'::timestamp[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsTimestampArray + } + $$)); + jsonb_pretty +------------------------------------ + { + + "data": { + + "returnsTimestampArray": [+ + "2023-07-28T12:39:05",+ + "2023-08-28T12:39:05",+ + "2023-09-28T12:39:05" + + ] + + } + + } +(1 row) + + select jsonb_pretty(graphql.resolve($$ + query IntrospectionQuery { + __schema { + queryType { + fields { + name + description + type { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + args { + name + type { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } + } $$)); + jsonb_pretty +------------------------------------------------------------------------ + { + + "data": { + + "__schema": { + + "queryType": { + + "fields": [ + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "BigInt" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getBigintArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "BigInt", + + "ofType": { + + "kind": "SCALAR", + + "name": "BigInt", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Boolean" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getBoolArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Boolean", + + "ofType": { + + "kind": "SCALAR", + + "name": "Boolean", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Date" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getDateArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Date", + + "ofType": { + + "kind": "SCALAR", + + "name": "Date", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Float" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getDoubleArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Float", + + "ofType": { + + "kind": "SCALAR", + + "name": "Float", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getIntArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "JSON" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getJsonArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "JSON", + + "ofType": { + + "kind": "SCALAR", + + "name": "JSON", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "JSON" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getJsonbArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "JSON", + + "ofType": { + + "kind": "SCALAR", + + "name": "JSON", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "BigFloat" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getNumericArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "BigFloat", + + "ofType": { + + "kind": "SCALAR", + + "name": "BigFloat", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Float" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getRealArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Float", + + "ofType": { + + "kind": "SCALAR", + + "name": "Float", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getSmallintArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "String" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getTextArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "String", + + "ofType": { + + "kind": "SCALAR", + + "name": "String", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Time" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getTimeArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Time", + + "ofType": { + + "kind": "SCALAR", + + "name": "Time", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Datetime" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getTimestampArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Datetime", + + "ofType": { + + "kind": "SCALAR", + + "name": "Datetime", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "UUID" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getUuidArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "UUID", + + "ofType": { + + "kind": "SCALAR", + + "name": "UUID", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "nodeId", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "ID", + + "ofType": null + + } + + } + + } + + ], + + "name": "node", + + "type": { + + "kind": "INTERFACE", + + "name": "Node", + + "ofType": null + + }, + + "description": "Retrieve a record by its `ID`"+ + }, + + { + + "args": [ + + ], + + "name": "returnsBigintArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "BigInt" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsBoolArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Boolean" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsDateArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Date" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsDoubleArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Float" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsIntArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsJsonArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "JSON" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsJsonbArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "JSON" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsNumericArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "BigFloat" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsRealArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Float" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsSmallintArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsTextArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "String" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsTimeArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Time" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsTimestampArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Datetime" + + } + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsUuidArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "UUID" + + } + + } + + }, + + "description": null + + } + + ] + + } + + } + + } + + } +(1 row) + rollback; From 688826bc2bece46592f0877b4732a9d450a2dcbc Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Thu, 23 Nov 2023 10:26:34 +0530 Subject: [PATCH 07/17] fix return type in schema --- src/builder.rs | 15 +-- test/expected/array_args.out | 210 ++++++++++------------------------- 2 files changed, 62 insertions(+), 163 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 18874412..d1a225e0 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -2195,7 +2195,12 @@ impl __Schema { None => __TypeField::PossibleTypes(None), }, "ofType" => { - let unwrapped_type_builder = match type_ { + let field_type = if let __Type::FuncCallResponse(func_call_resp_type) = type_ { + func_call_resp_type.return_type.deref() + } else { + type_ + }; + let unwrapped_type_builder = match field_type { __Type::List(list_type) => { let inner_type: __Type = (*(list_type.type_)).clone(); Some(self.to_type_builder_from_type( @@ -2214,14 +2219,6 @@ impl __Schema { variables, )?) } - __Type::FuncCallResponse(func_call_resp_type) => { - Some(self.to_type_builder_from_type( - &func_call_resp_type.return_type, - selection_field, - fragment_definitions, - variables, - )?) - } _ => None, }; __TypeField::OfType(unwrapped_type_builder) diff --git a/test/expected/array_args.out b/test/expected/array_args.out index fe8924ba..79b69dfa 100644 --- a/test/expected/array_args.out +++ b/test/expected/array_args.out @@ -607,11 +607,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "BigInt", + - "ofType": { + - "kind": "SCALAR", + - "name": "BigInt", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -649,11 +645,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "Boolean", + - "ofType": { + - "kind": "SCALAR", + - "name": "Boolean", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -691,11 +683,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "Date", + - "ofType": { + - "kind": "SCALAR", + - "name": "Date", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -733,11 +721,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "Float", + - "ofType": { + - "kind": "SCALAR", + - "name": "Float", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -775,11 +759,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "Int", + - "ofType": { + - "kind": "SCALAR", + - "name": "Int", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -817,11 +797,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "JSON", + - "ofType": { + - "kind": "SCALAR", + - "name": "JSON", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -859,11 +835,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "JSON", + - "ofType": { + - "kind": "SCALAR", + - "name": "JSON", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -901,11 +873,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "BigFloat", + - "ofType": { + - "kind": "SCALAR", + - "name": "BigFloat", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -943,11 +911,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "Float", + - "ofType": { + - "kind": "SCALAR", + - "name": "Float", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -985,11 +949,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "Int", + - "ofType": { + - "kind": "SCALAR", + - "name": "Int", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -1027,11 +987,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "String", + - "ofType": { + - "kind": "SCALAR", + - "name": "String", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -1069,11 +1025,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "Time", + - "ofType": { + - "kind": "SCALAR", + - "name": "Time", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -1111,11 +1063,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "Datetime", + - "ofType": { + - "kind": "SCALAR", + - "name": "Datetime", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -1153,11 +1101,7 @@ begin; "type": { + "kind": "SCALAR", + "name": "UUID", + - "ofType": { + - "kind": "SCALAR", + - "name": "UUID", + - "ofType": null + - } + + "ofType": null + }, + "description": null + }, + @@ -1192,12 +1136,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "BigInt" + - } + + "kind": "SCALAR", + + "name": "BigInt", + + "ofType": null + } + }, + "description": null + @@ -1210,12 +1151,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "Boolean" + - } + + "kind": "SCALAR", + + "name": "Boolean", + + "ofType": null + } + }, + "description": null + @@ -1228,12 +1166,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "Date" + - } + + "kind": "SCALAR", + + "name": "Date", + + "ofType": null + } + }, + "description": null + @@ -1246,12 +1181,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "Float" + - } + + "kind": "SCALAR", + + "name": "Float", + + "ofType": null + } + }, + "description": null + @@ -1264,12 +1196,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "Int" + - } + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + } + }, + "description": null + @@ -1282,12 +1211,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "JSON" + - } + + "kind": "SCALAR", + + "name": "JSON", + + "ofType": null + } + }, + "description": null + @@ -1300,12 +1226,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "JSON" + - } + + "kind": "SCALAR", + + "name": "JSON", + + "ofType": null + } + }, + "description": null + @@ -1318,12 +1241,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "BigFloat" + - } + + "kind": "SCALAR", + + "name": "BigFloat", + + "ofType": null + } + }, + "description": null + @@ -1336,12 +1256,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "Float" + - } + + "kind": "SCALAR", + + "name": "Float", + + "ofType": null + } + }, + "description": null + @@ -1354,12 +1271,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "Int" + - } + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + } + }, + "description": null + @@ -1372,12 +1286,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "String" + - } + + "kind": "SCALAR", + + "name": "String", + + "ofType": null + } + }, + "description": null + @@ -1390,12 +1301,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "Time" + - } + + "kind": "SCALAR", + + "name": "Time", + + "ofType": null + } + }, + "description": null + @@ -1408,12 +1316,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "Datetime" + - } + + "kind": "SCALAR", + + "name": "Datetime", + + "ofType": null + } + }, + "description": null + @@ -1426,12 +1331,9 @@ begin; "kind": "LIST", + "name": null, + "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "UUID" + - } + + "kind": "SCALAR", + + "name": "UUID", + + "ofType": null + } + }, + "description": null + From ee18460979daca1bf25b5a0630a5242ad11b1495 Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Thu, 23 Nov 2023 10:37:10 +0530 Subject: [PATCH 08/17] merge duplicate match arms --- src/transpile.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/transpile.rs b/src/transpile.rs index 921ac5c5..0b87dcb8 100644 --- a/src/transpile.rs +++ b/src/transpile.rs @@ -560,11 +560,7 @@ impl FunctionCallBuilder { let func_name = quote_ident(&self.function.name); let query = match &self.return_type_builder { - FuncCallReturnTypeBuilder::Scalar => { - let type_adjustment_clause = apply_suffix_casts(self.function.type_oid); - format!("select to_jsonb({func_schema}.{func_name}{args_clause}{type_adjustment_clause}) {block_name};") - } - FuncCallReturnTypeBuilder::List => { + FuncCallReturnTypeBuilder::Scalar | FuncCallReturnTypeBuilder::List => { let type_adjustment_clause = apply_suffix_casts(self.function.type_oid); format!("select to_jsonb({func_schema}.{func_name}{args_clause}{type_adjustment_clause}) {block_name};") } From e3175cb0572c8cb2e7e544383f936633120b2dbf Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Thu, 23 Nov 2023 10:47:41 +0530 Subject: [PATCH 09/17] rename array test file names --- test/expected/{array_args.out => array_args_and_return_types.out} | 0 test/sql/{array_args.sql => array_args_and_return_types.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename test/expected/{array_args.out => array_args_and_return_types.out} (100%) rename test/sql/{array_args.sql => array_args_and_return_types.sql} (100%) diff --git a/test/expected/array_args.out b/test/expected/array_args_and_return_types.out similarity index 100% rename from test/expected/array_args.out rename to test/expected/array_args_and_return_types.out diff --git a/test/sql/array_args.sql b/test/sql/array_args_and_return_types.sql similarity index 100% rename from test/sql/array_args.sql rename to test/sql/array_args_and_return_types.sql From c8d65bbac23674523930dee09249cb5b7f4605c0 Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Thu, 23 Nov 2023 11:18:55 +0530 Subject: [PATCH 10/17] arrays with default args are not yet supported --- test/expected/array_args_and_return_types.out | 51 +++++++++++++++++++ test/sql/array_args_and_return_types.sql | 11 ++++ 2 files changed, 62 insertions(+) diff --git a/test/expected/array_args_and_return_types.out b/test/expected/array_args_and_return_types.out index 79b69dfa..b2b8781f 100644 --- a/test/expected/array_args_and_return_types.out +++ b/test/expected/array_args_and_return_types.out @@ -528,6 +528,27 @@ begin; } (1 row) + -- array args with default values are not yet supported + create function return_input_array(arr smallint[] = '{4, 2}'::smallint[]) + returns smallint[] language sql stable + as $$ select arr; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnInputArray + } + $$)); + jsonb_pretty +--------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Invalid input for NonNull type"+ + } + + ] + + } +(1 row) + select jsonb_pretty(graphql.resolve($$ query IntrospectionQuery { __schema { @@ -1128,6 +1149,36 @@ begin; }, + "description": "Retrieve a record by its `ID`"+ }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int" + + } + + } + + } + + } + + ], + + "name": "returnInputArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + }, + + "description": null + + }, + { + "args": [ + ], + diff --git a/test/sql/array_args_and_return_types.sql b/test/sql/array_args_and_return_types.sql index d28055ef..a2dcba38 100644 --- a/test/sql/array_args_and_return_types.sql +++ b/test/sql/array_args_and_return_types.sql @@ -282,6 +282,17 @@ begin; } $$)); + -- array args with default values are not yet supported + create function return_input_array(arr smallint[] = '{4, 2}'::smallint[]) + returns smallint[] language sql stable + as $$ select arr; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnInputArray + } + $$)); + select jsonb_pretty(graphql.resolve($$ query IntrospectionQuery { __schema { From ff8b029fba9fa9d8141e5f679853ddd9f01df3e5 Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Thu, 23 Nov 2023 11:35:05 +0530 Subject: [PATCH 11/17] add mutation tests --- test/expected/array_args_and_return_types.out | 1433 ++++++++++++++++- test/sql/array_args_and_return_types.sql | 343 +++- 2 files changed, 1719 insertions(+), 57 deletions(-) diff --git a/test/expected/array_args_and_return_types.out b/test/expected/array_args_and_return_types.out index b2b8781f..fe3320aa 100644 --- a/test/expected/array_args_and_return_types.out +++ b/test/expected/array_args_and_return_types.out @@ -1,4 +1,6 @@ begin; + savepoint a; + --query tests -- functions accepting arrays create function get_smallint_array_item(arr smallint[], i int) returns smallint language sql stable @@ -528,27 +530,6 @@ begin; } (1 row) - -- array args with default values are not yet supported - create function return_input_array(arr smallint[] = '{4, 2}'::smallint[]) - returns smallint[] language sql stable - as $$ select arr; $$; - select jsonb_pretty(graphql.resolve($$ - query { - returnInputArray - } - $$)); - jsonb_pretty ---------------------------------------------------------- - { + - "data": null, + - "errors": [ + - { + - "message": "Invalid input for NonNull type"+ - } + - ] + - } -(1 row) - select jsonb_pretty(graphql.resolve($$ query IntrospectionQuery { __schema { @@ -1149,36 +1130,6 @@ begin; }, + "description": "Retrieve a record by its `ID`"+ }, + - { + - "args": [ + - { + - "name": "arr", + - "type": { + - "kind": "NON_NULL", + - "name": null, + - "ofType": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "Int" + - } + - } + - } + - } + - ], + - "name": "returnInputArray", + - "type": { + - "kind": "LIST", + - "name": null, + - "ofType": { + - "kind": "SCALAR", + - "name": "Int", + - "ofType": null + - } + - }, + - "description": null + - }, + { + "args": [ + ], + @@ -1396,4 +1347,1384 @@ begin; } (1 row) + rollback to savepoint a; + --mutation tests + -- functions accepting arrays + create function get_smallint_array_item(arr smallint[], i int) + returns smallint language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getSmallintArrayItem(arr: [1, 2, 3], i: 1) + } + $$)); + jsonb_pretty +------------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getSmallintArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_int_array_item(arr int[], i int) + returns int language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getIntArrayItem(arr: [1, 2, 3], i: 2) + } + $$)); + jsonb_pretty +-------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getIntArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_bigint_array_item(arr bigint[], i int) + returns bigint language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getBigintArrayItem(arr: ["1", "2", "3"], i: 3) + } + $$)); + jsonb_pretty +----------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getBigintArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_real_array_item(arr real[], i int) + returns real language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getRealArrayItem(arr: [1.1, 2.2, 3.3], i: 1) + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getRealArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_double_array_item(arr double precision[], i int) + returns double precision language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getDoubleArrayItem(arr: [1.1, 2.2, 3.3], i: 2) + } + $$)); + jsonb_pretty +----------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getDoubleArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_numeric_array_item(arr numeric[], i int) + returns numeric language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getNumericArrayItem(arr: ["1.1", "2.2", "3.3"], i: 3) + } + $$)); + jsonb_pretty +------------------------------------------------------------------------------ + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getNumericArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_bool_array_item(arr bool[], i int) + returns bool language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getBoolArrayItem(arr: [true, false], i: 1) + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getBoolArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_uuid_array_item(arr uuid[], i int) + returns uuid language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getUuidArrayItem(arr: ["e8dc3a9a-2c72-11ee-b094-776acede6790", "d3ef3a8c-2c72-11ee-b094-776acede7221"], i: 2) + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getUuidArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_text_array_item(arr text[], i int) + returns text language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getTextArrayItem(arr: ["hello", "world"], i: 1) + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getTextArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_json_array_item(arr json[], i int) + returns json language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getJsonArrayItem(arr: ["{\"hello\": \"world\"}", "{\"bye\": \"world\"}"], i: 2) + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getJsonArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_jsonb_array_item(arr jsonb[], i int) + returns jsonb language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getJsonbArrayItem(arr: ["{\"hello\": \"world\"}", "{\"bye\": \"world\"}"], i: 1) + } + $$)); + jsonb_pretty +---------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getJsonbArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_date_array_item(arr date[], i int) + returns date language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getDateArrayItem(arr: ["2023-11-22", "2023-11-23", "2023-11-24"], i: 3) + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getDateArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_time_array_item(arr time[], i int) + returns time language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getTimeArrayItem(arr: ["5:05", "5:06", "5:07"], i: 1) + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getTimeArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + create function get_timestamp_array_item(arr timestamp[], i int) + returns timestamp language sql volatile + as $$ select arr[i]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + getTimestampArrayItem(arr: ["2023-07-28 12:39:05", "2023-08-28 12:39:05", "2023-09-28 12:39:05"], i: 2) + } + $$)); + jsonb_pretty +-------------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"getTimestampArrayItem\" on type Query"+ + } + + ] + + } +(1 row) + + -- functions returning arrays + create function returns_smallint_array() + returns smallint[] language sql volatile + as $$ select '{1, 2, 3}'::smallint[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsSmallintArray + } + $$)); + jsonb_pretty +------------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsSmallintArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_int_array() + returns int[] language sql volatile + as $$ select '{1, 2, 3}'::int[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsIntArray + } + $$)); + jsonb_pretty +-------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsIntArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_bigint_array() + returns bigint[] language sql volatile + as $$ select '{1, 2, 3}'::bigint[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsBigintArray + } + $$)); + jsonb_pretty +----------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsBigintArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_real_array() + returns real[] language sql volatile + as $$ select '{1.1, 2.2, 3.3}'::real[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsRealArray + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsRealArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_double_array() + returns double precision[] language sql volatile + as $$ select '{1.1, 2.2, 3.3}'::double precision[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsDoubleArray + } + $$)); + jsonb_pretty +----------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsDoubleArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_numeric_array() + returns numeric[] language sql volatile + as $$ select '{1.1, 2.2, 3.3}'::numeric[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsNumericArray + } + $$)); + jsonb_pretty +------------------------------------------------------------------------------ + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsNumericArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_bool_array() + returns bool[] language sql volatile + as $$ select '{true, false}'::bool[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsBoolArray + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsBoolArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_uuid_array() + returns uuid[] language sql volatile + as $$ select '{"e8dc3a9a-2c72-11ee-b094-776acede6790", "d3ef3a8c-2c72-11ee-b094-776acede7221"}'::uuid[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsUuidArray + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsUuidArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_text_array() + returns text[] language sql volatile + as $$ select '{"hello", "world"}'::text[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsTextArray + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsTextArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_json_array() + returns json[] language sql volatile + as $$ select array[json_build_object('hello', 'world'), json_build_object('bye', 'world')]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsJsonArray + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsJsonArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_jsonb_array() + returns jsonb[] language sql volatile + as $$ select array[jsonb_build_object('hello', 'world'), jsonb_build_object('bye', 'world')]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsJsonbArray + } + $$)); + jsonb_pretty +---------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsJsonbArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_date_array() + returns date[] language sql volatile + as $$ select '{"2023-11-22", "2023-11-23", "2023-11-24"}'::date[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsDateArray + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsDateArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_time_array() + returns time[] language sql volatile + as $$ select '{"5:05", "5:06", "5:07"}'::time[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsTimeArray + } + $$)); + jsonb_pretty +--------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsTimeArray\" on type Query"+ + } + + ] + + } +(1 row) + + create function returns_timestamp_array() + returns timestamp[] language sql volatile + as $$ select '{"2023-07-28 12:39:05", "2023-08-28 12:39:05", "2023-09-28 12:39:05"}'::timestamp[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsTimestampArray + } + $$)); + jsonb_pretty +-------------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsTimestampArray\" on type Query"+ + } + + ] + + } +(1 row) + + select jsonb_pretty(graphql.resolve($$ + query IntrospectionQuery { + __schema { + mutationType { + fields { + name + description + type { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + args { + name + type { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } + } $$)); + jsonb_pretty +---------------------------------------------------------------- + { + + "data": { + + "__schema": { + + "mutationType": { + + "fields": [ + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "BigInt" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getBigintArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "BigInt", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Boolean" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getBoolArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Boolean", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Date" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getDateArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Date", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Float" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getDoubleArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Float", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getIntArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "JSON" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getJsonArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "JSON", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "JSON" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getJsonbArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "JSON", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "BigFloat"+ + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getNumericArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "BigFloat", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Float" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getRealArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Float", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getSmallintArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "String" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getTextArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "String", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Time" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getTimeArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Time", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Datetime"+ + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getTimestampArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "Datetime", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + { + + "name": "arr", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "UUID" + + } + + } + + } + + }, + + { + + "name": "i", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + } + + } + + ], + + "name": "getUuidArrayItem", + + "type": { + + "kind": "SCALAR", + + "name": "UUID", + + "ofType": null + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsBigintArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "BigInt", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsBoolArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Boolean", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsDateArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Date", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsDoubleArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Float", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsIntArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsJsonArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "JSON", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsJsonbArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "JSON", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsNumericArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "BigFloat", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsRealArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Float", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsSmallintArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Int", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsTextArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "String", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsTimeArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Time", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsTimestampArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "Datetime", + + "ofType": null + + } + + }, + + "description": null + + }, + + { + + "args": [ + + ], + + "name": "returnsUuidArray", + + "type": { + + "kind": "LIST", + + "name": null, + + "ofType": { + + "kind": "SCALAR", + + "name": "UUID", + + "ofType": null + + } + + }, + + "description": null + + } + + ] + + } + + } + + } + + } +(1 row) + + rollback to savepoint a; + -- array args with default values are not yet supported + create function return_input_array(arr smallint[] = '{4, 2}'::smallint[]) + returns smallint[] language sql stable + as $$ select arr; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnInputArray + } + $$)); + jsonb_pretty +--------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Invalid input for NonNull type"+ + } + + ] + + } +(1 row) + rollback; diff --git a/test/sql/array_args_and_return_types.sql b/test/sql/array_args_and_return_types.sql index a2dcba38..8aa62219 100644 --- a/test/sql/array_args_and_return_types.sql +++ b/test/sql/array_args_and_return_types.sql @@ -1,5 +1,8 @@ begin; + savepoint a; + --query tests + -- functions accepting arrays create function get_smallint_array_item(arr smallint[], i int) returns smallint language sql stable @@ -282,21 +285,335 @@ begin; } $$)); - -- array args with default values are not yet supported - create function return_input_array(arr smallint[] = '{4, 2}'::smallint[]) - returns smallint[] language sql stable - as $$ select arr; $$; + select jsonb_pretty(graphql.resolve($$ + query IntrospectionQuery { + __schema { + queryType { + fields { + name + description + type { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + args { + name + type { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } + } $$)); + + rollback to savepoint a; + + --mutation tests + + -- functions accepting arrays + create function get_smallint_array_item(arr smallint[], i int) + returns smallint language sql volatile + as $$ select arr[i]; $$; select jsonb_pretty(graphql.resolve($$ query { - returnInputArray + getSmallintArrayItem(arr: [1, 2, 3], i: 1) + } + $$)); + + create function get_int_array_item(arr int[], i int) + returns int language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getIntArrayItem(arr: [1, 2, 3], i: 2) + } + $$)); + + create function get_bigint_array_item(arr bigint[], i int) + returns bigint language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getBigintArrayItem(arr: ["1", "2", "3"], i: 3) + } + $$)); + + create function get_real_array_item(arr real[], i int) + returns real language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getRealArrayItem(arr: [1.1, 2.2, 3.3], i: 1) + } + $$)); + + create function get_double_array_item(arr double precision[], i int) + returns double precision language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getDoubleArrayItem(arr: [1.1, 2.2, 3.3], i: 2) + } + $$)); + + create function get_numeric_array_item(arr numeric[], i int) + returns numeric language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getNumericArrayItem(arr: ["1.1", "2.2", "3.3"], i: 3) + } + $$)); + + create function get_bool_array_item(arr bool[], i int) + returns bool language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getBoolArrayItem(arr: [true, false], i: 1) + } + $$)); + + create function get_uuid_array_item(arr uuid[], i int) + returns uuid language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getUuidArrayItem(arr: ["e8dc3a9a-2c72-11ee-b094-776acede6790", "d3ef3a8c-2c72-11ee-b094-776acede7221"], i: 2) + } + $$)); + + create function get_text_array_item(arr text[], i int) + returns text language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getTextArrayItem(arr: ["hello", "world"], i: 1) + } + $$)); + + create function get_json_array_item(arr json[], i int) + returns json language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getJsonArrayItem(arr: ["{\"hello\": \"world\"}", "{\"bye\": \"world\"}"], i: 2) + } + $$)); + + create function get_jsonb_array_item(arr jsonb[], i int) + returns jsonb language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getJsonbArrayItem(arr: ["{\"hello\": \"world\"}", "{\"bye\": \"world\"}"], i: 1) + } + $$)); + + create function get_date_array_item(arr date[], i int) + returns date language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getDateArrayItem(arr: ["2023-11-22", "2023-11-23", "2023-11-24"], i: 3) + } + $$)); + + create function get_time_array_item(arr time[], i int) + returns time language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getTimeArrayItem(arr: ["5:05", "5:06", "5:07"], i: 1) + } + $$)); + + create function get_timestamp_array_item(arr timestamp[], i int) + returns timestamp language sql volatile + as $$ select arr[i]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + getTimestampArrayItem(arr: ["2023-07-28 12:39:05", "2023-08-28 12:39:05", "2023-09-28 12:39:05"], i: 2) + } + $$)); + + -- functions returning arrays + create function returns_smallint_array() + returns smallint[] language sql volatile + as $$ select '{1, 2, 3}'::smallint[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsSmallintArray + } + $$)); + + create function returns_int_array() + returns int[] language sql volatile + as $$ select '{1, 2, 3}'::int[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsIntArray + } + $$)); + + create function returns_bigint_array() + returns bigint[] language sql volatile + as $$ select '{1, 2, 3}'::bigint[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsBigintArray + } + $$)); + + create function returns_real_array() + returns real[] language sql volatile + as $$ select '{1.1, 2.2, 3.3}'::real[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsRealArray + } + $$)); + + create function returns_double_array() + returns double precision[] language sql volatile + as $$ select '{1.1, 2.2, 3.3}'::double precision[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsDoubleArray + } + $$)); + + create function returns_numeric_array() + returns numeric[] language sql volatile + as $$ select '{1.1, 2.2, 3.3}'::numeric[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsNumericArray + } + $$)); + + create function returns_bool_array() + returns bool[] language sql volatile + as $$ select '{true, false}'::bool[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsBoolArray + } + $$)); + + create function returns_uuid_array() + returns uuid[] language sql volatile + as $$ select '{"e8dc3a9a-2c72-11ee-b094-776acede6790", "d3ef3a8c-2c72-11ee-b094-776acede7221"}'::uuid[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsUuidArray + } + $$)); + + create function returns_text_array() + returns text[] language sql volatile + as $$ select '{"hello", "world"}'::text[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsTextArray + } + $$)); + + create function returns_json_array() + returns json[] language sql volatile + as $$ select array[json_build_object('hello', 'world'), json_build_object('bye', 'world')]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsJsonArray + } + $$)); + + create function returns_jsonb_array() + returns jsonb[] language sql volatile + as $$ select array[jsonb_build_object('hello', 'world'), jsonb_build_object('bye', 'world')]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsJsonbArray + } + $$)); + + create function returns_date_array() + returns date[] language sql volatile + as $$ select '{"2023-11-22", "2023-11-23", "2023-11-24"}'::date[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsDateArray + } + $$)); + + create function returns_time_array() + returns time[] language sql volatile + as $$ select '{"5:05", "5:06", "5:07"}'::time[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsTimeArray + } + $$)); + + create function returns_timestamp_array() + returns timestamp[] language sql volatile + as $$ select '{"2023-07-28 12:39:05", "2023-08-28 12:39:05", "2023-09-28 12:39:05"}'::timestamp[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsTimestampArray } $$)); select jsonb_pretty(graphql.resolve($$ query IntrospectionQuery { __schema { - queryType { + mutationType { fields { name description @@ -331,4 +648,18 @@ begin; } } } $$)); + + rollback to savepoint a; + + -- array args with default values are not yet supported + create function return_input_array(arr smallint[] = '{4, 2}'::smallint[]) + returns smallint[] language sql stable + as $$ select arr; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnInputArray + } + $$)); + rollback; From 6cb0c065a39e54593e24ef273264a9cf13f00ac9 Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Wed, 29 Nov 2023 13:20:09 +0530 Subject: [PATCH 12/17] arrays of composite types not supported --- src/sql_types.rs | 27 +++++++++++++++++++ test/expected/array_args_and_return_types.out | 27 +++++++++++++++++++ test/sql/array_args_and_return_types.sql | 18 +++++++++++++ 3 files changed, 72 insertions(+) diff --git a/src/sql_types.rs b/src/sql_types.rs index 8cc7732b..83b6c004 100644 --- a/src/sql_types.rs +++ b/src/sql_types.rs @@ -134,15 +134,42 @@ impl Function { fn return_type_is_supported(&self, types: &HashMap>) -> bool { if let Some(return_type) = types.get(&self.type_oid) { + let array_element_type_is_supported = self.array_element_type_is_supported( + &return_type.category, + return_type.array_element_type_oid, + types, + ); return_type.category != TypeCategory::Pseudo && return_type.name != "record" && return_type.name != "trigger" && return_type.name != "event_trigger" + && array_element_type_is_supported } else { false } } + fn array_element_type_is_supported( + &self, + type_category: &TypeCategory, + array_element_type_oid: Option, + types: &HashMap>, + ) -> bool { + if *type_category == TypeCategory::Array { + if let Some(array_element_type_oid) = array_element_type_oid { + if let Some(array_element_type) = types.get(&array_element_type_oid) { + array_element_type.category == TypeCategory::Other + } else { + false + } + } else { + false + } + } else { + true + } + } + fn is_function_overloaded(&self, function_name_to_count: &HashMap<&String, u32>) -> bool { if let Some(&count) = function_name_to_count.get(&self.name) { count > 1 diff --git a/test/expected/array_args_and_return_types.out b/test/expected/array_args_and_return_types.out index fe3320aa..4f532c44 100644 --- a/test/expected/array_args_and_return_types.out +++ b/test/expected/array_args_and_return_types.out @@ -2727,4 +2727,31 @@ begin; } (1 row) + -- composite type arrays are not supported + create table account( + id serial primary key, + email varchar(255) not null + ); + create function returns_account_array() + returns Account[] language sql stable + as $$ select '{"(1, \"a@example.com\")", "(2, \"b@example.com\")"}'::Account[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnsAccountArray { + id + } + } + $$)); + jsonb_pretty +------------------------------------------------------------------------------ + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnsAccountArray\" on type Query"+ + } + + ] + + } +(1 row) + rollback; diff --git a/test/sql/array_args_and_return_types.sql b/test/sql/array_args_and_return_types.sql index 8aa62219..62431791 100644 --- a/test/sql/array_args_and_return_types.sql +++ b/test/sql/array_args_and_return_types.sql @@ -662,4 +662,22 @@ begin; } $$)); + -- composite type arrays are not supported + create table account( + id serial primary key, + email varchar(255) not null + ); + + create function returns_account_array() + returns Account[] language sql stable + as $$ select '{"(1, \"a@example.com\")", "(2, \"b@example.com\")"}'::Account[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnsAccountArray { + id + } + } + $$)); + rollback; From da9d19b7b96120e503e43cf30e77679eb1c646e9 Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Wed, 29 Nov 2023 13:21:13 +0530 Subject: [PATCH 13/17] add array type support feature to changelog --- docs/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.md b/docs/changelog.md index 29aab36d..2ab1c66c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -56,3 +56,4 @@ ## master - bugfix: make non-default args non-null in UDFs +- feature: add support for array types in UDFs From 280fdea2ff8dee97f0d42849a5648c8957bf45bc Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Wed, 29 Nov 2023 13:51:16 +0530 Subject: [PATCH 14/17] update docs --- docs/functions.md | 93 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/docs/functions.md b/docs/functions.md index c07ae0ec..9500aae8 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -278,6 +278,97 @@ Functions returning multiple rows of a table or view are exposed as [collections A set returning function with any of its argument names clashing with argument names of a collection (`first`, `last`, `before`, `after`, `filter`, or `orderBy`) will not be exposed. +Functions accepting or returning arrays of non-composite types are also supported. In the following example, the `ids` array is used to filter rows from the `Account` table: + +=== "Function" + + ```sql + create table "Account"( + id serial primary key, + email varchar(255) not null + ); + + insert into "Account"(email) + values + ('a@example.com'), + ('b@example.com'), + ('c@example.com'); + + create function "accountsByIds"("ids" int[]) + returns setof "Account" + stable + language sql + as $$ select id, email from "Account" where id = any(ids); $$; + ``` + +=== "QueryType" + + ```graphql + type Query { + accountsByIds( + ids: Int[]! + + """Query the first `n` records in the collection""" + first: Int + + """Query the last `n` records in the collection""" + last: Int + + """Query values in the collection before the provided cursor""" + before: Cursor + + """Query values in the collection after the provided cursor""" + after: Cursor + + """Filters to apply to the results set when querying from the collection""" + filter: AccountFilter + + """Sort order to apply to the collection""" + orderBy: [AccountOrderBy!] + ): AccountConnection + } + ``` + +=== "Query" + + ```graphql + query { + accountsByIds(ids: [1, 2]) { + edges { + node { + id + email + } + } + } + } + ``` + +=== "Response" + + ```json + { + "data": { + "accountsByIds": { + "edges": [ + { + "node": { + "id": 1, + "email": "a@example.com" + } + }, + { + "node": { + "id": 2, + "email": "b@example.com" + } + } + ] + } + } + } + ``` + ## Default Arguments Functions with default arguments can have their default arguments omitted. @@ -329,4 +420,4 @@ The following features are not yet supported. Any function using these features * Functions with a nameless argument * Functions returning void * Variadic functions -* Function that accept or return an array type +* Functions that accept or return an array of composite type From cecaf44641ecfbc7ca96b57a64b4afed90769074 Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Thu, 30 Nov 2023 11:47:06 +0530 Subject: [PATCH 15/17] filter out functions which return an enum --- docs/functions.md | 1 + src/sql_types.rs | 1 + test/expected/function_calls_unsupported.out | 23 ++++++++++++++++++++ test/sql/function_calls_unsupported.sql | 15 +++++++++++++ 4 files changed, 40 insertions(+) diff --git a/docs/functions.md b/docs/functions.md index d3031627..223ba626 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -420,3 +420,4 @@ The following features are not yet supported. Any function using these features * Functions returning void * Variadic functions * Functions that accept or return an array of composite type +* Functions that return an enum type diff --git a/src/sql_types.rs b/src/sql_types.rs index f1c553d7..be1e07a4 100644 --- a/src/sql_types.rs +++ b/src/sql_types.rs @@ -140,6 +140,7 @@ impl Function { types, ); return_type.category != TypeCategory::Pseudo + && return_type.category != TypeCategory::Enum && return_type.name != "record" && return_type.name != "trigger" && return_type.name != "event_trigger" diff --git a/test/expected/function_calls_unsupported.out b/test/expected/function_calls_unsupported.out index ea90810e..4017c076 100644 --- a/test/expected/function_calls_unsupported.out +++ b/test/expected/function_calls_unsupported.out @@ -240,6 +240,29 @@ begin; } (1 row) + -- function returning enum + create type "Algorithm" as enum ('aead-ietf'); + comment on type "Algorithm" is '@graphql({"mappings": {"aead-ietf": "AEAD_IETF"}})'; + create function return_algorithm() + returns "Algorithm" language sql volatile + as $$ select 'aead-ietf'::"Algorithm"; $$; + select jsonb_pretty(graphql.resolve($$ + mutation { + returnAlgorithm + } + $$)); + jsonb_pretty +----------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnAlgorithm\" on type Mutation"+ + } + + ] + + } +(1 row) + select jsonb_pretty(graphql.resolve($$ query IntrospectionQuery { __schema { diff --git a/test/sql/function_calls_unsupported.sql b/test/sql/function_calls_unsupported.sql index 4adae612..cf2acddf 100644 --- a/test/sql/function_calls_unsupported.sql +++ b/test/sql/function_calls_unsupported.sql @@ -145,6 +145,21 @@ begin; } $$)); + + -- function returning enum + create type "Algorithm" as enum ('aead-ietf'); + comment on type "Algorithm" is '@graphql({"mappings": {"aead-ietf": "AEAD_IETF"}})'; + + create function return_algorithm() + returns "Algorithm" language sql volatile + as $$ select 'aead-ietf'::"Algorithm"; $$; + + select jsonb_pretty(graphql.resolve($$ + mutation { + returnAlgorithm + } + $$)); + select jsonb_pretty(graphql.resolve($$ query IntrospectionQuery { __schema { From f08a4038a70636a57d8a74ad03b4cafd607d71db Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Thu, 30 Nov 2023 11:55:25 +0530 Subject: [PATCH 16/17] add test to check that enum array type returns are filtered out --- docs/functions.md | 2 +- test/expected/array_args_and_return_types.out | 23 +++++++++++++++++++ test/sql/array_args_and_return_types.sql | 14 +++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/functions.md b/docs/functions.md index 223ba626..6adcac6e 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -420,4 +420,4 @@ The following features are not yet supported. Any function using these features * Functions returning void * Variadic functions * Functions that accept or return an array of composite type -* Functions that return an enum type +* Functions that return an enum type or an array of enum type diff --git a/test/expected/array_args_and_return_types.out b/test/expected/array_args_and_return_types.out index 4f532c44..8b1a56f7 100644 --- a/test/expected/array_args_and_return_types.out +++ b/test/expected/array_args_and_return_types.out @@ -2754,4 +2754,27 @@ begin; } (1 row) + -- enum type arrays are not supported + create type "Algorithm" as enum ('aead-ietf'); + comment on type "Algorithm" is '@graphql({"mappings": {"aead-ietf": "AEAD_IETF"}})'; + create function return_algorithm_array() + returns "Algorithm"[] language sql stable + as $$ select array['aead-ietf']::"Algorithm"[]; $$; + select jsonb_pretty(graphql.resolve($$ + query { + returnAlgorithmArray + } + $$)); + jsonb_pretty +------------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"returnAlgorithmArray\" on type Query"+ + } + + ] + + } +(1 row) + rollback; diff --git a/test/sql/array_args_and_return_types.sql b/test/sql/array_args_and_return_types.sql index 62431791..c56abc0e 100644 --- a/test/sql/array_args_and_return_types.sql +++ b/test/sql/array_args_and_return_types.sql @@ -680,4 +680,18 @@ begin; } $$)); + -- enum type arrays are not supported + create type "Algorithm" as enum ('aead-ietf'); + comment on type "Algorithm" is '@graphql({"mappings": {"aead-ietf": "AEAD_IETF"}})'; + + create function return_algorithm_array() + returns "Algorithm"[] language sql stable + as $$ select array['aead-ietf']::"Algorithm"[]; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + returnAlgorithmArray + } + $$)); + rollback; From 47de62779b5671380c063e3ee9fc84ce022cb466 Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Thu, 30 Nov 2023 12:09:26 +0530 Subject: [PATCH 17/17] filter out functions with enum or array of enums as args --- docs/functions.md | 2 +- src/sql_types.rs | 11 +++++++--- test/expected/array_args_and_return_types.out | 20 +++++++++++++++++++ test/expected/function_calls_unsupported.out | 20 +++++++++++++++++++ test/sql/array_args_and_return_types.sql | 10 ++++++++++ test/sql/function_calls_unsupported.sql | 10 ++++++++++ 6 files changed, 69 insertions(+), 4 deletions(-) diff --git a/docs/functions.md b/docs/functions.md index 6adcac6e..d148eaca 100644 --- a/docs/functions.md +++ b/docs/functions.md @@ -420,4 +420,4 @@ The following features are not yet supported. Any function using these features * Functions returning void * Variadic functions * Functions that accept or return an array of composite type -* Functions that return an enum type or an array of enum type +* Functions that accept or return an enum type or an array of enum type diff --git a/src/sql_types.rs b/src/sql_types.rs index be1e07a4..231f2bb8 100644 --- a/src/sql_types.rs +++ b/src/sql_types.rs @@ -123,9 +123,14 @@ impl Function { fn arg_types_are_supported(&self, types: &HashMap>) -> bool { self.args().all(|(arg_type, _, _, _)| { - if let Some(return_type) = types.get(&arg_type) { - return_type.category == TypeCategory::Other - || return_type.category == TypeCategory::Array + if let Some(arg_type) = types.get(&arg_type) { + let array_element_type_is_supported = self.array_element_type_is_supported( + &arg_type.category, + arg_type.array_element_type_oid, + types, + ); + arg_type.category == TypeCategory::Other + || (arg_type.category == TypeCategory::Array && array_element_type_is_supported) } else { false } diff --git a/test/expected/array_args_and_return_types.out b/test/expected/array_args_and_return_types.out index 8b1a56f7..656dcc17 100644 --- a/test/expected/array_args_and_return_types.out +++ b/test/expected/array_args_and_return_types.out @@ -2777,4 +2777,24 @@ begin; } (1 row) + create function accept_algorithm_array(e "Algorithm"[]) + returns int language sql stable + as $$ select 0; $$; + select jsonb_pretty(graphql.resolve($$ + query { + acceptAlgorithmArray(e: ["AEAD_IETF"]) + } + $$)); + jsonb_pretty +------------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"acceptAlgorithmArray\" on type Query"+ + } + + ] + + } +(1 row) + rollback; diff --git a/test/expected/function_calls_unsupported.out b/test/expected/function_calls_unsupported.out index 4017c076..8c86b669 100644 --- a/test/expected/function_calls_unsupported.out +++ b/test/expected/function_calls_unsupported.out @@ -263,6 +263,26 @@ begin; } (1 row) + create function accept_algorithm(e "Algorithm") + returns int language sql stable + as $$ select 0; $$; + select jsonb_pretty(graphql.resolve($$ + query { + acceptAlgorithm(e: "AEAD_IETF") + } + $$)); + jsonb_pretty +-------------------------------------------------------------------------- + { + + "data": null, + + "errors": [ + + { + + "message": "Unknown field \"acceptAlgorithm\" on type Query"+ + } + + ] + + } +(1 row) + select jsonb_pretty(graphql.resolve($$ query IntrospectionQuery { __schema { diff --git a/test/sql/array_args_and_return_types.sql b/test/sql/array_args_and_return_types.sql index c56abc0e..8b167b49 100644 --- a/test/sql/array_args_and_return_types.sql +++ b/test/sql/array_args_and_return_types.sql @@ -694,4 +694,14 @@ begin; } $$)); + create function accept_algorithm_array(e "Algorithm"[]) + returns int language sql stable + as $$ select 0; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + acceptAlgorithmArray(e: ["AEAD_IETF"]) + } + $$)); + rollback; diff --git a/test/sql/function_calls_unsupported.sql b/test/sql/function_calls_unsupported.sql index cf2acddf..b6cb3f32 100644 --- a/test/sql/function_calls_unsupported.sql +++ b/test/sql/function_calls_unsupported.sql @@ -160,6 +160,16 @@ begin; } $$)); + create function accept_algorithm(e "Algorithm") + returns int language sql stable + as $$ select 0; $$; + + select jsonb_pretty(graphql.resolve($$ + query { + acceptAlgorithm(e: "AEAD_IETF") + } + $$)); + select jsonb_pretty(graphql.resolve($$ query IntrospectionQuery { __schema {