Rails sql issue with a simple where statement - sql

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'

Related

Rails and SQL straight request

In rails we have Model.find(id_number) request. What I want to ask is there any possibility to create straight request to DB, for example: SELECT * FROM users WHERE id = 1 and replace rails basic Model.where(id: 1)?
EDIT
In my model.rb I have:
scope :search_import, -> {includes(:translations)}
default_scope -> {self.search_import}
And request (in rails console) looks like:
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User::Translation Load (0.6ms) SELECT `users_translations`.* FROM `users_translations` WHERE `users_translations`.`category_id` IN (1)
But I don't want to User::Translation was executed (with unscoped I have the same result)
Sure, something like
ActiveRecord::Base.connection.execute("SELECT * FROM users WHERE id = 1")

How to fix a multiple assignment issue? on Rails 4

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

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 Rest Service GET

I have set up a Rails REST Service and I am having a problem showing a single record. Here is the URL that I am trying to hit:
http://website:3000/users/2/timesheets/21
Controller code:
def show
puts "SHOW"
puts params.inspect
#timesheets = User.find(params[:user_id]).timesheets(params[:id])
respond_to do |format|
format.json { render json: #timesheets }
end
end
I know the params are getting to the controller, but it is not using the :timesheet_id. Here is the console output to show what I mean:
Started GET "/users/2/timesheets/21" for **.**.***.** at 2013-03-19 06:12:11 -0400
Processing by TimesheetsController#show as */*
Parameters: {"user_id"=>"2", "id"=>"21"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "2"]]
Timesheet Load (0.5ms) SELECT "timesheets".* FROM "timesheets" WHERE "timesheets"."user_id" = 2
Completed 200 OK in 120ms (Views: 36.5ms | ActiveRecord: 2.9ms)
I see the timesheet id of 21 in the parameters hash. A query is then made to get the user, but then all of the timesheets for that user are grabbed. Any help would be appreciated. Thanks.
What Prakash suggests works, but executes two queries: one to get the user and one the get the timesheet. There does not seem to be any reason to do User.find(...) first. Might as well query the timesheets table only, which will only execute one query and is thus faster:
#timesheet = Timesheet.where('user_id = ? and id = ?', params[:user_id], params[:id]).first
Try this:
#user = User.find(params[:user_id])
#timesheet = #user.timesheets.find(params[:id])
This should run a query as follows:
SELECT "timesheets".* FROM "timesheets" WHERE "timesheets"."id" = 21 AND ("timesheets"."user_id" = 2) LIMIT 1
The corresponding view should be referring to #timesheet variable and not #timesheets.