sequel 2.2.0 → 2.3.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 (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