Skip to content

Relation#ids answers a uuid key as its leading digits (.to_i on every key) #310

Description

@eddygarcas

Probed at main a643ac7 (the runtime is unchanged at bc4acba), Linux x86-64, gcc 16.2.1 (Spinel 01d9d2f02).

Relation#ids answers the primary keys of a uuid-keyed model as Integers: the runtime reads #{@table}.id and calls .to_i on each value, and the sidecar seeds it Array[Integer]. So a uuid 44444444-4444-4444-8444-444444444441 comes back as 44444444, and a key starting with a letter as 0. The neighbours already carry string keys since #90 and #266: find casts through _cast_primary_key, the model's own finders take its key type, and pluck(:id) answers the stored value.

db/schema.rb:

ActiveRecord::Schema[8.1].define(version: 2026_01_01_000000) do
  create_table "widgets", id: :uuid, force: :cascade do |t|
    t.string "name"
    t.boolean "active", default: true
  end
end

app/controllers/widgets_controller.rb:

class WidgetsController < ApplicationController
  def index
    render plain: Widget.count.to_s
  end

  def exists
    render plain: Widget.where(active: true).exists?(params[:id]).to_s
  end

  def ids
    render plain: Widget.where(active: true).ids.join(",")
  end
end

(config/routes.rb: root "widgets#index", get "widgets/:id/exists", to: "widgets#exists", get "widget_ids", to: "widgets#ids"; Widget < ApplicationRecord with no body.)

With one row, id = '44444444-4444-4444-8444-444444444441', after spin build:

request Rails binary
GET /widget_ids 44444444-4444-4444-8444-444444444441 44444444
GET /widgets/44444444-4444-4444-8444-444444444441/exists true true
GET /widgets/nope/exists false false

The runtime, runtime/ruby/active_record/relation.rb:

    # `ids` — primary keys, as integers.
    def ids
      prior = @select_sql
      @select_sql = "#{@table}.id AS v"
      rows = ActiveRecord.adapter.select_rows(to_sql)
      @select_sql = prior
      rows.map { |row| row["v"].to_i }
    end

A model with self.primary_key = "identifier" would read the wrong column as well (I read that from the code rather than ran it).

A possible shape: select @model.primary_key and map each value through @model._cast_primary_key (as find does), with the ids seed widened for string-keyed apps the way widen_key_contract widens id. I have not tried that against the integer-keyed corpus, where the narrower Array[Integer] may matter to the typing, so I'm leaving the approach to you.

Found while compiling a Rails API app with --target spinel.

Activity

  1. thomasklemm commented on Oct 5, 2026

    @thomasklemm
    Collaborator

    Fix stacked on Hill climb #451 (30f06242): Relation#ids selects @model.primary_key and casts through _cast_primary_key (no hard-coded .id / .to_i). String/uuid apps also widen Relation#ids via widen_key_contract. Pinned by emit_and_run relation_ids_preserves_uuid_keys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions