Skip to content

fix: honour ci_string in contains/2, string_starts_with/2, and string_ends_with/2 - #248

Merged
zachdaniel merged 1 commit into
ash-project:mainfrom
sfelixalejandro:fix/ci-string-case-insensitive-substring-search
Oct 26, 2026
Merged

zachdaniel merged 1 commit into
ash-project:mainfrom
sfelixalejandro:fix/ci-string-case-insensitive-substring-search

Conversation

@sfelixalejandro

Copy link
Copy Markdown
Contributor

Resolves #228

Overview

In SQLite, collation sequences like COLLATE NOCASE only apply to comparison operators (=, !=, <, >, IN). Built-in scalar functions like instr(X, Y) completely ignore collations attached to their arguments. Additionally, because AshSqlite.Repo sets case_sensitive_like: :on, LIKE in SQLite is also case-sensitive and ignores COLLATE NOCASE.

As reported in #228, queries using contains or string_starts_with against a %Ash.CiString{} or a resource attribute typed as :ci_string were compiled into instr(col, (? COLLATE NOCASE)) or col LIKE ?, failing to match case-insensitively.

Solution

In AshSqlite.SqlImplementation, we intercept Contains, StringStartsWith, and StringEndsWith:

  • When either operand is a ci_string (struct %Ash.CiString{}, attribute with storage type :ci_string, calculation, or typed expression):
    • contains/2: compiles to instr(LOWER(left), LOWER(right)) > 0
    • string_starts_with/2: compiles to instr(LOWER(left), LOWER(right)) = 1
    • string_ends_with/2: compiles to like(LOWER(left), escape_pattern)
  • When neither operand is a ci_string, it returns :error and falls through to AshSql.Expr's default behaviour, preserving normal case-sensitive behavior without performance overhead.

Tests

  • Added unit tests in test/calculation_test.exs verifying case-insensitive matching for contains, string_starts_with, and string_ends_with using both %Ash.CiString{} and :ci_string resource attributes.
  • Verified standard strings continue to match case-sensitively.
  • Full test suite passing (330/330 tests).

@zachdaniel
zachdaniel merged commit 1285c1b into ash-project:main Oct 26, 2026
@zachdaniel

Copy link
Copy Markdown
Contributor

🚀 Thank you for your contribution! 🚀

@sfelixalejandro

Copy link
Copy Markdown
Contributor Author

Glad to help! 🚀

@sfelixalejandro
sfelixalejandro deleted the fix/ci-string-case-insensitive-substring-search branch October 26, 2026 03:19
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.

ci_string is not honoured on SQLite for contains/2 or for attribute sorts

2 participants