On Jul 16, 12:51 pm, Jeremy Evans <[EMAIL PROTECTED]> wrote:
> I've done some research on prepared statements, stored procedures, and
> bind variables, and I'm currently considering the following API.

I've given some more thought and have some ideas that I think would
work better and be more consistent across databases.  It relies on
keeping a copy of the dataset that creates the prepared statement and
overriding its #literal method, and creating a mapping object that
will transform bound variables (given as an array and/or hash).  This
makes sure that the default case (using interpolation) will not
require Sequel to parse an SQL string (which is one of Sequel's
cardinal design ideas).  I haven't worked on any code yet, but I'm
pretty sure it will work.

> 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.

Scratch that.  With my new idea, if you use :$2 before :$1, :$2 will
always refer to the second bound argument.  And named bound arguments
(e.g. :$name) will be supported on all databases.  For the databases
that don't natively support them (all but SQLite), a mapping object
will transform the named argument hash into an array.  The databases
that don't supported numbered arguments (all but SQLite and
PostgreSQL, I believe), will use an array with repeated elements if
the same numbered/named argument is used more than once in the query.
There will not be support for unnamed/unnumbered arguments (such as :
$).

>   # 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)

With the new idea, all databases will support this:

  ds = DB[:items].filter(:number=>:$1).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')

This won't change much, except that it will allow you to use hashes
instead of arrays of pairs for the argument to call_insert/
call_update, as well as accepting a hash for the named bound
arguments:

ds.call_insert({:number=>:$1, :name=>:$name}, 1, :name=>'Jim')

> To create a prepared statement that you can call later (by default,
> this will just store the SQL for later use):

Instead of storing the SQL string, we'll store the dataset, but the
results are basically similar.

>   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.

After thinking some more about it, it would probably be better to use
a different method for stored procedures anyway.  I'm thinking of
Database#call_procedure.

Comments/questions are appreciated.

Thanks,
Jeremy
--~--~---------~--~----~------------~-------~--~----~
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