| rlucas.net | |||||
|
Categories
Alternate Views
|
Wed, 09 May 2007
Ruby's ActiveRecord Makes Dropping to Raw SQL a Royal Pain (Probably on Purpose)
The opinionated programmers behind Rails have generally done a good job. (There are couple of FUBARs in their bag of tricks, such as the boneheaded choice to use pluralized table names (in some places) and use automagical pluralization code to try and mediate between the singular and plural.) There's another item I'd like to bring up, however, and that's the fact that ActiveRecord intentionally cripples your ability to do raw SQL queries. This is, I'm sure, done to discourage raw SQL hacking in favor of using the ActiveRecord objects (which, for small numbers of objects, is admittedly a superior way to do many things, because of concerns for clarity, maintainability, etc.). However, sometimes you need SQL, dammit. Especially when you're doing a correlation between, say, different tags that describe business plans, and the people that link those business plans together, plus the number of times that such tags appear, there's just no sense in pulling thousands of records into memory, instantiating Ruby objects, and improperly reimplementing basic CS sorting algorithms to link them up. You've got all that sitting right there in your RDBMS. ActiveRecord lets you do something like this:
Which will run the complicated SQL and replace the bind vars (question marks) in the raw SQL with var1, var2, var3, etc., and give you a bunch of BusinessPlan objects that it's instantiated off those IDs. Easy enough. But what if you need not merely to get the objects, but to get some other important info (say, COUNT(something)) out? You're shit out of luck with ActiveRecord. The .connection.select_all method returns you an array of record hashes, but it requires fully-baked SQL (no bind vars).
WTF? If you read the code for the find_by_sql method, you'll see:
Given this, you might think: "aha, I'll just use a similar method and pass to sanitize_sql an array with my SQL and bind vars, then pass that on to select_all. No can do. sanitize_sql is a protected method. So, here's my encapsulation-breaking, OO-unfriendly, scofflaw workaround to let you have access to what you should already get: a decent bit of code for binding SQL parameters: (In helpers/application_helper.rb) arb = ActiveRecord::Base def arb.sanitize_fucking_sql(*args) sanitize_sql(*args) end Now, you can happily go about your business and, when necessary, call ActiveRecord.sanitize_fucking_sql(...) to get 'er done. No special-purpose DB connections, no wrangling thousands of objects in memory. Caveats:
$Id: ruby_active_record_makes_raw_sql_a_royal_pain.txt 863 2007-05-09 18:07:04Z rlucas $ [category: /bugfix] [permalink] |
||||