I am trying to use the prepared statements in ruby with pg gem. This is how my statement looks like
conn.prepare("insert_values", "insert into " + objectName + "(" + headerStr + ") values (" + prep_values + ")")
conn.exec_prepared("insert_values", arr)
I keep getting the error
Prepared Statement insert_values already exists.
How Do i Fix this?? Thanks
Try to run:
conn.exec("DEALLOCATE name_of_prepared_statement")
In your example:
conn.exec("DEALLOCATE insert_values")
Simple test and it is working in my irb:
1.8.7 :001 > require 'rubygems'
=> true
1.8.7 :002 > require 'pg'
=> true
1.8.7 :003 > conn = PGconn.connect(:host => 'localhost', :port => 5912, :user => 'test', :dbname => 'test' )
=> #<PGconn:0x7fe6ac703970>
1.8.7 :005 > conn.prepare("insert_values", "select * from data where id < $1")
=> #<PGresult:0x7fe6ac6b2e58>
1.8.7 :006 > conn.prepare("insert_values", "select * from data where id < $1 and id > $2")
PGError: ERROR: prepared statement "insert_values" already exists
from (irb):6:in 'prepare'
from (irb):6
1.8.7 :007 > conn.prepare("insert_values", "select * from data where id < $1")
PGError: ERROR: prepared statement "insert_values" already exists
from (irb):7:in 'prepare'
from (irb):7
1.8.7 :008 > conn.exec("DEALLOCATE insert_values")
=> #<PGresult:0x7fe6ac6738c0>
1.8.7 :009 > conn.prepare("insert_values", "select * from data where id < $1")
=> #<PGresult:0x7fe6ac665fe0>
1.8.7 :010 > conn.exec_prepared("insert_values",[200])
=> #<PGresult:0x7fe6ac65d188>
1.8.7 :011 > conn.exec("DEALLOCATE insert_values")
=> #<PGresult:0x7fe6ac654df8>
1.8.7 :012 > conn.exec_prepared("insert_values",[200])
PGError: ERROR: prepared statement "insert_values" does not exist
from (irb):12:in 'exec_prepared'
from (irb):12
This is a better way reusing prepare(), avoid prepared statement "my_statement" already exists:
sql = "select * from my_table where id = $1"
begin
ActiveRecord::Base.connection.raw_connection.prepare('my_statement', sql)
rescue PG::DuplicatePstatement => e
end
pg_result = ActiveRecord::Base.connection.raw_connection.exec_prepared('my_statement', [my_table_id])
Related
What is the proper way to build a string dynamically using items from an array. Currently I am building it using this;
#ar.each do |b|
#str = #str + "select '#{b[0]}' as SourceGUID, '#{b[1]}' as SourceFileName, '#{b[2]}' as FullPath, #{b[3]} as SizeInMB, #{b[4]} as ItemCount, #{b[5]} as ParentCount, #{b[6]} as ChildCount, '#{b[7]}' as CaseName, #{b[8]} as ProcessedSizeMB\n union all\n"
end
This is obviously a horrible way to do it. Is there a way to achieve it using .join?
A draft
2.3.1 :028 > row = %w(my_id my_name my_age)
=> ["my_id", "my_name", "my_age"]
2.3.1 :029 > aliases = %w(id name age)
=> ["id", "name", "age"]
2.3.1 :030 > fields = row.zip aliases
=> [["my_id", "id"], ["my_name", "name"], ["my_age", "age"]]
2.3.1 :031 > fields.map do |field|
2.3.1 :032 > field[0] = "'#{field[0]}'"
2.3.1 :033?> field.join(" AS ")
2.3.1 :034?> end.join(", ")
=> "'my_id' AS id, 'my_name' AS name, 'my_age' AS age"
I have a problem when I'm trying to import data from a file into a database for a test. The following code does exactly as I expect, in the Ruby shell:
jruby-1.7.4 :001 > require('sequel')
=> true
jruby-1.7.4 :002 > require('csv')
=> true
jruby-1.7.4 :003 > file = CSV.read('./spec/test_data/registration_sessions.csv')
=> [["01", "EG000000001"], ["02", "EG000000002"], ["03", "EG000000002"]]
jruby-1.7.4 :004 > db = Sequel.connect('jdbc:sqlite::memory:')
=> #<Sequel::JDBC::Database: "jdbc:sqlite::memory:">
jruby-1.7.4 :005 > db.create_table(:registration_sessions) do
jruby-1.7.4 :006 > Integer :id
jruby-1.7.4 :007?> String :device_serial
jruby-1.7.4 :008?> end
=> nil
jruby-1.7.4 :009 > db[:registration_sessions]
=> #<Sequel::JDBC::Dataset: "SELECT * FROM `registration_sessions`">
jruby-1.7.4 :010 > db[:registration_sessions].import(db[:registration_sessions].columns, file)
=> ["INSERT INTO `registration_sessions` (`id`, `device_serial`) VALUES ('01', 'EG000000001')", "INSERT INTO `registration_sessions` (`id`, `device_serial`) VALUES ('02', 'EG000000002')", "INSERT INTO `registration_sessions` (`id`, `device_serial`) VALUES ('03', 'EG000000002')"]
jruby-1.7.4 :012 > db[:registration_sessions].all
=> [{:id=>1, :device_serial=>"EG000000001"}, {:id=>2, :device_serial=>"EG000000002"}, {:id=>3, :device_serial=>"EG000000002"}]
But in my test, things don't go as expected:
let(:registration_metrics_db) { create_registration_metrics_db }
let(:session_seed_data) { CSV.read('./spec/test_data/registration_sessions.csv') }
before(:each) do
seed_test_data(registration_metrics_db[:registration_sessions], session_seed_data)
end
def create_registration_metrics_db
db = Sequel.connect('jdbc:sqlite::memory:')
db.create_table(:registration_sessions) do
Integer :id
String :device_serial
end
db
end
def seed_test_data(table, data)
table.import(table.columns, data)
end
Which leads to the following, when I run my tests:
14) RegistrationMetricsQueries #get_registration_metrics should return only durations for sessions that contain the start and end moment
Failure/Error: seed_test_data(registration_metrics_db[:registration_sessions], session_seed_data)
Sequel::Error:
Different number of values and columns given to insert_sql
# ./spec/services/tablet_usage_queries_spec.rb:88:in `seed_test_data'
# ./spec/services/registration_metrics_queries_spec.rb:10:in `(root)'
15) RegistrationMetricsQueries #get_registration_metrics should return durations in seconds for valid registration sessions, which contain the start and end moment
Failure/Error: seed_test_data(registration_metrics_db[:registration_sessions], session_seed_data)
Sequel::Error:
Different number of values and columns given to insert_sql
# ./spec/services/tablet_usage_queries_spec.rb:88:in `seed_test_data'
# ./spec/services/registration_metrics_queries_spec.rb:10:in `(root)'
Am I doing it wrong?
edit: oh, and my sample data
01,EG000000001
02,EG000000002
03,EG000000002
It turns out that the issue is a result of a failure to insert. There's a table join that happens under the hood and it's causing Sequel to lock up because sqlite doesn't support right joins. I'm migrating my tests to use an actual mysql database, not an in-memory sqlite db.
I am trying to use the Ruby Sequel gem for DB operations.
I am stuck for incrementing and decrementing values.
The doc says that this should work, even though it seems very strange for me to be able to add a number and a symbol.
2.0.0-p247 :019 > require 'sequel'
=> true
2.0.0-p247 :020 > s = Sequel.connect('sqlite://db.sqlite')
=> #<Sequel::SQLite::Database: "sqlite://db.sqlite">
2.0.0-p247 :021 > s[:query_volume].update_sql(:queries => 3)
=> "UPDATE `query_volume` SET `queries` = 3"
2.0.0-p247 :022 > s[:query_volume].update_sql(:queries => :queries + 3)
NoMethodError: undefined method `+' for :queries:Symbol
from (irb):21
from /Users/avandra/.rvm/rubies/ruby-2.0.0-p247/bin/irb:16:in `<main>'
But as you can see it gives undefined method on the queries symbol. Which is kindof concurs with why it was strange for me.
I tried using curly braces, but that gives another error:
2.0.0-p247 :023 > s[:query_volume].update_sql{:queries => :queries + 3}
SyntaxError: (irb):23: syntax error, unexpected =>, expecting '}'
s[:query_volume].update_sql{:queries => :queries + 3}
^
from /Users/avandra/.rvm/rubies/ruby-2.0.0-p247/bin/irb:16:in `<main>'
And using
2.0.0-p247 :033 > s[:query_volume].update_sql{queries = queries + 3}
=> "UPDATE `query_volume` SET "
just gives a badly formatted SQL...
Could anyone shed some light on how this can be done?
You should use Sequel.expr for that:
s[:query_volume].update_sql(:queries => Sequel.expr(3) + :queries)
Have anyone noticed something like this? Why is it happening?
ruby-1.9.2-p290 :006 > User.count
(0.4ms) SELECT COUNT(*) FROM "users"
=> 102
ruby-1.9.2-p290 :007 > User.count + 1
(0.4ms) SELECT COUNT(*) FROM "users"
=> 103
ruby-1.9.2-p290 :008 > User.count+ 1
(0.4ms) SELECT COUNT(*) FROM "users"
=> 103
ruby-1.9.2-p290 :009 > User.count+1
(0.4ms) SELECT COUNT(*) FROM "users"
=> 103
ruby-1.9.2-p290 :010 > User.count +1
(0.5ms) SELECT COUNT(1) FROM "users"
=> 102
Using ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-linux] && Rails 3.1.3
Extra stuff:
ruby-1.9.2-p290 :007 > "ayay".length + 1
=> 5
ruby-1.9.2-p290 :008 > "ayay".length +1
ArgumentError: wrong number of arguments(1 for 0)
from (irb):8:in `length'
...
This depends on the fact that in Ruby white spaces can be significant. You see different results because Ruby interprets your example in different ways. So
The first:
"ayay".length + 1
is like
"ayay".length.+(1)
And the second:
"ayay".length +1
is like
"ayay".length(+1)
In this way you can see why Ruby gives an error in the second case.
Regarding the count problem: Ruby interprets the code as:
User.count(+1)
And, as you can see from the generated SQL, there is a difference because +1 is considered the column_name parameter.
Can someone explain what's happening here?
It seems that the placeholding syntax in SQL statement string doesn't work as expected (or, to say it in a different way, it violates the principle of least surprise), and during runtime an unexpected substitution/escaping is done for var2:
ruby-1.9.2-p290 :001 > puts RUBY_VERSION
1.9.2
=> nil
ruby-1.9.2-p290 :002 > require 'ipaddr'
=> true
ruby-1.9.2-p290 :003 > require 'sqlite3'
=> true
ruby-1.9.2-p290 :004 > var1 = Addrinfo.ip("1.2.3.4")
=> #<Addrinfo: 1.2.3.4>
ruby-1.9.2-p290 :005 > var2 = var1.ip_address
=> "1.2.3.4"
ruby-1.9.2-p290 :006 > var3 = "1.2.3.4"
=> "1.2.3.4"
ruby-1.9.2-p290 :007 > var2 == var3
=> true
ruby-1.9.2-p290 :008 > var2 === var3
=> true
ruby-1.9.2-p290 :009 > var2.eql?(var3)
=> true
ruby-1.9.2-p290 :010 > db = SQLite3::Database.open( "test.db" )
=> #<SQLite3::Database:0x00000100bcfce0>
ruby-1.9.2-p290 :011 > db.execute( "SELECT * FROM devices WHERE deviceaddr=?", var2 )
=> []
ruby-1.9.2-p290 :011 > db.execute( "SELECT * FROM devices WHERE deviceaddr=?", var2.to_s )
=> []
ruby-1.9.2-p290 :012 > db.execute( "SELECT * FROM devices WHERE deviceaddr=?", var3 )
=> [["TEST_DEVICE", "1.2.3.4"]]
Without the SQL placeholder it works (but exposes the db to SQL injections!):
ruby-1.9.2-p290 :013 > db.execute( "SELECT * FROM devices WHERE deviceaddr='#{var2}'" )
=> [["TEST_DEVICE", "1.2.3.4"]]
So what is a safe way to make this work?
TL;DR: SQLite uses UTF; convert Addrinfo's 8-bit ASCII output.
One "safe" way is to use force_encoding("UTF-8") on the output from Addrinfo, so:
> var1.ip_address.encoding
=> #<Encoding:ASCII-8BIT>
> var3.encoding
=> #<Encoding:UTF-8>
> db.execute("SELECT * FROM foo WHERE ip=?", var2.force_encoding("UTF-8"))
=> [["1.2.3.4"]]