Skip to content
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

Update table PATCH endpoint to work with columns #578

Merged
merged 23 commits into from
Aug 23, 2021
Merged

Update table PATCH endpoint to work with columns #578

merged 23 commits into from
Aug 23, 2021

Conversation

kgodey
Copy link
Contributor

@kgodey kgodey commented Aug 18, 2021

Fixes #562

The table detail API now accepts columns as a valid key in a PATCH request. It will updates names and types of columns as well as drop columns. It takes the same options as the table previews endpoint.

Note to reviewers

Please expand the test_table_api.py file, GitHub is hiding it by default as a "large diff".

Technical details

In order to get multiple operations to happen within a single transaction, we're passing connection and table objects around. This code is a little messy, but I didn't want to hold this up since it's blocking our next milestone so I created a new issue to refactor it. I also created a separate issue to look into whether we can support altering the table's name in the same request as altering columns.

Work done in this PR

  • Fix existing tests (thanks, @eito-fis!)
  • Update table's PATCH endpoint to handle updating column names and types
  • Ensure that all actions happen in a single transaction.
  • Updated PATCH function to also handle dropping columns
  • Checked that type actually needs to be changed before calling retype function.
  • Improved code readability and minor refactoring
  • Investigated if it was possible to easily alter the table's name in the same request (it wasn't)
  • Wrote tests that cover:
    • No changes to columns
    • Changing names only
    • Changing types only
    • Dropping columns only
    • Changing names and types of different columns
    • Changing names and types of the same column
    • Changing names and types of different columns and dropping a column
    • Changing names and types of the same column and dropping a column
    • Changing name + the type to an invalid type and making sure that the name doesn't change
    • Changing type to an invalid type and dropping a column and making sure that the column doesn't get dropped
    • Changing both column data and table name
    • Doing some of the same things at the db level

Checklist

  • My pull request has a descriptive title (not a vague title like Update index.md).
  • My pull request targets the master branch of the repository
  • My commit messages follow best practices.
  • My code follows the established code style of the repository.
  • I added tests for the changes I made (if applicable).
  • I added or updated documentation (if applicable).
  • I tried running the project locally and verified that there are no
    visible errors.

Developer Certificate of Origin

Developer Certificate of Origin
Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.

@kgodey
Copy link
Contributor Author

kgodey commented Aug 19, 2021

@eito-fis @powellc and anyone else interested in backend code: I'm working on supporting a bunch of different operations in one transaction in this PR. it seems to work, but the way I got it to work is by updating a bunch of functions to take connection_to_use and table_to_use objects and use those instead of opening a new connection or re-reflecting tables. I can see it getting hairy to use very quickly, I'd like to come up with a clean framework for when to open a new connection and when to use an existing connection and create a convention we can follow.

Any suggestions/thoughts would be appreciated

@eito-fis
Copy link
Contributor

The sqlalchemy documentation has some suggestion for dealing with the nested transaction pattern, which seem reasonable, if not much different from what we have now.

If we're alright with digging into some of the internals, I think we could also try extending the Engine and Connection classes to handle something like a .begin_or_continue() method (or extend the normal .begin() method)? In particular, we could write a modification of the existing context manager that checks to see if conn._transaction exists and starts a new transaction accordingly. The Engine.begin() method is also very simple, so I don't think it would be much work to extend or make a copy of.

Wrt to the table, I don't have a great solution. Maybe just a function that takes a table and OID, either of which could be None, and returns a table? That way we could just replace all the reflection calls with a new function call and have minimal change.

@kgodey
Copy link
Contributor Author

kgodey commented Aug 20, 2021

Thanks @eito-fis! Those are all good ideas, I'll think about it and figure out an approach.

@kgodey
Copy link
Contributor Author

kgodey commented Aug 21, 2021

@kgodey kgodey changed the title WIP - Update table PATCH endpoint to work with columns Update table PATCH endpoint to work with columns Aug 21, 2021
@kgodey kgodey marked this pull request as ready for review August 22, 2021 09:50
@kgodey kgodey requested review from a team, pavish and eito-fis August 22, 2021 09:50
@kgodey
Copy link
Contributor Author

kgodey commented Aug 23, 2021

@eito-fis @powellc This is blocking @pavish so we're prioritizing merging it. Please review this even if it is merged, I'll make any code review changes in a new PR.

Copy link
Member

@pavish pavish left a comment

Choose a reason for hiding this comment

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

Merging this PR, as it is blocking work on the frontend. Review changes, if any, will be taken up as a separate PR.

@pavish pavish merged commit 7b33cbd into master Aug 23, 2021
@pavish pavish deleted the table_patch branch August 23, 2021 11:56
Copy link
Contributor

@eito-fis eito-fis left a comment

Choose a reason for hiding this comment

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

Looks great! Just a couple small comments.

return False


def retype_column_in_connection(table, connection, engine, column_index, new_type, type_options={}):
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we create a new function here instead of using the connection_to_use=None, table_to_use=None pattern used elsewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was trying out different patterns to figure out which one might work better, I'll refactor this to be more consistent with the other functions.

columns.set_column_default(table_oid, column_index, new_default, engine)
default_stmt = select(text(cast_stmt))
new_default = str(execute_statement(engine, default_stmt, connection_to_use).first()[0])
columns.set_column_default(table_oid, column_index, new_default, engine, connection_to_use)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this also pass table_to_use?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice catch, thanks!

assert updated_table.columns[index].name == column_data[index - 2]['name']


def test_batch_update_column_all_operations(engine_email_type):
Copy link
Contributor

Choose a reason for hiding this comment

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

Really like these tests 👍

def _check_columns(actual_column_list, expected_column_list):
# Columns will return an extra type_options key in actual_dict
# so we need to check equality only for the keys in expect_dict
for index, column_dict in enumerate(expected_column_list):
Copy link
Contributor

Choose a reason for hiding this comment

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

Small nitpick, it might be nicer to iterate over a zip of the two lists.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, I'll make the change. Thanks!

@kgodey kgodey mentioned this pull request Aug 24, 2021
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

Tables PATCH endpoint does not update column properties
3 participants