dwyl / alog

🌲 alog (Append-only Log) is an easy way to start using the Lambda/Kappa architecture in your Elixir/Phoenix Apps while still using PostgreSQL (with Ecto).
GNU General Public License v2.0
15 stars 2 forks source link

unique_constraint #21

Closed Danwhy closed 5 years ago

Danwhy commented 5 years ago

We can't currently use unique_constraint with alog because as we append data, it will no longer be unique, and will throw an error when it shouldn't.

We will need a way to make sure unique constraint errors are returned only when they should. (ie. in an insert function, but not an update).

Is there a way we can use the existing unique_constraint functionality, or will we have to create a new function? Also, will we have a problem if people create a unique_index in their migration?

Danwhy commented 5 years ago

The Ecto uniqueness constraint works on the database level. You need to create a unique index, which will then throw an error if duplicate fields are inserted into the database. Ecto.Changeset.unique_constraint just catches this error and reports it back.

So we'll have to create our own unique function that actually checks if a field already exists in the database, in the insert function, and also in the update function if the entry_id does not match.

If possible, we should also detect if a database has a unique index, and throw an error if Alog is used with it.

Danwhy commented 5 years ago

The following query will return any unique indexes on a table, that aren't the primary key:

SELECT * FROM pg_indexes WHERE tablename = $1 and indexname NOT LIKE '%_pkey' AND indexdef LIKE 'CREATE UNIQUE INDEX%';, where $1 is the name of our table

We can use this to throw an error if people try to use alog with a table that has a unique index.

Unfortunately it's only possible to perform this check at runtime (unlike checking the presence of fields in the schema which can happen at compile time). We're therefore having to check for indexes every time a user inserts or updates an entry. If anyone can think of a better/more efficient way of doing this, please let me know.

Danwhy commented 5 years ago

When you apply unique_constraint to a changeset, it gets added to a constraints field (which isn't visible if you inspect it, but can be retrieved with Map.get(changeset, :constraints), or Ecto.Changeset.constraints(changeset) in Ecto 3).

We can use this field to validate all constraints before we insert or update an item.