Skip to content

Commit 48b214d

Browse files
authored
feat(postgres): Support for IS JSON predicate (#3971)
* feat(postgres): Support for IS JSON predicate * PR Feedback 1
1 parent 378500f commit 48b214d

File tree

4 files changed

+51
-4
lines changed

4 files changed

+51
-4
lines changed

sqlglot/expressions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5441,6 +5441,11 @@ class IsInf(Func):
54415441
_sql_names = ["IS_INF", "ISINF"]
54425442

54435443

5444+
# https://www.postgresql.org/docs/current/functions-json.html
5445+
class JSON(Expression):
5446+
arg_types = {"this": False, "with": False, "unique": False}
5447+
5448+
54445449
class JSONPath(Expression):
54455450
arg_types = {"expressions": True}
54465451

sqlglot/generator.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4136,3 +4136,20 @@ def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
41364136
expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
41374137

41384138
return self.sql(expr)
4139+
4140+
def json_sql(self, expression: exp.JSON) -> str:
4141+
this = self.sql(expression, "this")
4142+
this = f" {this}" if this else ""
4143+
4144+
_with = expression.args.get("with")
4145+
4146+
if _with is None:
4147+
with_sql = ""
4148+
elif not _with:
4149+
with_sql = " WITHOUT"
4150+
else:
4151+
with_sql = " WITH"
4152+
4153+
unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
4154+
4155+
return f"JSON{this}{with_sql}{unique_sql}"

sqlglot/parser.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,8 @@ class Parser(metaclass=_Parser):
12521252

12531253
COPY_INTO_VARLEN_OPTIONS = {"FILE_FORMAT", "COPY_OPTIONS", "FORMAT_OPTIONS", "CREDENTIAL"}
12541254

1255+
IS_JSON_PREDICATE_KIND = {"VALUE", "SCALAR", "ARRAY", "OBJECT"}
1256+
12551257
STRICT_CAST = True
12561258

12571259
PREFIXED_PIVOT_COLUMNS = False
@@ -4288,10 +4290,26 @@ def _parse_is(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expressi
42884290
klass = exp.NullSafeEQ if negate else exp.NullSafeNEQ
42894291
return self.expression(klass, this=this, expression=self._parse_bitwise())
42904292

4291-
expression = self._parse_null() or self._parse_boolean()
4292-
if not expression:
4293-
self._retreat(index)
4294-
return None
4293+
if self._match(TokenType.JSON):
4294+
kind = self._match_texts(self.IS_JSON_PREDICATE_KIND) and self._prev.text.upper()
4295+
4296+
if self._match_text_seq("WITH"):
4297+
_with = True
4298+
elif self._match_text_seq("WITHOUT"):
4299+
_with = False
4300+
else:
4301+
_with = None
4302+
4303+
unique = self._match(TokenType.UNIQUE)
4304+
self._match_text_seq("KEYS")
4305+
expression: t.Optional[exp.Expression] = self.expression(
4306+
exp.JSON, **{"this": kind, "with": _with, "unique": unique}
4307+
)
4308+
else:
4309+
expression = self._parse_null() or self._parse_boolean()
4310+
if not expression:
4311+
self._retreat(index)
4312+
return None
42954313

42964314
this = self.expression(exp.Is, this=this, expression=expression)
42974315
return self.expression(exp.Not, this=this) if negate else this

tests/dialects/test_postgres.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,13 @@ def test_postgres(self):
788788
},
789789
)
790790

791+
self.validate_identity(
792+
'SELECT js, js IS JSON AS "json?", js IS JSON VALUE AS "scalar?", js IS JSON SCALAR AS "scalar?", js IS JSON OBJECT AS "object?", js IS JSON ARRAY AS "array?" FROM t'
793+
)
794+
self.validate_identity(
795+
'SELECT js, js IS JSON ARRAY WITH UNIQUE KEYS AS "array w. UK?", js IS JSON ARRAY WITHOUT UNIQUE KEYS AS "array w/o UK?", js IS JSON ARRAY UNIQUE KEYS AS "array w UK 2?" FROM t'
796+
)
797+
791798
def test_ddl(self):
792799
# Checks that user-defined types are parsed into DataType instead of Identifier
793800
self.parse_one("CREATE TABLE t (a udt)").this.expressions[0].args["kind"].assert_is(

0 commit comments

Comments
 (0)