sequel_core 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/CHANGELOG +1003 -0
  2. data/COPYING +18 -0
  3. data/README +81 -0
  4. data/Rakefile +176 -0
  5. data/bin/sequel +41 -0
  6. data/lib/sequel_core.rb +59 -0
  7. data/lib/sequel_core/adapters/adapter_skeleton.rb +68 -0
  8. data/lib/sequel_core/adapters/ado.rb +100 -0
  9. data/lib/sequel_core/adapters/db2.rb +158 -0
  10. data/lib/sequel_core/adapters/dbi.rb +126 -0
  11. data/lib/sequel_core/adapters/informix.rb +87 -0
  12. data/lib/sequel_core/adapters/jdbc.rb +108 -0
  13. data/lib/sequel_core/adapters/mysql.rb +269 -0
  14. data/lib/sequel_core/adapters/odbc.rb +145 -0
  15. data/lib/sequel_core/adapters/odbc_mssql.rb +93 -0
  16. data/lib/sequel_core/adapters/openbase.rb +90 -0
  17. data/lib/sequel_core/adapters/oracle.rb +99 -0
  18. data/lib/sequel_core/adapters/postgres.rb +519 -0
  19. data/lib/sequel_core/adapters/sqlite.rb +192 -0
  20. data/lib/sequel_core/array_keys.rb +296 -0
  21. data/lib/sequel_core/connection_pool.rb +152 -0
  22. data/lib/sequel_core/core_ext.rb +59 -0
  23. data/lib/sequel_core/core_sql.rb +191 -0
  24. data/lib/sequel_core/database.rb +433 -0
  25. data/lib/sequel_core/dataset.rb +409 -0
  26. data/lib/sequel_core/dataset/convenience.rb +321 -0
  27. data/lib/sequel_core/dataset/sequelizer.rb +354 -0
  28. data/lib/sequel_core/dataset/sql.rb +586 -0
  29. data/lib/sequel_core/exceptions.rb +45 -0
  30. data/lib/sequel_core/migration.rb +191 -0
  31. data/lib/sequel_core/model.rb +8 -0
  32. data/lib/sequel_core/pretty_table.rb +73 -0
  33. data/lib/sequel_core/schema.rb +8 -0
  34. data/lib/sequel_core/schema/schema_generator.rb +131 -0
  35. data/lib/sequel_core/schema/schema_sql.rb +131 -0
  36. data/lib/sequel_core/worker.rb +58 -0
  37. data/spec/adapters/informix_spec.rb +139 -0
  38. data/spec/adapters/mysql_spec.rb +330 -0
  39. data/spec/adapters/oracle_spec.rb +130 -0
  40. data/spec/adapters/postgres_spec.rb +189 -0
  41. data/spec/adapters/sqlite_spec.rb +345 -0
  42. data/spec/array_keys_spec.rb +679 -0
  43. data/spec/connection_pool_spec.rb +356 -0
  44. data/spec/core_ext_spec.rb +67 -0
  45. data/spec/core_sql_spec.rb +301 -0
  46. data/spec/database_spec.rb +812 -0
  47. data/spec/dataset_spec.rb +2381 -0
  48. data/spec/migration_spec.rb +261 -0
  49. data/spec/pretty_table_spec.rb +66 -0
  50. data/spec/rcov.opts +4 -0
  51. data/spec/schema_generator_spec.rb +86 -0
  52. data/spec/schema_spec.rb +230 -0
  53. data/spec/sequelizer_spec.rb +448 -0
  54. data/spec/spec.opts +5 -0
  55. data/spec/spec_helper.rb +44 -0
  56. data/spec/worker_spec.rb +96 -0
  57. metadata +162 -0
@@ -0,0 +1,192 @@
1
+ require 'sqlite3'
2
+
3
+ module Sequel
4
+ module SQLite
5
+ class Database < Sequel::Database
6
+ set_adapter_scheme :sqlite
7
+
8
+ def serial_primary_key_options
9
+ {:primary_key => true, :type => :integer, :auto_increment => true}
10
+ end
11
+
12
+ def connect
13
+ if @opts[:database].nil? || @opts[:database].empty?
14
+ @opts[:database] = ':memory:'
15
+ end
16
+ db = ::SQLite3::Database.new(@opts[:database])
17
+ db.type_translation = true
18
+ # fix for timestamp translation
19
+ db.translator.add_translator("timestamp") do |t, v|
20
+ v =~ /^\d+$/ ? Time.at(v.to_i) : Time.parse(v)
21
+ end
22
+ db
23
+ end
24
+
25
+ def disconnect
26
+ @pool.disconnect {|c| c.close}
27
+ end
28
+
29
+ def dataset(opts = nil)
30
+ SQLite::Dataset.new(self, opts)
31
+ end
32
+
33
+ TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'"
34
+
35
+ def tables
36
+ self[:sqlite_master].filter(TABLES_FILTER).map {|r| r[:name].to_sym}
37
+ end
38
+
39
+ def execute(sql)
40
+ @logger.info(sql) if @logger
41
+ @pool.hold {|conn| conn.execute_batch(sql); conn.changes}
42
+ end
43
+
44
+ def execute_insert(sql)
45
+ @logger.info(sql) if @logger
46
+ @pool.hold {|conn| conn.execute(sql); conn.last_insert_row_id}
47
+ end
48
+
49
+ def single_value(sql)
50
+ @logger.info(sql) if @logger
51
+ @pool.hold {|conn| conn.get_first_value(sql)}
52
+ end
53
+
54
+ def execute_select(sql, &block)
55
+ @logger.info(sql) if @logger
56
+ @pool.hold {|conn| conn.query(sql, &block)}
57
+ end
58
+
59
+ def pragma_get(name)
60
+ single_value("PRAGMA #{name}")
61
+ end
62
+
63
+ def pragma_set(name, value)
64
+ execute("PRAGMA #{name} = #{value}")
65
+ end
66
+
67
+ AUTO_VACUUM = {'0' => :none, '1' => :full, '2' => :incremental}.freeze
68
+
69
+ def auto_vacuum
70
+ AUTO_VACUUM[pragma_get(:auto_vacuum)]
71
+ end
72
+
73
+ def auto_vacuum=(value)
74
+ value = AUTO_VACUUM.index(value) || (raise Error, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
75
+ pragma_set(:auto_vacuum, value)
76
+ end
77
+
78
+ SYNCHRONOUS = {'0' => :off, '1' => :normal, '2' => :full}.freeze
79
+
80
+ def synchronous
81
+ SYNCHRONOUS[pragma_get(:synchronous)]
82
+ end
83
+
84
+ def synchronous=(value)
85
+ value = SYNCHRONOUS.index(value) || (raise Error, "Invalid value for synchronous option. Please specify one of :off, :normal, :full.")
86
+ pragma_set(:synchronous, value)
87
+ end
88
+
89
+ TEMP_STORE = {'0' => :default, '1' => :file, '2' => :memory}.freeze
90
+
91
+ def temp_store
92
+ TEMP_STORE[pragma_get(:temp_store)]
93
+ end
94
+
95
+ def temp_store=(value)
96
+ value = TEMP_STORE.index(value) || (raise Error, "Invalid value for temp_store option. Please specify one of :default, :file, :memory.")
97
+ pragma_set(:temp_store, value)
98
+ end
99
+
100
+ def alter_table_sql(table, op)
101
+ case op[:op]
102
+ when :add_column
103
+ "ALTER TABLE #{table} ADD #{column_definition_sql(op)}"
104
+ else
105
+ raise Error, "Unsupported ALTER TABLE operation"
106
+ end
107
+ end
108
+
109
+ def transaction(&block)
110
+ @pool.hold do |conn|
111
+ if conn.transaction_active?
112
+ return yield(conn)
113
+ end
114
+ begin
115
+ result = nil
116
+ conn.transaction {result = yield(conn)}
117
+ result
118
+ rescue => e
119
+ raise e unless Error::Rollback === e
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ class Dataset < Sequel::Dataset
126
+ def literal(v)
127
+ case v
128
+ when Time
129
+ literal(v.iso8601)
130
+ else
131
+ super
132
+ end
133
+ end
134
+
135
+ def insert_sql(*values)
136
+ if (values.size == 1) && values.first.is_a?(Sequel::Dataset)
137
+ "INSERT INTO #{@opts[:from]} #{values.first.sql};"
138
+ else
139
+ super(*values)
140
+ end
141
+ end
142
+
143
+ def fetch_rows(sql, &block)
144
+ @db.execute_select(sql) do |result|
145
+ @columns = result.columns.map {|c| c.to_sym}
146
+ column_count = @columns.size
147
+ result.each do |values|
148
+ row = {}
149
+ column_count.times {|i| row[@columns[i]] = values[i]}
150
+ block.call(row)
151
+ end
152
+ end
153
+ end
154
+
155
+ def array_tuples_fetch_rows(sql, &block)
156
+ @db.execute_select(sql) do |result|
157
+ @columns = result.columns.map {|c| c.to_sym}
158
+ result.each {|r| r.keys = @columns; block[r]}
159
+ end
160
+ end
161
+
162
+ def insert(*values)
163
+ @db.execute_insert insert_sql(*values)
164
+ end
165
+
166
+ def update(*args, &block)
167
+ @db.execute update_sql(*args, &block)
168
+ end
169
+
170
+ def delete(opts = nil)
171
+ # check if no filter is specified
172
+ unless (opts && opts[:where]) || @opts[:where]
173
+ @db.transaction do
174
+ unfiltered_count = count
175
+ @db.execute delete_sql(opts)
176
+ unfiltered_count
177
+ end
178
+ else
179
+ @db.execute delete_sql(opts)
180
+ end
181
+ end
182
+
183
+ EXPLAIN = 'EXPLAIN %s'.freeze
184
+
185
+ def explain
186
+ res = []
187
+ @db.result_set(EXPLAIN % select_sql(opts), nil) {|r| res << r}
188
+ res
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,296 @@
1
+ # ArrayKeys provide support for accessing array elements by keys. ArrayKeys are
2
+ # based on the arrayfields gem by Ara Howard, and can be used as substitutes
3
+ # for fetching records tuples as Ruby hashes.
4
+ #
5
+ # The main advantage offered by ArrayKeys over hashes is that the values are
6
+ # always ordered according to the column order in the query. Another purported
7
+ # advantage is that they reduce the memory footprint, but this has turned out
8
+ # to be a false claim.
9
+ module ArrayKeys
10
+ # The KeySet module contains methods that extend an array of keys to return
11
+ # a key's position in the key set.
12
+ module KeySet
13
+ # Returns the key's position in the key set. Provides indifferent access
14
+ # for symbols and strings.
15
+ def key_pos(key)
16
+ @key_indexes ||= inject({}) {|h, k| h[k.to_sym] = h.size; h}
17
+ @key_indexes[key] || @key_indexes[key.to_sym] || @key_indexes[key.to_s]
18
+ end
19
+
20
+ # Adds a key to the key set.
21
+ def add_key(key)
22
+ self << key
23
+ @key_indexes[key] = @key_indexes.size
24
+ end
25
+
26
+ # Removes a key from the key set by its index.
27
+ def del_key(idx)
28
+ delete_at(idx)
29
+ @key_indexes = nil # reset key indexes
30
+ end
31
+ end
32
+
33
+ # The KeyAccess provides a large part of the Hash API for arrays with keys.
34
+ module KeyAccess
35
+ # Returns a value referenced by an array index or a key.
36
+ def [](idx, *args)
37
+ if String === idx or Symbol === idx
38
+ (idx = @keys.key_pos(idx)) ? super(idx, *args) : nil
39
+ else
40
+ super
41
+ end
42
+ end
43
+
44
+ # Sets the value referenced by an array index or a key.
45
+ def []=(idx,*args)
46
+ if String === idx or Symbol === idx
47
+ idx = @keys.key_pos(idx) || @keys.add_key(idx.to_sym)
48
+ end
49
+ super(idx, *args)
50
+ end
51
+
52
+ # Stores a value by index or key.
53
+ def store(k, v); self[k] = v; end
54
+
55
+ # Slices the array, and returns an array with its keys sliced accordingly.
56
+ def slice(*args)
57
+ s = super(*args)
58
+ s.keys = @keys.slice(*args)
59
+ s
60
+ end
61
+
62
+ # Converts the array into a hash.
63
+ def to_hash
64
+ h = {}
65
+ each_with_index {|v, i| h[@keys[i].to_sym] = v}
66
+ h
67
+ end
68
+ alias_method :to_h, :to_hash
69
+
70
+ # Iterates over each key-value pair in the array.
71
+ def each_pair
72
+ each_with_index {|v, i| yield @keys[i], v}
73
+ end
74
+
75
+ # Iterates over the array's associated keys.
76
+ def each_key(&block)
77
+ @keys.each(&block)
78
+ end
79
+
80
+ # Iterates over the array's values.
81
+ def each_value(&block)
82
+ each(&block)
83
+ end
84
+
85
+ # Deletes a value by its key.
86
+ def delete(key, *args)
87
+ if (idx = @keys.key_pos(key))
88
+ delete_at(idx)
89
+ end
90
+ end
91
+
92
+ # Deletes a value by its index.
93
+ def delete_at(idx)
94
+ super(idx)
95
+ @keys = @keys.clone
96
+ @keys.del_key(idx)
97
+ end
98
+
99
+ # Returns true if the array's key set contains the given key.
100
+ def include?(k)
101
+ @keys.include?(k) || @keys.include?(k.to_sym) || @keys.include?(k.to_s)
102
+ end
103
+
104
+ # Returns true if the array's key set contains the given key.
105
+ def has_key?(k)
106
+ @keys.include?(k) || @keys.include?(k.to_sym) || @keys.include?(k.to_s)
107
+ end
108
+ alias_method :member?, :has_key?
109
+ alias_method :key?, :has_key?
110
+
111
+ # Returns true if the array contains the given value.
112
+ def has_value?(k); orig_include?(k); end
113
+ alias_method :value?, :has_value?
114
+
115
+ # Fetches a value by its key and optionally passes it through the given
116
+ # block:
117
+ #
118
+ # row.fetch(:name) {|v| v.to_sym}
119
+ #
120
+ # You can also give a default value
121
+ #
122
+ # row.fetch(:name, 'untitled')
123
+ #
124
+ def fetch(k, *args, &block)
125
+ if idx = @keys.key_pos(k)
126
+ v = at idx
127
+ else
128
+ !args.empty? ? (v = args.first) : (raise IndexError, "key not found")
129
+ end
130
+ block ? block[v] : v
131
+ end
132
+
133
+ # Returns self.
134
+ def values
135
+ self
136
+ end
137
+
138
+ # Creates a copy of self with the same key set.
139
+ def dup
140
+ copy = super
141
+ copy.keys = @keys
142
+ copy
143
+ end
144
+
145
+ # Creates a copy of self with a copy of the key set.
146
+ def clone
147
+ copy = super
148
+ copy.keys = @keys.clone
149
+ copy
150
+ end
151
+
152
+ # Returns an array merged from self and the given array.
153
+ def merge(values, &block)
154
+ clone.merge!(values, &block)
155
+ end
156
+
157
+ # Merges the given array with self, optionally passing the values from self
158
+ # through the given block:
159
+ #
160
+ # row.merge!(new_values) {|k, old, new| (k == :name) ? old : new}
161
+ #
162
+ def merge!(values, &block)
163
+ values.each_pair do |k, v|
164
+ self[k] = (has_key?(k) && block) ? block[k, self[k], v] : v
165
+ end
166
+ self
167
+ end
168
+ alias_method :update, :merge!
169
+ alias_method :update!, :merge!
170
+ end
171
+
172
+ # The ArrayExtensions module provides extensions for the Array class.
173
+ module ArrayExtensions
174
+ attr_reader :keys
175
+
176
+ # Sets the key set for the array. Once a key set has been set for an array,
177
+ # it is extended with the KeyAccess API
178
+ def keys=(keys)
179
+ extend ArrayKeys::KeyAccess if keys
180
+ @keys = keys.frozen? ? keys.dup : keys
181
+ unless @keys.respond_to?(:key_pos)
182
+ @keys.extend(ArrayKeys::KeySet)
183
+ end
184
+ end
185
+
186
+ alias_method :columns, :keys
187
+ alias_method :columns=, :keys=
188
+ end
189
+
190
+ # The DatasetExtensions module provides extensions that modify
191
+ # a dataset to return Array tuples instead of Hash tuples.
192
+ module DatasetExtensions
193
+ # Fetches a dataset's records, converting each tuple into an array with
194
+ # keys.
195
+ def array_tuples_each(opts = nil, &block)
196
+ fetch_rows(select_sql(opts)) {|h| block[Array.from_hash(h)]}
197
+ end
198
+
199
+ # Provides the corresponding behavior to Sequel::Dataset#update_each_method,
200
+ # using array tuples.
201
+ def array_tuples_update_each_method
202
+ # warning: ugly code generation ahead
203
+ if @row_proc && @transform
204
+ class << self
205
+ def each(opts = nil, &block)
206
+ if opts && opts[:naked]
207
+ fetch_rows(select_sql(opts)) {|r| block[transform_load(Array.from_hash(r))]}
208
+ else
209
+ fetch_rows(select_sql(opts)) {|r| block[@row_proc[transform_load(Array.from_hash(r))]]}
210
+ end
211
+ self
212
+ end
213
+ end
214
+ elsif @row_proc
215
+ class << self
216
+ def each(opts = nil, &block)
217
+ if opts && opts[:naked]
218
+ fetch_rows(select_sql(opts)) {|r| block[Array.from_hash(r)]}
219
+ else
220
+ fetch_rows(select_sql(opts)) {|r| block[@row_proc[Array.from_hash(r)]]}
221
+ end
222
+ self
223
+ end
224
+ end
225
+ elsif @transform
226
+ class << self
227
+ def each(opts = nil, &block)
228
+ fetch_rows(select_sql(opts)) {|r| block[transform_load(Array.from_hash(r))]}
229
+ self
230
+ end
231
+ end
232
+ else
233
+ class << self
234
+ def each(opts = nil, &block)
235
+ fetch_rows(select_sql(opts)) {|r| block[Array.from_hash(r)]}
236
+ self
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ # Array extensions.
245
+ class Array
246
+ alias_method :orig_include?, :include?
247
+
248
+ include ArrayKeys::ArrayExtensions
249
+
250
+ # Converts a hash into an array with keys.
251
+ def self.from_hash(h)
252
+ a = []; a.keys = []
253
+ a.merge!(h)
254
+ end
255
+ end
256
+
257
+ module Sequel
258
+ # Modifies all dataset classes to fetch records as arrays with keys. By
259
+ # default records are fetched as hashes.
260
+ def self.use_array_tuples
261
+ Dataset.dataset_classes.each do |c|
262
+ c.class_eval do
263
+ if method_defined?(:array_tuples_fetch_rows)
264
+ alias_method :hash_tuples_fetch_rows, :fetch_rows
265
+ alias_method :fetch_rows, :array_tuples_fetch_rows
266
+ else
267
+ alias_method :orig_each, :each
268
+ alias_method :orig_update_each_method, :update_each_method
269
+ include ArrayKeys::DatasetExtensions
270
+ alias_method :each, :array_tuples_each
271
+ alias_method :update_each_method, :array_tuples_update_each_method
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ # Modifies all dataset classes to fetch records as hashes.
278
+ def self.use_hash_tuples
279
+ Dataset.dataset_classes.each do |c|
280
+ c.class_eval do
281
+ if method_defined?(:hash_tuples_fetch_rows)
282
+ alias_method :fetch_rows, :hash_tuples_fetch_rows
283
+ else
284
+ if method_defined?(:orig_each)
285
+ alias_method :each, :orig_each
286
+ undef_method :orig_each
287
+ end
288
+ if method_defined?(:orig_update_each_method)
289
+ alias_method :update_each_method, :orig_update_each_method
290
+ undef_method :orig_update_each_method
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end
296
+ end