Get inserted ID from Sequel prepare - ruby

I have a prepared insert statement in Sequel (using Oracle).
prepared_statement = DB[:table_name].prepare(:insert, :name, :value=>:$value)
When I call it the row gets added just fine.
prepared_statement.call :value=>'Some value'
I have a trigger and a sequence set up so the ID will be auto generated. I would like to get back the row (or the id) I just added, but I can't see how. I can't use insert because value is a CLOB and may be greater than 4000 characters.

In JRuby, using the JDBC adapter you can override the insert and pass in the returning clause. The tricky part is that you don't always know what the primary key is at this level so you may have to use ROWID or request all of the columns back.
You end up with something that looks similar to this:
module Sequel
module JDBC
class Database
def execute_insert_with_returning(conn, sql, opts = {})
columns = opts[:key_columns] || ["ROWID"]
q = "{ call #{sql} returning #{columns.join(',')} into #{columns.collect {|_| '?'}.join(',')} }"
stmt = conn.prepare_call(q)
raise "Unable to prepare call for insert" if stmt.nil?
begin
columns.each_with_index do |_, index|
stmt.registerOutParameter(index+1, JavaSQL::Types::VARCHAR)
end
return nil if 0 == stmt.executeQuery
values = (1..columns.count).inject({}) do |memo, index|
key = columns[index-1].downcase.to_sym rescue nil
memo[key] = stmt.get_string(index) unless key.nil?
memo
end
values
ensure
stmt.close
end
end # #execute_insert_with_returning
alias execute_without_specialized_insert execute
def execute(sql, opts={}, &block)
if opts[:type] == :insert
synchronize(opts[:server]) do |conn|
execute_insert_with_returning conn, sql, opts
end
else
execute_without_specialized_insert sql, opts, &block
end
end # #execute
end # Database
end # JDBC
end # Sequel
I've done something pretty much like this and it works pretty good. I think we had to override the Sequel::Model as well so it passes the primary key in as opts[:key_columns] but I may be remembering incorrectly.
This is a bit of a load bearing kludge that gets the job done. It would be more elegant to specialize it to the Oracle JDBC adapter and to ensure that all of the error handling code is present from the original execute statement. Given the time I'd love to get something better and give it back to the Sequel project.

The way to get the populated sequence values is through the RETURNING clause of the INSERT
statement, as I discuss in this response to a similar question regarding CodeIgniter.
I'm not sure whether the base version of RoR supports that syntax, but it appears to be possible to extend ActiveRecord to handle it. Find out more.

Sequel's Oracle adapter doesn't have native prepared statement support, so it falls back to issuing a regular query. If you can use JRuby, the jdbc adapter has native prepared statement support, so it should just work there. If you can't use JRuby, you'll have to work on adding native prepared statement support to the Oracle adapter. I don't have access to an Oracle installation, so I can't test any support, but I'll be happy to provide advice if you run into problems.

Related

Ruby Mongo DB multiple records of same value

I'm new to MongoDB and databases in general. I'm using Ruby and I would like to query against a specific UUID in the database.
The ID is stored as _id and the value is '101b437a-be16-44f6-b0b0-0201cdee6510'
I have the following that usually queries my database:
field = '_id:'
value = 101b437a-be16-44f6-b0b0-0201cdee6510
def query_field(field,value)
query = {#{field}: value}
#result = #mongo_interface.get(query)
expect(#result.count).to be >= 1
puts "Number of matched values: #{#result.count}"
end
def get(param_hash, collection_name = nil)
col_name = (collection_name.nil? || collection_name.empty?) ? #collection : collection_name
#docs = #db[col_name].find(param_hash)
end
When I look within the _id field, I'm assuming it's stored as some sort of binary key and thus isn't found using my search.
Is there some conversion I could/should do to make the query above work?
Thank you.
Using an ODM like Mongoid will ease your pain. Add it to your Gemfile:
gem 'mongoid'
and run bundle install. Make sure you skimmed through the installation guide to add all the necessary configs.
Then include the following line to your model/class, say:
class Product
include Mongoid::Document
...
end
You'll be able to query the records like Product.find(id) right after.

Threading sqlite connections in Ruby

I've been trying to get my ruby script threaded since yesterday. I've since opted for SQLite to save data, with the parallel gem to manage concurrency.
I've built a quick script for testing, but I'm having trouble getting the threading working; the database is locked. I've added db.close to the end, which doesn't help, and I've tried adding sleep until db.closed?, but that just sleeps indefinitely. What am I doing wrong?
The error is "database is locked (SQLite3::BusyException)".
Here's my code:
require 'sqlite3'
require 'pry'
require 'parallel'
STDOUT.sync = true
db = SQLite3::Database.new "test.db"
arr = [1,2,3,4,5,6,7,8,9,10]
rows = db.execute <<-SQL
create table test_table (
original string,
conversion string
);
SQL
def test(num)
db = SQLite3::Database.new "test.db"
puts "the num: #{num}"
sleep 4
{ num => num + 10}.each do |pair|
db.execute "insert into test_table values (?, ?)", pair
end
db.close
end
Parallel.each( -> { arr.pop || Parallel::Stop}, in_processes: 3) { |number| test(number) }
SQLite is threadsafe by default (using its "serialized" mode) and the ruby wrapper apparently supports this to whatever extent it needs to. However, it's not safe across processes, which makes a certain sense since the adapter or engine probably has to negotiate some state in the process to prevent locks.
To fix your example change in_processes to in_threads

Ruby activerecord result to array

My aim is to take the result of my activerecord search and print it into a nice array but the print part is where I am having trouble.
I first build my oracle connection with the following which works in isolation.
def oracle_connection(adapter, database, username, password)
begin
ActiveRecord::Base.establish_connection(
adapter: adapter,
database: database,
username: username,
password: password)
end
end
I then create my query with the following function:
def query
"select * from owner.appn where appn_id = #{$id}"
end
And here is the part where I am asking the question on. I want to pass the result of the query being returned out into an 2D array. Below is what I currently have to execute the active connection query.
def oracle_query_into_array(query)
result_set = ActiveRecord::Base.connection.execute(query)
if result_set.present?
#add logic here
else
return nil
end
end
Thanks
I'm assuming you have reasons to use the underlying connection calls rather than the abstractions that are common practise.
With the ActiveRecord::Base.connection.execute(query) I would expect this to return true if it executes. What you want is a cursor on the data, so try this:
result = ActiveRecord::Base.connection.exec_query(query)
puts result.to_a
=> [array of results]
A usual abstraction (ActiveRecord::Base) would take the form of creating a model to represent your data, so in your case, this could look like:
class Appn < ActiveRecord::Base
end
This will be automatically mapped to a table within your connection called Appnn allowing you to update the above code to:
results = Appn.where(appn_id: $id)
puts results.to_a
=> [array of results]

What is the difference between `PG.connect` and `PG::Connection.open` in the Ruby 'pg' gem?

From the pg module doc, it seems like the right way to connect to a PG db is to use:
conn = PG::Connection.open(dbname: 'test')
However, I find other examples online which make use of the PG.connect method instead:
conn = PG.connect(dbname: 'testdb', user: 'janbodnar', password: 'pswd37')
Is there a difference between these two ways of connecting to a postgresql database? If yes, what is it? Is one way better than the other? What are the drawbacks / advantages of each method?
From the documentation for the PG module itself, you can see that PG.connect is a "convenience alias" for PG::Connection.new:
def self::connect( *args )
return PG::Connection.new( *args )
end
From the source code of PG::Connection, it's also clear that PG::Connection.open is an alias of PG::Connection.new:
void
init_pg_connection()
{
…
SINGLETON_ALIAS(rb_cPGconn, "open", "new");
…
}
So, all three are actually identical in terms of how they connect to the database. PG.connect adds the cost of one extra method call, since it internally calls PG::Connection.new.

postgres avoiding extra quotes inside a string

I have following select query which I will be passing to the database to get results back,
sql = "select * from movies where title = #{movie_title};"
movie_title contains a value that can sometimes contain single quotes and other chars that need escaping. I have come across dollar quoted string which is working well when used inside a INSERT statement but SELECT is not behaving the same, if I use $$#{movie_title}$$ like this it just doesn't get converted to a value inside movie_title. Is there any solution for this?
I am using postgres 9.5.0 and I am programming using ruby.
Bad idea. Don't do that, as you are making your code vulnerable to SQL injection attacks, and also making your life harder. Read more about prepared SQL statements, SQL injection etc.
In short, unless you are using some ORM, you should do something like:
#!/usr/bin/ruby
require 'pg'
if ARGV.length != 1 then
puts "Usage: prepared_statement.rb rowId"
exit
end
rowId = ARGV[0]
begin
con = PG.connect :dbname => 'testdb', :user => 'janbodnar'
con.prepare 'stm1', "SELECT * FROM Cars WHERE Id=$1"
rs = con.exec_prepared 'stm1', [rowId]
puts rs.values
rescue PG::Error => e
puts e.message
ensure
rs.clear if rs
con.close if con
end
(an example taken from http://zetcode.com/db/postgresqlruby/)
Edit: You don't need to use prepared statements, you can also use your DB lib's methods which provide proper parameter binding:
require 'pg'
conn = PG::Connection.open(:dbname => 'test')
res = conn.exec_params('SELECT $1 AS a, $2 AS b, $3 AS c', [1, 2, nil])
Take a look at docs for PG#exec_params

Resources