Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sqlglot/dialects/duckdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,7 @@ class Parser(parser.Parser):
"JSON": exp.ParseJSON.from_arg_list,
"JSON_EXTRACT_PATH": parser.build_extract_json_with_path(exp.JSONExtract),
"JSON_EXTRACT_STRING": parser.build_extract_json_with_path(exp.JSONExtractScalar),
"JSON_KEYS": exp.JsonKeys.from_arg_list,
"LIST_APPEND": exp.ArrayAppend.from_arg_list,
"LIST_CONTAINS": exp.ArrayContains.from_arg_list,
"LIST_COSINE_DISTANCE": exp.CosineDistance.from_arg_list,
Expand Down Expand Up @@ -1407,6 +1408,7 @@ class Generator(generator.Generator):
exp.SHA1Digest: lambda self, e: self.func("UNHEX", self.func("SHA1", e.this)),
exp.SHA2Digest: lambda self, e: self.func("UNHEX", sha2_digest_sql(self, e)),
exp.MonthsBetween: months_between_sql,
exp.ObjectKeys: rename_func("JSON_KEYS"),
exp.PercentileCont: rename_func("QUANTILE_CONT"),
exp.PercentileDisc: rename_func("QUANTILE_DISC"),
# DuckDB doesn't allow qualified columns inside of PIVOT expressions.
Expand Down
1 change: 1 addition & 0 deletions sqlglot/dialects/snowflake.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,7 @@ class Parser(parser.Parser):
"LOCALTIMESTAMP": exp.CurrentTimestamp.from_arg_list,
"NULLIFZERO": _build_if_from_nullifzero,
"OBJECT_CONSTRUCT": _build_object_construct,
"OBJECT_KEYS": exp.ObjectKeys.from_arg_list,
"OCTET_LENGTH": exp.ByteLength.from_arg_list,
"PARSE_URL": lambda args: exp.ParseUrl(
this=seq_get(args, 0), permissive=seq_get(args, 1)
Expand Down
10 changes: 10 additions & 0 deletions sqlglot/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7259,6 +7259,16 @@ class ObjectInsert(Func):
}


# https://docs.snowflake.com/en/sql-reference/functions/object_keys
class ObjectKeys(Func):
pass


# https://duckdb.org/docs/extensions/json#json-extraction-functions
class JsonKeys(Func):
_sql_names = ["JSON_KEYS"]
Comment on lines +7262 to +7269
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should maintain only one representation for this AST node - JSONKeys(Func) is sufficient.

When adding a new function in SQLGlot, we search across all dialects (using their official documentation and manual testing) to determine if the function is supported elsewhere. This allows us to come up with the most generic AST node that supports all variations.

For example:

  • Snowflake: OBJECT_KEYS(object) - single argument
  • DuckDB: json_keys(json [, path]) - accepts an optional path argument (can be a single value or list)

With the current addition we only support a single argument, this will fail in future uses.

So, let's make a search among the dialects and check all the vacations, and make the representation generic.



class OpenJSONColumnDef(Expression):
arg_types = {"this": True, "kind": True, "path": False, "as_json": False}

Expand Down
7 changes: 7 additions & 0 deletions tests/dialects/test_snowflake.py
Original file line number Diff line number Diff line change
Expand Up @@ -2219,6 +2219,13 @@ def test_snowflake(self):
self.validate_identity("SYSDATE()")
self.validate_identity("SYSTIMESTAMP()", "CURRENT_TIMESTAMP()")
self.validate_identity("GETDATE()", "CURRENT_TIMESTAMP()")
self.validate_all(
"SELECT OBJECT_KEYS(my_obj)",
write={
"snowflake": "SELECT OBJECT_KEYS(my_obj)",
"duckdb": "SELECT JSON_KEYS(my_obj)",
},
)
self.validate_identity("LOCALTIMESTAMP", "CURRENT_TIMESTAMP")
self.validate_identity("LOCALTIMESTAMP()", "CURRENT_TIMESTAMP()")
self.validate_identity("LOCALTIMESTAMP(3)", "CURRENT_TIMESTAMP(3)")
Expand Down