Rails - Summing an encrypted field - ruby-on-rails

I have an encrypted column :amount following using Lockbox that I need to sum in my controller.
Currently, I have -
transaction_controller.rb
#one_month_transactions_sum = transactions.where(transaction_date: 1.month.ago..Date.today).sum(:amount).abs()
This is giving me the error - PG::UndefinedColumn: ERROR: column "amount" does not exist LINE 1: SELECT SUM(amount) FROM "transactions" WHERE "transactions".... which makes sense because I am asking rails to use the PG SUM function on a column I changed to be a ciphertext.
How would I sum the transactions at the controller level with an encrypted column?

If you send data to database as opaque binary blobs, you (naturally) lose ability to manipulate those fields directly in the database. It appears that your only option is to fetch encrypted values into the app, decrypt and sum in ruby (slowly). I haven't tried this specific library, but I'd guess that something like this should work:
transactions.where(...).map(&:amount).sum

Related

Handling dollars in rails with gem

I have a rails 4 application that has a form with many dollar input fields (all inside the same model).
I've made all of the fields decimals with precision => 10 and scale => 2. The issue I'm having is the input fields are masked with commas separating every '000'. I need to parse the commas out of all of these inputs before validating and saving to the database.
I need to store the value in the database as '150,000.00' so an external service can read the dollar value.
# Params
Parameters: {"requested_amount"=>"150,000.00"}
# SQL Update
SQL (0.7ms) UPDATE "requested_amount", "150.0"
With my current configuration, the database only saves what comes before the comma. I need to remove the comma so the SQL update can pass through the proper value.
Is there a gem that can do something like this? I've read into the rails-money gem and it seems a bit overkill for what I'm trying to accomplish.
Thanks
You want to use the money gem.
I just got done writing a financial application and it's the way to go.
There is some info missing.
What are you using to create the form?
Do you have verified if the value in the database is storing with commas?
Look at simple form gem to generate the form
If you think the money-rails gem is overkill, here's one approach:
Use integer column type to store money amount as cents, so 15000000 is stored.
Have a getter method to return a formatted value of the column, i.e. '150,000.00'

2 column table, ignore duplicates on mass insert postgresql

I have a Join table in Rails which is just a 2 column table with ids.
In order to mass insert into this table, I use
ActiveRecord::Base.connection.execute("INSERT INTO myjointable (first_id,second_id) VALUES #{values})
Unfortunately this gives me errors when there are duplicates. I don't need to update any values, simply move on to the next insert if a duplicate exists.
How would I do this?
As an fyi I have searched stackoverflow and most the answers are a bit advanced for me to understand. I've also checked the postgresql documents and played around in the rails console but still to no avail. I can't figure this one out so i'm hoping someone else can help tell me what I'm doing wrong.
The closest statement I've tried is:
INSERT INTO myjointable (first_id,second_id) SELECT 1,2
WHERE NOT EXISTS (
SELECT first_id FROM myjointable
WHERE first_id = 1 AND second_id IN (...))
Part of the problem with this statement is that I am only inserting 1 value at a time whereas I want a statement that mass inserts. Also the second_id IN (...) section of the statement can include up to 100 different values so I'm not sure how slow that will be.
Note that for the most part there should not be many duplicates so I am not sure if mass inserting to a temporary table and finding distinct values is a good idea.
Edit to add context:
The reason I need a mass insert is because I have a many to many relationship between 2 models where 1 of the models is never populated by a form. I have stocks, and stock price histories. The stock price histories are never created in a form, but rather mass inserted themselves by pulling the data from YahooFinance with their yahoo finance API. I use the activerecord-import gem to mass insert for stock price histories (i.e. Model.import columns,values) but I can't type jointable.import columns,values because I get the jointable is an undefined local variable
I ended up using the WITH clause to select my values and give it a name. Then I inserted those values and used WHERE NOT EXISTS to effectively skip any items that are already in my database.
So far it looks like it is working...
WITH withqueryname(first_id,second_id) AS (VALUES(1,2),(3,4),(5,6)...etc)
INSERT INTO jointablename (first_id,second_id)
SELECT * FROM withqueryname
WHERE NOT EXISTS(
SELECT first_id FROM jointablename WHERE
first_id = 1 AND
second_id IN (1,2,3,4,5,6..etc))
You can interchange the Values with a variable. Mine was VALUES#{values}
You can also interchange the second_id IN with a variable. Mine was second_id IN #{variable}.
Here's how I'd tackle it: Create a temp table and populate it with your new values. Then lock the old join values table to prevent concurrent modification (important) and insert all value pairs that appear in the new table but not the old one.
One way to do this is by doing a left outer join of the old values onto the new ones and filtering for rows where the old join table values are null. Another approach is to use an EXISTS subquery. The two are highly likely to result in the same query plan once the query optimiser is done with them anyway.
Example, untested (since you didn't provide an SQLFiddle or sample data) but should work:
BEGIN;
CREATE TEMPORARY TABLE newjoinvalues(
first_id integer,
second_id integer,
primary key(first_id,second_id)
);
-- Now populate `newjoinvalues` with multi-valued inserts or COPY
COPY newjoinvalues(first_id, second_id) FROM stdin;
LOCK TABLE myjoinvalues IN EXCLUSIVE MODE;
INSERT INTO myjoinvalues
SELECT n.first_id, n.second_id
FROM newjoinvalues n
LEFT OUTER JOIN myjoinvalues m ON (n.first_id = m.first_id AND n.second_id = m.second_id)
WHERE m.first_id IS NULL AND m.second_id IS NULL;
COMMIT;
This won't update existing values, but you can do that fairly easily too by using with a second query that does an UPDATE ... FROM while still holding the write table lock.
Note that the lock mode specified above will not block SELECTs, only writes like INSERT, UPDATE and DELETE, so queries can continue to be made to the table while the process is ongoing, you just can't update it.
If you can't accept that an alternative is to run the update in SERIALIZABLE isolation (only works properly for this purpose in Pg 9.1 and above). This will result in the query failing whenever a concurrent write occurs so you have to be prepared to retry it over and over and over again. For that reason it's likely to be better to just live with locking the table for a while.

ActiveRecord and illegal column names

I want to access the Limesurvey database via ActiveRecord. Some tables have column names like '79924X192X1240'. When I want to access the model, I get the following error:
ActionView::Template::Error (/usr/local/rvm/gems/ruby-1.9.2-p318/gems/activerecord-3.1.10/lib/active_record/attribute_methods/time_zone_conversion.rb:44: syntax error, unexpected tINTEGER
def 79924X192X1240=(original_time)
^):
I guess, the error appears, because it's not allowed, that a method starts with a digit. But I can't alter the column name, because Limesurvey generates these columns.
This error appears only if the data type of a column (like 79924X192X1240) is datetime. Other data types like varchar don't make any problems.
How can I access these datetime columns, too?
You need to do raw sql queries for that. ActiveRecord is written for ruby which does not allow method names to start with numbers. Since each column on the table is equivalent to a method, then trying to access that information will throw a syntax error every time.
You need to use: ActiveRecord::Base.connection.execute

Can't insert row in table with computed column?

I've got a Rails 3 applicaiton running on SQL Server against a legacy database with some computed columns. When I try to save a new record, I get an error "The column "ContractPendingDays" cannot be modified because it is either a computed column or is the result of a UNION operator
I'm not updating this column, it just seems to be one of the columns activerecord tries to write to when adding a new record into the db.
Is there some way to stop this behavior? I even tried changing schema rb but that didn't help. (and suboptimal anyway since I'd have to do it every time I change the db.)
Is there some way to mark a column as not updatable so activerecord doesn't try to write to it?
Found answer here:
Prevent NULL for active record INSERT?
(Use attributes.delete in a before_create filter)

ActiveRecord query group and Postgresql [duplicate]

I have a Rails 3 app which keeps high scores. I'm hosting it on Heroku which uses postgresql as the db.
I need to extract the top scores from the scores table. The table has columns score and user_id. It was working in mysql with the following:
Score.order('score DESC').group('user_id').limit(25)
This ranks each user's top score.
When I put the app on Heroku, I get the following psql error PGError: ERROR: column "scores.id" must appear in the GROUP BY clause or be used in an aggregate function
I've read around but haven't found a clear answer. What is the most optimal way to recreate the above query to work with PostgreSQL?
Thanks!
Tim
That means your select query is selecting the "id" column but not including it in the group by clause. I'm not familiar with rails but could it be selecting * or all columns?
Could you do
select id, max(score) group by id;

Resources