How to fix a multiple assignment issue? on Rails 4 - sql

So, I have 3 tables Accounts, Users, and Subscriptions all of which have the deleted_at column. Now when I delete an account the request gets sent to all models at once causing a Multiple Assignments error as shown below:
Started DELETE "/admin/accounts/3" for 127.0.0.1 at 2014-10-27 19:38:48 +0200
Processing by SaasAdmin::AccountsController#destroy as HTML
Parameters: {"authenticity_token"=>"qo8NxYhezJUz2YylGxHlx2ou125UNoRuEcXbrnzyiN4=", "id"=>"3"}
AppSetting Load (0.3ms) SELECT "app_settings".* FROM "app_settings" ORDER BY "app_settings"."id" ASC LIMIT 1
Tag Load (0.2ms) SELECT "tags".* FROM "tags" WHERE "tags"."id" = $1 LIMIT 1 [["id", 22]]
SaasAdmin Load (0.2ms) SELECT "saas_admins".* FROM "saas_admins" WHERE "saas_admins"."id" = 4 ORDER BY "saas_admins"."id" ASC LIMIT 1
Account Load (0.1ms) SELECT "accounts".* FROM "accounts" WHERE (accounts.deleted_at IS NULL) AND "accounts"."id" = $1 LIMIT 1 [["id", "3"]]
(0.1ms) BEGIN
Subscription Load (0.3ms) SELECT "subscriptions".* FROM "subscriptions" WHERE (subscriptions.deleted_at IS NULL) AND "subscriptions"."subscriber_id" = $1 AND "subscriptions"."subscriber_type" = $2 LIMIT 1 [["subscriber_id", 3], ["subscriber_type", "Account"]]
SQL (0.3ms) UPDATE "subscriptions" SET deleted_at = '2014-10-27 17:38:48.909001', deleted_at = '2014-10-27 17:38:48.909025' WHERE (subscriptions.deleted_at IS NULL) AND "subscriptions"."id" = 3
PG::SyntaxError: ERROR: multiple assignments to same column "deleted_at"
: UPDATE "subscriptions" SET deleted_at = '2014-10-27 17:38:48.909001', deleted_at = '2014-10-27 17:38:48.909025' WHERE (subscriptions.deleted_at IS NULL) AND "subscriptions"."id" = 3
(0.1ms) ROLLBACK
Completed 500 Internal Server Error in 7ms
ActiveRecord::StatementInvalid (PG::SyntaxError: ERROR: multiple assignments to same column "deleted_at"
: UPDATE "subscriptions" SET deleted_at = '2014-10-27 17:38:48.909001', deleted_at = '2014-10-27 17:38:48.909025' WHERE (subscriptions.deleted_at IS NULL) AND "subscriptions"."id" = 3):
activerecord (4.0.4) lib/active_record/connection_adapters/postgresql_adapter.rb:791:in `async_exec'
Additionally, the sql is generated by act as paranoid gem, so we don't write the finder. We do account.destroy, the rest goes by dependecies. Here's the link to the gem if helpful: https://github.com/ActsAsParanoid/acts_as_paranoid
What can we do to fix this?
See the full logs below by visiting the Gist link: https://gist.github.com/rmagnum2002/2358536587bb34dbbc52

Related

Make a request with activerecord to get only the users from groups and not from others

I'm trying to get users from few groups (with given ids) and exclude the users from other groups.
I've tried something like :
User.joins(:groups).where(groups: {id: ["8939","8950"]}).where.not(groups: {id: 8942}).map(&:id)
User Load (0.9ms) SELECT "users".* FROM "users" INNER JOIN "groups_users" ON "groups_users"."user_id" = "users"."id" INNER JOIN "groups" ON "groups"."id" = "groups_users"."group_id" WHERE "groups"."id" IN (8939, 8950) AND "groups"."id" != $1 [["id", 8942]]
=> [119491, 119489, 119490, 119492, 119488, 119484, 119483, 119491, 119482]
But that's not correct
The users in group 8942.
Group.find(8942).users.pluck(:id)
Group Load (0.4ms) SELECT "groups".* FROM "groups" WHERE "groups"."id" = $1 LIMIT 1 [["id", 8942]]
(0.6ms) SELECT "users"."id" FROM "users" INNER JOIN "groups_users" ON "users"."id" = "groups_users"."user_id" WHERE "groups_users"."group_id" = $1 [["group_id", 8942]]
=> [119490, 119492, 119491, 119457, 119423]
The where.not doesn't work on user "groups"."id" != $1 [["id", 8942]]. Why ?
Correct way to do such things is to use SQL EXISTS condition. I wish there was a specific ActiveRecord helper method for that, but there isn't at the moment.
Well, using pure SQL is just fine:
User.where("EXISTS (SELECT 1 FROM groups_users WHERE groups_users.user_id = users.id AND groups_users.group_id IN (?))", [8939, 8950]).
where("NOT EXISTS (SELECT 1 FROM groups_users WHERE groups_users.user_id = users.id AND groups_users.group_id IN (?))", [8942])
What you were doing with your original query is asking for not joining groups with [8942] ids to your query, and only joining groups with ids [8939, 8950]. Well, you can see right now that this doesn't make any sense: that's like asking to select every user whose name is bob and NOT charlie. Second condition doesn't add anything to the first one.
Join query is multiplicating columns, so if your user is in every group, result set would be:
user_id | group_id
1 | 8939
1 | 8950
1 | 8942
Then you filter out the latter row: 1 | 8942. Still, user 1 is in the result set and is returned.
And to ask the database to return only records which doesn't connect with another relation you should explicitly use NOT EXISTS which exists explicitly for that purpose :)
There is now a Where Exists gem which you can use. (Full disclosure: I've created that gem recently.)
With it you can achieve your task as simple as:
User.where_exists(:groups, id: [1, 2]).where_not_exists(:groups, id: [3, 4])

Rails sql issue with a simple where statement

I am trying to get a very simple statement working.
Node.where("nodeid = ?", nstart).select('id')
Results in
Parameters: {"utf8"=>"✓", "authenticity_token"=>"WpSpzLzaFUx8QgFbwEfygQUkkqvbUgZl8Hh0UxJvT8E=", "edge"=>{"kind"=>"IsA", "start_id"=>"blabla", "end_id"=>"bliblib", "property1"=>"bloblbo"}, "commit"=>"Create Edge"}
(0.0ms) begin transaction
Node Load (0.1ms) SELECT "nodes".* FROM "nodes" WHERE "nodes"."id" = ? LIMIT 1 [["id", 0]]
CACHE (0.0ms) SELECT "nodes".* FROM "nodes" WHERE "nodes"."id" = ? LIMIT 1 [["id", 0]]
(0.0ms) rollback transaction
It should be just a ´select nodes.id from nodes where nodeid = blabla´ The limit doesn't matter.
However if I add .first.
Node.where("nodeid = ?", nstart).select('id').first
I get
Parameters: {"utf8"=>"✓", "authenticity_token"=>"WpSpzLzaFUx8QgFbwEfygQUkkqvbUgZl8Hh0UxJvT8E=", "edge"=>{"kind"=>"IsA", "start_id"=>"blabla", "end_id"=>"bliblib", "property1"=>"bloblbo"}, "commit"=>"Create Edge"}
Node Load (0.1ms) SELECT "nodes"."id" FROM "nodes" WHERE (nodeid = 'blabla') ORDER BY "nodes"."id" ASC LIMIT 1
Node Load (0.1ms) SELECT "nodes"."id" FROM "nodes" WHERE (nodeid = 'bliblib') ORDER BY "nodes"."id" ASC LIMIT 1
(0.0ms) begin transaction
Node Load (0.1ms) SELECT "nodes".* FROM "nodes" WHERE "nodes"."id" = ? LIMIT 1 [["id", 0]]
CACHE (0.0ms) SELECT "nodes".* FROM "nodes" WHERE "nodes"."id" = ? LIMIT 1 [["id", 0]]
(0.1ms) rollback transaction
The first select now is what it should be but the follow up is again like before and seems to determine the eventual return value (because it doesn't work either). I just want the id when I only know the nodeid which basically is the name of the node.
What is going on in Rails here?
If you just want the id value you could do this
Node.where("nodeid = ?", nstart).limit(1).pluck(:id).first
This would return 1 integer with the value
EDIT:
ok scratch that, i guess you don't really need to use limit so a simple first would just do
Node.where("nodeid = ?", nstart).first[:id]
or
Node.where("nodeid = ?", nstart).first.id
You're making a query:
Node.where("nodeid = ?", nstart).select('id')
will return an array of objects (or empty array if there's no suitable records) *not array actually. Activerecord::Relation
If you use 'first' like Mohammad AbuShady told:
Node.where("nodeid = ?", nstart).select('id').first
you get an object (or nil if there's no record)
If you want an integer id value, you can get it from the object
Node.where("nodeid = ?", nstart).first.id
select('id') tells active record not to retrieve all the fields in the table, just 'id'

SQL Injection in ActiveRecord

I'm trying to build a vulnerable demo application. I'm using SQLite, and I have ruby code that looks like this:
#value = current_user.accounts.calculate(:sum, params[:column])
And the SQL generates the following by default:
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
(0.1ms) SELECT SUM("accounts"."account_value") AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ? [["user_id", 1]]
Next, I put ssn) FROM users WHERE name = 'Texas'; -- into the form and I get the following:
(0.3ms) SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --)) AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ? [["user_id", 1]]
SQLite3::RangeException: bind or column index out of range: SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --)) AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ?
Completed 500 Internal Server Error in 2ms
ActiveRecord::StatementInvalid (SQLite3::RangeException: bind or column index out of range: SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --)) AS sum_id FROM "accounts" WHERE "accounts"."user_id" = ?):
app/controllers/instant_calculator_controller.rb:3:in `sum'
I think the issue is that the 'user_id' section tacked onto the end as a paramiterized query is messing this up. I tried doing something like ssn) FROM users WHERE name = 'Texas'OR user_id = ?; -- just to throw that part of the query away, but that didn't seem to help.
Does anyone have any thoughts on I could make this work? I can change the code as well as the query, but I'd prefer to change the query before changing to code to make it SQLiable.
EDIT:
A bit more info. If I take the SQL that is generated and just change the last user_id to '1' so it looks like SELECT SUM(ssn) FROM users WHERE name = 'Texas'; --) AS sum_id FROM 'accounts' WHERE 'accounts'.'user_id' = 1 it works perfectly. I don't understand why this matters as everything after -- should be ignored.

Rspec controller test hanging

Here is my controller test ("spec/controllers/api/tasks_controller_spec.rb")
require 'spec_helper'
describe Api::TasksController do
before :each do
#quadros = create :cat
#task = Task.new(:content => "Example task for #api")
#quadros.add_task(#task)
end
describe "GET index" do
it "returns a list of all user's tasks" do
get :index, :format => 'json'
expect(response).to eq(User.first.all_tasks)
end
end
end
Here is my Api::BaseController
class Api::BaseController < ApplicationController
respond_to :json
skip_before_filter :authenticate_user!
end
And Api::TasksController
class Api::TasksController < Api::BaseController
def index
#tasks = User.first.all_tasks
respond_with #tasks.to_json
end
end
My other test run fine.
When I run the api test, it executes the before block, makes the request as json, and then hangs on this query:
Processing by Api::TasksController#index as JSON
User Load (0.3ms) SELECT `users`.* FROM `users` LIMIT 1
Tag Load (0.3ms) SELECT `tags`.* FROM `tags` WHERE (user_id = 418 AND parent_tag_id IS NOT NULL)
Tag Load (0.2ms) SELECT `tags`.* FROM `tags` WHERE `tags`.`id` IN (NULL)
Tag Load (0.3ms) SELECT `tags`.* FROM `tags` WHERE (user_id = 418 AND parent_tag_id IS NULL)
Task Load (0.7ms) SELECT tasks.* FROM `tasks` JOIN tag_tasks on tasks.id = tag_tasks.task_id WHERE (tag_tasks.tag_id IN (301) OR creator_id = 418) GROUP BY tasks.id ORDER BY tasks.created_at DESC
Completed 200 OK in 99ms (Views: 0.1ms | ActiveRecord: 0.0ms)
User Load (0.3ms) SELECT `users`.* FROM `users` LIMIT 1
Tag Load (0.2ms) SELECT `tags`.* FROM `tags` WHERE (user_id = 418 AND parent_tag_id IS NOT NULL)
Tag Load (0.2ms) SELECT `tags`.* FROM `tags` WHERE `tags`.`id` IN (NULL)
Tag Load (0.2ms) SELECT `tags`.* FROM `tags` WHERE (user_id = 418 AND parent_tag_id IS NULL)
Task Load (0.7ms) SELECT tasks.* FROM `tasks` JOIN tag_tasks on tasks.id = tag_tasks.task_id WHERE (tag_tasks.tag_id IN (301) OR creator_id = 418) GROUP BY tasks.id ORDER BY tasks.created_at DESC
Where it will just sit forever.
Any ideas why this might be happening?
I've come across a similar issue, and it appears the problem is with your expectation line:
expect(response).to eq(User.first.all_tasks)
This is not how RSpec wants you to test the response body. Notice that in the docs, specialized matchers are used instead of the equality matcher:
expect(response).to render_template("index")
So the response object, which is an ActionController::TestResponse, is meant to be queried about what happened, not what the response body was. So your test should be something like:
expect(JSON.parse(response.body)).to eq(User.first.all_tasks)
(Note that the response body is a string.)
As for the explanation of why the test hangs—instead of outright failing—it appears that this block of code (lib/rspec/expectations/fail_with.rb:22 in the rspec-expectations gem version 2.14.0) is the culprit:
if actual && expected
if all_strings?(actual, expected)
if any_multiline_strings?(actual, expected)
message << "\nDiff:" << differ.diff_as_string(coerce_to_string(actual), coerce_to_string(expected))
end
elsif no_procs?(actual, expected) && no_numbers?(actual, expected)
message << "\nDiff:" << differ.diff_as_object(actual, expected)
end
end
The all_strings?, any_multiline_strings?, no_procs?, and no_numbers? methods (defined in the same file) all invoke args.flatten on [actual, expected]. In this case, from what I can tell the problem is that actual is a TestResponse, which causes the flatten method itself to hang without raising a runtime error. I didn't have time to investigate this further, but the source for Array.flatten is here if anyone is interested.

rails_admin collection action executed twice

I'm using rails_admin to email all users when a button is clicked. The action is called twice instead of once.
lib/rails_admin_email_everyone.rb:
require 'rails_admin/config/actions'
require 'rails_admin/config/actions/base'
module RailsAdmin
module Config
module Actions
class EmailEveryone < RailsAdmin::Config::Actions::Base
register_instance_option :visible? do
authorized?
end
register_instance_option :link_icon do
'icon-envelope'
end
register_instance_option :collection? do
true
end
register_instance_option :member? do
false
end
register_instance_option :controller do
Proc.new do
#topics_with_owners = Topic.where('user_id IS NOT NULL')
#topics_with_owners.each do |topic|
#reminder = Reminder.new(:topic_id=> topic.id,
:to_user_id => student.id, :from_user_id => current_user.id)
if #reminder.save
TopicMailer.reminder(#reminder.topic).deliver
#reminder.update_attribute(:sent, true)
else
logger.error "Error sending email reminder: "
+ #reminder.errors.full_messages.map {|msg| msg}.join(',')
end
end
flash[:success] = "Reminders successfully sent."
redirect_to back_or_index
end
end
end
end
end
end
config/initializers/rails_admin.rb:
require Rails.root.join('lib', 'rails_admin_email_everyone.rb')
RailsAdmin.config do |config|
config.authenticate_with do
redirect_to(main_app.root_path, flash:
{warning: "You must be signed-in as an administrator to access that page"})
unless signed_in? && current_user.admin?
end
module RailsAdmin
module Config
module Actions
class EmailEveryone < RailsAdmin::Config::Actions::Base
RailsAdmin::Config::Actions.register(self)
end
end
end
end
config.actions do
# root actions
dashboard # mandator y
# collection actions
index # mandatory
new
export
history_index
bulk_delete
# member actions
show
edit
delete
history_show
show_in_app
email_everyone do
visible do
bindings[:abstract_model].model.to_s == "Reminder"
end
end
end
end
When I examine my log I see it executes twice, but with slightly different parameters. One including pjax. I found this stackoverflow question relating a similar issue to the pjax timeout:
Started GET "/admin/reminder/email_everyone?_pjax=%5Bdata-pjax-container%5D" for 127.0.0.1 at 2012-12-01 20:28:12 -0900
Processing by RailsAdmin::MainController#email_everyone as HTML
Parameters: {"_pjax"=>"[data-pjax-container]", "model_name"=>"reminder"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Topic Load (1.3ms) SELECT "topics".* FROM "topics" WHERE (user_id IS NOT NULL)
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
(0.2ms) BEGIN
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Topic Load (0.5ms) SELECT "topics".* FROM "topics" WHERE "topics"."id" = 4398 LIMIT 1
SQL (6.5ms) INSERT INTO "reminders" ("created_at", "from_user_id", "sent", "topic_id", "to_user_id", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["created_at", Sun, 02 Dec 2012 05:28:12 UTC +00:00], ["from_user_id", 1], ["sent", false], ["topic_id", 4398], ["to_user_id", 1], ["updated_at", Sun, 02 Dec 2012 05:28:12 UTC +00:00]]
(1.1ms) COMMIT
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Rendered topic_mailer/reminder.text.erb (375.8ms)
Started GET "/admin/reminder/email_everyone" for 127.0.0.1 at 2012-12-01 20:28:14 -0900
Processing by RailsAdmin::MainController#email_everyone as HTML
Parameters: {"model_name"=>"reminder"}
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Topic Load (1.1ms) SELECT "topics".* FROM "topics" WHERE (user_id IS NOT NULL)
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
(0.2ms) BEGIN
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Topic Load (0.5ms) SELECT "topics".* FROM "topics" WHERE "topics"."id" = 4398 LIMIT 1
SQL (7.1ms) INSERT INTO "reminders" ("created_at", "from_user_id", "sent", "topic_id", "to_user_id", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["created_at", Sun, 02 Dec 2012 05:28:14 UTC +00:00], ["from_user_id", 1], ["sent", false], ["topic_id", 4398], ["to_user_id", 1], ["updated_at", Sun, 02 Dec 2012 05:28:14 UTC +00:00]]
(1.0ms) COMMIT
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Rendered topic_mailer/reminder.text.erb (368.9ms)
I found the same issue,
I just added following code into lib/rails_admin_custom_action.rb
i.e Added pjax? option to false
module RailsAdmin
module Config
module Actions
class EmailEveryone < RailsAdmin::Config::Actions::Base
register_instance_option :pjax? do
false
end
end
end
end
end
may this will help you..
There's a pull request marked as good to merge that creates a pjax option for rails_admin. Unfortunately, it requires tests and there hasn't been any action for 3 months:
https://github.com/sferik/rails_admin/pull/1295