Skip to content

feat(snowflake)!: transpilation support MAP_PICK #7189

Open
fivetran-ashashankar wants to merge 2 commits intomainfrom
RD-1147671-transpile_map_pick
Open

feat(snowflake)!: transpilation support MAP_PICK #7189
fivetran-ashashankar wants to merge 2 commits intomainfrom
RD-1147671-transpile_map_pick

Conversation

@fivetran-ashashankar
Copy link
Collaborator

@fivetran-ashashankar fivetran-ashashankar commented Mar 2, 2026

Adds transpilation support for Snowflake MAP_PICK to duckdb

@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

SQLGlot Integration Test Results

Comparing:

  • this branch (sqlglot:RD-1147671-transpile_map_pick, sqlglot version: RD-1147671-transpile_map_pick)
  • baseline (main, sqlglot version: 29.0.2.dev29)

⚠️ Limited to dialects: duckdb

By Dialect

dialect main sqlglot:RD-1147671-transpile_map_pick transitions links
duckdb -> duckdb 4003/4003 passed (100.0%) 4003/4003 passed (100.0%) No change full result / delta

Overall

main: 4003 total, 4003 passed (pass rate: 100.0%), sqlglot version: 29.0.2.dev29

sqlglot:RD-1147671-transpile_map_pick: 4003 total, 4003 passed (pass rate: 100.0%), sqlglot version: RD-1147671-transpile_map_pick

Transitions:
No change

Comment on lines +3710 to +3731
def mappick_sql(self, expression: exp.MapPick) -> str:
map_arg = expression.this
keys_to_pick = expression.expressions

x_dot_key = exp.Dot(this=exp.to_identifier("x"), expression=exp.to_identifier("key"))

if len(keys_to_pick) == 1 and isinstance(keys_to_pick[0], exp.Array):
lambda_expr = exp.Lambda(
this=exp.ArrayContains(this=keys_to_pick[0], expression=x_dot_key),
expressions=[exp.to_identifier("x")],
)
else:
lambda_expr = exp.Lambda(
this=exp.In(this=x_dot_key, expressions=keys_to_pick),
expressions=[exp.to_identifier("x")],
)

result = exp.func(
"MAP_FROM_ENTRIES",
exp.ArrayFilter(this=exp.func("MAP_ENTRIES", map_arg), expression=lambda_expr),
)
return self.sql(result)
Copy link
Collaborator

@geooo109 geooo109 Mar 3, 2026

Choose a reason for hiding this comment

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

For exp.ArrayContains and exp.ArrayFilter we can use directly self.func(...)

edit: I meant exp.Func, not self.func(...).

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it's fine as-is because we're building the ASTs and generating once, at the end. Unless you're suggesting using exp.func, not self.func? In that case, I agree.

Copy link
Collaborator

@geooo109 geooo109 Mar 3, 2026

Choose a reason for hiding this comment

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

Yeap sorry, I meant exp.Func, I have edited my first comment.

Copy link
Collaborator

@georgesittas georgesittas left a comment

Choose a reason for hiding this comment

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

A couple of comments from me.


x_dot_key = exp.Dot(this=exp.to_identifier("x"), expression=exp.to_identifier("key"))

if len(keys_to_pick) == 1 and isinstance(keys_to_pick[0], exp.Array):
Copy link
Collaborator

Choose a reason for hiding this comment

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

This condition isn't quite right, we shouldn't be checking for Array literals (i.e., [1, 2, 3]). We should check if is_type(exp.DType.ARRAY), instead. The current logic doesn't handle, for example, columns of type ARRAY.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, please include unit tests that cover the type inference part. I.e., annotate types, then do sql(...), etc.

Copy link
Collaborator

@geooo109 geooo109 Mar 3, 2026

Choose a reason for hiding this comment

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

agh, nice catch @georgesittas , i miss read it and thought it was a type check.

Copy link
Collaborator

@geooo109 geooo109 Mar 3, 2026

Choose a reason for hiding this comment

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

To add on top of george's comment this is the same as this comment: #7190 (comment) . This check isinstance(keys_to_pick[0], exp.Array) can fail, if for example the input is just an identifier:

SELECT MAP_PICK(x , y) FROM t;

In this ^ example, we don't know if y has a value of an Array, but we know on parse time that it is a column and its type is an Array (this is true on parse time but also on run time).

So, checking static values at parse time isn't the correct approach. On the other hand, checking types at parse time is valid because the static typing won't change at run time.

Comment on lines +3710 to +3731
def mappick_sql(self, expression: exp.MapPick) -> str:
map_arg = expression.this
keys_to_pick = expression.expressions

x_dot_key = exp.Dot(this=exp.to_identifier("x"), expression=exp.to_identifier("key"))

if len(keys_to_pick) == 1 and isinstance(keys_to_pick[0], exp.Array):
lambda_expr = exp.Lambda(
this=exp.ArrayContains(this=keys_to_pick[0], expression=x_dot_key),
expressions=[exp.to_identifier("x")],
)
else:
lambda_expr = exp.Lambda(
this=exp.In(this=x_dot_key, expressions=keys_to_pick),
expressions=[exp.to_identifier("x")],
)

result = exp.func(
"MAP_FROM_ENTRIES",
exp.ArrayFilter(this=exp.func("MAP_ENTRIES", map_arg), expression=lambda_expr),
)
return self.sql(result)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it's fine as-is because we're building the ASTs and generating once, at the end. Unless you're suggesting using exp.func, not self.func? In that case, I agree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants