Multiple Foreign Keys

Jul 31, 2014

I’d like to document a problem that I’ve never had before, and the solution I arrived at. It was not a real complicated problem, but I got hung up on an even simpler problem.

Say I want to store directories in the database, and the files within those directories in an associated table. The actual problem is much more specific, but I’ve made it generic. Here are some sample entries:

Directories:

id: 1, name: home
id: 2, name: example

Files:

a_dir_id: 1, filename: 'test', content: 'some content'

Looking at this, it seemed fairly basic. The catch was that the directory id and filename would have to be a composite primary key, which I haven’t really used all that much. On top of that, the directory id also needs to be a foreign key. I read a bit and decided that this was probably doable, so this is where I got to:

require 'data_mapper'
DataMapper.setup(:default, 'mysql://root@127.0.0.1/sample')

class ADir
  include DataMapper::Resource
  property :id, Serial
  property :name, String, :unique => true
  has n, :a_files
end

class AFile
  include DataMapper::Resource
  property :a_dir_id, Integer, :key => true
  property :filename, String, :key => true
  property :content, Text
  belongs_to :a_dir
end

DataMapper.auto_migrate!

Running this, I ended up with this cryptic error:

...Can't create table 'sample.#sql-1ed_113' (errno: 150) (DataObjects::SQLError)

It was not a helpful error message. I assumed that what I was trying was simply not possible, but I blazed through and started writing the raw SQL to acheive what I wanted, and it turns out that it certainly is possible in MySQL.

After many rabbit trails, it dawned on me that Serial columns are unsigned and Integer columns are not. Adding a :min argument to this one line made it work like a charm:

property :a_dir_id, Integer, :min => 0, :key => true

Success!!