On Wed, Jul 16, 2008 at 7:51 PM, Jeremy Evans <[EMAIL PROTECTED]> wrote:
>
> On Jul 16, 8:13 am, Jeremy Evans <[EMAIL PROTECTED]> wrote:
>> Before I get started on this work, I need to read up on how different
>> databases handle things so I don't tie the design to any one database.
>
> I've done some research on prepared statements, stored procedures, and
> bind variables, and I'm currently considering the following API.
>
> To set bind variables, use :$1, :$2, etc. for cross database support.
> By default, this will use ? placeholders, since that is most
> common, which means the if :$2 comes up before $:1, you'll have
> issues.
>
>  # All databases
>  ds = DB[:items].filter(:number=>:$1).filter(:name=>:$2)
>  # Databases without number arguments (MySQL, JDBC, Oracle, MSSQL)
>  DB[:items].filter(:number=>:$).filter(:name=>:$)
>  # SQLite named arguments
>  ds = DB[:items].filter(:number=>:$number).filter(:name=>:$name)
>
> To use the bind variables without actually preparing a statement
> (by default, this will interpolate, so it works on all databases):
>
>  ds.call_select(1, 'Jim')
>  ds.call_insert([[:number, :$1], [:name, :$2]], 1, 'Jim')
>  ds.call_update([[:number, :number + :$3], \
>    [:name,:name.sql_string + :$4]], 1, 'Jim', 2, 'bo')
>  ds.call_delete(1, 'Jim')
>

I know stuck records are annoying :)
Please consider using call_* and prepare_*, where * is one of: create,
read, update, delete.

Cheers

> To create a prepared statement that you can call later (by default,
> this will just store the SQL for later use):
>
>  ps = ds.prepare_select(:select_number_name)
>  ds.prepare_insert(:insert_number_name, [[:number, :$1], \
>    [:name, :$2]])
>  ds.prepare_update(:update_number_name, [[:number, :number + :$3], \
>    [:name,:name.sql_string + :$4]])
>  ds.prepare_delete(:delete_number_name)
>
> To call a statement previously prepared (by default, will interpolate
> the stored SQL, so it works on all databases):
>
>  ps.call(1, 'Jim')
>  DB.call(:select_number_name, 1, 'Jim')
>  # SQLite named arguments
>  ps.call(:number=>1, :name=>'Jim')
>  DB.call(:select_number_name, :number=>1, :name=>'Jim')
>
> If the database supports stored procedures and no matching prepared
> statement with that name has been created, the DB.call method could
> be used to call a stored procedure.  There won't be default support
> for that, though.
>
> To drop a prepared statement:
>
>  ps.drop
>  DB.prepared(:select_number_name).drop
>
> Below is some database specific information related to implementing
> the above inferfaces with real database support.  I plan to support
> PostgreSQL, JDBC, SQLite, and MySQL, probably in that order.
> Hopefully the interface is flexible enough to accommodate other
> databases. I looked briefly at Oracle and MSSQL and it should work
> for them.
>
> As always, comments and ideas are appreciated.
>
> Thanks,
> Jeremy
>
> PostgreSQL:
>
> SQL Bind variables: $1, $2, ...
> Ruby Bind variables: :$1, :$2
> Interpolation: not required
> Prepared statements: supported
> Stored procedures: not supported
> ds.prepare_select(:select_number_name):
>  conn.prepare('select_number_name', 'SELECT * FROM items WHERE ((x =
> $1) AND (y = $2)')
> ds.call_select(1, 'Jim'):
>  conn.exec('SELECT * FROM items WHERE ((x = $1) AND (y = $2)', [1,
> 'Jim'])
> x.call(1, 'Jim'):
>  conn.exec_prepared('find_x_y', [1, 'Jim'])
>
> MySQL:
>
> SQL Bind variables: ?, ?, ...
> Ruby Bind variables: :$, :$, ... OR :$1, :$2, ... (but order
> matters!!)
> Interpolation: required
> Prepared statements: supported
> Stored procedures: supported
> ds.prepare_select(:select_number_name):
>  db.execute('PREPARE select_number_name FROM SELECT * FROM items
> WHERE ((x = ?) AND (y = ?)')
> ds.call_select(1, 'Jim'):
>  db.execute("PREPARE select_number_name FROM SELECT * FROM items
> WHERE ((x = 1) AND (y = 'Jim')")
> x.call(1, 'Jim'):
>  db.execute("EXECUTE select_number_name USING 1, 'Jim'")
> DB.call(:stored_procedure, 1, 'Jim'):
>  db.execute("CALL stored_procedure(1, 'Jim')")
>
> SQLite:
>
> SQL Bind variables: ?, ?, ... OR ?1, ?2, ... OR :a, :b, ...
> Ruby Bind variables: :$, :$, ... OR :$1, :$2, ... OR :$a, :$b
> Interpolation: not required
> Prepared statements: supported
> Stored procedures: not supported
> ds.prepare_select(:select_number_name):
>  stmt = db.prepare('SELECT * FROM items WHERE ((x = ?1) AND (y = ?
> 2)')
> ds.call_select(1, 'Jim'):
>  db.execute('SELECT * FROM items WHERE ((x = ?1) AND (y = ?2)', 1,
> 'Jim')
> x.call(1, 'Jim'):
>  stmt.execute(1, 'Jim')
>
> JDBC:
>
> SQL Bind variables: ?, ?, ...
> Ruby Bind variables: :$, :$, ... OR :$1, :$2, ... (but order
> matters!!)
> Interpolation: not required
> Prepared statements: supported
> Stored procedures: supported
> # For postgres, org.postgresql.PGStatement.setPrepareThreshold(2)
> ds.prepare_select(:select_number_name, :type=>:select):
>  pstmt = conn.prepareStatement('SELECT * FROM items WHERE ((x = ?)
> AND (y = ?)')
> ds.call_select(1, 'Jim', :type=>:select):
>  pstmt = ds.prepare_select(nil, :type=>:select)
>  pstmt.call(1, 'Jim')
>  pstmt.close
> x.call(1, 'Jim'):
>  pstmt.setInt(1, 1)
>  pstmt.setString(2, 'Jim')
>  case opts[:type]
>  when :select
>    cstmt.executeQuery
>  when :delete, :insert, :update
>    cstmt.executeUpdate
>  else
>    cstmt.execute
>  end
> DB.call(:stored_procedure, 1, 'Jim', :type=>:select):
>  cstmt = conn.prepareCall("{call stored_procedure(?, ?)}")
>  cstmt.setInt(1, 1)
>  cstmt.setString(2, 'Jim')
>  case opts[:type]
>  when :select
>    cstmt.executeQuery
>  when :delete, :insert, :update
>    cstmt.executeUpdate
>  else
>    cstmt.execute
>  end
> >
>

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sequel-talk" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sequel-talk?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to