swift 0.14.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/API.rdoc +14 -14
  2. data/README.md +110 -61
  3. data/Rakefile +2 -5
  4. data/VERSION +1 -1
  5. data/lib/swift/adapter/mysql.rb +30 -0
  6. data/lib/swift/adapter/postgres.rb +27 -0
  7. data/lib/swift/adapter/sql.rb +23 -29
  8. data/lib/swift/adapter/sqlite3.rb +59 -0
  9. data/lib/swift/adapter.rb +129 -70
  10. data/lib/swift/attribute.rb +19 -8
  11. data/lib/swift/eventmachine.rb +49 -0
  12. data/lib/swift/identity_map.rb +7 -7
  13. data/lib/swift/migrations.rb +12 -12
  14. data/lib/swift/{scheme.rb → record.rb} +16 -17
  15. data/lib/swift/result.rb +24 -0
  16. data/lib/swift/statement.rb +25 -0
  17. data/lib/swift/synchrony.rb +38 -0
  18. data/lib/swift/validations.rb +2 -2
  19. data/lib/swift.rb +8 -6
  20. data/swift.gemspec +19 -31
  21. data/test/helper.rb +11 -6
  22. data/test/test_adapter.rb +11 -25
  23. data/test/test_async.rb +9 -12
  24. data/test/test_encoding.rb +2 -2
  25. data/test/test_error.rb +8 -8
  26. data/test/test_io.rb +2 -2
  27. data/test/{test_scheme.rb → test_record.rb} +6 -6
  28. data/test/test_swift.rb +9 -51
  29. data/test/test_timestamps.rb +1 -1
  30. data/test/test_transactions.rb +2 -2
  31. data/test/test_types.rb +3 -3
  32. data/test/test_validations.rb +2 -2
  33. metadata +20 -27
  34. data/ext/adapter.cc +0 -479
  35. data/ext/adapter.h +0 -13
  36. data/ext/adapter_io.cc +0 -62
  37. data/ext/adapter_io.h +0 -24
  38. data/ext/attribute.cc +0 -22
  39. data/ext/attribute.h +0 -8
  40. data/ext/datetime.cc +0 -96
  41. data/ext/datetime.h +0 -12
  42. data/ext/extconf.rb +0 -61
  43. data/ext/query.cc +0 -104
  44. data/ext/query.h +0 -20
  45. data/ext/result.cc +0 -229
  46. data/ext/result.h +0 -27
  47. data/ext/statement.cc +0 -116
  48. data/ext/statement.h +0 -22
  49. data/ext/swift.cc +0 -114
  50. data/ext/swift.h +0 -60
  51. data/lib/swift/db.rb +0 -89
data/API.rdoc CHANGED
@@ -7,7 +7,7 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
7
7
  Swift
8
8
  .setup #=> Adapter
9
9
  .db #=> Adapter
10
- .schema #=> [Scheme, ...]
10
+ .schema #=> [Record, ...]
11
11
  .trace
12
12
 
13
13
  # Abstract.
@@ -15,15 +15,15 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
15
15
  .new #=> Adapter
16
16
  #begin #=> Adapter
17
17
  #commit
18
- #create #=> Scheme or Result
18
+ #create #=> Record or Result
19
19
  #delete #=> Result
20
20
  #execute #=> Result
21
21
  #async_execute #=> Result
22
- #get #=> Scheme
22
+ #get #=> Record
23
23
  #prepare #=> Statement
24
24
  #rollback
25
25
  #transaction #=> Adapter
26
- #update #=> Scheme or Result
26
+ #update #=> Record or Result
27
27
  #reconnect
28
28
 
29
29
  # Concrete.
@@ -32,7 +32,7 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
32
32
  Postgres < Adapter::Sql
33
33
  Sqlite3 < Adapter::Sql
34
34
 
35
- # Enumerable collection of Scheme or Hash tuples.
35
+ # Enumerable collection of Record or Hash tuples.
36
36
  Result
37
37
  .new #=> Result
38
38
  #insert_id #=> Numeric
@@ -46,21 +46,21 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
46
46
  #execute #=> Result
47
47
  #command #=> String # The SQL command executed or to be executed
48
48
 
49
- Scheme
49
+ Record
50
50
  .attribute #=> Type
51
- .create #=> Scheme or Result
52
- .get #=> Scheme
51
+ .create #=> Record or Result
52
+ .get #=> Record
53
53
  .header #=> Header
54
- .load #=> Scheme
55
- .new #=> Scheme
54
+ .load #=> Record
55
+ .new #=> Record
56
56
  .store #=> Symbol
57
57
  #execute #=> Result
58
58
  #prepare #=> Statement
59
- #scheme #=> Alias for self.class
59
+ #record #=> Alias for self.class
60
60
  #tuple #=> Hash
61
- #update #=> Scheme or Result
61
+ #update #=> Record or Result
62
62
 
63
- # Enumerable collection of Types for Scheme.
63
+ # Enumerable collection of Types for Record.
64
64
  Header
65
65
  .new #=> Header
66
66
  #all #=> [Type, ...]
@@ -79,7 +79,7 @@ Public API minus the optional stuff like Pool, IdentityMap, Migrations etc.
79
79
  #key #=> true or false
80
80
  #serial #=> Symbol or nil
81
81
  #default #=> Object
82
- #define_scheme_methods
82
+ #define_record_methods
83
83
 
84
84
  # Concrete.
85
85
  Type
data/README.md CHANGED
@@ -10,9 +10,26 @@ A rational rudimentary object relational mapper.
10
10
 
11
11
  ## Dependencies
12
12
 
13
- * ruby >= 1.9.1
14
- * [dbic++](http://github.com/deepfryed/dbicpp) >= 0.6.0
15
- * mysql >= 5.0.17, postgresql >= 8.4 or sqlite3 >= 3.7
13
+ * MRI Ruby >= 1.9.1
14
+ * swift-db-sqlite3 or swift-db-postgres or swift-db-mysql
15
+
16
+ ## Installation
17
+
18
+ ### Dependencies
19
+
20
+ Install one of the following drivers you would like to use.
21
+
22
+ ```
23
+ gem install swift-db-sqlite3
24
+ gem install swift-db-postgres
25
+ gem install swift-db-mysql
26
+ ```
27
+
28
+ ### Install Swift
29
+
30
+ ```
31
+ gem install swift
32
+ ```
16
33
 
17
34
  ## Features
18
35
 
@@ -24,20 +41,13 @@ A rational rudimentary object relational mapper.
24
41
  * IdentityMap.
25
42
  * Migrations.
26
43
 
27
- ## Performance notes
28
-
29
- 1. The current version creates DateTime objects for timestamp fields and this is roughly 80% slower on
30
- rubies older than 1.9.3.
31
- 2. On rubies older than 1.9.3, Swift will try using [home_run](https://github.com/jeremyevans/home_run)
32
- for performance.
33
-
34
44
  ### DB
35
45
 
36
46
  ```ruby
37
47
  require 'swift'
38
48
 
39
49
  Swift.trace true # Debugging.
40
- Swift.setup :default, Swift::DB::Postgres, db: 'swift'
50
+ Swift.setup :default, Swift::Adapter::Postgres, db: 'swift'
41
51
 
42
52
  # Block form db context.
43
53
  Swift.db do |db|
@@ -62,7 +72,7 @@ A rational rudimentary object relational mapper.
62
72
  end
63
73
  ```
64
74
 
65
- ### DB Scheme Operations
75
+ ### DB Record Operations
66
76
 
67
77
  Rudimentary object mapping. Provides a definition to the db methods for prepared (and cached) statements plus native
68
78
  primitive Ruby type conversion.
@@ -72,9 +82,9 @@ primitive Ruby type conversion.
72
82
  require 'swift/migrations'
73
83
 
74
84
  Swift.trace true # Debugging.
75
- Swift.setup :default, Swift::DB::Postgres, db: 'swift'
85
+ Swift.setup :default, Swift::Adapter::Postgres, db: 'swift'
76
86
 
77
- class User < Swift::Scheme
87
+ class User < Swift::Record
78
88
  store :users
79
89
  attribute :id, Swift::Type::Integer, serial: true, key: true
80
90
  attribute :name, Swift::Type::String
@@ -85,7 +95,7 @@ primitive Ruby type conversion.
85
95
  Swift.db do |db|
86
96
  db.migrate! User
87
97
 
88
- # Select Scheme instance (relation) instead of Hash.
98
+ # Select Record instance (relation) instead of Hash.
89
99
  users = db.prepare(User, 'select * from users limit 1').execute
90
100
 
91
101
  # Make a change and update.
@@ -98,18 +108,18 @@ primitive Ruby type conversion.
98
108
  end
99
109
  ```
100
110
 
101
- ### Scheme CRUD
111
+ ### Record CRUD
102
112
 
103
- Scheme/relation level helpers.
113
+ Record/relation level helpers.
104
114
 
105
115
  ```ruby
106
116
  require 'swift'
107
117
  require 'swift/migrations'
108
118
 
109
119
  Swift.trace true # Debugging.
110
- Swift.setup :default, Swift::DB::Postgres, db: 'swift'
120
+ Swift.setup :default, Swift::Adapter::Postgres, db: 'swift'
111
121
 
112
- class User < Swift::Scheme
122
+ class User < Swift::Record
113
123
  store :users
114
124
  attribute :id, Swift::Type::Integer, serial: true, key: true
115
125
  attribute :name, Swift::Type::String
@@ -142,7 +152,7 @@ SQL is easy and most people know it so Swift ORM provides simple #to_s
142
152
  attribute to table and field name typecasting.
143
153
 
144
154
  ```ruby
145
- class User < Swift::Scheme
155
+ class User < Swift::Record
146
156
  store :users
147
157
  attribute :id, Swift::Type::Integer, serial: true, key: true
148
158
  attribute :age, Swift::Type::Integer, field: 'ega'
@@ -167,7 +177,7 @@ Swift comes with a simple identity map. Just require it after you load swift.
167
177
  require 'swift/identity_map'
168
178
  require 'swift/migrations'
169
179
 
170
- class User < Swift::Scheme
180
+ class User < Swift::Record
171
181
  store :users
172
182
  attribute :id, Swift::Type::Integer, serial: true, key: true
173
183
  attribute :age, Swift::Type::Integer, field: 'ega'
@@ -204,7 +214,7 @@ But you can do it almost as fast in ruby,
204
214
  ```ruby
205
215
  require 'swift'
206
216
 
207
- Swift.setup :default, Swift::DB::Mysql, db: 'swift'
217
+ Swift.setup :default, Swift::Adapter::Mysql, db: 'swift'
208
218
 
209
219
  # MySQL packet size is the usual limit, 8k is the packet size by default.
210
220
  Swift.db do |db|
@@ -226,7 +236,7 @@ which implicitly uses `rb_thread_wait_fd`
226
236
  ```ruby
227
237
  require 'swift'
228
238
 
229
- pool = 3.times.map.with_index {|n| Swift.setup n, Swift::DB::Postgres, db: 'swift' }
239
+ pool = 3.times.map.with_index {|n| Swift.setup n, Swift::Adapter::Postgres, db: 'swift' }
230
240
 
231
241
  Thread.new do
232
242
  pool[0].async_execute('select pg_sleep(3), 1 as qid') {|row| p row}
@@ -243,31 +253,43 @@ which implicitly uses `rb_thread_wait_fd`
243
253
  Thread.list.reject {|thread| Thread.current == thread}.each(&:join)
244
254
  ```
245
255
 
256
+ or use the `swift/eventmachine` api.
257
+
246
258
  ```ruby
247
- require 'swift'
248
- require 'eventmachine'
249
-
250
- pool = 3.times.map.with_index {|n| Swift.setup n, Swift::DB::Postgres, db: 'swift'}
251
-
252
- module Handler
253
- attr_reader :result
254
-
255
- def initialize result
256
- @result = result
257
- end
258
-
259
- def notify_readable
260
- result.retrieve
261
- result.each {|row| p row }
262
- unbind
259
+ require 'swift/eventmachine'
260
+
261
+ EM.run do
262
+ pool = 3.times.map { Swift.setup(:default, Swift::Adapter::Postgres, db: "swift") }
263
+
264
+ 3.times.each do |n|
265
+ defer = pool[n].execute("select pg_sleep(3 - #{n}), #{n + 1} as qid")
266
+
267
+ defer.callback do |res|
268
+ p res.first
269
+ end
270
+
271
+ defer.errback do |e|
272
+ p 'error', e
273
+ end
263
274
  end
264
275
  end
265
-
276
+ ```
277
+
278
+ or use the `em-synchrony` api for `swift`
279
+
280
+ ```ruby
281
+ require 'swift/synchrony'
282
+
266
283
  EM.run do
267
- EM.watch(pool[0].fileno, Handler, pool[0].async_execute('select pg_sleep(3), 1 as qid')){|c| c.notify_readable = true}
268
- EM.watch(pool[1].fileno, Handler, pool[1].async_execute('select pg_sleep(2), 2 as qid')){|c| c.notify_readable = true}
269
- EM.watch(pool[2].fileno, Handler, pool[2].async_execute('select pg_sleep(1), 3 as qid')){|c| c.notify_readable = true}
270
- EM.add_timer(4) { EM.stop }
284
+ 3.times.each do |n|
285
+ EM.synchrony do
286
+ db = Swift.setup(:default, Swift::Adapter::Postgres, db: "swift")
287
+ result = db.execute("select pg_sleep(3 - #{n}), #{n + 1} as qid")
288
+
289
+ p result.first
290
+ EM.stop if n == 0
291
+ end
292
+ end
271
293
  end
272
294
  ```
273
295
 
@@ -284,8 +306,35 @@ http://github.com/shanna/swift/tree/master/benchmarks
284
306
 
285
307
  The test environment:
286
308
 
287
- * ruby 1.9.3p0
288
- * Intel Core2Duo P8700 2.53GHz, 4G RAM and Kingston SATA2 SSD
309
+ ```
310
+ $ uname -a
311
+
312
+ Linux deepfryed.local 3.0.0-1-amd64 #1 SMP Sun Jul 24 02:24:44 UTC 2011 x86_64 GNU/Linux
313
+
314
+ $ cat /proc/cpuinfo | grep "processor\|model name"
315
+
316
+ processor : 0
317
+ model name : Intel(R) Core(TM) i7-2677M CPU @ 1.80GHz
318
+ processor : 1
319
+ model name : Intel(R) Core(TM) i7-2677M CPU @ 1.80GHz
320
+ processor : 2
321
+ model name : Intel(R) Core(TM) i7-2677M CPU @ 1.80GHz
322
+ processor : 3
323
+ model name : Intel(R) Core(TM) i7-2677M CPU @ 1.80GHz
324
+
325
+ $ ruby -v
326
+
327
+ ruby 1.9.3p125 (2012-02-16 revision 34643) [x86_64-linux]
328
+ ```
329
+
330
+ PostgreSQL config:
331
+
332
+ ```
333
+ shared_buffers = 800MB # min 128kB
334
+ effective_cache_size = 512MB
335
+ work_mem = 64MB # min 64kB
336
+ maintenance_work_mem = 64MB # min 1MB
337
+ ```
289
338
 
290
339
  The test setup:
291
340
 
@@ -300,25 +349,25 @@ The test setup:
300
349
  ```
301
350
  ./simple.rb -n1 -r10000 -s ar -s dm -s sequel -s swift
302
351
 
303
- benchmark sys user total real rss
304
- ar #create 0.75 7.18 7.93 10.5043 366.95m
305
- ar #select 0.07 0.26 0.33 0.3680 40.71m
306
- ar #update 0.96 7.92 8.88 11.7537 436.38m
352
+ benchmark sys user total real rss
353
+
354
+ ar #create 1.960000 15.81000 17.770000 22.753109 266.21m
355
+ ar #select 0.020000 0.38000 0.400000 0.433041 50.82m
356
+ ar #update 2.000000 17.90000 19.900000 26.674921 317.48m
307
357
 
308
- dm #create 0.33 3.73 4.06 5.0908 245.68m
309
- dm #select 0.08 1.51 1.59 1.6154 87.95m
310
- dm #update 0.44 7.09 7.53 8.8685 502.77m
358
+ dm #create 0.660000 11.55000 12.210000 15.592424 236.86m
359
+ dm #select 0.030000 1.30000 1.330000 1.351911 87.18m
360
+ dm #update 0.950000 17.25000 18.200000 22.109859 474.81m
311
361
 
312
- sequel #create 0.60 5.07 5.67 7.9804 236.69m
313
- sequel #select 0.02 0.12 0.14 0.1778 12.75m
314
- sequel #update 0.82 4.95 5.77 8.2062 230.00m
362
+ sequel #create 1.960000 14.48000 16.440000 23.004864 226.68m
363
+ sequel #select 0.000000 0.09000 0.090000 0.134619 12.77m
364
+ sequel #update 1.900000 14.37000 16.270000 22.945636 200.20m
315
365
 
316
- swift #create 0.27 0.59 0.86 1.5085 84.85m
317
- swift #select 0.03 0.06 0.09 0.1037 11.24m
318
- swift #update 0.20 0.69 0.89 1.5867 62.19m
366
+ swift #create 0.520000 1.95000 2.470000 5.828846 75.26m
367
+ swift #select 0.010000 0.070000 0.080000 0.095124 11.23m
368
+ swift #update 0.440000 1.95000 2.390000 6.044971 59.35m
369
+ swift #write 0.010000 0.050000 0.060000 0.117195 13.46m
319
370
 
320
- -- bulk insert api --
321
- swift #write 0.04 0.06 0.10 0.1699 14.05m
322
371
  ```
323
372
 
324
373
  ## TODO
data/Rakefile CHANGED
@@ -9,8 +9,7 @@ begin
9
9
  gem.email = %w{shane.hanna@gmail.com deepfryed@gmail.com}
10
10
  gem.homepage = 'http://github.com/shanna/swift'
11
11
  gem.authors = ["Shane Hanna", "Bharanee 'Barney' Rathna"]
12
- gem.extensions = FileList['ext/extconf.rb']
13
- gem.files.reject!{|f| f =~ %r{\.gitignore|examples|benchmarks|memory/.*}}
12
+ gem.files.reject!{|f| f =~ %r{\.gitignore|examples|benchmarks|memory|gems/.*|Gemfile}}
14
13
 
15
14
  gem.add_development_dependency 'minitest', '>= 1.7.0'
16
15
  end
@@ -26,11 +25,9 @@ Rake::TestTask.new(:test) do |test|
26
25
  test.verbose = true
27
26
  end
28
27
 
29
- task :test => :check_dependencies
30
28
  task :default => :test
31
29
 
32
30
  require 'yard'
33
31
  YARD::Rake::YardocTask.new do |yard|
34
- yard.files = ['lib/**/*.rb', 'ext/*.cc']
32
+ yard.files = Dir["lib/**/*.rb"].reject {|file| %r{eventmachine|synchrony}.match(file)}
35
33
  end
36
-
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.0
1
+ 1.0.0
@@ -0,0 +1,30 @@
1
+ require 'swift/db/mysql'
2
+ require 'swift/adapter/sql'
3
+
4
+ module Swift
5
+ class Adapter
6
+ class Mysql < Sql
7
+ def initialize options = {}
8
+ super Swift::DB::Mysql.new(options)
9
+ end
10
+
11
+ def returning?
12
+ false
13
+ end
14
+
15
+ # TODO Swift::Type::Bignum ?
16
+ # serial is an alias for bigint in mysql, we want integer type to be migrated as integer
17
+ # type in the database (not bigint or smallint or shortint or whatever).
18
+ def field_type attribute
19
+ case attribute
20
+ when Type::Integer then attribute.serial ? 'integer auto_increment' : 'integer'
21
+ else super
22
+ end
23
+ end
24
+
25
+ def tables
26
+ execute("show tables").map(&:values).flatten
27
+ end
28
+ end # Mysql
29
+ end # Adapter
30
+ end # Swift
@@ -0,0 +1,27 @@
1
+ require 'swift/db/postgres'
2
+ require 'swift/adapter/sql'
3
+
4
+ module Swift
5
+ class Adapter
6
+ class Postgres < Sql
7
+ def initialize options = {}
8
+ super Swift::DB::Postgres.new(options)
9
+ end
10
+
11
+ def returning?
12
+ true
13
+ end
14
+
15
+ def field_type attribute
16
+ case attribute
17
+ when Type::IO then 'bytea'
18
+ else super
19
+ end
20
+ end
21
+
22
+ def tables
23
+ execute('select tablename from pg_tables where schemaname = current_schema').map(&:values).flatten
24
+ end
25
+ end # Postgres
26
+ end # Adapter
27
+ end # Swift
@@ -1,19 +1,26 @@
1
1
  require 'swift/adapter'
2
+ require 'forwardable'
2
3
 
3
4
  module Swift
4
5
  class Adapter
5
-
6
6
  # Abstract SQL Adapter.
7
7
  #
8
8
  # @abstract
9
9
  class Sql < Adapter
10
+ extend Forwardable
11
+ def_delegators :db, :begin, :commit, :rollback, :close, :closed?, :query, :fileno, :result, :write
12
+
10
13
  def tables
11
14
  raise NotImplementedError
12
15
  end
13
16
 
14
17
  def fields table
15
18
  result = execute("select * from #{table} limit 0")
16
- Hash[result.fields.map(&:to_sym).zip(result.field_types)]
19
+ Hash[result.fields.map(&:to_sym).zip(result.types)]
20
+ end
21
+
22
+ def transaction *args
23
+ db.transaction(*args) {|db| yield self}
17
24
  end
18
25
 
19
26
  protected
@@ -21,39 +28,26 @@ module Swift
21
28
  raise NotImplementedError
22
29
  end
23
30
 
24
- def prepare_cached scheme, name, &block
25
- @prepared ||= Hash.new{|h,k| h[k] = Hash.new}
26
- @prepared[scheme][name] ||= prepare(scheme, yield)
27
- end
28
-
29
- def prepare_get scheme
30
- prepare_cached(scheme, :get) do
31
- where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
32
- "select * from #{scheme.store} where #{where} limit 1"
33
- end
31
+ def command_get record
32
+ where = record.header.keys.map{|key| "#{key} = ?"}.join(' and ')
33
+ "select * from #{record.store} where #{where} limit 1"
34
34
  end
35
35
 
36
- def prepare_create scheme
37
- prepare_cached(scheme, :create) do
38
- values = (['?'] * scheme.header.insertable.size).join(', ')
39
- returning = "returning #{scheme.header.serial}" if scheme.header.serial and returning?
40
- "insert into #{scheme.store} (#{scheme.header.insertable.join(', ')}) values (#{values}) #{returning}"
41
- end
36
+ def command_create record
37
+ values = (['?'] * record.header.insertable.size).join(', ')
38
+ returning = "returning #{record.header.serial}" if record.header.serial and returning?
39
+ "insert into #{record.store} (#{record.header.insertable.join(', ')}) values (#{values}) #{returning}"
42
40
  end
43
41
 
44
- def prepare_update scheme
45
- prepare_cached(scheme, :update) do
46
- set = scheme.header.updatable.map{|field| "#{field} = ?"}.join(', ')
47
- where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
48
- "update #{scheme.store} set #{set} where #{where}"
49
- end
42
+ def command_update record
43
+ set = record.header.updatable.map{|field| "#{field} = ?"}.join(', ')
44
+ where = record.header.keys.map{|key| "#{key} = ?"}.join(' and ')
45
+ "update #{record.store} set #{set} where #{where}"
50
46
  end
51
47
 
52
- def prepare_delete scheme
53
- prepare_cached(scheme, :delete) do
54
- where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
55
- "delete from #{scheme.store} where #{where}"
56
- end
48
+ def command_delete record
49
+ where = record.header.keys.map{|key| "#{key} = ?"}.join(' and ')
50
+ "delete from #{record.store} where #{where}"
57
51
  end
58
52
 
59
53
  def field_definition attribute
@@ -0,0 +1,59 @@
1
+ require 'swift/db/sqlite3'
2
+ require 'swift/adapter/sql'
3
+
4
+ module Swift
5
+ class Adapter
6
+ class Sqlite3 < Sql
7
+ def initialize options = {}
8
+ super Swift::DB::Sqlite3.new(options)
9
+ end
10
+
11
+ def returning?
12
+ false
13
+ end
14
+
15
+ def migrate! record
16
+ keys = record.header.keys
17
+ serial = record.header.find(&:serial)
18
+ fields = record.header.map{|p| field_definition(p)}.join(', ')
19
+ fields += ", primary key (#{keys.join(', ')})" unless serial or keys.empty?
20
+
21
+ execute("drop table if exists #{record.store}")
22
+ execute("create table #{record.store} (#{fields})")
23
+ end
24
+
25
+ def field_type attribute
26
+ case attribute
27
+ when Type::String then 'text'
28
+ when Type::Integer then attribute.serial ? 'integer primary key' : 'integer'
29
+ when Type::Float then 'float'
30
+ when Type::BigDecimal then 'numeric'
31
+ when Type::Time then 'timestamp' # deprecated
32
+ when Type::DateTime then 'timestamp'
33
+ when Type::Date then 'date'
34
+ when Type::Boolean then 'boolean'
35
+ when Type::IO then 'blob'
36
+ else 'text'
37
+ end
38
+ end
39
+
40
+ def tables
41
+ execute('select name from sqlite_master where type = ?', 'table').map(&:values).flatten
42
+ end
43
+
44
+ def write table, fields = nil, io
45
+ fields = execute("select * from #{table} limit 0").fields if fields.nil? or fields.empty?
46
+ statement = prepare("insert into #{table}(#{fields.join(',')}) values (%s)" % (['?'] * fields.size).join(','))
47
+
48
+ r = 0
49
+ io = io.read if io.respond_to?(:read)
50
+ io.split(/\n+/).each do |line|
51
+ r += statement.execute(*line.split(/\t/).map {|value| value == '\N' ? nil : value}).affected_rows
52
+ end
53
+
54
+ # TODO: a better way to return a pretend result
55
+ Struct.new(:affected_rows).new(r)
56
+ end
57
+ end # Sqlite3
58
+ end # Adapter
59
+ end # Swift