server: fix issue with tracking custom functions that return SETOF materialized view (close #5294) (#5945)

https://github.com/hasura/graphql-engine/pull/5945
This commit is contained in:
Sameer Kolhar 2020-10-22 18:56:42 +05:30 committed by GitHub
parent ee940b4a84
commit b72fc6922a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 201 additions and 38 deletions

View File

@ -79,6 +79,7 @@ This release contains the [PDV refactor (#4111)](https://github.com/hasura/graph
- server: limit the length of event trigger names (close #5786)
**NOTE:** If you have event triggers with names greater than 42 chars, then you should update their names to avoid running into Postgres identifier limit bug (#5786)
- server: validate remote schema queries (fixes #4143)
- server: fix issue with tracking custom functions that return `SETOF` materialized view (close #5294) (#5945)
- console: allow user to cascade Postgres dependencies when dropping Postgres objects (close #5109) (#5248)
- console: mark inconsistent remote schemas in the UI (close #5093) (#5181)
- cli: add missing global flags for seed command (#5565)

View File

@ -222,8 +222,8 @@ handleMultipleFunctions qf = \case
throw400 NotSupported $
"function " <> qf <<> " is overloaded. Overloaded functions are not supported"
fetchRawFunctioInfo :: MonadTx m => QualifiedFunction -> m RawFunctionInfo
fetchRawFunctioInfo qf@(QualifiedObject sn fn) =
fetchRawFunctionInfo :: MonadTx m => QualifiedFunction -> m RawFunctionInfo
fetchRawFunctionInfo qf@(QualifiedObject sn fn) =
handleMultipleFunctions qf =<< map (Q.getAltJ . runIdentity) <$> fetchFromDatabase
where
fetchFromDatabase = liftTx $

View File

@ -195,30 +195,34 @@ instance ToTxt TableName where
data TableType
= TTBaseTable
| TTView
| TTMaterializedView
| TTForeignTable
| TTLocalTemporary
deriving (Eq)
tableTyToTxt :: TableType -> T.Text
tableTyToTxt TTBaseTable = "BASE TABLE"
tableTyToTxt TTView = "VIEW"
tableTyToTxt TTForeignTable = "FOREIGN TABLE"
tableTyToTxt TTLocalTemporary = "LOCAL TEMPORARY"
tableTyToTxt TTBaseTable = "BASE TABLE"
tableTyToTxt TTView = "VIEW"
tableTyToTxt TTMaterializedView = "MATERIALIZED VIEW"
tableTyToTxt TTForeignTable = "FOREIGN TABLE"
tableTyToTxt TTLocalTemporary = "LOCAL TEMPORARY"
instance Show TableType where
show = T.unpack . tableTyToTxt
instance Q.FromCol TableType where
fromCol bs = flip Q.fromColHelper bs $ PD.enum $ \case
"BASE TABLE" -> Just TTBaseTable
"VIEW" -> Just TTView
"FOREIGN TABLE" -> Just TTForeignTable
"LOCAL TEMPORARY" -> Just TTLocalTemporary
_ -> Nothing
"BASE TABLE" -> Just TTBaseTable
"VIEW" -> Just TTView
"MATERIALIZED VIEW" -> Just TTMaterializedView
"FOREIGN TABLE" -> Just TTForeignTable
"LOCAL TEMPORARY" -> Just TTLocalTemporary
_ -> Nothing
isView :: TableType -> Bool
isView TTView = True
isView _ = False
isView TTView = True
isView TTMaterializedView = True
isView _ = False
newtype ConstraintName
= ConstraintName { getConstraintTxt :: T.Text }

View File

@ -1 +1 @@
39
40

View File

@ -568,33 +568,42 @@ CREATE VIEW hdb_catalog.hdb_function_info_agg AS (
(
SELECT
e
FROM
(
FROM
(
SELECT
description,
has_variadic,
function_type,
return_type_schema,
return_type_name,
return_type_type,
returns_set,
input_arg_types,
input_arg_names,
default_args,
exists(
SELECT
description,
has_variadic,
function_type,
return_type_schema,
return_type_name,
return_type_type,
returns_set,
input_arg_types,
input_arg_names,
default_args,
exists(
SELECT
1
FROM
information_schema.tables
WHERE
table_schema = return_type_schema
AND table_name = return_type_name
) AS returns_table
) AS e
1
FROM
information_schema.tables
WHERE
table_schema = return_type_schema
AND table_name = return_type_name
)
OR exists(
SELECT
1
FROM
pg_matviews
WHERE
schemaname = return_type_schema
AND matviewname = return_type_name
) AS returns_table
) AS e
)
) AS "function_info"
FROM
hdb_catalog.hdb_function_agg
FROM
hdb_catalog.hdb_function_agg
);
CREATE OR REPLACE FUNCTION

View File

@ -0,0 +1,46 @@
CREATE
OR REPLACE VIEW hdb_catalog.hdb_function_info_agg AS (
SELECT
function_name,
function_schema,
row_to_json (
(
SELECT
e
FROM
(
SELECT
description,
has_variadic,
function_type,
return_type_schema,
return_type_name,
return_type_type,
returns_set,
input_arg_types,
input_arg_names,
default_args,
exists(
SELECT
1
FROM
information_schema.tables
WHERE
table_schema = return_type_schema
AND table_name = return_type_name
)
OR exists(
SELECT
1
FROM
pg_matviews
WHERE
schemaname = return_type_schema
AND matviewname = return_type_name
) AS returns_table
) AS e
)
) AS "function_info"
FROM
hdb_catalog.hdb_function_agg
);

View File

@ -0,0 +1,37 @@
CREATE
OR REPLACE VIEW hdb_catalog.hdb_function_info_agg AS (
SELECT
function_name,
function_schema,
row_to_json (
(
SELECT
e
FROM
(
SELECT
description,
has_variadic,
function_type,
return_type_schema,
return_type_name,
return_type_type,
returns_set,
input_arg_types,
input_arg_names,
default_args,
exists(
SELECT
1
FROM
information_schema.tables
WHERE
table_schema = return_type_schema
AND table_name = return_type_name
) AS returns_table
) AS e
)
) AS "function_info"
FROM
hdb_catalog.hdb_function_agg
);

View File

@ -0,0 +1,18 @@
description: Custom GraphQL query using search_author_mview function which returns results from a materialized view
url: /v1/graphql
status: 200
response:
data:
search_author_mview:
- first_name: franz
last_name: kafka
query:
query: |
query {
search_author_mview(
args: {query: "kafka"}
) {
first_name
last_name
}
}

View File

@ -176,3 +176,45 @@ args:
name: get_session_var
configuration:
session_argument: hasura_session
# track & query functions that return SETOF materialized views
- type: run_sql
args:
sql: |
CREATE TABLE author(
id SERIAL PRIMARY KEY,
first_name TEXT,
last_name TEXT
);
INSERT INTO author(first_name, last_name) VALUES
('enid', 'blyton'),
('ruskin', 'bond'),
('franz', 'kafka');
CREATE MATERIALIZED VIEW author_mat_view AS
SELECT * FROM author;
CREATE FUNCTION search_author_mview(query text)
RETURNS SETOF author_mat_view AS $FUNCTION$
SELECT * FROM author_mat_view WHERE
first_name = query OR
last_name = query
$FUNCTION$ LANGUAGE sql STABLE;
- type: track_table
args:
name: author
schema: public
- type: track_table
args:
name: author_mat_view
schema: public
- type: track_function
version: 2
args:
function:
schema: public
name: search_author_mview

View File

@ -9,4 +9,6 @@ args:
DROP TABLE integer_column cascade;
DROP TABLE "user" cascade;
DROP TABLE text_result cascade;
DROP TABLE author cascade;
DROP MATERIALIZED VIEW IF EXISTS author_mat_view cascade;
cascade: true

View File

@ -544,6 +544,10 @@ class TestGraphQLQueryFunctions:
@pytest.mark.parametrize("transport", ['http', 'websocket'])
def test_query_get_test_session_id(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/query_get_test_session_id.yaml')
@pytest.mark.parametrize("transport", ['http', 'websocket'])
def test_query_search_author_mview(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/query_search_author_mview.yaml')
@classmethod
def dir(cls):