From 8571facea3b51717b3c57c50b2deae5dbf997c6e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 13 Apr 2011 10:41:12 -0700 Subject: insert statements are prepared, but values are not escaped properly --- .../abstract/database_statements.rb | 22 ++++++++++++++-- .../connection_adapters/mysql_adapter.rb | 4 +++ .../connection_adapters/postgresql_adapter.rb | 12 +++++++++ .../connection_adapters/sqlite_adapter.rb | 4 +++ activerecord/lib/active_record/relation.rb | 30 +++++++++++++++++----- 5 files changed, 64 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index a3082b8f01..6d9b5c7b32 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -56,8 +56,17 @@ module ActiveRecord end # Returns the last auto-generated ID from the affected table. - def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) - insert_sql(sql, name, pk, id_value, sequence_name) + # + # +id_value+ will be returned unless the value is nil, in + # which case the database will attempt to calculate the last inserted + # id and return that value. + # + # If the next id was calculated in advance (as in Oracle), it should be + # passed in as +id_value+. + def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) + sql, binds = sql_for_insert(sql, pk, id_value, sequence_name, binds) + value = exec_insert(sql, name, binds) + id_value || last_inserted_id(value) end # Executes the update statement and returns the number of rows affected. @@ -364,6 +373,15 @@ module ActiveRecord end end end + + def sql_for_insert(sql, pk, id_value, sequence_name, binds) + [sql, binds] + end + + def last_inserted_id(result) + row = result.rows.first + row && row.first + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index d88075fdea..f461162dde 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -425,6 +425,10 @@ module ActiveRecord exec_query(sql, name, binds) end + def last_inserted_id(result) + @connection.insert_id + end + def exec_without_stmt(sql, name = 'SQL') # :nodoc: # Some queries, like SHOW CREATE TABLE don't work through the prepared # statement API. For those queries, we need to use this method. :'( diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 05f0e5ebe1..0884968363 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -544,6 +544,18 @@ module ActiveRecord exec_query(sql, name, binds) end + def sql_for_insert(sql, pk, id_value, sequence_name, binds) + unless pk + _, table = extract_schema_and_table(sql.split(" ", 4)[2]) + + pk = primary_key(table) + end + + sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk + + [sql, binds] + end + # Executes an UPDATE query and returns the number of affected tuples. def update_sql(sql, name = nil) super.cmd_tuples diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 14c4fd689b..9e7f874f4b 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -177,6 +177,10 @@ module ActiveRecord exec_query(sql, name, binds) end + def last_inserted_id(result) + @connection.last_insert_row_id + end + def execute(sql, name = nil) #:nodoc: log(sql, name) { @connection.execute(sql) } end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 490360ccb5..2f9970dec1 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -48,17 +48,35 @@ module ActiveRecord im = arel.create_insert im.into @table + conn = @klass.connection + if values.empty? # empty insert im.values = im.create_values [connection.null_insert_value], [] + @klass.connection.insert( + im.to_sql, + 'SQL', + primary_key, + primary_key_value) else - im.insert values + substitutes = values.to_a + binds = substitutes.map do |arel_attr, value| + [@klass.columns_hash[arel_attr.name], value] + end + substitutes.each_with_index do |tuple, i| + tuple[1] = conn.substitute_at(tuple.first, i) + end + + im.insert substitutes + + conn.insert( + im.to_sql, + 'SQL', + primary_key, + primary_key_value, + nil, + binds) end - @klass.connection.insert( - im.to_sql, - 'SQL', - primary_key, - primary_key_value) end def new(*args, &block) -- cgit v1.2.3