sequel 3.11.0 → 3.12.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 (136) hide show
  1. data/CHANGELOG +70 -0
  2. data/Rakefile +1 -1
  3. data/doc/active_record.rdoc +896 -0
  4. data/doc/advanced_associations.rdoc +46 -31
  5. data/doc/association_basics.rdoc +14 -9
  6. data/doc/dataset_basics.rdoc +3 -3
  7. data/doc/migration.rdoc +1011 -0
  8. data/doc/model_hooks.rdoc +198 -0
  9. data/doc/querying.rdoc +811 -86
  10. data/doc/release_notes/3.12.0.txt +304 -0
  11. data/doc/sharding.rdoc +17 -0
  12. data/doc/sql.rdoc +537 -0
  13. data/doc/validations.rdoc +501 -0
  14. data/lib/sequel/adapters/jdbc.rb +19 -27
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -7
  16. data/lib/sequel/adapters/mysql.rb +5 -4
  17. data/lib/sequel/adapters/odbc.rb +3 -2
  18. data/lib/sequel/adapters/shared/mssql.rb +7 -6
  19. data/lib/sequel/adapters/shared/mysql.rb +2 -7
  20. data/lib/sequel/adapters/shared/postgres.rb +2 -8
  21. data/lib/sequel/adapters/shared/sqlite.rb +2 -5
  22. data/lib/sequel/adapters/sqlite.rb +4 -4
  23. data/lib/sequel/core.rb +0 -1
  24. data/lib/sequel/database.rb +2 -1060
  25. data/lib/sequel/database/connecting.rb +227 -0
  26. data/lib/sequel/database/dataset.rb +58 -0
  27. data/lib/sequel/database/dataset_defaults.rb +127 -0
  28. data/lib/sequel/database/logging.rb +62 -0
  29. data/lib/sequel/database/misc.rb +246 -0
  30. data/lib/sequel/database/query.rb +390 -0
  31. data/lib/sequel/database/schema_generator.rb +7 -3
  32. data/lib/sequel/database/schema_methods.rb +351 -7
  33. data/lib/sequel/dataset/actions.rb +9 -2
  34. data/lib/sequel/dataset/misc.rb +6 -2
  35. data/lib/sequel/dataset/mutation.rb +3 -11
  36. data/lib/sequel/dataset/query.rb +49 -6
  37. data/lib/sequel/exceptions.rb +3 -0
  38. data/lib/sequel/extensions/migration.rb +395 -113
  39. data/lib/sequel/extensions/schema_dumper.rb +21 -13
  40. data/lib/sequel/model.rb +27 -25
  41. data/lib/sequel/model/associations.rb +72 -34
  42. data/lib/sequel/model/base.rb +74 -18
  43. data/lib/sequel/model/errors.rb +8 -1
  44. data/lib/sequel/plugins/active_model.rb +8 -0
  45. data/lib/sequel/plugins/association_pks.rb +87 -0
  46. data/lib/sequel/plugins/association_proxies.rb +8 -0
  47. data/lib/sequel/plugins/boolean_readers.rb +12 -6
  48. data/lib/sequel/plugins/caching.rb +14 -7
  49. data/lib/sequel/plugins/class_table_inheritance.rb +15 -9
  50. data/lib/sequel/plugins/composition.rb +2 -1
  51. data/lib/sequel/plugins/force_encoding.rb +10 -7
  52. data/lib/sequel/plugins/hook_class_methods.rb +12 -11
  53. data/lib/sequel/plugins/identity_map.rb +9 -0
  54. data/lib/sequel/plugins/instance_hooks.rb +23 -13
  55. data/lib/sequel/plugins/lazy_attributes.rb +4 -1
  56. data/lib/sequel/plugins/many_through_many.rb +18 -4
  57. data/lib/sequel/plugins/nested_attributes.rb +1 -0
  58. data/lib/sequel/plugins/optimistic_locking.rb +1 -1
  59. data/lib/sequel/plugins/rcte_tree.rb +9 -8
  60. data/lib/sequel/plugins/schema.rb +8 -0
  61. data/lib/sequel/plugins/serialization.rb +1 -3
  62. data/lib/sequel/plugins/sharding.rb +135 -0
  63. data/lib/sequel/plugins/single_table_inheritance.rb +117 -25
  64. data/lib/sequel/plugins/skip_create_refresh.rb +35 -0
  65. data/lib/sequel/plugins/string_stripper.rb +26 -0
  66. data/lib/sequel/plugins/tactical_eager_loading.rb +8 -0
  67. data/lib/sequel/plugins/timestamps.rb +15 -2
  68. data/lib/sequel/plugins/touch.rb +13 -0
  69. data/lib/sequel/plugins/update_primary_key.rb +48 -0
  70. data/lib/sequel/plugins/validation_class_methods.rb +8 -0
  71. data/lib/sequel/plugins/validation_helpers.rb +1 -1
  72. data/lib/sequel/sql.rb +17 -20
  73. data/lib/sequel/version.rb +1 -1
  74. data/spec/adapters/postgres_spec.rb +5 -5
  75. data/spec/core/core_sql_spec.rb +17 -1
  76. data/spec/core/database_spec.rb +17 -5
  77. data/spec/core/dataset_spec.rb +31 -8
  78. data/spec/core/schema_generator_spec.rb +8 -1
  79. data/spec/core/schema_spec.rb +13 -0
  80. data/spec/extensions/association_pks_spec.rb +85 -0
  81. data/spec/extensions/hook_class_methods_spec.rb +9 -9
  82. data/spec/extensions/migration_spec.rb +339 -219
  83. data/spec/extensions/schema_dumper_spec.rb +28 -17
  84. data/spec/extensions/sharding_spec.rb +272 -0
  85. data/spec/extensions/single_table_inheritance_spec.rb +92 -4
  86. data/spec/extensions/skip_create_refresh_spec.rb +17 -0
  87. data/spec/extensions/string_stripper_spec.rb +23 -0
  88. data/spec/extensions/update_primary_key_spec.rb +65 -0
  89. data/spec/extensions/validation_class_methods_spec.rb +5 -5
  90. data/spec/files/bad_down_migration/001_create_alt_basic.rb +4 -0
  91. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +4 -0
  92. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  93. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +9 -0
  94. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +3 -0
  95. data/spec/files/bad_up_migration/001_create_alt_basic.rb +4 -0
  96. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +3 -0
  97. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +9 -0
  98. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +9 -0
  99. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +4 -0
  100. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +9 -0
  101. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +9 -0
  102. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +4 -0
  103. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +4 -0
  104. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  105. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +9 -0
  106. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +4 -0
  107. data/spec/files/integer_migrations/001_create_sessions.rb +9 -0
  108. data/spec/files/integer_migrations/002_create_nodes.rb +9 -0
  109. data/spec/files/integer_migrations/003_3_create_users.rb +4 -0
  110. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  111. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +9 -0
  112. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +9 -0
  113. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +9 -0
  114. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +4 -0
  115. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +4 -0
  116. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +4 -0
  117. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  118. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +4 -0
  119. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +9 -0
  120. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +9 -0
  121. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +4 -0
  122. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +9 -0
  123. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +9 -0
  124. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +4 -0
  125. data/spec/integration/eager_loader_test.rb +20 -20
  126. data/spec/integration/migrator_test.rb +187 -0
  127. data/spec/integration/plugin_test.rb +150 -0
  128. data/spec/integration/schema_test.rb +13 -2
  129. data/spec/model/associations_spec.rb +41 -14
  130. data/spec/model/base_spec.rb +69 -0
  131. data/spec/model/eager_loading_spec.rb +7 -3
  132. data/spec/model/record_spec.rb +79 -4
  133. data/spec/model/validations_spec.rb +21 -9
  134. metadata +66 -5
  135. data/doc/schema.rdoc +0 -36
  136. data/lib/sequel/database/schema_sql.rb +0 -320
@@ -0,0 +1,227 @@
1
+ module Sequel
2
+ class Database
3
+ # ---------------------
4
+ # :section: Methods relating to adapters, connecting, disconnecting, and sharding
5
+ # This methods involve the Database's connection pool.
6
+ # ---------------------
7
+
8
+ # Array of supported database adapters
9
+ ADAPTERS = %w'ado amalgalite db2 dbi do firebird informix jdbc mysql odbc openbase oracle postgres sqlite'.collect{|x| x.to_sym}
10
+
11
+ # Whether to use the single threaded connection pool by default
12
+ @@single_threaded = false
13
+
14
+ # The Database subclass for the given adapter scheme.
15
+ # Raises Sequel::AdapterNotFound if the adapter
16
+ # could not be loaded.
17
+ def self.adapter_class(scheme)
18
+ scheme = scheme.to_s.gsub('-', '_').to_sym
19
+
20
+ unless klass = ADAPTER_MAP[scheme]
21
+ # attempt to load the adapter file
22
+ begin
23
+ Sequel.tsk_require "sequel/adapters/#{scheme}"
24
+ rescue LoadError => e
25
+ raise Sequel.convert_exception_class(e, AdapterNotFound)
26
+ end
27
+
28
+ # make sure we actually loaded the adapter
29
+ unless klass = ADAPTER_MAP[scheme]
30
+ raise AdapterNotFound, "Could not load #{scheme} adapter"
31
+ end
32
+ end
33
+ klass
34
+ end
35
+
36
+ # Returns the scheme for the Database class.
37
+ def self.adapter_scheme
38
+ @scheme
39
+ end
40
+
41
+ # Connects to a database. See Sequel.connect.
42
+ def self.connect(conn_string, opts = {})
43
+ case conn_string
44
+ when String
45
+ if match = /\A(jdbc|do):/o.match(conn_string)
46
+ c = adapter_class(match[1].to_sym)
47
+ opts = {:uri=>conn_string}.merge(opts)
48
+ else
49
+ uri = URI.parse(conn_string)
50
+ scheme = uri.scheme
51
+ scheme = :dbi if scheme =~ /\Adbi-/
52
+ c = adapter_class(scheme)
53
+ uri_options = c.send(:uri_to_options, uri)
54
+ uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
55
+ uri_options.entries.each{|k,v| uri_options[k] = URI.unescape(v) if v.is_a?(String)}
56
+ opts = uri_options.merge(opts)
57
+ end
58
+ when Hash
59
+ opts = conn_string.merge(opts)
60
+ c = adapter_class(opts[:adapter] || opts['adapter'])
61
+ else
62
+ raise Error, "Sequel::Database.connect takes either a Hash or a String, given: #{conn_string.inspect}"
63
+ end
64
+ # process opts a bit
65
+ opts = opts.inject({}) do |m, kv| k, v = *kv
66
+ k = :user if k.to_s == 'username'
67
+ m[k.to_sym] = v
68
+ m
69
+ end
70
+ begin
71
+ db = c.new(opts)
72
+ db.test_connection if opts[:test] && db.send(:typecast_value_boolean, opts[:test])
73
+ result = yield(db) if block_given?
74
+ ensure
75
+ if block_given?
76
+ db.disconnect if db
77
+ ::Sequel::DATABASES.delete(db)
78
+ end
79
+ end
80
+ block_given? ? result : db
81
+ end
82
+
83
+ # Sets the default single_threaded mode for new databases.
84
+ # See Sequel.single_threaded=.
85
+ def self.single_threaded=(value)
86
+ @@single_threaded = value
87
+ end
88
+
89
+ # Sets the adapter scheme for the Database class. Call this method in
90
+ # descendants of Database to allow connection using a URL. For example the
91
+ # following:
92
+ #
93
+ # class Sequel::MyDB::Database < Sequel::Database
94
+ # set_adapter_scheme :mydb
95
+ # ...
96
+ # end
97
+ #
98
+ # would allow connection using:
99
+ #
100
+ # Sequel.connect('mydb://user:password@dbserver/mydb')
101
+ def self.set_adapter_scheme(scheme) # :nodoc:
102
+ @scheme = scheme
103
+ ADAPTER_MAP[scheme.to_sym] = self
104
+ end
105
+ private_class_method :set_adapter_scheme
106
+
107
+ # The connection pool for this database
108
+ attr_reader :pool
109
+
110
+ # Dynamically add new servers or modify server options at runtime. Also adds new
111
+ # servers to the connection pool. Intended for use with master/slave or shard
112
+ # configurations where it is useful to add new server hosts at runtime.
113
+ #
114
+ # servers argument should be a hash with server name symbol keys and hash or
115
+ # proc values. If a servers key is already in use, it's value is overridden
116
+ # with the value provided.
117
+ #
118
+ # DB.add_servers(:f=>{:host=>"hash_host_f"})
119
+ def add_servers(servers)
120
+ @opts[:servers] = @opts[:servers] ? @opts[:servers].merge(servers) : servers
121
+ @pool.add_servers(servers.keys)
122
+ end
123
+
124
+ # Connects to the database. This method should be overridden by descendants.
125
+ def connect(server)
126
+ raise NotImplemented, "#connect should be overridden by adapters"
127
+ end
128
+
129
+ # The database type for this database object, the same as the adapter scheme
130
+ # by default. Should be overridden in adapters (especially shared adapters)
131
+ # to be the correct type, so that even if two separate Database objects are
132
+ # using different adapters you can tell that they are using the same database
133
+ # type. Even better, you can tell that two Database objects that are using
134
+ # the same adapter are connecting to different database types (think JDBC or
135
+ # DataObjects).
136
+ def database_type
137
+ self.class.adapter_scheme
138
+ end
139
+
140
+ # Disconnects all available connections from the connection pool. Any
141
+ # connections currently in use will not be disconnected. Options:
142
+ # * :servers - Should be a symbol specifing the server to disconnect from,
143
+ # or an array of symbols to specify multiple servers.
144
+ def disconnect(opts = {})
145
+ pool.disconnect(opts)
146
+ end
147
+
148
+ # Yield a new database object for every server in the connection pool.
149
+ # Intended for use in sharded environments where there is a need to make schema
150
+ # modifications (DDL queries) on each shard.
151
+ #
152
+ # DB.each_server{|db| db.create_table(:users){primary_key :id; String :name}}
153
+ def each_server(&block)
154
+ servers.each{|s| self.class.connect(server_opts(s), &block)}
155
+ end
156
+
157
+ # Dynamically remove existing servers from the connection pool. Intended for
158
+ # use with master/slave or shard configurations where it is useful to remove
159
+ # existing server hosts at runtime.
160
+ #
161
+ # servers should be symbols or arrays of symbols. If a nonexistent server
162
+ # is specified, it is ignored. If no servers have been specified for
163
+ # this database, no changes are made. If you attempt to remove the :default server,
164
+ # an error will be raised.
165
+ #
166
+ # DB.remove_servers(:f1, :f2)
167
+ def remove_servers(*servers)
168
+ if @opts[:servers] && !@opts[:servers].empty?
169
+ servs = @opts[:servers].dup
170
+ servers.flatten!
171
+ servers.each{|s| servs.delete(s)}
172
+ @opts[:servers] = servs
173
+ @pool.remove_servers(servers)
174
+ end
175
+ end
176
+
177
+ # An array of servers/shards for this Database object.
178
+ def servers
179
+ pool.servers
180
+ end
181
+
182
+ # Returns true if the database is using a single-threaded connection pool.
183
+ def single_threaded?
184
+ @single_threaded
185
+ end
186
+
187
+ # Acquires a database connection, yielding it to the passed block.
188
+ def synchronize(server=nil, &block)
189
+ @pool.hold(server || :default, &block)
190
+ end
191
+
192
+ # Attempts to acquire a database connection. Returns true if successful.
193
+ # Will probably raise an error if unsuccessful.
194
+ def test_connection(server=nil)
195
+ synchronize(server){|conn|}
196
+ true
197
+ end
198
+
199
+ private
200
+
201
+ # The default options for the connection pool.
202
+ def connection_pool_default_options
203
+ {}
204
+ end
205
+
206
+ # Return the options for the given server by merging the generic
207
+ # options for all server with the specific options for the given
208
+ # server specified in the :servers option.
209
+ def server_opts(server)
210
+ opts = if @opts[:servers] && server_options = @opts[:servers][server]
211
+ case server_options
212
+ when Hash
213
+ @opts.merge(server_options)
214
+ when Proc
215
+ @opts.merge(server_options.call(self))
216
+ else
217
+ raise Error, 'Server opts should be a hash or proc'
218
+ end
219
+ else
220
+ @opts.dup
221
+ end
222
+ opts.delete(:servers)
223
+ opts
224
+ end
225
+
226
+ end
227
+ end
@@ -0,0 +1,58 @@
1
+ module Sequel
2
+ class Database
3
+ # ---------------------
4
+ # :section: Methods that create datasets
5
+ # These methods all return instances of this database's dataset class.
6
+ # ---------------------
7
+
8
+ # Returns a dataset from the database. If the first argument is a string,
9
+ # the method acts as an alias for Database#fetch, returning a dataset for
10
+ # arbitrary SQL:
11
+ #
12
+ # DB['SELECT * FROM items WHERE name = ?', my_name].all
13
+ #
14
+ # Otherwise, acts as an alias for Database#from, setting the primary
15
+ # table for the dataset:
16
+ #
17
+ # DB[:items].sql #=> "SELECT * FROM items"
18
+ def [](*args)
19
+ (String === args.first) ? fetch(*args) : from(*args)
20
+ end
21
+
22
+ # Returns a blank dataset for this database
23
+ def dataset
24
+ ds = Sequel::Dataset.new(self)
25
+ end
26
+
27
+ # Fetches records for an arbitrary SQL statement. If a block is given,
28
+ # it is used to iterate over the records:
29
+ #
30
+ # DB.fetch('SELECT * FROM items'){|r| p r}
31
+ #
32
+ # The method returns a dataset instance:
33
+ #
34
+ # DB.fetch('SELECT * FROM items').all
35
+ #
36
+ # Fetch can also perform parameterized queries for protection against SQL
37
+ # injection:
38
+ #
39
+ # DB.fetch('SELECT * FROM items WHERE name = ?', my_name).all
40
+ def fetch(sql, *args, &block)
41
+ ds = dataset.with_sql(sql, *args)
42
+ ds.each(&block) if block
43
+ ds
44
+ end
45
+
46
+ # Returns a new dataset with the from method invoked. If a block is given,
47
+ # it is used as a filter on the dataset.
48
+ def from(*args, &block)
49
+ ds = dataset.from(*args)
50
+ block ? ds.filter(&block) : ds
51
+ end
52
+
53
+ # Returns a new dataset with the select method invoked.
54
+ def select(*args, &block)
55
+ dataset.select(*args, &block)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,127 @@
1
+ module Sequel
2
+ class Database
3
+ # ---------------------
4
+ # :section: Methods that set defaults for created datasets
5
+ # This methods change the default behavior of this database's datasets.
6
+ # ---------------------
7
+
8
+ # The identifier input method to use by default
9
+ @@identifier_input_method = nil
10
+
11
+ # The identifier output method to use by default
12
+ @@identifier_output_method = nil
13
+
14
+ # Whether to quote identifiers (columns and tables) by default
15
+ @@quote_identifiers = nil
16
+
17
+ # The method to call on identifiers going into the database
18
+ def self.identifier_input_method
19
+ @@identifier_input_method
20
+ end
21
+
22
+ # Set the method to call on identifiers going into the database
23
+ # See Sequel.identifier_input_method=.
24
+ def self.identifier_input_method=(v)
25
+ @@identifier_input_method = v || ""
26
+ end
27
+
28
+ # The method to call on identifiers coming from the database
29
+ def self.identifier_output_method
30
+ @@identifier_output_method
31
+ end
32
+
33
+ # Set the method to call on identifiers coming from the database
34
+ # See Sequel.identifier_output_method=.
35
+ def self.identifier_output_method=(v)
36
+ @@identifier_output_method = v || ""
37
+ end
38
+
39
+ # Sets the default quote_identifiers mode for new databases.
40
+ # See Sequel.quote_identifiers=.
41
+ def self.quote_identifiers=(value)
42
+ @@quote_identifiers = value
43
+ end
44
+
45
+ # The default schema to use, generally should be nil.
46
+ attr_accessor :default_schema
47
+
48
+ # The method to call on identifiers going into the database
49
+ def identifier_input_method
50
+ case @identifier_input_method
51
+ when nil
52
+ @identifier_input_method = @opts.fetch(:identifier_input_method, (@@identifier_input_method.nil? ? identifier_input_method_default : @@identifier_input_method))
53
+ @identifier_input_method == "" ? nil : @identifier_input_method
54
+ when ""
55
+ nil
56
+ else
57
+ @identifier_input_method
58
+ end
59
+ end
60
+
61
+ # Set the method to call on identifiers going into the database
62
+ def identifier_input_method=(v)
63
+ reset_schema_utility_dataset
64
+ @identifier_input_method = v || ""
65
+ end
66
+
67
+ # The method to call on identifiers coming from the database
68
+ def identifier_output_method
69
+ case @identifier_output_method
70
+ when nil
71
+ @identifier_output_method = @opts.fetch(:identifier_output_method, (@@identifier_output_method.nil? ? identifier_output_method_default : @@identifier_output_method))
72
+ @identifier_output_method == "" ? nil : @identifier_output_method
73
+ when ""
74
+ nil
75
+ else
76
+ @identifier_output_method
77
+ end
78
+ end
79
+
80
+ # Set the method to call on identifiers coming from the database
81
+ def identifier_output_method=(v)
82
+ reset_schema_utility_dataset
83
+ @identifier_output_method = v || ""
84
+ end
85
+
86
+ # Whether to quote identifiers (columns and tables) for this database
87
+ def quote_identifiers=(v)
88
+ reset_schema_utility_dataset
89
+ @quote_identifiers = v
90
+ end
91
+
92
+ # Returns true if the database quotes identifiers.
93
+ def quote_identifiers?
94
+ return @quote_identifiers unless @quote_identifiers.nil?
95
+ @quote_identifiers = @opts.fetch(:quote_identifiers, (@@quote_identifiers.nil? ? quote_identifiers_default : @@quote_identifiers))
96
+ end
97
+
98
+ private
99
+
100
+ # The default value for default_schema.
101
+ def default_schema_default
102
+ nil
103
+ end
104
+
105
+ # The method to apply to identifiers going into the database by default.
106
+ # Should be overridden in subclasses for databases that fold unquoted
107
+ # identifiers to lower case instead of uppercase, such as
108
+ # MySQL, PostgreSQL, and SQLite.
109
+ def identifier_input_method_default
110
+ :upcase
111
+ end
112
+
113
+ # The method to apply to identifiers coming the database by default.
114
+ # Should be overridden in subclasses for databases that fold unquoted
115
+ # identifiers to lower case instead of uppercase, such as
116
+ # MySQL, PostgreSQL, and SQLite.
117
+ def identifier_output_method_default
118
+ :downcase
119
+ end
120
+
121
+ # Whether to quote identifiers by default for this database, true
122
+ # by default.
123
+ def quote_identifiers_default
124
+ true
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,62 @@
1
+ module Sequel
2
+ class Database
3
+ # ---------------------
4
+ # :section: Methods relating to logging
5
+ # This methods affect relating to the logging of executed SQL.
6
+ # ---------------------
7
+
8
+ # Numeric specifying the duration beyond which queries are logged at warn
9
+ # level instead of info level.
10
+ attr_accessor :log_warn_duration
11
+
12
+ # Array of SQL loggers to use for this database
13
+ attr_accessor :loggers
14
+
15
+ # Log a message at level info to all loggers.
16
+ def log_info(message, args=nil)
17
+ log_each(:info, args ? "#{message}; #{args.inspect}" : message)
18
+ end
19
+
20
+ # Yield to the block, logging any errors at error level to all loggers,
21
+ # and all other queries with the duration at warn or info level.
22
+ def log_yield(sql, args=nil)
23
+ return yield if @loggers.empty?
24
+ sql = "#{sql}; #{args.inspect}" if args
25
+ start = Time.now
26
+ begin
27
+ yield
28
+ rescue => e
29
+ log_each(:error, "#{e.class}: #{e.message.strip}: #{sql}")
30
+ raise
31
+ ensure
32
+ log_duration(Time.now - start, sql) unless e
33
+ end
34
+ end
35
+
36
+ # Remove any existing loggers and just use the given logger.
37
+ def logger=(logger)
38
+ @loggers = Array(logger)
39
+ end
40
+
41
+ private
42
+
43
+ # Log the given SQL and then execute it on the connection, used by
44
+ # the transaction code.
45
+ def log_connection_execute(conn, sql)
46
+ log_yield(sql){conn.send(connection_execute_method, sql)}
47
+ end
48
+
49
+ # Log message with message prefixed by duration at info level, or
50
+ # warn level if duration is greater than log_warn_duration.
51
+ def log_duration(duration, message)
52
+ log_each((lwd = log_warn_duration and duration >= lwd) ? :warn : :info, "(#{sprintf('%0.6fs', duration)}) #{message}")
53
+ end
54
+
55
+ # Log message at level (which should be :error, :warn, or :info)
56
+ # to all loggers.
57
+ def log_each(level, message)
58
+ @loggers.each{|logger| logger.send(level, message)}
59
+ end
60
+
61
+ end
62
+ end