Converting Time or DateTime to MySQL compatible DATETIME - mysql

According to "Ruby datetime suitable for mysql comparison", I should be able to do:
Time.now.to_s(:db)
This doesn't appear to be valid anymore. I get:
irb(main):001:0> Time.now.to_s(:db)
ArgumentError: wrong number of arguments (1 for 0)
from (irb):1:in `to_s'
from (irb):1
from C:/Ruby22/bin/irb:11:in `<main>'
Does this functionality still exist or do I have to manually format the date and time to fit MySQL format?
I'm using ruby 2.2.2.

Time#to_s doesn't accept arguments in Ruby. If you're using Rails, ActiveSupport::TimeWithZone supplies the to_s method you were referring to.
To get this format in Ruby without ActiveSupport you can use:
Time.now.strftime('%Y-%m-%d %H:%M:%S')

Related

How to query array column from mysql in Rails 5.2?

I am using gem rails~> 5.2 and gem mysql2 >= 0.3.13, < 0.5.
I have a model Lawer, which has an array column lawer_filed [sic].
# Model lawer.rb
serialize :lawer_field, Array
Then I created a Lawer, and I can get the lawer_field value as follows:
=> Lawer.first.lawer_field
=> ["2", "3", "5"]
Now, I want to find one Lawer with a query using lawer_field. I tried:
#lawer = Lawer.where("lawer_field && ARRAY[?]", "2")
which raised an error like this:
ActiveRecord::StatementInvalid (Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '['2']) LIMIT 11' at line 1: SELECT `lawers`.* FROM `lawers` WHERE (lawer_field && ARRAY['2']) LIMIT 11)
There is a mistake in my SQL syntax, but I don't how to fix it. Can anyone help?
MySQL, unlike PostgreSQL, does not support arrays in database. Therefore you needed to add this line:
serialize :lawer_field, Array
This means that you have a string field in your database, but whenever ActiveRecord is unpacking results returned by the database, it maps them directly to an instance of Ruby Array. What this means is that your only option to filter the results in the database is with any MySQL string comparison functions, LIKE, etc.
Your options are to either use LIKE or perform some other String functions (which will not perform well as you will be unable to use indices) or build another table, add a has_many association to it and use MySQL the way it was supposed to be used. You could also, of course, migrate to PostgreSQL, but that seems to be the most extreme option.
EDIT: you could also consider using MySQL`s JSON, which has been added recently. That depends on your version of MySQL though.
I didn't try this answer because I don't have any Rails project ready for testing, but I think the problem is in the syntax.
I think it should be something like this:
Lawer.where("lawer_field IN (?)", "2")

Disable Rails 4.2 fractional second support on MySQL

So Rails 4.2 starts to support fractional seconds for MySQL. However it requires a migration of column change from datetime to datetime(6). Currently we do not want to do this, and just want to ignore fractional seconds as we have always been doing before.
Rails is not smart enough to see that my datetime has precision 0 and change queries accordingly, so a lot of our spec broke. For example we assert to "select all Foo created within the last hour", but values are persisted without milliseconds, but select statements still uses milliseconds, so lots of records won't be selected.
Before Rails 4.2:
where("foo_at >= ?", 1.day.ago) => foo_at >= '2015-11-02 04:48:18'
In Rails 4.2:
where("foo_at >= ?", 1.day.ago) => foo_at >= '2015-11-02 04:48:18.934162'
Is there a global setting to force AR queries to strip out fractional/milliseconds as it has been doing before?
There is no official way to turn it off, but you could overwrite the behavior,
module ActiveRecord
module ConnectionAdapters
class Mysql2Adapter < AbstractMysqlAdapter
def quoted_date(value)
super
end
end
end
end
It is in https://github.com/rails/rails/blob/v4.2.5/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L77-L83
And you could remove it in Rails 5, the commit in rails 5 https://github.com/rails/rails/commit/bb9d20cf3434e20ac6f275e3ad29be42ebec207f should Format the time string according to the precision of the time column
From reading Rails source for 4.2.x branch, I don't see any option to change that.
I see a merged PR, which will see what the database column's precision is, and build the sql datetime string according to that precision. This should eliminate my issue that my spec broke due to mismatch of precision between persisted data and queries. However this is not merged in 4.2.x so it probably will only be available in Rails 5.

Postgres not allowing ">=" but mysql does, how to overcome?

My local rails database is mysql but my server host (heroku) is Postgres.
Probably a fairly common combination.
I have an advanced search form that work locally in development mode but not in production and it looks like it might be a Postgres specific thing as the heroku log shows I am getting:
LINE 1: ...,18,19,17,4,32,23,24,16,6,13) and (version_number >= 0.0 or ...
2014-06-23T01:47:54.198026+00:00 app[web.1]: ^
2014-06-23T01:47:54.198022+00:00 app[web.1]: ActiveRecord::StatementInvalid (PG::UndefinedFunction: ERROR: operator does not exist: character varying >= numeric
2014-06-23T01:47:54.198028+00:00 app[web.1]: HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
in the log.
Is there another way to do >= in postgres.
Locally I do see that the datatype is string in schema.rb which is probably the problem. Is there a way I can cast it into integer for rails for pg?
PostgreSQL definitely does have the >= operator: http://www.postgresql.org/docs/current/static/functions-comparison.html
Your problem is that you seem to be comparing a string with a number.
Is there a way I can cast it into integer for rails for pg?
Probably - but we can't see your code. Did you write the SQL? Or did you rely on ActiveRecord? DataMapper? Sequel? Can't help without seeing what you did.

Rails: How to handle existing invalid dates in database?

First, this is directly related to my other question:
How to gracefully handle "Mysql2::Error: Invalid date" in ActiveRecord?
But I still do not want to jump through all the loops of writing migrations which fix dates. That won't be the last table with invalid dates and I need some more generic approach.
So here we go:
I'm using a legacy MySQL database which contains invalid dates, sometimes like 2010-01-00 or 0000-04-25... Rails does not load such records (older versions of Rails did).
I do not want to (and cannot) correct these dates manually or automated. It should be up to the authors of those records to correct these dates. The old system was a PHP application which allowed such annoyances. The Rails application should/will just prevent the user from saving the record until the dates are valid.
The problem does not seem to be within Rails itself, but deeper within an .so library of the rails mysql gem.
So my question is not about how to validate the date or how to insert invalid dates. I don't want to do that and that's covered by numerous answers all over stackoverflow and the rest of the internet. My question is how to READ invalid dates from MySQL that already exist in the database without Rails exploding into 1000 little pieces...
The column type is DATETIME and I'm not sure if casting to string could help because Rails chokes before any ActiveRecord related parsing kicks in.
Here's the exact error and backtrace:
$ rails c
Loading development environment (Rails 3.2.13)
irb(main):001:0> Poll.first
Poll Load (0.5ms) SELECT `polls`.* FROM `polls` LIMIT 1
Mysql2::Error: Invalid date: 2003-00-01 00:00:00
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/mysql2_adapter.rb:216:in `each'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/mysql2_adapter.rb:216:in `to_a'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/mysql2_adapter.rb:216:in `exec_query'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/mysql2_adapter.rb:224:in `select'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/abstract/database_statements.rb:18:in `select_all'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/abstract/query_cache.rb:63:in `select_all'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/querying.rb:38:in `find_by_sql'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/explain.rb:41:in `logging_query_plan'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/querying.rb:37:in `find_by_sql'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/relation.rb:171:in `exec_queries'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/relation.rb:160:in `to_a'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/explain.rb:34:in `logging_query_plan'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/relation.rb:159:in `to_a'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/relation/finder_methods.rb:380:in `find_first'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/relation/finder_methods.rb:122:in `first'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/querying.rb:5:in `__send__'
from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/querying.rb:5:in `first'
from (irb):1
The backtrace remains the same even when I do Poll.first.title so some date should never reach any output routine in IRB and thus should never be parsed. So suggestions to use a value before typecasting would not help.
I think the simplest solution that worked for me was to set in database.yml file cast: false, e.g. for development section
development
<<: *default
adapter: mysql2
(... some other settings ...)
cast: false
try this out
ActiveRecord::AttributeMethods::BeforeTypeCast provides a way to read the value of the attributes before typecasting and deserialization.
http://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/BeforeTypeCast.html

Mysql "Time" type gives an "ArgumentError: argument out of range" in Rails if over 24 hours

I'm writing a rails application on top of a legacy mysql db which also feeds a PHP production tool. Because of this setup so its not possible for me to change the databases structure.
The problem I'm having is that two table have a "time" attribute (duration) as long as the time is under 24:00:00 rails handles this, but as soon as rails comes across something like 39:00:34 I get this "ArgumentError: argument out of range".
I've looked into this problem and seen how rails handle the time type, and from my understanding it treats it like a datetime, so a value of 39:00:34 would throw this error.
I need some way of mapping / or changing the type cast so I don't get this error. Reading the value as a string would also be fine.
Any ideas would be most appreciated.
Cheers
I'm not familiar with Rails so there can be a clean, native solution to this, but if all else fails, one workaround might be writing into a VARCHAR field, then running a 2nd query to copy it over into a TIME field within mySQL:
INSERT INTO tablename (name, stringfield)
VALUES ("My Record", "999:02:02");
UPDATE tablename SET datefield = CAST(stringfield as TIME)
WHERE id = LAST_INSERT_ID();