googleapis / ruby-spanner-activerecord

Spanner<->ActiveRecord adapter for Ruby
MIT License
88 stars 32 forks source link

Cannot use ActiveRecord helper to create associated model from parent model record #192

Open mghadley opened 2 years ago

mghadley commented 2 years ago

Environment details

Steps to reproduce

  1. Set up a rails app with a model that has_many of another model
  2. Attempt to create a record of the child model from a record of the parent model with the following syntax: parent_model.child_models.create()

Whenever I do this I get this error: google-cloud-ruby/google-cloud-spanner/lib/google/cloud/spanner/convert.rb:120:in field_for_object': ArgumentError: Cannot determine type for nil values. (ActiveRecord::StatementInvalid)

olavloite commented 2 years ago

@mghadley I think I'm misunderstanding what it is you are doing (or there is a difference between our environments that causes this), as I'm not able to reproduce this error. I've added a couple of tests in https://github.com/googleapis/ruby-spanner-activerecord/pull/197/files to simulate this, both for interleaved tables and normal relations, but they seem to work.

Would you mind adding a more elaborate example of how this goes wrong for you? Or in any other way point me in the right direction for how I can reproduce this?

mghadley commented 2 years ago

Hi @olavloite, sorry for the slow response. I was able to narrow down the situation in which this is happening. A simplified version of our code with the relevant pieces is below. With this setup, this fails: account.transactions.create(transaction_guid: <guid>)

Migration:

class DataStoreSetup < ActiveRecord::Migration[7.0]
  def change
    create_table :institutions, :id => false do |t|
      t.string :institution_guid, :primary_key => true

      t.timestamps
    end

    create_table :customers, :id => false do |t|
      t.interleave_in :institutions
      t.string :customer_guid, :primary_key => true
      t.parent_key :institution_guid, :type => :string

      t.timestamps
    end

    create_table :accounts, :id => false do |t|
      t.interleave_in :institutions
      t.string :account_guid, :primary_key => true
      t.parent_key :institution_guid, :type => :string

      t.timestamps
    end

    create_table :customer_accounts, :id => false do |t|
      t.interleave_in :institutions
      t.string :customer_account_guid, :primary_key => true
      t.parent_key :institution_guid, :type => :string
      t.string :customer_guid, :foregin_key => true, :null => false
      t.string :account_guid, :foregin_key => true, :null => false

      t.timestamps
    end

    create_table :transactions, :id => false do |t|
      t.interleave_in :accounts
      t.parent_key :institution_guid, :type => :string
      t.parent_key :account_guid, :type => :string
      t.string :transaction_guid, :primary_key => true

      t.timestamps
    end
  end
end

Account model:

module Spanner
  class Account < ::Spanner::Record
    self.primary_keys = :institution_guid, :account_guid

    belongs_to :institution, :class_name => "::Spanner::Institution", :foreign_key => :institution_guid
    has_many :customer_accounts, :class_name => "::Spanner::CustomerAccount",
                                 :foreign_key => [:institution_guid, :account_guid]
    has_many :transactions, :class_name => "::Spanner::Transaction", :foreign_key => [:institution_guid, :account_guid]
  end
end

Transaction model:

module Spanner
  class Transaction < ::Spanner::Record
    self.primary_keys = :institution_guid, :account_guid, :transaction_guid

    belongs_to :account, :class_name => "::Spanner::Account", :foreign_key => [:institution_guid, :account_guid]
  end
end
olavloite commented 1 year ago

@mghadley I've been trying to reproduce this with our standard test cases for interleaved tables. These also include an interleaved table structure with multiple layers, but also in this case I'm not able to reproduce the issue that you are seeing. See the test case in #223 (Specifically this line).

Do you have any clues for me what I need to change in that test case to get the same error as you are getting?