I am getting an error complaining about the OUT Parameter ID as an Undefined name when I try to set its value at the end of the procedure. Commenting it out the procedure executes fine.
What am I doing wrong?
CREATE PROCEDURE P3.CUST_CRT(IN NAME VARCHAR(15),
IN GENDER CHAR(1),
IN AGE INTEGER,
IN PIN INTEGER,
OUT ID INTEGER)
LANGUAGE SQL
P1: BEGIN
--Check if Customer NAME is NULL.
IF NAME IS NULL THEN
SIGNAL SQLSTATE VALUE '20010'
SET MESSAGE_TEXT = 'No NAME.';
END IF;
--Check if Customer NAME is an empty string.
IF NAME = '' THEN
SIGNAL SQLSTATE VALUE '20020'
SET MESSAGE_TEXT = 'NAME cannot be an empty string.';
END IF;
--Check if Customer GENDER falls in either of the two acceptable categories.
IF GENDER NOT IN ('M','F') THEN
SIGNAL SQLSTATE VALUE '20030'
SET MESSAGE_TEXT = 'GENDER can either be M or F';
END IF;
--Check if Customer AGE is not null.
IF AGE IS NULL THEN
SIGNAL SQLSTATE VALUE '20040'
SET MESSAGE_TEXT = 'AGE cannot be NULL';
END IF;
--Check that AGE is not negative.
IF AGE < 0 THEN
SIGNAL SQLSTATE VALUE '20060'
SET MESSAGE_TEXT = 'AGE cannot be negative.';
END IF;
--Check that the Customer is an adult.
IF AGE < 18 THEN
SIGNAL SQLSTATE VALUE '20070'
SET MESSAGE_TEXT = 'You have to be over 18 years to have an account.';
END IF;
--Check that PIN is not null.
IF PIN IS NULL THEN
SIGNAL SQLSTATE VALUE '20080'
SET MESSAGE_TEXT = 'PIN cannot be empty.';
END IF;
--Pin cannot be less than zero.
IF PIN < 0 THEN
SIGNAL SQLSTATE VALUE '20090'
SET MESSAGE_TEXT = 'PIN cannot be less than 0.';
END IF;
INSERT INTO P3.CUSTOMER(Name, Gender, Age, Pin) VALUES(NAME, GENDER, AGE, P3.ENCRYPT(PIN));
SET ID = ID.CURRVAL;
END P1 #
Always specify your Db2 Server version and operating-system when asking for help. Never write "getting an error" unless you specify the exact error message and error code. You code presumes that the sequence object you named "ID" is in the schema, and you should not allow an output parameter name to be the same as a sequence name. Give a sequence object a different name from the output parameter
For "Code: -204, SQL State: 42704] "DB2ADMIN.ID" is an undefined name.. SQLCODE=-204, SQLSTATE=42704, DRIVER=4.22.29", Db2 is telling you that the output parameter (named ID) has the same name as the sequence object, and that Db2 cannot find the sequence object in the schema called DB2ADMIN - that is the schema that you are connecting to the database to do the compile.
So, either qualify your sequence object name (i.e. put the schema name before it for example P3.ID (if that is your sequence fully qualified name), or give the correct full name for your sequence object. The sequence object needs to exist in the specified or implied schema before your code will compile.
It's unclear from your code where you consume the next value of the sequence (you only use its current value), but that is a separate matter from the -204 - in other words you may have other errors.
If you seek to return the most recently consumed sequence value, then one way (there are other ways, including more elegant ways) to do it is like this (in this example the sequence object is pre-created as P3.THEID ):
INSERT INTO P3.CUSTOMER(ID, Name, Gender, Age, Pin) VALUES( NEXT VALUE FOR P3.THEID , NAME, GENDER, AGE, P3.ENCRYPT(PIN));
SET ID = P3.THEID.CURRVAL;
Your ID variable is defined as INTEGER while in order to use the CURRVAL function it has to be defined as a SEQUENCE object.
Related
I need to create a method that dynamically filters a model by a column. It needs to receive the column that I want to filter by (called attr_name), an operator as a string, and the value as a string.
I need to first cast the string value to the database column type so I can then do the sql query.
scope :filtered_by_attribute, (lambda do |attr_name, operator, value|
comparing_value = Customer.attribute_types[attr_name].cast(value)
casting_error = !value.nil? && comparing_value.nil?
raise I18n.t('api.errors.unaplicable_customer_scope') if casting_error
sql_query = sanitize_sql("#{attr_name} #{operator} ?")
where(sql_query, comparing_value)
end)
The problem above is when it comes to enums. Enums are integers on the db but when I perform the casting it will return the same string value since for rails it is a string. Then, in the where query, it explodes since in the database it's comparing an integer column with a string.
Do you know how I cast a string value to match the type of a column in the database?
Thank you!
The cast method casts a value from user input when it is assigned to an instance. In the case of enum when you assign a string value it remains a string value. It is converted to an integer only when it is persisted in DB.
class Order < ActiveRecord::Base
enum status: {confirmed: 1, cancelled: 2}
end
# this is where the `cast` method is called
#order.status = "cancelled"
# still a string since the `cast` method didn't do anything.
#order.status # => "cancelled"
What you really need is the serialize method. It casts a value from a ruby type to a type that the database knows how to understand.
Order.attribute_types["status"].serialize("cancelled") # => 2
I have a model in Rails which accepts an integer value. Sometimes it's submitted as a string with leading zeroes and I'd the option to output it as a string later with the leading zeroes.
I could store as a string in the db instead but I was wondering if there was another way. Simplified code below.
s = Speed.create(value: "03", measurement: "KPH")
expect(s.value).to eq(3)
expect(s.to_s).to eq("03KPH") # FAILS, returns "3KPH"
expect(s.padding).to eq(1) # FAILS, returns 0
I have a padding field in the database in which to store the number of leading zeroes.
If you want to keep original information about the string, then store it as a string rather than an integer. You can then add a method to translate it to an integer if needed, or just call "to_i" on it when you use it and want to use it as a number.
You could change your test to
s = Speed.create(value: "03", measurement: "KPH")
expect(s.value.to_i).to eq(3)
expect(s.to_s).to eq("03KPH")
expect(s.padding).to eq(1)
You can manually set/get your active record attributes by doing something like below,
class Speed < ActiveRecord::Base
def value=(i)
self.value = i
end
def value
value.to_i
end
end
I'm trying to pass a value calculated in my controller to a model method and use it in a SELECT query.
I calc a value and want to pass it as an argument to the method below as 'control' and use it in place of the .02 in the sum calculations. The query below works as is, but if I add a third argument, call it 'control' and try to use it in the sum calc it fails. I tried it as I did with the bom and eom values by sticking {:control => control} on to the SELECT statement after the quote and before the parenthesis. The error output shows me that that :control is equal to the value passed in, but it errors on the rest of the instances of :control in the SELECT statement. The DB is PostgreSQL.
def self.rolling_total_month(bom, eom)
select("SUM((usages.usg_amount * materials.mat_voc_with)/2000) AS uc_emit_tons,
SUM(((.02) * usages.usg_amount * materials.mat_voc_with)/2000) AS c_emit_tons,
SUM((.02) * (usages.usg_amount * materials.mat_pounds_per_gal)/2000) AS hap_tons
").
joins("JOIN materials ON usages.material_id = materials.id").
where("usages.material_id <> 65 AND materials.mat_active_flag = TRUE AND materials.mat_report_flag = TRUE AND :bom <= usages.usg_date AND usages.usg_date <= :eom", { :bom => bom, :eom => eom })
end
Every time, I submit a form supposed to create a Deal and sending a very high nb of Prizes (>200K) to the Prize table using a transaction and raw postgresql, I have first the error 'undefined method exec_prepared' then if I reload the form then I get a new error 'ERROR: prepared statement 'xixie' already exists'.
I used this question wrong number of arguments (1 for 2..3) for Active Record postgresql query (Rails 4/postgresql 9.4) and Prepared Statement on Postgresql in Rails to create the following Postgresql query:
models deals.rb
CONNEXION = ActiveRecord::Base.connection.raw_connection
def create_prizes
Deal.transaction do
self.prize_number.times do |i|
st = CONNEXION.prepare('xixie', 'INSERT INTO prizes (deal_id) values ($1)')
values = [ { value: self.id} ]
st.exec_prepared('xixie', values )
st.close()
end
end
end
I have this problem in Local (not production) and I am not using any puma/unicorn. I do use Zeus and Guard.
Is it impossible with Rails4/postgresql prepared_statements to insert multiple rows at a time ?
How can I change the query to make it work ?
Also as Rails gives me ' ERROR: prepared statement 'xixie' already exists', I had to change multiple times the name of the prepared_statements but will they "live" forever? how can I "kill" them after I do all theses iterations trying to find the appropriate query.
EDIT
Updated the code after some proposed answer:
CONNECTION = ActiveRecord::Base.connection.raw_connection
def create_prizes
Deal.transaction do
self.prize_number.times do |i|
CONNECTION.prepare('mimiku', 'INSERT INTO deal_prizes (deal_id, created_at, updated_at) values ($1, $2, $3)')
CONNECTION.exec_prepared('mimiku', [ { value: self.id}, { value: '2009-01-23 20:21:13' }, { value: '2009-01-23 20:21:13' } ] )
end
# CONNECTION.close()
end
end
(added '2009-01-23 20:21:13' as Rails required created_at and updated_at for some reason).
I get this error:
ERROR: prepared statement "mimiku" already exists
Even if I change the name from 'mimiku' to 'somethingelse', I still get this type of error.
The prepare method returns a result according to the docs:
http://deveiate.org/code/pg/PG/Connection.html#method-i-prepare
Maybe try call exec_prepared on the connection object
connection = ActiveRecord::Base.connection.raw_connection
def create_prizes
begin
connection.describe_prepared('xixie')
rescue PG::InvalidSqlStatementName
connection.prepare('xixie', 'INSERT INTO prizes (deal_id) values ($1)')
end
Deal.transaction do
self.prize_number.times do |i|
connection.exec_prepared('xixie', [ { value: self.id} ] )
end
end
end
UPDATE: I reworked the code above to first check if a prepared statement exists. If it doesn't exist it creates it. Sorry I haven't realized it in the first place but you don't need to prepare a statement more than once. This is the actual benefit of such a statement, since it has to be only parsed once and can than be executed with different values, which is then much faster than a regular query.
As prepared statements last for the duration of the AR connection you only need to prepare it once.
For instance, I have a table of text_fields like this:
Entry 1 | Value (with flag=TRUE) | Value(with flag=FALSE)
Entry 2 ...
.
.
.
I need to be able to assign the "Value" whether it is in the left or right hand column (and set the corresponding flag).
Then on that same row if one column has an entry, then the other column should be grayed out (otherwise it would overwrite the other one).
I'd do this with extra, non-DB attributes on the model. Something like this:
class MyModel < ActiveRecord
attr_accessor :val1, :val2
def val1=(value)
self.real_value = value # Make sure your real database column is updated
self.the_flag = true
end
def val1
the_flag ? real_value : nil # Return real database column when asked
end
def val2=(value)
self.real_value = value # Make sure your real database column is updated
self.the_flag = false
end
def val2
the_flag ? nil : real_value # Return real database column when asked
end
Then in your view, you hook up to val1 and val2 instead of the real column and use your flag to determine what's grayed out.