epugh-sequel 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. data/README.rdoc +652 -0
  2. data/VERSION.yml +4 -0
  3. data/bin/sequel +104 -0
  4. data/lib/sequel.rb +1 -0
  5. data/lib/sequel/adapters/ado.rb +85 -0
  6. data/lib/sequel/adapters/db2.rb +132 -0
  7. data/lib/sequel/adapters/dbi.rb +101 -0
  8. data/lib/sequel/adapters/do.rb +197 -0
  9. data/lib/sequel/adapters/do/mysql.rb +38 -0
  10. data/lib/sequel/adapters/do/postgres.rb +92 -0
  11. data/lib/sequel/adapters/do/sqlite.rb +31 -0
  12. data/lib/sequel/adapters/firebird.rb +307 -0
  13. data/lib/sequel/adapters/informix.rb +75 -0
  14. data/lib/sequel/adapters/jdbc.rb +485 -0
  15. data/lib/sequel/adapters/jdbc/h2.rb +62 -0
  16. data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
  17. data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
  18. data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
  19. data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
  20. data/lib/sequel/adapters/mysql.rb +370 -0
  21. data/lib/sequel/adapters/odbc.rb +184 -0
  22. data/lib/sequel/adapters/openbase.rb +57 -0
  23. data/lib/sequel/adapters/oracle.rb +140 -0
  24. data/lib/sequel/adapters/postgres.rb +453 -0
  25. data/lib/sequel/adapters/shared/mssql.rb +93 -0
  26. data/lib/sequel/adapters/shared/mysql.rb +341 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +62 -0
  28. data/lib/sequel/adapters/shared/postgres.rb +743 -0
  29. data/lib/sequel/adapters/shared/progress.rb +34 -0
  30. data/lib/sequel/adapters/shared/sqlite.rb +263 -0
  31. data/lib/sequel/adapters/sqlite.rb +243 -0
  32. data/lib/sequel/adapters/utils/date_format.rb +21 -0
  33. data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
  34. data/lib/sequel/adapters/utils/unsupported.rb +62 -0
  35. data/lib/sequel/connection_pool.rb +258 -0
  36. data/lib/sequel/core.rb +204 -0
  37. data/lib/sequel/core_sql.rb +185 -0
  38. data/lib/sequel/database.rb +687 -0
  39. data/lib/sequel/database/schema_generator.rb +324 -0
  40. data/lib/sequel/database/schema_methods.rb +164 -0
  41. data/lib/sequel/database/schema_sql.rb +324 -0
  42. data/lib/sequel/dataset.rb +422 -0
  43. data/lib/sequel/dataset/convenience.rb +237 -0
  44. data/lib/sequel/dataset/prepared_statements.rb +220 -0
  45. data/lib/sequel/dataset/sql.rb +1105 -0
  46. data/lib/sequel/deprecated.rb +529 -0
  47. data/lib/sequel/exceptions.rb +44 -0
  48. data/lib/sequel/extensions/blank.rb +42 -0
  49. data/lib/sequel/extensions/inflector.rb +288 -0
  50. data/lib/sequel/extensions/pagination.rb +96 -0
  51. data/lib/sequel/extensions/pretty_table.rb +78 -0
  52. data/lib/sequel/extensions/query.rb +48 -0
  53. data/lib/sequel/extensions/string_date_time.rb +47 -0
  54. data/lib/sequel/metaprogramming.rb +44 -0
  55. data/lib/sequel/migration.rb +212 -0
  56. data/lib/sequel/model.rb +142 -0
  57. data/lib/sequel/model/association_reflection.rb +263 -0
  58. data/lib/sequel/model/associations.rb +1024 -0
  59. data/lib/sequel/model/base.rb +911 -0
  60. data/lib/sequel/model/deprecated.rb +188 -0
  61. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  62. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  63. data/lib/sequel/model/deprecated_validations.rb +384 -0
  64. data/lib/sequel/model/errors.rb +37 -0
  65. data/lib/sequel/model/exceptions.rb +7 -0
  66. data/lib/sequel/model/inflections.rb +230 -0
  67. data/lib/sequel/model/plugins.rb +74 -0
  68. data/lib/sequel/object_graph.rb +230 -0
  69. data/lib/sequel/plugins/caching.rb +122 -0
  70. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  71. data/lib/sequel/plugins/schema.rb +53 -0
  72. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  73. data/lib/sequel/plugins/validation_class_methods.rb +373 -0
  74. data/lib/sequel/sql.rb +854 -0
  75. data/lib/sequel/version.rb +11 -0
  76. data/lib/sequel_core.rb +1 -0
  77. data/lib/sequel_model.rb +1 -0
  78. data/spec/adapters/ado_spec.rb +46 -0
  79. data/spec/adapters/firebird_spec.rb +376 -0
  80. data/spec/adapters/informix_spec.rb +96 -0
  81. data/spec/adapters/mysql_spec.rb +875 -0
  82. data/spec/adapters/oracle_spec.rb +272 -0
  83. data/spec/adapters/postgres_spec.rb +692 -0
  84. data/spec/adapters/spec_helper.rb +10 -0
  85. data/spec/adapters/sqlite_spec.rb +550 -0
  86. data/spec/core/connection_pool_spec.rb +526 -0
  87. data/spec/core/core_ext_spec.rb +156 -0
  88. data/spec/core/core_sql_spec.rb +528 -0
  89. data/spec/core/database_spec.rb +1214 -0
  90. data/spec/core/dataset_spec.rb +3513 -0
  91. data/spec/core/expression_filters_spec.rb +363 -0
  92. data/spec/core/migration_spec.rb +261 -0
  93. data/spec/core/object_graph_spec.rb +280 -0
  94. data/spec/core/pretty_table_spec.rb +58 -0
  95. data/spec/core/schema_generator_spec.rb +167 -0
  96. data/spec/core/schema_spec.rb +778 -0
  97. data/spec/core/spec_helper.rb +82 -0
  98. data/spec/core/version_spec.rb +7 -0
  99. data/spec/extensions/blank_spec.rb +67 -0
  100. data/spec/extensions/caching_spec.rb +201 -0
  101. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  102. data/spec/extensions/inflector_spec.rb +122 -0
  103. data/spec/extensions/pagination_spec.rb +99 -0
  104. data/spec/extensions/pretty_table_spec.rb +91 -0
  105. data/spec/extensions/query_spec.rb +85 -0
  106. data/spec/extensions/schema_spec.rb +111 -0
  107. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  108. data/spec/extensions/spec_helper.rb +90 -0
  109. data/spec/extensions/string_date_time_spec.rb +93 -0
  110. data/spec/extensions/validation_class_methods_spec.rb +1054 -0
  111. data/spec/integration/dataset_test.rb +160 -0
  112. data/spec/integration/eager_loader_test.rb +683 -0
  113. data/spec/integration/prepared_statement_test.rb +130 -0
  114. data/spec/integration/schema_test.rb +183 -0
  115. data/spec/integration/spec_helper.rb +75 -0
  116. data/spec/integration/type_test.rb +96 -0
  117. data/spec/model/association_reflection_spec.rb +93 -0
  118. data/spec/model/associations_spec.rb +1780 -0
  119. data/spec/model/base_spec.rb +494 -0
  120. data/spec/model/caching_spec.rb +217 -0
  121. data/spec/model/dataset_methods_spec.rb +78 -0
  122. data/spec/model/eager_loading_spec.rb +1165 -0
  123. data/spec/model/hooks_spec.rb +472 -0
  124. data/spec/model/inflector_spec.rb +126 -0
  125. data/spec/model/model_spec.rb +588 -0
  126. data/spec/model/plugins_spec.rb +142 -0
  127. data/spec/model/record_spec.rb +1243 -0
  128. data/spec/model/schema_spec.rb +92 -0
  129. data/spec/model/spec_helper.rb +124 -0
  130. data/spec/model/validations_spec.rb +1080 -0
  131. data/spec/rcov.opts +6 -0
  132. data/spec/spec.opts +0 -0
  133. data/spec/spec_config.rb.example +10 -0
  134. metadata +202 -0
@@ -0,0 +1,34 @@
1
+ Sequel.require %w'date_format unsupported', 'adapters/utils'
2
+
3
+ module Sequel
4
+ module Progress
5
+ module DatabaseMethods
6
+
7
+ def dataset(opts = nil)
8
+ ds = super
9
+ ds.extend(DatasetMethods)
10
+ ds
11
+ end
12
+ end
13
+
14
+ module DatasetMethods
15
+ include Dataset::UnsupportedIntersectExcept
16
+ include Dataset::SQLStandardDateFormat
17
+
18
+ SELECT_CLAUSE_ORDER = %w'limit distinct columns from join where group order having compounds'.freeze
19
+
20
+ private
21
+
22
+ def select_clause_order
23
+ SELECT_CLAUSE_ORDER
24
+ end
25
+
26
+ # Progress uses TOP for limit, but it is only supported in Progress 10.
27
+ # The Progress adapter targets Progress 9, so it silently ignores the option.
28
+ def select_limit_sql(sql, opts)
29
+ raise(Error, "OFFSET not supported") if opts[:offset]
30
+ #sql << " TOP #{opts[:limit]}" if opts[:limit]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,263 @@
1
+ Sequel.require 'adapters/utils/unsupported'
2
+
3
+ module Sequel
4
+ module SQLite
5
+ module DatabaseMethods
6
+ AUTO_VACUUM = [:none, :full, :incremental].freeze
7
+ SYNCHRONOUS = [:off, :normal, :full].freeze
8
+ TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'"
9
+ TEMP_STORE = [:default, :file, :memory].freeze
10
+ TYPES = Sequel::Database::TYPES.merge(Bignum=>'integer')
11
+
12
+ # Run all alter_table commands in a transaction. This is technically only
13
+ # needed for drop column.
14
+ def alter_table(name, generator=nil, &block)
15
+ transaction{super}
16
+ end
17
+
18
+ # SQLite supports limited table modification. You can add a column
19
+ # or an index. Dropping columns is supported by copying the table into
20
+ # a temporary table, dropping the table, and creating a new table without
21
+ # the column inside of a transaction.
22
+ def alter_table_sql(table, op)
23
+ case op[:op]
24
+ when :add_column, :add_index, :drop_index
25
+ super
26
+ when :drop_column
27
+ qt = quote_schema_table(table)
28
+ bt = quote_identifier(backup_table_name(qt.gsub('`', '')))
29
+ columns_str = dataset.send(:identifier_list, columns_for(table, :except => op[:name]))
30
+ defined_columns_str = column_list_sql(defined_columns_for(table, :except => op[:name]))
31
+ ["CREATE TEMPORARY TABLE #{bt}(#{defined_columns_str})",
32
+ "INSERT INTO #{bt} SELECT #{columns_str} FROM #{qt}",
33
+ "DROP TABLE #{qt}",
34
+ "CREATE TABLE #{qt}(#{defined_columns_str})",
35
+ "INSERT INTO #{qt} SELECT #{columns_str} FROM #{bt}",
36
+ "DROP TABLE #{bt}"]
37
+ when :rename_column
38
+ qt = quote_schema_table(table)
39
+ bt = quote_identifier(backup_table_name(qt.gsub('`', '')))
40
+ old_columns = dataset.send(:identifier_list, columns_for(table))
41
+ new_columns_arr = columns_for(table)
42
+
43
+ # Replace the old column in place. This is extremely important.
44
+ new_columns_arr[new_columns_arr.index(op[:name])] = op[:new_name]
45
+
46
+ new_columns = dataset.send(:identifier_list, new_columns_arr)
47
+
48
+ def_old_columns = column_list_sql(defined_columns_for(table))
49
+
50
+ def_new_columns_arr = defined_columns_for(table).map do |c|
51
+ c[:name] = op[:new_name].to_s if c[:name] == op[:name].to_s
52
+ c
53
+ end
54
+
55
+ def_new_columns = column_list_sql(def_new_columns_arr)
56
+
57
+ [
58
+ "CREATE TEMPORARY TABLE #{bt}(#{def_old_columns})",
59
+ "INSERT INTO #{bt}(#{old_columns}) SELECT #{old_columns} FROM #{qt}",
60
+ "DROP TABLE #{qt}",
61
+ "CREATE TABLE #{qt}(#{def_new_columns})",
62
+ "INSERT INTO #{qt}(#{new_columns}) SELECT #{old_columns} FROM #{bt}",
63
+ "DROP TABLE #{bt}"
64
+ ]
65
+
66
+ else
67
+ raise Error, "Unsupported ALTER TABLE operation"
68
+ end
69
+ end
70
+
71
+ # A symbol signifying the value of the auto_vacuum PRAGMA.
72
+ def auto_vacuum
73
+ AUTO_VACUUM[pragma_get(:auto_vacuum).to_i]
74
+ end
75
+
76
+ # Set the auto_vacuum PRAGMA using the given symbol (:none, :full, or
77
+ # :incremental).
78
+ def auto_vacuum=(value)
79
+ value = AUTO_VACUUM.index(value) || (raise Error, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
80
+ pragma_set(:auto_vacuum, value)
81
+ end
82
+
83
+ # Get the value of the given PRAGMA.
84
+ def pragma_get(name)
85
+ self["PRAGMA #{name}"].single_value
86
+ end
87
+
88
+ # Set the value of the given PRAGMA to value.
89
+ def pragma_set(name, value)
90
+ execute_ddl("PRAGMA #{name} = #{value}")
91
+ end
92
+
93
+ # A symbol signifying the value of the synchronous PRAGMA.
94
+ def synchronous
95
+ SYNCHRONOUS[pragma_get(:synchronous).to_i]
96
+ end
97
+
98
+ # Set the synchronous PRAGMA using the given symbol (:off, :normal, or :full).
99
+ def synchronous=(value)
100
+ value = SYNCHRONOUS.index(value) || (raise Error, "Invalid value for synchronous option. Please specify one of :off, :normal, :full.")
101
+ pragma_set(:synchronous, value)
102
+ end
103
+
104
+ # Array of symbols specifying the table names in the current database.
105
+ #
106
+ # Options:
107
+ # * :server - Set the server to use.
108
+ def tables(opts={})
109
+ ds = self[:sqlite_master].server(opts[:server]).filter(TABLES_FILTER)
110
+ ds.identifier_output_method = nil
111
+ ds.identifier_input_method = nil
112
+ ds2 = dataset
113
+ ds.map{|r| ds2.send(:output_identifier, r[:name])}
114
+ end
115
+
116
+ # A symbol signifying the value of the temp_store PRAGMA.
117
+ def temp_store
118
+ TEMP_STORE[pragma_get(:temp_store).to_i]
119
+ end
120
+
121
+ # Set the temp_store PRAGMA using the given symbol (:default, :file, or :memory).
122
+ def temp_store=(value)
123
+ value = TEMP_STORE.index(value) || (raise Error, "Invalid value for temp_store option. Please specify one of :default, :file, :memory.")
124
+ pragma_set(:temp_store, value)
125
+ end
126
+
127
+ private
128
+
129
+ # The array of column symbols in the table, except for ones given in opts[:except]
130
+ def backup_table_name(table, opts={})
131
+ (opts[:times]||1000).times do |i|
132
+ table_name = "#{table}_backup#{i}"
133
+ return table_name unless table_exists?(table_name)
134
+ end
135
+ end
136
+
137
+ # The array of column symbols in the table, except for ones given in opts[:except]
138
+ def columns_for(table, opts={})
139
+ cols = schema_parse_table(table, {}).map{|c| c[0]}
140
+ cols = cols - Array(opts[:except])
141
+ cols
142
+ end
143
+
144
+ # The array of column schema hashes, except for the ones given in opts[:except]
145
+ def defined_columns_for(table, opts={})
146
+ cols = parse_pragma(table, {})
147
+ cols.each{|c| c[:default] = LiteralString.new(c[:default]) if c[:default]}
148
+ if opts[:except]
149
+ nono= Array(opts[:except]).compact.map{|n| n.to_s}
150
+ cols.reject!{|c| nono.include? c[:name] }
151
+ end
152
+ cols
153
+ end
154
+
155
+ # SQLite folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
156
+ def identifier_input_method_default
157
+ nil
158
+ end
159
+
160
+ # SQLite folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
161
+ def identifier_output_method_default
162
+ nil
163
+ end
164
+
165
+ # Parse the output of the table_info pragma
166
+ def parse_pragma(table_name, opts)
167
+ ds2 = dataset
168
+ ds = self["PRAGMA table_info(?)", ds2.send(:input_identifier, table_name)]
169
+ ds.identifier_output_method = nil
170
+ ds.map do |row|
171
+ row.delete(:cid)
172
+ row[:allow_null] = row.delete(:notnull).to_i == 0
173
+ row[:default] = row.delete(:dflt_value)
174
+ row[:primary_key] = row.delete(:pk).to_i == 1
175
+ row[:default] = nil if blank_object?(row[:default])
176
+ row[:db_type] = row.delete(:type)
177
+ row[:type] = schema_column_type(row[:db_type])
178
+ row
179
+ end
180
+ end
181
+
182
+ # SQLite supports schema parsing using the table_info PRAGMA, so
183
+ # parse the output of that into the format Sequel expects.
184
+ def schema_parse_table(table_name, opts)
185
+ ds = dataset
186
+ parse_pragma(table_name, opts).map do |row|
187
+ [ds.send(:output_identifier, row.delete(:name)), row]
188
+ end
189
+ end
190
+
191
+ # Override the standard type conversions with SQLite specific ones
192
+ def type_literal_base(column)
193
+ TYPES[column[:type]]
194
+ end
195
+ end
196
+
197
+ # Instance methods for datasets that connect to an SQLite database
198
+ module DatasetMethods
199
+ include Dataset::UnsupportedIntersectExceptAll
200
+ include Dataset::UnsupportedIsTrue
201
+
202
+ # SQLite does not support pattern matching via regular expressions.
203
+ # SQLite is case insensitive (depending on pragma), so use LIKE for
204
+ # ILIKE.
205
+ def complex_expression_sql(op, args)
206
+ case op
207
+ when :~, :'!~', :'~*', :'!~*'
208
+ raise Error, "SQLite does not support pattern matching via regular expressions"
209
+ when :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
210
+ # SQLite is case insensitive for ASCII, and non case sensitive for other character sets
211
+ "#{'NOT ' if [:'NOT LIKE', :'NOT ILIKE'].include?(op)}(#{literal(args.at(0))} LIKE #{literal(args.at(1))})"
212
+ else
213
+ super(op, args)
214
+ end
215
+ end
216
+
217
+ # SQLite performs a TRUNCATE style DELETE if no filter is specified.
218
+ # Since we want to always return the count of records, add a condition
219
+ # that is always true and then delete.
220
+ def delete(opts = (defarg=true;{}))
221
+ # check if no filter is specified
222
+ if defarg
223
+ @opts[:where] ? super() : filter(1=>1).delete
224
+ else
225
+ opts = @opts.merge(opts)
226
+ super(opts[:where] ? opts : opts.merge(:where=>{1=>1}))
227
+ end
228
+ end
229
+
230
+ # Insert the values into the database.
231
+ def insert(*values)
232
+ execute_insert(insert_sql(*values))
233
+ end
234
+
235
+ # Allow inserting of values directly from a dataset.
236
+ def insert_sql(*values)
237
+ if (values.size == 1) && values.first.is_a?(Sequel::Dataset)
238
+ "INSERT INTO #{source_list(@opts[:from])} #{values.first.sql};"
239
+ else
240
+ super(*values)
241
+ end
242
+ end
243
+
244
+ # SQLite uses the nonstandard ` (backtick) for quoting identifiers.
245
+ def quoted_identifier(c)
246
+ "`#{c}`"
247
+ end
248
+
249
+ private
250
+
251
+ def literal_blob(v)
252
+ blob = ''
253
+ v.each_byte{|x| blob << sprintf('%02x', x)}
254
+ "X'#{blob}'"
255
+ end
256
+
257
+ # SQLite uses string literals instead of identifiers in AS clauses.
258
+ def as_sql(expression, aliaz)
259
+ "#{expression} AS #{literal(aliaz.to_s)}"
260
+ end
261
+ end
262
+ end
263
+ end
@@ -0,0 +1,243 @@
1
+ require 'sqlite3'
2
+ Sequel.require 'adapters/shared/sqlite'
3
+
4
+ module Sequel
5
+ # Top level module for holding all SQLite-related modules and classes
6
+ # for Sequel.
7
+ module SQLite
8
+ # Database class for PostgreSQL databases used with Sequel and the
9
+ # ruby-sqlite3 driver.
10
+ class Database < Sequel::Database
11
+ UNIX_EPOCH_TIME_FORMAT = /\A\d+\z/.freeze
12
+ include ::Sequel::SQLite::DatabaseMethods
13
+
14
+ set_adapter_scheme :sqlite
15
+
16
+ # Mimic the file:// uri, by having 2 preceding slashes specify a relative
17
+ # path, and 3 preceding slashes specify an absolute path.
18
+ def self.uri_to_options(uri) # :nodoc:
19
+ { :database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}" }
20
+ end
21
+
22
+ private_class_method :uri_to_options
23
+
24
+ # Connect to the database. Since SQLite is a file based database,
25
+ # the only options available are :database (to specify the database
26
+ # name), and :timeout, to specify how long to wait for the database to
27
+ # be available if it is locked, given in milliseconds (default is 5000).
28
+ def connect(server)
29
+ opts = server_opts(server)
30
+ opts[:database] = ':memory:' if blank_object?(opts[:database])
31
+ db = ::SQLite3::Database.new(opts[:database])
32
+ db.busy_timeout(opts.fetch(:timeout, 5000))
33
+ db.type_translation = true
34
+
35
+ # Handle datetime's with Sequel.datetime_class
36
+ prok = proc do |t,v|
37
+ v = Time.at(v.to_i).iso8601 if UNIX_EPOCH_TIME_FORMAT.match(v)
38
+ Sequel.string_to_datetime(v)
39
+ end
40
+ db.translator.add_translator("timestamp", &prok)
41
+ db.translator.add_translator("datetime", &prok)
42
+
43
+ # Handle numeric values with BigDecimal
44
+ prok = proc{|t,v| BigDecimal.new(v) rescue v}
45
+ db.translator.add_translator("numeric", &prok)
46
+ db.translator.add_translator("decimal", &prok)
47
+ db.translator.add_translator("money", &prok)
48
+
49
+ # Handle floating point values with Float
50
+ prok = proc{|t,v| Float(v) rescue v}
51
+ db.translator.add_translator("float", &prok)
52
+ db.translator.add_translator("real", &prok)
53
+ db.translator.add_translator("double precision", &prok)
54
+
55
+ # Handle blob values with Sequel::SQL::Blob
56
+ db.translator.add_translator("blob"){|t,v| ::Sequel::SQL::Blob.new(v)}
57
+
58
+ db
59
+ end
60
+
61
+ # Return instance of Sequel::SQLite::Dataset with the given options.
62
+ def dataset(opts = nil)
63
+ SQLite::Dataset.new(self, opts)
64
+ end
65
+
66
+ # Run the given SQL with the given arguments and return the number of changed rows.
67
+ def execute_dui(sql, opts={})
68
+ _execute(sql, opts){|conn| conn.execute_batch(sql, opts[:arguments]); conn.changes}
69
+ end
70
+
71
+ # Run the given SQL with the given arguments and return the last inserted row id.
72
+ def execute_insert(sql, opts={})
73
+ _execute(sql, opts){|conn| conn.execute(sql, opts[:arguments]); conn.last_insert_row_id}
74
+ end
75
+
76
+ # Run the given SQL with the given arguments and yield each row.
77
+ def execute(sql, opts={}, &block)
78
+ _execute(sql, opts){|conn| conn.query(sql, opts[:arguments], &block)}
79
+ end
80
+
81
+ # Run the given SQL with the given arguments and return the first value of the first row.
82
+ def single_value(sql, opts={})
83
+ _execute(sql, opts){|conn| conn.get_first_value(sql, opts[:arguments])}
84
+ end
85
+
86
+ # Use the native driver transaction method if there isn't already a transaction
87
+ # in progress on the connection, always yielding a connection inside a transaction
88
+ # transaction.
89
+ def transaction(opts={})
90
+ unless opts.is_a?(Hash)
91
+ Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
92
+ opts = {:server=>opts}
93
+ end
94
+ synchronize(opts[:server]) do |conn|
95
+ return yield(conn) if conn.transaction_active?
96
+ begin
97
+ result = nil
98
+ log_info('Transaction.begin')
99
+ conn.transaction{result = yield(conn)}
100
+ result
101
+ rescue ::Exception => e
102
+ log_info('Transaction.rollback')
103
+ transaction_error(e, SQLite3::Exception)
104
+ ensure
105
+ log_info('Transaction.commit') unless e
106
+ end
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ # Log the SQL and the arguments, and yield an available connection. Rescue
113
+ # any SQLite3::Exceptions and turn the into Error::InvalidStatements.
114
+ def _execute(sql, opts)
115
+ begin
116
+ log_info(sql, opts[:arguments])
117
+ synchronize(opts[:server]){|conn| yield conn}
118
+ rescue SQLite3::Exception => e
119
+ raise_error(e)
120
+ end
121
+ end
122
+
123
+ # SQLite does not need the pool to convert exceptions.
124
+ # Also, force the max connections to 1 if a memory database is being
125
+ # used, as otherwise each connection gets a separate database.
126
+ def connection_pool_default_options
127
+ o = super.merge(:pool_convert_exceptions=>false)
128
+ # Default to only a single connection if a memory database is used,
129
+ # because otherwise each connection will get a separate database
130
+ o[:max_connections] = 1 if @opts[:database] == ':memory:' || blank_object?(@opts[:database])
131
+ o
132
+ end
133
+
134
+ # Disconnect given connections from the database.
135
+ def disconnect_connection(c)
136
+ c.close
137
+ end
138
+ end
139
+
140
+ # Dataset class for SQLite datasets that use the ruby-sqlite3 driver.
141
+ class Dataset < Sequel::Dataset
142
+ include ::Sequel::SQLite::DatasetMethods
143
+
144
+ EXPLAIN = 'EXPLAIN %s'.freeze
145
+ PREPARED_ARG_PLACEHOLDER = ':'.freeze
146
+
147
+ # SQLite already supports named bind arguments, so use directly.
148
+ module ArgumentMapper
149
+ include Sequel::Dataset::ArgumentMapper
150
+
151
+ protected
152
+
153
+ # Return a hash with the same values as the given hash,
154
+ # but with the keys converted to strings.
155
+ def map_to_prepared_args(hash)
156
+ args = {}
157
+ hash.each{|k,v| args[k.to_s] = v}
158
+ args
159
+ end
160
+
161
+ private
162
+
163
+ # SQLite uses a : before the name of the argument for named
164
+ # arguments.
165
+ def prepared_arg(k)
166
+ LiteralString.new("#{prepared_arg_placeholder}#{k}")
167
+ end
168
+ end
169
+
170
+ # SQLite prepared statement uses a new prepared statement each time
171
+ # it is called, but it does use the bind arguments.
172
+ module PreparedStatementMethods
173
+ include ArgumentMapper
174
+
175
+ private
176
+
177
+ # Run execute_select on the database with the given SQL and the stored
178
+ # bind arguments.
179
+ def execute(sql, opts={}, &block)
180
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
181
+ end
182
+
183
+ # Same as execute, explicit due to intricacies of alias and super.
184
+ def execute_dui(sql, opts={}, &block)
185
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
186
+ end
187
+
188
+ # Same as execute, explicit due to intricacies of alias and super.
189
+ def execute_insert(sql, opts={}, &block)
190
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
191
+ end
192
+ end
193
+
194
+ # Prepare an unnamed statement of the given type and call it with the
195
+ # given values.
196
+ def call(type, hash, values=nil, &block)
197
+ prepare(type, nil, values).call(hash, &block)
198
+ end
199
+
200
+ # Return an array of strings specifying a query explanation for the
201
+ # current dataset.
202
+ def explain
203
+ res = []
204
+ @db.result_set(EXPLAIN % select_sql(opts), nil) {|r| res << r}
205
+ res
206
+ end
207
+
208
+ # Yield a hash for each row in the dataset.
209
+ def fetch_rows(sql)
210
+ execute(sql) do |result|
211
+ @columns = result.columns.map{|c| output_identifier(c)}
212
+ column_count = @columns.size
213
+ result.each do |values|
214
+ row = {}
215
+ column_count.times {|i| row[@columns[i]] = values[i]}
216
+ yield row
217
+ end
218
+ end
219
+ end
220
+
221
+ # Prepare the given type of query with the given name and store
222
+ # it in the database. Note that a new native prepared statement is
223
+ # created on each call to this prepared statement.
224
+ def prepare(type, name=nil, values=nil)
225
+ ps = to_prepared_statement(type, values)
226
+ ps.extend(PreparedStatementMethods)
227
+ db.prepared_statements[name] = ps if name
228
+ ps
229
+ end
230
+
231
+ private
232
+
233
+ def literal_string(v)
234
+ "'#{::SQLite3::Database.quote(v)}'"
235
+ end
236
+
237
+ # SQLite uses a : before the name of the argument as a placeholder.
238
+ def prepared_arg_placeholder
239
+ PREPARED_ARG_PLACEHOLDER
240
+ end
241
+ end
242
+ end
243
+ end