sequel 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/CHANGELOG +1551 -4
  2. data/README +306 -19
  3. data/Rakefile +84 -56
  4. data/bin/sequel +106 -0
  5. data/doc/cheat_sheet.rdoc +225 -0
  6. data/doc/dataset_filtering.rdoc +182 -0
  7. data/lib/sequel_core.rb +136 -0
  8. data/lib/sequel_core/adapters/adapter_skeleton.rb +54 -0
  9. data/lib/sequel_core/adapters/ado.rb +80 -0
  10. data/lib/sequel_core/adapters/db2.rb +148 -0
  11. data/lib/sequel_core/adapters/dbi.rb +117 -0
  12. data/lib/sequel_core/adapters/informix.rb +78 -0
  13. data/lib/sequel_core/adapters/jdbc.rb +186 -0
  14. data/lib/sequel_core/adapters/jdbc/mysql.rb +55 -0
  15. data/lib/sequel_core/adapters/jdbc/postgresql.rb +66 -0
  16. data/lib/sequel_core/adapters/jdbc/sqlite.rb +47 -0
  17. data/lib/sequel_core/adapters/mysql.rb +231 -0
  18. data/lib/sequel_core/adapters/odbc.rb +155 -0
  19. data/lib/sequel_core/adapters/odbc_mssql.rb +106 -0
  20. data/lib/sequel_core/adapters/openbase.rb +64 -0
  21. data/lib/sequel_core/adapters/oracle.rb +170 -0
  22. data/lib/sequel_core/adapters/postgres.rb +199 -0
  23. data/lib/sequel_core/adapters/shared/mysql.rb +275 -0
  24. data/lib/sequel_core/adapters/shared/postgres.rb +351 -0
  25. data/lib/sequel_core/adapters/shared/sqlite.rb +146 -0
  26. data/lib/sequel_core/adapters/sqlite.rb +138 -0
  27. data/lib/sequel_core/connection_pool.rb +194 -0
  28. data/lib/sequel_core/core_ext.rb +203 -0
  29. data/lib/sequel_core/core_sql.rb +184 -0
  30. data/lib/sequel_core/database.rb +471 -0
  31. data/lib/sequel_core/database/schema.rb +156 -0
  32. data/lib/sequel_core/dataset.rb +457 -0
  33. data/lib/sequel_core/dataset/callback.rb +13 -0
  34. data/lib/sequel_core/dataset/convenience.rb +245 -0
  35. data/lib/sequel_core/dataset/pagination.rb +96 -0
  36. data/lib/sequel_core/dataset/query.rb +41 -0
  37. data/lib/sequel_core/dataset/schema.rb +15 -0
  38. data/lib/sequel_core/dataset/sql.rb +889 -0
  39. data/lib/sequel_core/deprecated.rb +26 -0
  40. data/lib/sequel_core/exceptions.rb +42 -0
  41. data/lib/sequel_core/migration.rb +187 -0
  42. data/lib/sequel_core/object_graph.rb +216 -0
  43. data/lib/sequel_core/pretty_table.rb +71 -0
  44. data/lib/sequel_core/schema.rb +2 -0
  45. data/lib/sequel_core/schema/generator.rb +239 -0
  46. data/lib/sequel_core/schema/sql.rb +325 -0
  47. data/lib/sequel_core/sql.rb +812 -0
  48. data/lib/sequel_model.rb +5 -1
  49. data/lib/sequel_model/association_reflection.rb +3 -8
  50. data/lib/sequel_model/base.rb +15 -10
  51. data/lib/sequel_model/inflector.rb +3 -5
  52. data/lib/sequel_model/plugins.rb +1 -1
  53. data/lib/sequel_model/record.rb +11 -3
  54. data/lib/sequel_model/schema.rb +4 -4
  55. data/lib/sequel_model/validations.rb +6 -1
  56. data/spec/adapters/ado_spec.rb +17 -0
  57. data/spec/adapters/informix_spec.rb +96 -0
  58. data/spec/adapters/mysql_spec.rb +764 -0
  59. data/spec/adapters/oracle_spec.rb +222 -0
  60. data/spec/adapters/postgres_spec.rb +441 -0
  61. data/spec/adapters/spec_helper.rb +7 -0
  62. data/spec/adapters/sqlite_spec.rb +400 -0
  63. data/spec/integration/dataset_test.rb +51 -0
  64. data/spec/integration/eager_loader_test.rb +702 -0
  65. data/spec/integration/schema_test.rb +102 -0
  66. data/spec/integration/spec_helper.rb +44 -0
  67. data/spec/integration/type_test.rb +43 -0
  68. data/spec/rcov.opts +2 -0
  69. data/spec/sequel_core/connection_pool_spec.rb +363 -0
  70. data/spec/sequel_core/core_ext_spec.rb +156 -0
  71. data/spec/sequel_core/core_sql_spec.rb +427 -0
  72. data/spec/sequel_core/database_spec.rb +964 -0
  73. data/spec/sequel_core/dataset_spec.rb +2977 -0
  74. data/spec/sequel_core/expression_filters_spec.rb +346 -0
  75. data/spec/sequel_core/migration_spec.rb +261 -0
  76. data/spec/sequel_core/object_graph_spec.rb +234 -0
  77. data/spec/sequel_core/pretty_table_spec.rb +58 -0
  78. data/spec/sequel_core/schema_generator_spec.rb +122 -0
  79. data/spec/sequel_core/schema_spec.rb +497 -0
  80. data/spec/sequel_core/spec_helper.rb +51 -0
  81. data/spec/{association_reflection_spec.rb → sequel_model/association_reflection_spec.rb} +6 -6
  82. data/spec/{associations_spec.rb → sequel_model/associations_spec.rb} +47 -18
  83. data/spec/{base_spec.rb → sequel_model/base_spec.rb} +2 -1
  84. data/spec/{caching_spec.rb → sequel_model/caching_spec.rb} +0 -0
  85. data/spec/{dataset_methods_spec.rb → sequel_model/dataset_methods_spec.rb} +13 -1
  86. data/spec/{eager_loading_spec.rb → sequel_model/eager_loading_spec.rb} +75 -14
  87. data/spec/{hooks_spec.rb → sequel_model/hooks_spec.rb} +4 -4
  88. data/spec/sequel_model/inflector_spec.rb +119 -0
  89. data/spec/{model_spec.rb → sequel_model/model_spec.rb} +30 -11
  90. data/spec/{plugins_spec.rb → sequel_model/plugins_spec.rb} +0 -0
  91. data/spec/{record_spec.rb → sequel_model/record_spec.rb} +47 -6
  92. data/spec/{schema_spec.rb → sequel_model/schema_spec.rb} +18 -4
  93. data/spec/{spec_helper.rb → sequel_model/spec_helper.rb} +3 -2
  94. data/spec/{validations_spec.rb → sequel_model/validations_spec.rb} +37 -17
  95. data/spec/spec_config.rb +9 -0
  96. data/spec/spec_config.rb.example +10 -0
  97. metadata +110 -37
  98. data/spec/inflector_spec.rb +0 -34
@@ -0,0 +1,136 @@
1
+ %w'bigdecimal bigdecimal/util date enumerator thread time uri yaml'.each do |f|
2
+ require f
3
+ end
4
+ %w"core_ext sql core_sql connection_pool exceptions pretty_table
5
+ dataset migration schema database object_graph".each do |f|
6
+ require "sequel_core/#{f}"
7
+ end
8
+
9
+ # Top level module for Sequel
10
+ #
11
+ # There are some class methods that are added via metaprogramming, one for
12
+ # each supported adapter. For example:
13
+ #
14
+ # DB = Sequel.sqlite # Memory database
15
+ # DB = Sequel.sqlite('blog.db')
16
+ # DB = Sequel.postgres('database_name', :user=>'user',
17
+ # :password=>'password', :host=>'host', :port=>5432,
18
+ # :max_connections=>10)
19
+ #
20
+ # If a block is given to these methods, it is passed the opened Database
21
+ # object, which is closed (disconnected) when the block exits. For example:
22
+ #
23
+ # Sequel.sqlite('blog.db'){|db| puts db.users.count}
24
+ #
25
+ # Sequel can use either Time or DateTime for times returned from the
26
+ # database. It defaults to Time. To change it to DateTime, use:
27
+ #
28
+ # Sequel.datetime_class = DateTime
29
+ #
30
+ # Sequel converts the column type tinyint to a boolean by default,
31
+ # you can override the conversion to use tinyint as an integer:
32
+ #
33
+ # Sequel.convert_tinyint_to_bool = false
34
+ module Sequel
35
+ @datetime_class = Time
36
+ @convert_tinyint_to_bool = true
37
+
38
+ metaattr_accessor :datetime_class
39
+ metaattr_accessor :convert_tinyint_to_bool
40
+
41
+ # Creates a new database object based on the supplied connection string
42
+ # and optional arguments. The specified scheme determines the database
43
+ # class used, and the rest of the string specifies the connection options.
44
+ # For example:
45
+ #
46
+ # DB = Sequel.connect('sqlite:/') # Memory database
47
+ # DB = Sequel.connect('sqlite://blog.db') # ./blog.db
48
+ # DB = Sequel.connect('sqlite:///blog.db') # /blog.db
49
+ # DB = Sequel.connect('postgres://user:password@host:port/database_name')
50
+ # DB = Sequel.connect('sqlite:///blog.db', :max_connections=>10)
51
+ #
52
+ # If a block is given, it is passed the opened Database object, which is
53
+ # closed when the block exits. For example:
54
+ #
55
+ # Sequel.connect('sqlite://blog.db'){|db| puts db.users.count}
56
+ #
57
+ # This is also aliased as Sequel.open.
58
+ def self.connect(*args, &block)
59
+ Database.connect(*args, &block)
60
+ end
61
+ metaalias :open, :connect
62
+
63
+ # Set whether to quote identifiers for all databases by default. By default,
64
+ # Sequel quotes identifiers in all SQL strings, so to turn that off:
65
+ #
66
+ # Sequel.quote_identifiers = false
67
+ def self.quote_identifiers=(value)
68
+ Database.quote_identifiers = value
69
+ end
70
+
71
+ # Set whether to set the single threaded mode for all databases by default. By default,
72
+ # Sequel uses a threadsafe connection pool, which isn't as fast as the
73
+ # single threaded connection pool. If your program will only have one thread,
74
+ # and speed is a priority, you may want to set this to true:
75
+ #
76
+ # Sequel.single_threaded = true
77
+ #
78
+ # Note that some database adapters (e.g. MySQL) have issues with single threaded mode if
79
+ # you try to perform more than one query simultaneously. For example, the
80
+ # following code will not work well in single threaded mode on MySQL:
81
+ #
82
+ # DB[:items].each{|i| DB[:nodes].filter(:item_id=>i[:id]).each{|n| puts "#{i} #{n}"}}
83
+ #
84
+ # Basically, you can't issue another query inside a call to Dataset#each in single
85
+ # threaded mode. There is a fairly easy fix, just use Dataset#all inside
86
+ # Dataset#each for the outer query:
87
+ #
88
+ # DB[:items].all{|i| DB[:nodes].filter(:item_id=>i[:id]).each{|n| puts "#{i} #{n}"}}
89
+ #
90
+ # Dataset#all gets all of the returned objects before calling the block, so the query
91
+ # isn't left open. Some of the adapters do this internally, and thus don't have a
92
+ # problem issuing queries inside of Dataset#each.
93
+ def self.single_threaded=(value)
94
+ Database.single_threaded = value
95
+ end
96
+
97
+ # Always returns false, since ParseTree support has been removed.
98
+ def self.use_parse_tree
99
+ false
100
+ end
101
+
102
+ # Raises an error if attempting to turn ParseTree support on (since it no longer exists).
103
+ # Otherwise, is a no-op.
104
+ def self.use_parse_tree=(val)
105
+ raise(Error, 'ParseTree support has been removed from Sequel') if val
106
+ end
107
+
108
+ ### Private Class Methods ###
109
+
110
+ # Helper method that the database adapter class methods that are added to Sequel via
111
+ # metaprogramming use to parse arguments.
112
+ def self.adapter_method(adapter, *args, &block) # :nodoc:
113
+ raise(::Sequel::Error, "Wrong number of arguments, 0-2 arguments valid") if args.length > 2
114
+ opts = {:adapter=>adapter.to_sym}
115
+ opts[:database] = args.shift if args.length >= 1 && !(args[0].is_a?(Hash))
116
+ if Hash === (arg = args[0])
117
+ opts.merge!(arg)
118
+ elsif !arg.nil?
119
+ raise ::Sequel::Error, "Wrong format of arguments, either use (), (String), (Hash), or (String, Hash)"
120
+ end
121
+ connect(opts, &block)
122
+ end
123
+
124
+ # Method that adds a database adapter class method to Sequel that calls
125
+ # Sequel.adapter_method.
126
+ def self.def_adapter_method(*adapters) # :nodoc:
127
+ adapters.each do |adapter|
128
+ instance_eval("def #{adapter}(*args, &block); adapter_method('#{adapter}', *args, &block) end")
129
+ end
130
+ end
131
+
132
+ private_class_method :adapter_method, :def_adapter_method
133
+
134
+ # Add the database adapter class methods to Sequel via metaprogramming
135
+ def_adapter_method(*Database::ADAPTERS)
136
+ end
@@ -0,0 +1,54 @@
1
+ module Sequel
2
+ module Adapter
3
+ class Database < Sequel::Database
4
+ set_adapter_scheme :adapter
5
+
6
+ def connect
7
+ AdapterDB.new(@opts[:database], @opts[:user], @opts[:password])
8
+ end
9
+
10
+ def disconnect
11
+ @pool.disconnect {|c| c.disconnect}
12
+ end
13
+
14
+ def dataset(opts = nil)
15
+ Adapter::Dataset.new(self, opts)
16
+ end
17
+
18
+ def execute(sql)
19
+ log_info(sql)
20
+ @pool.hold {|conn| conn.exec(sql)}
21
+ end
22
+ end
23
+
24
+ class Dataset < Sequel::Dataset
25
+ def literal(v)
26
+ case v
27
+ when Time
28
+ literal(v.iso8601)
29
+ when Date, DateTime
30
+ literal(v.to_s)
31
+ else
32
+ super
33
+ end
34
+ end
35
+
36
+ def fetch_rows(sql, &block)
37
+ @db.synchronize do
38
+ cursor = @db.execute sql
39
+ begin
40
+ @columns = cursor.get_col_names.map {|c| c.to_sym}
41
+ while r = cursor.fetch
42
+ row = {}
43
+ r.each_with_index {|v, i| row[@columns[i]] = v}
44
+ yield row
45
+ end
46
+ ensure
47
+ cursor.close
48
+ end
49
+ end
50
+ self
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,80 @@
1
+ require 'win32ole'
2
+
3
+ module Sequel
4
+ # The ADO adapter provides connectivity to ADO databases in Windows. ADO
5
+ # databases can be opened using a URL with the ado schema:
6
+ #
7
+ # DB = Sequel.open('ado://mydb')
8
+ #
9
+ # or using the Sequel.ado method:
10
+ #
11
+ # DB = Sequel.ado('mydb')
12
+ #
13
+ module ADO
14
+ class Database < Sequel::Database
15
+ set_adapter_scheme :ado
16
+
17
+ AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
18
+
19
+ def auto_increment_sql
20
+ AUTO_INCREMENT
21
+ end
22
+
23
+ def connect
24
+ s = "driver=#{@opts[:driver] || 'SQL Server'};server=#{@opts[:host]};database=#{@opts[:database]}#{";uid=#{@opts[:user]};pwd=#{@opts[:password]}" if @opts[:user]}"
25
+ handle = WIN32OLE.new('ADODB.Connection')
26
+ handle.Open(s)
27
+ handle
28
+ end
29
+
30
+ def disconnect
31
+ @pool.disconnect {|conn| conn.Close}
32
+ end
33
+
34
+ def dataset(opts = nil)
35
+ ADO::Dataset.new(self, opts)
36
+ end
37
+
38
+ def execute(sql)
39
+ log_info(sql)
40
+ @pool.hold {|conn| conn.Execute(sql)}
41
+ end
42
+
43
+ alias_method :do, :execute
44
+ end
45
+
46
+ class Dataset < Sequel::Dataset
47
+ def literal(v)
48
+ case v
49
+ when Time
50
+ literal(v.iso8601)
51
+ when Date, DateTime
52
+ literal(v.to_s)
53
+ else
54
+ super
55
+ end
56
+ end
57
+
58
+ def fetch_rows(sql, &block)
59
+ @db.synchronize do
60
+ s = @db.execute sql
61
+
62
+ @columns = s.Fields.extend(Enumerable).map {|x| x.Name.to_sym}
63
+
64
+ unless s.eof
65
+ s.moveFirst
66
+ s.getRows.transpose.each {|r| yield hash_row(r)}
67
+ end
68
+ end
69
+ self
70
+ end
71
+
72
+ def hash_row(row)
73
+ @columns.inject({}) do |m, c|
74
+ m[c] = row.shift
75
+ m
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,148 @@
1
+ require 'db2/db2cli'
2
+
3
+ module Sequel
4
+ module DB2
5
+ class Database < Sequel::Database
6
+ set_adapter_scheme :db2
7
+ include DB2CLI
8
+
9
+ # AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
10
+ #
11
+ # def auto_increment_sql
12
+ # AUTO_INCREMENT
13
+ # end
14
+
15
+ def check_error(rc, msg)
16
+ case rc
17
+ when SQL_SUCCESS, SQL_SUCCESS_WITH_INFO
18
+ nil
19
+ else
20
+ raise Error, msg
21
+ end
22
+ end
23
+
24
+ rc, @@env = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE)
25
+ check_error(rc, "Could not allocate DB2 environment")
26
+
27
+ def connect
28
+ rc, dbc = SQLAllocHandle(SQL_HANDLE_DBC, @@env)
29
+ check_error(rc, "Could not allocate database connection")
30
+
31
+ rc = SQLConnect(dbc, @opts[:database], @opts[:user], @opts[:password])
32
+ check_error(rc, "Could not connect to database")
33
+
34
+ dbc
35
+ end
36
+
37
+ def disconnect
38
+ @pool.disconnect do |conn|
39
+ rc = SQLDisconnect(conn)
40
+ check_error(rc, "Could not disconnect from database")
41
+
42
+ rc = SQLFreeHandle(SQL_HANDLE_DBC, conn)
43
+ check_error(rc, "Could not free Database handle")
44
+ end
45
+ end
46
+
47
+ def test_connection
48
+ @pool.hold {|conn|}
49
+ true
50
+ end
51
+
52
+ def dataset(opts = nil)
53
+ DB2::Dataset.new(self, opts)
54
+ end
55
+
56
+ def execute(sql, &block)
57
+ log_info(sql)
58
+ @pool.hold do |conn|
59
+ rc, sth = SQLAllocHandle(SQL_HANDLE_STMT, @handle)
60
+ check_error(rc, "Could not allocate statement")
61
+
62
+ begin
63
+ rc = SQLExecDirect(sth, sql)
64
+ check_error(rc, "Could not execute statement")
65
+
66
+ block[sth] if block
67
+
68
+ rc, rpc = SQLRowCount(sth)
69
+ check_error(rc, "Could not get RPC")
70
+ rpc
71
+ ensure
72
+ rc = SQLFreeHandle(SQL_HANDLE_STMT, sth)
73
+ check_error(rc, "Could not free statement")
74
+ end
75
+ end
76
+ end
77
+ alias_method :do, :execute
78
+ end
79
+
80
+ class Dataset < Sequel::Dataset
81
+ def literal(v)
82
+ case v
83
+ when Time
84
+ literal(v.iso8601)
85
+ when Date, DateTime
86
+ literal(v.to_s)
87
+ else
88
+ super
89
+ end
90
+ end
91
+
92
+ def fetch_rows(sql, &block)
93
+ @db.synchronize do
94
+ @db.execute(sql) do |sth|
95
+ @column_info = get_column_info(sth)
96
+ @columns = @column_info.map {|c| c[:name]}
97
+ while (rc = SQLFetch(@handle)) != SQL_NO_DATA_FOUND
98
+ @db.check_error(rc, "Could not fetch row")
99
+ yield hash_row(sth)
100
+ end
101
+ end
102
+ end
103
+ self
104
+ end
105
+
106
+ MAX_COL_SIZE = 256
107
+
108
+ def get_column_info(sth)
109
+ rc, column_count = SQLNumResultCols(sth)
110
+ @db.check_error(rc, "Could not get number of result columns")
111
+
112
+ (1..column_count).map do |i|
113
+ rc, name, buflen, datatype, size, digits, nullable = SQLDescribeCol(sth, i, MAX_COL_SIZE)
114
+ @b.check_error(rc, "Could not describe column")
115
+
116
+ {:name => name, :db2_type => datatype, :precision => size}
117
+ end
118
+ end
119
+
120
+ def hash_row(sth)
121
+ row = {}
122
+ @column_info.each_with_index do |c, i|
123
+ rc, v = SQLGetData(sth, i+1, c[:db2_type], c[:precision])
124
+ @db.check_error(rc, "Could not get data")
125
+
126
+ @row[c[:name]] = convert_type(v)
127
+ end
128
+ row
129
+ end
130
+
131
+ def convert_type(v)
132
+ case v
133
+ when DB2CLI::Date
134
+ DBI::Date.new(v.year, v.month, v.day)
135
+ when DB2CLI::Time
136
+ DBI::Time.new(v.hour, v.minute, v.second)
137
+ when DB2CLI::Timestamp
138
+ DBI::Timestamp.new(v.year, v.month, v.day,
139
+ v.hour, v.minute, v.second, v.fraction)
140
+ when DB2CLI::Null
141
+ nil
142
+ else
143
+ v
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,117 @@
1
+ require 'dbi'
2
+
3
+ module Sequel
4
+ module DBI
5
+ class Database < Sequel::Database
6
+ attr_writer :lowercase
7
+
8
+ set_adapter_scheme :dbi
9
+
10
+ DBI_ADAPTERS = {
11
+ :ado => "ADO",
12
+ :db2 => "DB2",
13
+ :frontbase => "FrontBase",
14
+ :interbase => "InterBase",
15
+ :msql => "Msql",
16
+ :mysql => "Mysql",
17
+ :odbc => "ODBC",
18
+ :oracle => "Oracle",
19
+ :pg => "pg",
20
+ :proxy => "Proxy",
21
+ :sqlite => "SQLite",
22
+ :sqlrelay => "SQLRelay"
23
+ }
24
+
25
+ # Converts a uri to an options hash. These options are then passed
26
+ # to a newly created database object.
27
+ def self.uri_to_options(uri) # :nodoc:
28
+ database = (m = /\/(.*)/.match(uri.path)) && (m[1])
29
+ if m = /dbi-(.+)/.match(uri.scheme)
30
+ adapter = DBI_ADAPTERS[m[1].to_sym] || m[1]
31
+ database = "#{adapter}:dbname=#{database}"
32
+ end
33
+ {
34
+ :user => uri.user,
35
+ :password => uri.password,
36
+ :host => uri.host,
37
+ :port => uri.port,
38
+ :database => database
39
+ }
40
+ end
41
+
42
+ private_class_method :uri_to_options
43
+
44
+ def connect
45
+ dbname = @opts[:database]
46
+ if dbname !~ /^DBI:/ then
47
+ dbname = "DBI:#{dbname}"
48
+ [:host, :port].each{|sym| dbname += ";#{sym}=#{@opts[sym]}" unless @opts[sym].blank?}
49
+ end
50
+ ::DBI.connect(dbname, @opts[:user], @opts[:password])
51
+ end
52
+
53
+ def disconnect
54
+ @pool.disconnect {|c| c.disconnect}
55
+ end
56
+
57
+ def dataset(opts = nil)
58
+ DBI::Dataset.new(self, opts)
59
+ end
60
+
61
+ def execute(sql)
62
+ log_info(sql)
63
+ @pool.hold do |conn|
64
+ conn.execute(sql)
65
+ end
66
+ end
67
+
68
+ def do(sql)
69
+ log_info(sql)
70
+ @pool.hold do |conn|
71
+ conn.do(sql)
72
+ end
73
+ end
74
+ alias_method :execute_dui, :do
75
+
76
+ # Converts all column names to lowercase
77
+ def lowercase
78
+ @lowercase ||= false
79
+ end
80
+ end
81
+
82
+ class Dataset < Sequel::Dataset
83
+ def literal(v)
84
+ case v
85
+ when Time
86
+ literal(v.iso8601)
87
+ when Date, DateTime
88
+ literal(v.to_s)
89
+ else
90
+ super
91
+ end
92
+ end
93
+
94
+ def fetch_rows(sql, &block)
95
+ @db.synchronize do
96
+ s = @db.execute sql
97
+ begin
98
+ @columns = s.column_names.map do |c|
99
+ @db.lowercase ? c.downcase.to_sym : c.to_sym
100
+ end
101
+ s.fetch {|r| yield hash_row(s, r)}
102
+ ensure
103
+ s.finish rescue nil
104
+ end
105
+ end
106
+ self
107
+ end
108
+
109
+ def hash_row(stmt, row)
110
+ @columns.inject({}) do |m, c|
111
+ m[c] = row.shift
112
+ m
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end