Skip to content

Commit eae20af

Browse files
authored
Support enum types (#387)
### What This PR improves introspection support for enum types. Comparison operators and aggregation functions on enum types are implicitly defined in PostgreSQL and thus were not detected prior to this PR. ### How We simply collect which enum types are defined and make up the conventional comparison operators and the min/max aggregation functions for all enum types. We test that we can select and aggregate enum types, and that we can insert enum values which PostgreSQL validates. We also collect the enum value labels, which we will be using once ndc-spec v0.1.1 is released.
1 parent 73ab921 commit eae20af

21 files changed

+1337
-5
lines changed

crates/configuration/src/version3/version3.sql

+97-1
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,41 @@ WITH
296296
WHERE
297297
t.typtype = 'd'
298298
),
299+
300+
-- Enum types are scalar types that consist of a finite, enumerated set of
301+
-- labelled values. See
302+
-- https://www.postgresql.org/docs/current/datatype-enum.html
303+
--
304+
-- The catalog table `pg_catalog.pg_enum` records the enum types defined in
305+
-- the database. See https://www.postgresql.org/docs/current/catalog-pg-enum.html
306+
--
307+
-- Enum types support certain comparisons and aggregations, but these are not
308+
-- registered in any of the catalog tables. Therefore we need some amount of
309+
-- special case handling for enum types.
310+
--
311+
-- Furthermore we are interested in collecting the labels for each enum type
312+
-- to reflect in the NDC schema.
313+
enum_types AS
314+
(
315+
SELECT
316+
t.oid AS type_id,
317+
t.typnamespace AS schema_id,
318+
t.typname AS type_name,
319+
array_agg(e.enumlabel ORDER BY e.enumsortorder) AS enum_labels
320+
FROM
321+
pg_catalog.pg_type AS t
322+
INNER JOIN
323+
-- Until the schema is made part of our model of types we only consider
324+
-- those defined in the public schema.
325+
unqualified_schemas_for_types_and_procedures as q
326+
ON (t.typnamespace = q.schema_id)
327+
INNER JOIN
328+
pg_enum
329+
AS e
330+
ON (e.enumtypid = t.oid)
331+
GROUP BY (t.oid, t.typnamespace, t.typname)
332+
),
333+
299334
array_types AS
300335
(
301336
SELECT
@@ -394,11 +429,30 @@ WITH
394429
domain_types
395430
),
396431

432+
-- Enum types support the aggregates 'min' and 'max'. However, these are not
433+
-- registered as such in `pg_proc`, and so we have to make them them up
434+
-- ourselves.
435+
enum_aggregates AS
436+
(
437+
SELECT
438+
proc.proname AS proc_name,
439+
e.schema_id AS schema_id,
440+
e.type_name AS argument_type,
441+
e.type_name AS return_type
442+
FROM
443+
(VALUES
444+
('min'),
445+
('max')
446+
)
447+
AS proc(proname),
448+
enum_types e
449+
),
450+
397451
-- Aggregate functions are recorded across 'pg_proc' and 'pg_aggregate', see
398452
-- https://www.postgresql.org/docs/current/catalog-pg-proc.html and
399453
-- https://www.postgresql.org/docs/current/catalog-pg-aggregate.html for
400454
-- their schema.
401-
aggregates AS
455+
declared_aggregates AS
402456
(
403457
SELECT
404458
proc.oid AS proc_id,
@@ -440,6 +494,22 @@ WITH
440494
AND aggregate.aggnumdirectargs = 0
441495

442496
),
497+
aggregates AS
498+
(
499+
SELECT
500+
proc_name,
501+
return_type,
502+
argument_type
503+
FROM
504+
declared_aggregates
505+
UNION
506+
SELECT
507+
proc_name,
508+
return_type,
509+
argument_type
510+
FROM enum_aggregates
511+
),
512+
443513
aggregates_cast_extended AS
444514
(
445515
WITH
@@ -602,6 +672,30 @@ WITH
602672
ORDER BY op.oprname
603673
),
604674

675+
-- Enum types are totally ordered and support the conventional comparison operators.
676+
-- They are defined implicitly (i.e., not registered in `pg_proc` or
677+
-- `pg_operator`) so we have to make up some definitions for them.
678+
enum_comparison_operators AS
679+
(
680+
SELECT
681+
op.oprname AS operator_name,
682+
e.type_name AS argument1_type,
683+
e.type_name AS argument2_type,
684+
true AS is_infix
685+
FROM
686+
(VALUES
687+
('='),
688+
('!='),
689+
('<>'),
690+
('<='),
691+
('>'),
692+
('>='),
693+
('<')
694+
)
695+
AS op(oprname),
696+
enum_types e
697+
),
698+
605699
-- Here, we reunite our binary infix procedures and our binary prefix
606700
-- procedures under the umbrella of 'comparison_operators'. We do this
607701
-- here so that we can treat them uniformly form this point on.
@@ -612,6 +706,8 @@ WITH
612706
SELECT * FROM comparison_infix_operators
613707
UNION
614708
SELECT * FROM comparison_procedures
709+
UNION
710+
SELECT * FROM enum_comparison_operators
615711
),
616712

617713
-- Some comparison operators are not defined explicitly for every type they would be

crates/tests/databases-tests/src/citus/snapshots/databases_tests__citus__schema_tests__schema_test__get_schema.snap

+136
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,65 @@ expression: result
198198
}
199199
}
200200
},
201+
"card_suit": {
202+
"aggregate_functions": {
203+
"max": {
204+
"result_type": {
205+
"type": "named",
206+
"name": "card_suit"
207+
}
208+
},
209+
"min": {
210+
"result_type": {
211+
"type": "named",
212+
"name": "card_suit"
213+
}
214+
}
215+
},
216+
"comparison_operators": {
217+
"_eq": {
218+
"type": "equal"
219+
},
220+
"_gt": {
221+
"type": "custom",
222+
"argument_type": {
223+
"type": "named",
224+
"name": "card_suit"
225+
}
226+
},
227+
"_gte": {
228+
"type": "custom",
229+
"argument_type": {
230+
"type": "named",
231+
"name": "card_suit"
232+
}
233+
},
234+
"_in": {
235+
"type": "in"
236+
},
237+
"_lt": {
238+
"type": "custom",
239+
"argument_type": {
240+
"type": "named",
241+
"name": "card_suit"
242+
}
243+
},
244+
"_lte": {
245+
"type": "custom",
246+
"argument_type": {
247+
"type": "named",
248+
"name": "card_suit"
249+
}
250+
},
251+
"_neq": {
252+
"type": "custom",
253+
"argument_type": {
254+
"type": "named",
255+
"name": "card_suit"
256+
}
257+
}
258+
}
259+
},
201260
"char": {
202261
"aggregate_functions": {
203262
"max": {
@@ -2539,6 +2598,22 @@ expression: result
25392598
}
25402599
}
25412600
},
2601+
"deck_of_cards": {
2602+
"fields": {
2603+
"pips": {
2604+
"type": {
2605+
"type": "named",
2606+
"name": "int2"
2607+
}
2608+
},
2609+
"suit": {
2610+
"type": {
2611+
"type": "named",
2612+
"name": "card_suit"
2613+
}
2614+
}
2615+
}
2616+
},
25422617
"delete_playlist_track": {
25432618
"fields": {
25442619
"PlaylistId": {
@@ -3711,6 +3786,44 @@ expression: result
37113786
}
37123787
}
37133788
},
3789+
"v1_insert_deck_of_cards_object": {
3790+
"fields": {
3791+
"pips": {
3792+
"type": {
3793+
"type": "named",
3794+
"name": "int2"
3795+
}
3796+
},
3797+
"suit": {
3798+
"type": {
3799+
"type": "named",
3800+
"name": "card_suit"
3801+
}
3802+
}
3803+
}
3804+
},
3805+
"v1_insert_deck_of_cards_response": {
3806+
"description": "Responses from the 'v1_insert_deck_of_cards' procedure",
3807+
"fields": {
3808+
"affected_rows": {
3809+
"description": "The number of rows affected by the mutation",
3810+
"type": {
3811+
"type": "named",
3812+
"name": "int4"
3813+
}
3814+
},
3815+
"returning": {
3816+
"description": "Data from rows affected by the mutation",
3817+
"type": {
3818+
"type": "array",
3819+
"element_type": {
3820+
"type": "named",
3821+
"name": "deck_of_cards"
3822+
}
3823+
}
3824+
}
3825+
}
3826+
},
37143827
"v1_insert_even_numbers_object": {
37153828
"fields": {
37163829
"the_number": {
@@ -4113,6 +4226,13 @@ expression: result
41134226
}
41144227
}
41154228
},
4229+
{
4230+
"name": "deck_of_cards",
4231+
"arguments": {},
4232+
"type": "deck_of_cards",
4233+
"uniqueness_constraints": {},
4234+
"foreign_keys": {}
4235+
},
41164236
{
41174237
"name": "even_numbers",
41184238
"arguments": {},
@@ -4878,6 +4998,22 @@ expression: result
48784998
"name": "v1_insert_Track_response"
48794999
}
48805000
},
5001+
{
5002+
"name": "v1_insert_deck_of_cards",
5003+
"description": "Insert into the deck_of_cards table",
5004+
"arguments": {
5005+
"_object": {
5006+
"type": {
5007+
"type": "named",
5008+
"name": "v1_insert_deck_of_cards_object"
5009+
}
5010+
}
5011+
},
5012+
"result_type": {
5013+
"type": "named",
5014+
"name": "v1_insert_deck_of_cards_response"
5015+
}
5016+
},
48815017
{
48825018
"name": "v1_insert_even_numbers",
48835019
"description": "Insert into the even_numbers table",

0 commit comments

Comments
 (0)