Ecto doesn't return id for autogenerated ksuid string type

With Postgres, autogenerating a string ID is possible and Ecto doesn’t seem to recognize that. I’m not using Ecto migrations for creating my tables. Instead, I’m running raw SQL migrations (managed by another tool). This is what my table creation looks like:

CREATE OR REPLACE FUNCTION gen_ksuid()
RETURNS text AS $$
BEGIN
	# logic to generate ksuid....
	return generated_ksuid;
END
$$ language plpgsql;

CREATE TABLE users (
    id text NOT NULL DEFAULT gen_ksuid(),
    email text,
    # other fields....
)

If I set @primary_key false and mention ID as a string field this way:

defmodule MyApp.Accounts.User do
  use Ecto.Schema

  @primary_key false

  schema "users" do
    field :id, :string
    # other fields...
  done
done

the id field is not returned after running insert Accounts.create_user(data):

%MyApp.Accounts.User{
  __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
  id: nil,
  email: "test@test.com",
  created_at: ~U[2022-12-02 03:34:57.970072Z],
  updated_at: ~U[2022-12-02 03:34:57.970072Z]
  # other fields...
}

And if I set @primary_key {:id, :string, autogenerate: true}, then I get this compilation error:

(ArgumentError) field :id does not support :autogenerate because it uses a primitive type :string

What can I do to get the inserted ID back? Please note, I don’t want to keep the ksuid generation logic in the phoenix application. Keeping the id generation logic on DB makes it easy to interact with the DB from the shell and other applications.

PS: I’m very new to Elixir and Phoenix.

Hi @manishs welcome. You should do field :id, :string, read_after_writes: true. This ensures that the field is queried out of the database via RETURNING when the row is inserted.

3 Likes

Wow, thanks for the quick turnaround @benwilson512 . That did help. It is great to be a part of this community.

2 Likes