-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Execute user script refactor #1695
Execute user script refactor #1695
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just some drive-by suggestions
0519636
to
ddf215c
Compare
def get_snowflake_facade(sql_executor: SqlExecutor | None) -> SnowflakeSQLFacade: | ||
"""Returns a Snowflake Facade""" | ||
return SnowflakeSQLFacade(sql_executor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why require an executor at all? At least we should use None by default IMHO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see @sfc-gh-fcampbell's comment here #1695 (comment)
Though we are currently always creating a new instance of sql_executor where we need it. imo the way we are using it rn either way ends up the same. But we can make a design decision as to if we want to pass the sql executor in or instantiate it in SnowflakeSQLFacade
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO the executor is pretty much an implementation detail here, and I'd only expect it to be replaced for tests so we can inspect our interaction with a low-level executor. I do like the simplicity of just acquiring a facade object (could be a global or per-thread singleton) from anywhere in the code, as long as we can easily mock it out when we need to test code that interacts with the facade.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
wh_result = self._sql_executor.execute_query( | ||
f"select current_warehouse()" | ||
).fetchone() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what should be the behaviour if this fails? It shouldn't fail, I know, but we should control the contract if it ever does. Right now we'd probably just let a ProgrammingError bubble up. Is that what we want?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think with our error classifications now bubbbling it up as a ProgrammingError is fair (categorizes it as not 100% the user's fault
). Would you say if we get a failure here it's fair to assume we need act on it and see what's wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a weird state for sure if something fails, but I'm not convinced that ProgrammingError is the right contract. We know what failed here, and it's not wrong SQL.
finally: | ||
if is_different_wh and prev_wh is not None: | ||
self._log.debug(f"Switching back to warehouse:{prev_wh}") | ||
self._sql_executor.execute_query(f"use warehouse {prev_wh}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto, what happens if this fails? Technically the user script could drop the warehouse or otherwise make it inaccessible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point. and the same can be said about the use database
case. See update. made a _use_object
method that has the error handling it and using it for all use object_type object_name
calls.
# TODO: Add more test coverage + define contract | ||
# TODO: Add test for non-identifier names |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fast-follow PRs? What's the plan here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, see note on my PR description. This class is not used in any code path (since Frank has removed apply_package_script in the meantime). So more tests and integ tests to be added in the upcoming PR for post deploy hooks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be able to cover the facade in isolation though...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added tests
role = "mock_role" | ||
wh = "mock_wh" | ||
database = "mock_db" | ||
side_effects, expected = mock_execute_helper( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you have a better pattern, use it instead. This is pretty much NADE legacy at this point. We could also create a new abstraction / helper for SQL testing in this file, since it will be used here almost exclusively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think using this helper is OK for when we actually want to test the sql.
And its usage in other tests will be reduced and removed as I refactor more SQL calls into this class.
Of course there are small improvements I can do to it like switching the order of expected call and side_effect but not sure how much we'll gain from that.
closing this PR, new PR on main here #1732 |
Pre-review checklist
Changes description
NOTE - This is a PR against @sfc-gh-mchok's telemetry branch, since I use some error types he's introduced
Started a SQLService abstraction layer. The goal is to extract execute_query calls that are embedded in the logic out to their own unit. This PR uses package scripts to show an example of the restructuring.
use role, warehouse, and database calls are moved to this layer. See
test_package_scripts_with_conn_warehouse
for and example of an updated test.NOTE - Since the start of this work,
apply_package_scripts
is no longer invoked by any commands (in favor of post-deploy hooks). That's why there are no integration tests associated with this change, even though some error handling has changed.Exhaustive test cases for
execute_user_script
as well as integ tests to be added when making similar change for runningpost_deploy
hooks in an upcoming PR....