sequel 3.10.0 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/CHANGELOG +68 -0
  2. data/COPYING +1 -1
  3. data/README.rdoc +87 -27
  4. data/bin/sequel +2 -4
  5. data/doc/association_basics.rdoc +1383 -0
  6. data/doc/dataset_basics.rdoc +106 -0
  7. data/doc/opening_databases.rdoc +45 -16
  8. data/doc/querying.rdoc +210 -0
  9. data/doc/release_notes/3.11.0.txt +254 -0
  10. data/doc/virtual_rows.rdoc +217 -31
  11. data/lib/sequel/adapters/ado.rb +28 -12
  12. data/lib/sequel/adapters/ado/mssql.rb +33 -1
  13. data/lib/sequel/adapters/amalgalite.rb +13 -8
  14. data/lib/sequel/adapters/db2.rb +1 -2
  15. data/lib/sequel/adapters/dbi.rb +7 -4
  16. data/lib/sequel/adapters/do.rb +14 -15
  17. data/lib/sequel/adapters/do/postgres.rb +4 -5
  18. data/lib/sequel/adapters/do/sqlite.rb +9 -0
  19. data/lib/sequel/adapters/firebird.rb +5 -10
  20. data/lib/sequel/adapters/informix.rb +2 -4
  21. data/lib/sequel/adapters/jdbc.rb +111 -49
  22. data/lib/sequel/adapters/jdbc/mssql.rb +1 -2
  23. data/lib/sequel/adapters/jdbc/mysql.rb +11 -0
  24. data/lib/sequel/adapters/jdbc/oracle.rb +4 -7
  25. data/lib/sequel/adapters/jdbc/postgresql.rb +8 -1
  26. data/lib/sequel/adapters/jdbc/sqlite.rb +12 -0
  27. data/lib/sequel/adapters/mysql.rb +14 -5
  28. data/lib/sequel/adapters/odbc.rb +2 -4
  29. data/lib/sequel/adapters/odbc/mssql.rb +2 -4
  30. data/lib/sequel/adapters/openbase.rb +1 -2
  31. data/lib/sequel/adapters/oracle.rb +4 -8
  32. data/lib/sequel/adapters/postgres.rb +4 -11
  33. data/lib/sequel/adapters/shared/mssql.rb +22 -9
  34. data/lib/sequel/adapters/shared/mysql.rb +33 -30
  35. data/lib/sequel/adapters/shared/oracle.rb +0 -5
  36. data/lib/sequel/adapters/shared/postgres.rb +13 -11
  37. data/lib/sequel/adapters/shared/sqlite.rb +56 -10
  38. data/lib/sequel/adapters/sqlite.rb +16 -9
  39. data/lib/sequel/connection_pool.rb +6 -1
  40. data/lib/sequel/connection_pool/single.rb +1 -0
  41. data/lib/sequel/core.rb +6 -1
  42. data/lib/sequel/database.rb +52 -23
  43. data/lib/sequel/database/schema_generator.rb +6 -0
  44. data/lib/sequel/database/schema_methods.rb +5 -5
  45. data/lib/sequel/database/schema_sql.rb +1 -1
  46. data/lib/sequel/dataset.rb +4 -190
  47. data/lib/sequel/dataset/actions.rb +323 -1
  48. data/lib/sequel/dataset/features.rb +18 -2
  49. data/lib/sequel/dataset/graph.rb +7 -0
  50. data/lib/sequel/dataset/misc.rb +119 -0
  51. data/lib/sequel/dataset/mutation.rb +64 -0
  52. data/lib/sequel/dataset/prepared_statements.rb +6 -0
  53. data/lib/sequel/dataset/query.rb +272 -6
  54. data/lib/sequel/dataset/sql.rb +186 -394
  55. data/lib/sequel/model.rb +4 -2
  56. data/lib/sequel/model/associations.rb +31 -14
  57. data/lib/sequel/model/base.rb +32 -13
  58. data/lib/sequel/model/exceptions.rb +8 -4
  59. data/lib/sequel/model/plugins.rb +3 -13
  60. data/lib/sequel/plugins/active_model.rb +26 -7
  61. data/lib/sequel/plugins/instance_filters.rb +98 -0
  62. data/lib/sequel/plugins/many_through_many.rb +1 -1
  63. data/lib/sequel/plugins/optimistic_locking.rb +25 -9
  64. data/lib/sequel/version.rb +1 -1
  65. data/spec/adapters/mssql_spec.rb +26 -0
  66. data/spec/adapters/mysql_spec.rb +33 -4
  67. data/spec/adapters/postgres_spec.rb +24 -1
  68. data/spec/adapters/spec_helper.rb +6 -0
  69. data/spec/adapters/sqlite_spec.rb +28 -0
  70. data/spec/core/connection_pool_spec.rb +17 -5
  71. data/spec/core/database_spec.rb +101 -1
  72. data/spec/core/dataset_spec.rb +42 -4
  73. data/spec/core/schema_spec.rb +13 -0
  74. data/spec/extensions/active_model_spec.rb +34 -11
  75. data/spec/extensions/caching_spec.rb +2 -0
  76. data/spec/extensions/instance_filters_spec.rb +55 -0
  77. data/spec/extensions/spec_helper.rb +2 -0
  78. data/spec/integration/dataset_test.rb +12 -1
  79. data/spec/integration/model_test.rb +12 -0
  80. data/spec/integration/plugin_test.rb +61 -1
  81. data/spec/integration/schema_test.rb +14 -3
  82. data/spec/model/base_spec.rb +27 -0
  83. data/spec/model/plugins_spec.rb +0 -22
  84. data/spec/model/record_spec.rb +32 -1
  85. data/spec/model/spec_helper.rb +2 -0
  86. metadata +14 -3
  87. data/lib/sequel/dataset/convenience.rb +0 -326
@@ -1,29 +1,227 @@
1
1
  = Virtual Row Blocks
2
2
 
3
- Dataset methods filter, order, and select all take blocks that either yield
4
- instances of Sequel::SQL::VirtualRow (if the block takes an argument), or
5
- are evaluated in the context of an instance of Sequel::SQL::VirtualRow. These are referred to as
3
+ Dataset methods filter, order, and select all take blocks that are referred to as
6
4
  virtual row blocks. Many other dataset methods pass the blocks
7
5
  they are given into one of those three methods, so there are actually
8
- many Sequel methods that take virtual row blocks.
6
+ many Sequel::Dataset methods that take virtual row blocks.
9
7
 
10
- VirtualRow is a class that returns SQL::Indentifiers,
11
- SQL::QualifiedIdentifiers, SQL::Functions, or SQL::WindowFunctions depending on how it is
12
- called. This is best shown by example:
8
+ == Why Virtual Rows
9
+
10
+ Virtual Rows were created to work around the issue that some parts of
11
+ Sequel's standard DSL could not be used on ruby 1.9. For example, the
12
+ following Sequel code works on ruby 1.8, but not ruby 1.9:
13
+
14
+ dataset.filter(:a > :b[:c])
15
+ # WHERE a > b(c)
16
+
17
+ This code does not work on ruby 1.9 for two reasons. First, Symbol#>
18
+ (like other inequality methods) is already defined in ruby 1.9, so Sequel
19
+ does not override it to return an SQL inequality expression. Second, Symbol#[]
20
+ is already defined on ruby 1.9, so Sequel does not override it to return an
21
+ SQL function expression.
22
+
23
+ Prior to the introduction of virtual rows, the way to handle this was
24
+ to use the methods that work on both ruby 1.8 and ruby 1.9:
25
+
26
+ dataset.filter(:a.identifier > :b.sql_function(:c))
27
+ # WHERE a > b(c)
28
+
29
+ However, that code is a little verbose. The virtual row DSL makes such code
30
+ more concise:
31
+
32
+ dataset.filter{a > b(c)}
33
+
34
+ Another use of virtual rows is when you turn off Sequel's core extensions off
35
+ using the SEQUEL_NO_CORE_EXTENSIONS constant or environment variable. With
36
+ the core extensions turned off, much of the standard Sequel DSL is not
37
+ available. For example, you are no longer able to do:
38
+
39
+ dataset.filter(:a & (:b | ~:c))
40
+ # WHERE a AND (b OR NOT c)
41
+
42
+ Because Symbol#&, Symbol#| and Symbol#~ are not defined when the core
43
+ extensions are turned off. However, with virtual rows allow almost the same
44
+ syntax even without the core extensions:
45
+
46
+ dataset.filter{a & (b | ~c)}
47
+
48
+ == Regular Procs vs Instance Evaled Procs
49
+
50
+ Virtual row blocks behave differently depending on whether the block accepts
51
+ an argument. If the block accepts an argument, it is called with an instance
52
+ of Sequel::SQL::VirtualRow. If it does not accept an argument, it is
53
+ evaluated in the context of an instance of Sequel::SQL::VirtualRow.
13
54
 
14
55
  ds = DB[:items]
15
- ds.filter{column > 1} # column > 1
16
- ds.filter{table__column > 1} # table.column > 1
17
- ds.filter{function(1) > 1} # function(1) > 1
18
- ds.select{[column1, sum(column2).as(sum)]} # column1, sum(column2) AS sum
19
- ds.select{version{}} # version()
20
- ds.select{count(:*){}} # count(*)
21
- ds.select{count(:distinct, col1){}} # count(DISTINCT col1)
22
- ds.select{rank(:over){}} # rank() OVER ()
23
- ds.select{count(:over, :*=>true){}} # count(*) OVER ()
24
- ds.select{sum(:over, :args=>col1, :partition=>col2, :order=>col3){}} # sum(col1) OVER (PARTITION BY col2 ORDER BY col3)
25
-
26
- Basically, the rules are:
56
+ # Regular proc
57
+ ds.filter{|o| o.column > 1}
58
+ # WHERE column > 1
59
+
60
+ # Instance-evaled proc
61
+ ds.filter{column > 1}
62
+ # WHERE column > 1
63
+
64
+ If you aren't familiar with the difference between regular blocks and instance
65
+ evaled blocks, you should probably consult a general ruby reference, but briefly,
66
+ with regular procs, methods called without an explicit receiver inside the
67
+ proc call the method on the receiver in the surrounding scope, while instance
68
+ evaled procs call the method on the receiver of the instance_eval call. However,
69
+ in both cases, local variables available in the surrounding scope will be available
70
+ inside the proc. If that doesn't make sense, maybe this example will help:
71
+
72
+ def self.a
73
+ 42
74
+ end
75
+ b = 32
76
+
77
+ # Regular proc
78
+ ds.filter{|o| o.c > a - b}
79
+ # WHERE c > 10
80
+
81
+ # Instance-evaled proc
82
+ ds.filter{c > a - b}
83
+ # WHERE c > (a - 32)
84
+
85
+ There are two related differences here. First is the usage of "o.c" vs "c",
86
+ and second is the difference between the the use of "a". In the regular proc,
87
+ you couldn't call c without an explicit receiver in the proc, unless the self of the
88
+ surrounding scope responded to it. For a, note how ruby calls the method on
89
+ the receiver of the surrounding scope in the regular proc, which returns an integer,
90
+ and does the substraction before Sequel gets access to it. In the instance evaled
91
+ proc, calling a without a receiver calls the a method on the VirtualRow instance.
92
+ For b, note that it operates the same in both cases, as it is a local variable.
93
+
94
+ Basically, the choice for whether to use a regular proc or an instance evaled proc is
95
+ completely up to you. The same things can be accomplished with both.
96
+ Instance evaled procs tend to produce shorter code, but by modifying the scope
97
+ can be more difficult for a new user to understand. That being said, I usually
98
+ use instance evaled procs unless I need to call methods on the receiver of the
99
+ surrounding scope inside the proc.
100
+
101
+ == Local Variables vs Method Calls
102
+
103
+ If you have a method that accepts 0 arguments and has the same name as a local
104
+ variable, you can call it with () to differentiate the method call from the
105
+ local variable access. This is mostly useful in instance_evaled procs:
106
+
107
+ b = 32
108
+ ds.filter{b() > b}
109
+ # WHERE b > 32
110
+
111
+ == VirtualRow Methods
112
+
113
+ VirtualRow is a class that returns SQL::Identifiers, SQL::QualifiedIdentifiers,
114
+ SQL::Functions, or SQL::WindowFunctions depending on how it is called.
115
+
116
+ == SQL::Identifiers - Regular columns
117
+
118
+ SQL::Identifiers can be thought of as regular column references in SQL,
119
+ not qualified by any table. You get an SQL::Identifier if the method is called
120
+ without a block or arguments, and doesn't have a double underscore in the method
121
+ name:
122
+
123
+ ds.filter{|o| o.column > 1}
124
+ ds.filter{column > 1}
125
+ # WHERE column > 1
126
+
127
+ == SQL::QualifiedIdentifiers - Qualified columns
128
+
129
+ SQL::QualifiedIdentifiers can be thought of as column references in SQL that
130
+ are qualified to a specific table. You get an SQL::QualifiedIdentifier if
131
+ the method is called without a block or arguments, and has a double underscore
132
+ in the method name:
133
+
134
+ ds.filter{|o| o.table__column > 1}
135
+ ds.filter{table__column > 1}
136
+ # WHERE table.column > 1
137
+
138
+ Using the double underscore for SQL::QualifiedIdentifiers was done to make
139
+ usage very similar to using symbols, which also translate the double underscore
140
+ into a qualified column.
141
+
142
+ == SQL::Functions - SQL function calls
143
+
144
+ SQL::Functions can be thought of as function calls in SQL. You get a simple
145
+ function call if you call a method with arguments and without a block:
146
+
147
+ ds.filter{|o| o.function(1) > 1}
148
+ ds.filter{function(1) > 1}
149
+ # WHERE function(1) > 1
150
+
151
+ To call a SQL function with multiple arguments, just use those arguments in
152
+ your function call:
153
+
154
+ ds.filter{|o| o.function(1, o.a) > 1}
155
+ ds.filter{function(1, a) > 1}
156
+ # WHERE function(1, a) > 1
157
+
158
+ If the SQL function does not accept any arguments, you need to provide an empty
159
+ block to the method to distinguish it from a call that will produce an
160
+ SQL::Identifier:
161
+
162
+ ds.select{|o| o.version{}}
163
+ ds.select{version{}}
164
+ # SELECT version()
165
+
166
+ To use the SQL wildcard (*) as the sole argument in a function call (most often
167
+ used with the count function), you should provide :* as the sole argument to
168
+ the method, and provide an empty block to the method:
169
+
170
+ ds.select{|o| o.count(:*){}}
171
+ ds.select{count(:*){}}
172
+ # SELECT count(*)
173
+
174
+ To append the DISTINCT keyword before the method arguments, you need to make
175
+ :distinct the first argument of the method call, and provide an empty block to
176
+ the method:
177
+
178
+ ds.select{|o| o.count(:distinct, o.col1){}}
179
+ ds.select{count(:distinct, col1){}}
180
+ # SELECT count(DISTINCT col1)
181
+
182
+ To use multiple columns with the DISTINCT keyword, use multiple arguments in
183
+ the method call:
184
+
185
+ ds.select{|o| o.count(:distinct, o.col1, o.col2){}}
186
+ ds.select{count(:distinct, col1, col2){}}
187
+ # SELECT count(DISTINCT col1, col2)
188
+
189
+ == SQL::WindowFunctions - SQL window function calls
190
+
191
+ SQL::WindowFunctions can be thought of as calls to SQL window functions. Not
192
+ all databases support them, but they are very helpful for certain types of
193
+ queries. To use them, you need to make :over the first argument of the method
194
+ call, with an optional hash as the second argument: Here are some examples of use:
195
+
196
+ ds.select{|o| o.rank(:over){}}
197
+ ds.select{rank(:over){}}
198
+ # SELECT rank() OVER ()
199
+
200
+ ds.select{|o| o.count(:over, :*=>true){}}
201
+ ds.select{count(:over, :*=>true){}}
202
+ # SELECT count(*) OVER ()
203
+
204
+ ds.select{|o| o.sum(:over, :args=>o.col1, :partition=>o.col2, :order=>o.col3){}}
205
+ ds.select{sum(:over, :args=>col1, :partition=>col2, :order=>col3){}}
206
+ # SELECT sum(col1) OVER (PARTITION BY col2 ORDER BY col3)
207
+
208
+ == Returning multiple values
209
+
210
+ It's common when using select and order virtual row blocks to want to
211
+ return multiple values. If you want to do that, you just need to return a single
212
+ array:
213
+
214
+ ds.select{|o| [o.column1, o.sum(o.column2).as(o.sum)]}
215
+ ds.select{[column1, sum(column2).as(sum)]}
216
+ # SELECT column1, sum(column2) AS sum
217
+
218
+ Note that if you forget the array brackets, you'll end up with a syntax error:
219
+
220
+ # Invalid ruby syntax
221
+ ds.select{|o| o.column1, o.sum(o.column2).as(o.sum)}
222
+ ds.select{column1, sum(column2).as(sum)}
223
+
224
+ == Alternative Description of the VirtualRow method call rules
27
225
 
28
226
  * If a block is given:
29
227
  * The block is currently not called. This may change in a future version.
@@ -45,15 +243,3 @@ Basically, the rules are:
45
243
  SQL::QualifiedIdentifier with the table and column.
46
244
  * Otherwise, create an SQL::Identifier with the name of the
47
245
  method.
48
-
49
- If you use a virtual row block that doesn't take an argument,
50
- the block is instance_evaled, so you can't reference methods
51
- in the enclosing scope. If you need to call methods of the
52
- enclosing scope, you should assign the results to local variables
53
- before the block, or just make the block take an argument and use
54
- that. If you want to create identifiers or qualified identifiers
55
- with the same name as existing local variables, make sure ruby
56
- knows it is a method call instead of a local variable reference
57
- by not ommiting the parentheses at the end of the method call.
58
- If you want to return multiple arguments (common for select and
59
- order), have the block return an array of arguments.
@@ -16,16 +16,26 @@ module Sequel
16
16
  end
17
17
  end
18
18
 
19
- # Connect to the database. In addition to the usual database options,
19
+ # In addition to the usual database options,
20
20
  # the following options have an effect:
21
21
  #
22
- # * :command_timeout - Sets the time in seconds to wait while attempting
23
- # to execute a command before cancelling the attempt and generating
24
- # an error. Specifically, it sets the ADO CommandTimeout property.
25
- # If this property is not set, the default of 30 seconds is used.
26
- # * :conn_string - The full ADO connection string. If this is provided,
27
- # the usual options are ignored.
28
- # * :provider - Sets the Provider of this ADO connection (for example, "SQLOLEDB")
22
+ # :command_timeout :: Sets the time in seconds to wait while attempting
23
+ # to execute a command before cancelling the attempt and generating
24
+ # an error. Specifically, it sets the ADO CommandTimeout property.
25
+ # If this property is not set, the default of 30 seconds is used.
26
+ # :driver :: The driver to use in the ADO connection string. If not provided, a default
27
+ # of "SQL Server" is used.
28
+ # :conn_string :: The full ADO connection string. If this is provided,
29
+ # the usual options are ignored.
30
+ # :provider :: Sets the Provider of this ADO connection (for example, "SQLOLEDB").
31
+ # If you don't specify a provider, the default one used by WIN32OLE
32
+ # has major problems, such as creating a new native database connection
33
+ # for every query, which breaks things such as temporary tables.
34
+ #
35
+ # Pay special attention to the :provider option, as without specifying a provider,
36
+ # many things will be broken. The SQLNCLI10 provider appears to work well if you
37
+ # are connecting to Microsoft SQL Server, but it is not the default as that would
38
+ # break backwards compatability.
29
39
  def connect(server)
30
40
  opts = server_opts(server)
31
41
  s = opts[:conn_string] || "driver=#{opts[:driver]};server=#{opts[:host]};database=#{opts[:database]}#{";uid=#{opts[:user]};pwd=#{opts[:password]}" if opts[:user]}"
@@ -41,10 +51,9 @@ module Sequel
41
51
  end
42
52
 
43
53
  def execute(sql, opts={})
44
- log_info(sql)
45
54
  synchronize(opts[:server]) do |conn|
46
55
  begin
47
- r = conn.Execute(sql)
56
+ r = log_yield(sql){conn.Execute(sql)}
48
57
  yield(r) if block_given?
49
58
  rescue ::WIN32OLERuntimeError => e
50
59
  raise_error(e)
@@ -56,9 +65,11 @@ module Sequel
56
65
 
57
66
  private
58
67
 
59
- # The ADO adapter doesn't support transactions, since it appears not to
60
- # use a single native connection for each connection in the pool
68
+ # The ADO adapter's default provider doesn't support transactions, since it
69
+ # creates a new native connection for each query. So Sequel only attempts
70
+ # to use transactions if an explicit :provider is given.
61
71
  def _transaction(conn)
72
+ return super if opts[:provider]
62
73
  th = Thread.current
63
74
  begin
64
75
  @transactions << th
@@ -81,6 +92,11 @@ module Sequel
81
92
  s.getRows.transpose.each{|r| yield cols.inject({}){|m,c| m[c] = r.shift; m}} unless s.eof
82
93
  end
83
94
  end
95
+
96
+ # ADO returns nil for all for delete and update statements.
97
+ def provides_accurate_rows_matched?
98
+ false
99
+ end
84
100
  end
85
101
  end
86
102
  end
@@ -7,11 +7,36 @@ module Sequel
7
7
  module MSSQL
8
8
  module DatabaseMethods
9
9
  include Sequel::MSSQL::DatabaseMethods
10
+ # Query to use to get the number of rows affected by an update or
11
+ # delete query.
12
+ ROWS_AFFECTED = "SELECT @@ROWCOUNT AS AffectedRows"
10
13
 
11
14
  # Return instance of Sequel::ADO::MSSQL::Dataset with the given opts.
12
15
  def dataset(opts=nil)
13
16
  Sequel::ADO::MSSQL::Dataset.new(self, opts)
14
17
  end
18
+
19
+ # Just execute so it doesn't attempt to return the number of rows modified.
20
+ def execute_ddl(sql, opts={})
21
+ execute(sql, opts)
22
+ end
23
+ alias execute_insert execute_ddl
24
+
25
+ # Issue a separate query to get the rows modified. ADO appears to
26
+ # use pass by reference with an integer variable, which is obviously
27
+ # not supported directly in ruby, and I'm not aware of a workaround.
28
+ def execute_dui(sql, opts={})
29
+ return super unless @opts[:provider]
30
+ synchronize(opts[:server]) do |conn|
31
+ begin
32
+ log_yield(sql){conn.Execute(sql)}
33
+ res = log_yield(ROWS_AFFECTED){conn.Execute(ROWS_AFFECTED)}
34
+ res.getRows.transpose.each{|r| return r.shift}
35
+ rescue ::WIN32OLERuntimeError => e
36
+ raise_error(e)
37
+ end
38
+ end
39
+ end
15
40
  end
16
41
 
17
42
  class Dataset < ADO::Dataset
@@ -19,11 +44,18 @@ module Sequel
19
44
 
20
45
  # Use a nasty hack of multiple SQL statements in the same call and
21
46
  # having the last one return the most recently inserted id. This
22
- # is necessary as ADO doesn't provide a consistent native connection.
47
+ # is necessary as ADO's default :provider uses a separate native
48
+ # connection for each query.
23
49
  def insert(*values)
24
50
  return super if @opts[:sql]
25
51
  with_sql("SET NOCOUNT ON; #{insert_sql(*values)}; SELECT CAST(SCOPE_IDENTITY() AS INTEGER)").single_value
26
52
  end
53
+
54
+ # If you use a better :provider option for the database, you can get an
55
+ # accurate number of rows matched.
56
+ def provides_accurate_rows_matched?
57
+ !!db.opts[:provider]
58
+ end
27
59
  end
28
60
  end
29
61
  end
@@ -84,37 +84,42 @@ module Sequel
84
84
 
85
85
  # Run the given SQL with the given arguments. Returns nil.
86
86
  def execute_ddl(sql, opts={})
87
- _execute(sql, opts){|conn| conn.execute_batch(sql);}
87
+ _execute(sql, opts){|conn| log_yield(sql){conn.execute_batch(sql)}}
88
88
  nil
89
89
  end
90
90
 
91
91
  # Run the given SQL with the given arguments and return the number of changed rows.
92
92
  def execute_dui(sql, opts={})
93
- _execute(sql, opts){|conn| conn.execute_batch(sql); conn.row_changes}
93
+ _execute(sql, opts){|conn| log_yield(sql){conn.execute_batch(sql)}; conn.row_changes}
94
94
  end
95
95
 
96
96
  # Run the given SQL with the given arguments and return the last inserted row id.
97
97
  def execute_insert(sql, opts={})
98
- _execute(sql, opts){|conn| conn.execute_batch(sql); conn.last_insert_rowid}
98
+ _execute(sql, opts){|conn| log_yield(sql){conn.execute_batch(sql)}; conn.last_insert_rowid}
99
99
  end
100
100
 
101
101
  # Run the given SQL with the given arguments and yield each row.
102
- def execute(sql, opts={}, &block)
103
- _execute(sql, opts){|conn| conn.prepare(sql, &block)}
102
+ def execute(sql, opts={})
103
+ _execute(sql, opts) do |conn|
104
+ begin
105
+ yield(stmt = log_yield(sql){conn.prepare(sql)})
106
+ ensure
107
+ stmt.close if stmt
108
+ end
109
+ end
104
110
  end
105
111
 
106
112
  # Run the given SQL with the given arguments and return the first value of the first row.
107
113
  def single_value(sql, opts={})
108
- _execute(sql, opts){|conn| conn.first_value_from(sql)}
114
+ _execute(sql, opts){|conn| log_yield(sql){conn.first_value_from(sql)}}
109
115
  end
110
116
 
111
117
  private
112
118
 
113
- # Log the SQL and yield an available connection. Rescue
119
+ # Yield an available connection. Rescue
114
120
  # any Amalgalite::Errors and turn them into DatabaseErrors.
115
121
  def _execute(sql, opts)
116
122
  begin
117
- log_info(sql)
118
123
  synchronize(opts[:server]){|conn| yield conn}
119
124
  rescue ::Amalgalite::Error, ::Amalgalite::SQLite3::Error => e
120
125
  raise_error(e)
@@ -33,13 +33,12 @@ module Sequel
33
33
  end
34
34
 
35
35
  def execute(sql, opts={})
36
- log_info(sql)
37
36
  synchronize(opts[:server]) do |conn|
38
37
  rc, sth = SQLAllocHandle(SQL_HANDLE_STMT, @handle)
39
38
  check_error(rc, "Could not allocate statement")
40
39
 
41
40
  begin
42
- rc = SQLExecDirect(sth, sql)
41
+ rc = log_yield(sql){SQLExecDirect(sth, sql)}
43
42
  check_error(rc, "Could not execute statement")
44
43
 
45
44
  yield(sth) if block_given?