Skip to content

Conversation

stevebrambilla
Copy link
Contributor

@stevebrambilla stevebrambilla commented Sep 25, 2025

We use the Citus extension for sharding our Postgres database, which adds a requirement that any functions used within CASE or COALESCE expressions must be IMMUTABLE. Currently this means that we can't use error expressions at all because ash_raise_error is currently marked as STABLE.

The current ash_raise_error could technically be IMMUTABLE as it meets all of the requirements. However, the postgres planner will constant-fold the function call, making all expressions that use it immediately raise an exception. To prevent this we need to make the function call dependent on the row so it can't be constant-folded.

This PR adds a new function, ash_raise_error_immutable, that is marked IMMUTABLE and accepts a third argument that is simply ignored. This works together with a change in AshSql that adds a row-dependent argument, preventing the caching.

It's opt-in only via an AshPostgres.Repo callback, which stores a value in the repo config. AshSql looks for that config value when building the query and decides whether to call the existing ash_raise_error function, or the new ash_raise_error_immutable.

Related AshSql PR: ash-project/ash_sql#175


Contributor checklist

Leave anything that you believe does not apply unchecked.

  • I accept the AI Policy, or AI was not used in the creation of this PR.
  • Bug fixes include regression tests
  • Chores
  • Documentation changes
  • Features include unit/acceptance tests
  • Refactoring
  • Update dependencies


def drop(2) do
"""
#{ash_raise_error()}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Btw, drop(2) used to call ash_raise_error(false), where the false arg removed the 'ash_error" prefix. It looks like that may have gotten lost unintentionally over a couple of commits:

Copy link
Contributor

Choose a reason for hiding this comment

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

😓 good catch.

@zachdaniel
Copy link
Contributor

So I like this, but I also hesitate to define this function automatically in everyone's databases when its only need by a few. My suggestion would be to create a new "extension" (not an Ash extension, just one of these postgres extension modules) that users can optionally add to define this function.

@stevebrambilla stevebrambilla force-pushed the immutable-raise-error branch 3 times, most recently from 3f56800 to ab4b99a Compare October 2, 2025 01:46
Copy link
Contributor Author

@stevebrambilla stevebrambilla left a comment

Choose a reason for hiding this comment

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

Good call, thanks for reminding me about those custom extensions, def. prefer this solution over adding it to ash-functions.

```
"""

use AshPostgres.CustomExtension, name: "immutable_raise_error", latest_version: 1
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would be the first AshPostgres.CustomExtension offered by AshPostgres — is this what you had in mind, or would you prefer special-casing this in the migrator like "ash-functions" and using a string value for it?

Copy link
Contributor

Choose a reason for hiding this comment

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

No need to special case, "ash-functions" existed before we had this.

end

# Conditionally apply @impl to keep compatibility across ash_sql versions
if {:immutable_errors?, 1} in AshSql.Implementation.behaviour_info(:callbacks) do
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe this is overkill? Would you prefer to just bump the AshSql dependency to the next release?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah let's just bump it. For now, replace the dep with a dependency on ash_sql main and we can get it merged, and then hex will make me point at a proper version before releasing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, done!

@zachdaniel zachdaniel merged commit 5c4a429 into ash-project:main Oct 3, 2025
62 of 64 checks passed
@zachdaniel
Copy link
Contributor

🚀 Thank you for your contribution! 🚀

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.

2 participants