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

Add safe_make_column_not_nullable #114

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

alexspeller
Copy link

Addresses #106

I also added some quoting for some related methods where it seemed to be missing

Copy link
Contributor

@jcoleman jcoleman left a comment

Choose a reason for hiding this comment

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

I'm wondering if we want to also (whether it's in this PR or not) add a method that converts a validated check constraint to a regular NOT NULL constraint (the benefit basically being that it would assert that the constraint exists and is already validated).

README.md Outdated Show resolved Hide resolved
lib/pg_ha_migrations/safe_statements.rb Outdated Show resolved Hide resolved
lib/pg_ha_migrations/safe_statements.rb Outdated Show resolved Hide resolved
lib/pg_ha_migrations/safe_statements.rb Show resolved Hide resolved
quoted_table_name = connection.quote_table_name(table)
quoted_column_name = connection.quote_column_name(column)

tmp_constraint_name = :tmp_not_null_constraint
Copy link
Contributor

Choose a reason for hiding this comment

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

Are constraint names global? If so, do we want to add a random tag on the end of this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I think constraint names need to be unique within a schema. Because this method is doing multiple things non-transactionally, there is a possibility we could error out before the constraint is removed. So a) I agree with @jcoleman that we should add some random characters to this string and b) we should document that this might leave behind a constraint on failure, similar to creating a partitioned index.

Copy link
Author

@alexspeller alexspeller Jan 7, 2025

Choose a reason for hiding this comment

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

The documentation says that constraints are only unique per-table:

https://www.postgresql.org/docs/current/sql-set-constraints.html#:~:text=Because%20PostgreSQL%20does%20not%20require,will%20act%20on%20all%20matches.

I don't have a strong opinion on the naming of the constraint or if it should still have randomness regardless

end
end

migration.suppress_messages { migration.migrate(:up) }
Copy link
Contributor

@jcoleman jcoleman Jan 2, 2025

Choose a reason for hiding this comment

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

I wonder if we should add an assertion that this doesn't use locks beyond a certain level.

@rkrage Thoughts on this? I don't think we have this, but I'd love to have a test helper something like this:

expect do
  ...
end.not_to acquire_locks_on_table(...).higher_than(...)

Or similar.

Thinking out loud here:

I don't think there's a way to get Postgres to log all locks acquired, and so we'd probably have to do something like lock the table and then run the second query to verify what type is waiting in pg_locks. That doesn't work easily for multiple statements, though, so I think in this case we might lock the table at a lower level and expect this to proceed. But I think the ALTER TABLE will still need an AccessExclusiveLock, it will just be very short lived.

Edit: apparently log_lock_waits is a thing.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll have to think about this a bit, but I think it's going to be super difficult to make a generic helper. As you pointed out, we could acquire an AccessExclusiveLock and sleep for x seconds in a thread and then concurrently run the code under test. That code would block until the sleep expires and then we could go inspect the logs for the output from log_lock_waits. However, that breaks down for methods that execute multiple SQL statements.

I guess we could do something crazy and mock the connection#execute method with and_wrap_original such that it automatically does the AEL shenanigans described above before any SQL is executed

Copy link
Contributor

Choose a reason for hiding this comment

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

Since we don't currently have a precedent for writing such a test, I'm not sure if it's fair to put this on @alexspeller 😄

This can maybe be something we do as a follow on to this PR

Copy link
Author

Choose a reason for hiding this comment

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

Yeah realistically I don't have the bandwidth at the moment to deep dive on that unfortunately - I didn't see any precedent for more in-depth lock testing for similar methods so I kept it quite basic

@alexspeller alexspeller requested review from rkrage and jcoleman January 7, 2025 01:24
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