#1110 ✓resolved
Greg Campbell

Save fails for a less-than-obvious reason involving Time representations

Reported by Greg Campbell | October 27th, 2009 @ 12:50 PM | in 0.10.2

The underlying problem is that DataMapper considers a particular property to have changed, but the storage engine (MySQL in this case) does not, due to differences in precision (Ruby Time objects include microseconds, MySQL TIMESTAMP and DATETIME columns do not). When a resource attempts to save, it first saves its parents, which fails here because MySQL returns that zero rows changed.

This caused a bunch of specs to break for me when updating my application for DM 0.10 (and took quite a while to track down using a debugger), and I'm not sure if it's intended behavior or not. For now, I've worked around it at an application level (comparing to_i representations of timestamps before setting them), but it would be nice if DM could at least provide some indication of why the save failed. I'm calling this a "regression" for now because it worked as I would expect it to in 0.9.x, but feel free to remove the tag if this is the intended behavior. Example follows:

require 'rubygems'
require 'dm-core'

DataMapper.setup(:default, 'mysql://localhost/save_failure')

class SecretPlan
  include DataMapper::Resource

  property :id, Serial
  property :name, String
  property :steps_changed_at, Time

  has n, :steps

class Step
  include DataMapper::Resource
  property :id, Serial
  belongs_to :secret_plan

  property :description, String


plan = SecretPlan.create(:name => "underpants", :steps_changed_at => Time.now)
step = plan.steps.create(:description => "Steal underpants")
step.description = "???"

# The next line causes the problem.  steps_changed_at will be considered to be "dirty" by DM, 
# as the Ruby Time object includes microseconds.  However, persisting to MySQL (and perhaps other
# storage engines?) will return that zero rows changed, because mysql dates/times only store
# precision down to the second, so it treats the value as unchanged.  This then prevents the 
# step from saving, as the parent did not save successfully.
plan.steps_changed_at = Time.now 

puts step.save #false
puts step.reload.description # "Steal underpants"

Comments and changes to this ticket

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

People watching this ticket