Skip to content
Merged
1 change: 1 addition & 0 deletions sqlglot/dialects/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class Tokenizer(tokens.Tokenizer):
"SERIAL": TokenType.SERIAL,
"SMALLSERIAL": TokenType.SMALLSERIAL,
"TEMP": TokenType.TEMPORARY,
"TYPE": TokenType.TYPE,
"REGCLASS": TokenType.OBJECT_IDENTIFIER,
"REGCOLLATION": TokenType.OBJECT_IDENTIFIER,
"REGCONFIG": TokenType.OBJECT_IDENTIFIER,
Expand Down
3 changes: 3 additions & 0 deletions sqlglot/generators/postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,9 @@ def datatype_sql(self, expression: exp.DataType) -> str:
return f"{self.expressions(expression, flat=True)}[{values}]"
return "ARRAY"

if expression.is_type(exp.DType.ENUM):
return f"ENUM ({self.expressions(expression, flat=True)})"

if expression.is_type(exp.DType.DOUBLE, exp.DType.FLOAT) and expression.expressions:
# Postgres doesn't support precision for REAL and DOUBLE PRECISION types
return f"FLOAT({self.expressions(expression, flat=True)})"
Expand Down
16 changes: 16 additions & 0 deletions sqlglot/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,7 @@ class Parser:
TokenType.INDEX,
TokenType.PROCEDURE,
TokenType.TRIGGER,
TokenType.TYPE,
*DB_CREATABLES,
}

Expand Down Expand Up @@ -754,6 +755,7 @@ class Parser:
TokenType.UNPIVOT,
TokenType.UPDATE,
TokenType.USE,
TokenType.TYPE,
Comment thread
georgesittas marked this conversation as resolved.
Outdated
Comment thread
georgesittas marked this conversation as resolved.
Outdated
TokenType.VOLATILE,
TokenType.WINDOW,
TokenType.CURRENT_CATALOG,
Expand Down Expand Up @@ -2485,6 +2487,20 @@ def extend_props(temp_props: exp.Properties | None) -> None:

this = trigger_name
extend_props(exp.Properties(expressions=[trigger_props] if trigger_props else []))
elif create_token_type == TokenType.TYPE:
this = self._parse_table_parts(schema=True)
if not this or not self._match(TokenType.ALIAS):
return self._parse_as_command(start)
Comment on lines +2491 to +2492
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually need this fallback? It seems that the non-AS syntax is:

CREATE TYPE name (
    INPUT = input_function,
    OUTPUT = output_function
    [ , RECEIVE = receive_function ]
    [ , SEND = send_function ]
    [ , TYPMOD_IN = type_modifier_input_function ]
    [ , TYPMOD_OUT = type_modifier_output_function ]
    [ , ANALYZE = analyze_function ]
    [ , SUBSCRIPT = subscript_function ]
    [ , INTERNALLENGTH = { internallength | VARIABLE } ]
    [ , PASSEDBYVALUE ]
    [ , ALIGNMENT = alignment ]
    [ , STORAGE = storage ]
    [ , LIKE = like_type ]
    [ , CATEGORY = category ]
    [ , PREFERRED = preferred ]
    [ , DEFAULT = default ]
    [ , ELEMENT = element ]
    [ , DELIMITER = delimiter ]
    [ , COLLATABLE = collatable ]
)

CREATE TYPE name

I think we may be able to "just" parse these with the existing logic as "properties", without having to add any more logic.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing that fallback regressed tests, unsupported CREATE TYPE forms no longer roundtrip as Command. The idea was to to scope this PR to AS ENUM / AS (...) and avoid partial ASTs


if self._match(TokenType.ENUM):
expression = exp.DataType(
this=exp.DType.ENUM,
expressions=self._parse_wrapped_csv(self._parse_string),
)
elif self._match(TokenType.L_PAREN, advance=False):
expression = self._parse_schema()
else:
return self._parse_as_command(start)
elif create_token_type in self.DB_CREATABLES:
table_parts = self._parse_table_parts(
schema=True, is_db_reference=create_token_type == TokenType.SCHEMA
Expand Down
1 change: 1 addition & 0 deletions sqlglot/tokenizer_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ class TokenType(IntEnum):
TRUE = auto()
TRUNCATE = auto()
TRIGGER = auto()
TYPE = auto()
UNCACHE = auto()
UNION = auto()
UNNEST = auto()
Expand Down
22 changes: 22 additions & 0 deletions tests/dialects/test_postgres.py
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,28 @@ def test_ddl(self):
exp.DataType
)

create_type = self.validate_identity(
"CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy')"
).assert_is(exp.Create)
self.assertTrue(create_type.expression.assert_is(exp.DataType).is_type(exp.DType.ENUM))

self.validate_identity("CREATE TYPE mood AS ENUM ()").assert_is(exp.Create)

create_type = self.validate_identity(
"CREATE TYPE inventory_item AS (name TEXT, supplier_id INT, price DECIMAL)"
).assert_is(exp.Create)
create_type.expression.assert_is(exp.Schema)

self.validate_identity("CREATE TYPE public.mood AS ENUM ('sad', 'ok')").assert_is(
exp.Create
)

self.validate_identity("CREATE TYPE widget", check_command_warning=True)
self.validate_identity(
"CREATE TYPE float8range AS RANGE (subtype = float8, subtype_diff = float8mi)",
check_command_warning=True,
)

# Checks that OID is parsed into a DataType (ObjectIdentifier)
self.assertIsInstance(
self.parse_one("CREATE TABLE p.t (c oid)").find(exp.DataType), exp.ObjectIdentifier
Expand Down
Loading