diff --git a/sql/load_sql_context.sql b/sql/load_sql_context.sql index ab8b5f06..cd449ee8 100644 --- a/sql/load_sql_context.sql +++ b/sql/load_sql_context.sql @@ -148,6 +148,7 @@ select 'local_table_meta', jsonb_build_object( 'oid', pf.conrelid::int, 'name', pa_local.relname::text, + 'is_rls_enabled', pa_local.relrowsecurity, 'schema', pa_local.relnamespace::regnamespace::text, 'column_names', ( select @@ -163,6 +164,7 @@ select 'referenced_table_meta', jsonb_build_object( 'oid', pf.confrelid::int, 'name', pa_referenced.relname::text, + 'is_rls_enabled', pa_referenced.relrowsecurity, 'schema', pa_referenced.relnamespace::regnamespace::text, 'column_names', ( select @@ -234,6 +236,7 @@ select 'name', pc.relname::text, 'relkind', pc.relkind::text, 'reltype', pc.reltype::int, + 'is_rls_enabled', pc.relrowsecurity, 'schema', schemas_.name, 'schema_oid', pc.relnamespace::int, 'comment', pg_catalog.obj_description(pc.oid, 'pg_class'), diff --git a/src/graphql.rs b/src/graphql.rs index fd16448a..72868ff1 100644 --- a/src/graphql.rs +++ b/src/graphql.rs @@ -1958,6 +1958,7 @@ impl NodeType { .columns .iter() .any(|c| &c.name == colname && c.is_not_null) + && !fkey.referenced_table_meta.is_rls_enabled }) { __Type::NonNull(NonNullType { type_: Box::new(type_), diff --git a/src/sql_types.rs b/src/sql_types.rs index 1b367c66..1645feec 100644 --- a/src/sql_types.rs +++ b/src/sql_types.rs @@ -425,6 +425,7 @@ pub struct ForeignKeyTableInfo { // The table's actual name pub name: String, pub schema: String, + pub is_rls_enabled: bool, pub column_names: Vec, } @@ -503,6 +504,7 @@ pub struct Table { pub schema: String, pub columns: Vec>, pub comment: Option, + pub is_rls_enabled: bool, pub relkind: String, // r = table, v = view, m = mat view, f = foreign table pub reltype: u32, pub permissions: TablePermissions, @@ -660,12 +662,14 @@ impl Context { oid: table.oid, name: table.name.clone(), schema: table.schema.clone(), + is_rls_enabled: table.is_rls_enabled, column_names: directive_fkey.local_columns.clone(), }, referenced_table_meta: ForeignKeyTableInfo { oid: referenced_t.oid, name: referenced_t.name.clone(), schema: referenced_t.schema.clone(), + is_rls_enabled: table.is_rls_enabled, column_names: directive_fkey.foreign_columns.clone(), }, directives: ForeignKeyDirectives { diff --git a/test/expected/issue_409_fkey_rls_nullability.out b/test/expected/issue_409_fkey_rls_nullability.out new file mode 100644 index 00000000..10ed82ab --- /dev/null +++ b/test/expected/issue_409_fkey_rls_nullability.out @@ -0,0 +1,394 @@ +begin; + create table account( + id int primary key + ); + create table address( + id int primary key, + account_id int not null references account(id) + ); + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Account") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + jsonb_pretty +------------------------------------------------------ + { + + "data": { + + "__type": { + + "kind": "OBJECT", + + "fields": [ + + { + + "name": "nodeId", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "ID" + + } + + } + + }, + + { + + "name": "id", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "Int" + + } + + } + + }, + + { + + "name": "addressCollection", + + "type": { + + "kind": "OBJECT", + + "name": "AddressConnection",+ + "ofType": null + + } + + } + + ] + + } + + } + + } +(1 row) + + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Address") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + jsonb_pretty +----------------------------------------------- + { + + "data": { + + "__type": { + + "kind": "OBJECT", + + "fields": [ + + { + + "name": "nodeId", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "ID" + + } + + } + + }, + + { + + "name": "id", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "Int" + + } + + } + + }, + + { + + "name": "accountId", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "Int" + + } + + } + + }, + + { + + "name": "account", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "Account"+ + } + + } + + } + + ] + + } + + } + + } +(1 row) + + alter table account enable row level security; + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Account") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + jsonb_pretty +------------------------------------------------------ + { + + "data": { + + "__type": { + + "kind": "OBJECT", + + "fields": [ + + { + + "name": "nodeId", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "ID" + + } + + } + + }, + + { + + "name": "id", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "Int" + + } + + } + + }, + + { + + "name": "addressCollection", + + "type": { + + "kind": "OBJECT", + + "name": "AddressConnection",+ + "ofType": null + + } + + } + + ] + + } + + } + + } +(1 row) + + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Address") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + jsonb_pretty +--------------------------------------------- + { + + "data": { + + "__type": { + + "kind": "OBJECT", + + "fields": [ + + { + + "name": "nodeId", + + "type": { + + "kind": "NON_NULL",+ + "name": null, + + "ofType": { + + "name": "ID" + + } + + } + + }, + + { + + "name": "id", + + "type": { + + "kind": "NON_NULL",+ + "name": null, + + "ofType": { + + "name": "Int" + + } + + } + + }, + + { + + "name": "accountId", + + "type": { + + "kind": "NON_NULL",+ + "name": null, + + "ofType": { + + "name": "Int" + + } + + } + + }, + + { + + "name": "account", + + "type": { + + "kind": "OBJECT", + + "name": "Account", + + "ofType": null + + } + + } + + ] + + } + + } + + } +(1 row) + + alter table account disable row level security; + alter table address enable row level security; + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Account") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + jsonb_pretty +------------------------------------------------------ + { + + "data": { + + "__type": { + + "kind": "OBJECT", + + "fields": [ + + { + + "name": "nodeId", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "ID" + + } + + } + + }, + + { + + "name": "id", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "Int" + + } + + } + + }, + + { + + "name": "addressCollection", + + "type": { + + "kind": "OBJECT", + + "name": "AddressConnection",+ + "ofType": null + + } + + } + + ] + + } + + } + + } +(1 row) + + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Address") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + jsonb_pretty +----------------------------------------------- + { + + "data": { + + "__type": { + + "kind": "OBJECT", + + "fields": [ + + { + + "name": "nodeId", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "ID" + + } + + } + + }, + + { + + "name": "id", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "Int" + + } + + } + + }, + + { + + "name": "accountId", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "Int" + + } + + } + + }, + + { + + "name": "account", + + "type": { + + "kind": "NON_NULL", + + "name": null, + + "ofType": { + + "name": "Account"+ + } + + } + + } + + ] + + } + + } + + } +(1 row) + +rollback; diff --git a/test/sql/issue_409_fkey_rls_nullability.sql b/test/sql/issue_409_fkey_rls_nullability.sql new file mode 100644 index 00000000..5b77f6c8 --- /dev/null +++ b/test/sql/issue_409_fkey_rls_nullability.sql @@ -0,0 +1,125 @@ +begin; + + create table account( + id int primary key + ); + + create table address( + id int primary key, + account_id int not null references account(id) + ); + + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Account") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Address") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + + alter table account enable row level security; + + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Account") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Address") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + + alter table account disable row level security; + alter table address enable row level security; + + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Account") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Address") { + kind + fields { + name + type { + name + kind + ofType { name } + } + } + } + } + $$) + ); + +rollback;