sequel 3.21.0 → 3.28.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 (158) hide show
  1. data/CHANGELOG +294 -0
  2. data/README.rdoc +20 -6
  3. data/Rakefile +20 -15
  4. data/doc/association_basics.rdoc +210 -43
  5. data/doc/dataset_basics.rdoc +4 -4
  6. data/doc/mass_assignment.rdoc +54 -0
  7. data/doc/migration.rdoc +15 -538
  8. data/doc/model_hooks.rdoc +64 -27
  9. data/doc/opening_databases.rdoc +37 -10
  10. data/doc/prepared_statements.rdoc +16 -10
  11. data/doc/reflection.rdoc +8 -2
  12. data/doc/release_notes/3.22.0.txt +39 -0
  13. data/doc/release_notes/3.23.0.txt +172 -0
  14. data/doc/release_notes/3.24.0.txt +420 -0
  15. data/doc/release_notes/3.25.0.txt +88 -0
  16. data/doc/release_notes/3.26.0.txt +88 -0
  17. data/doc/release_notes/3.27.0.txt +82 -0
  18. data/doc/release_notes/3.28.0.txt +304 -0
  19. data/doc/schema_modification.rdoc +547 -0
  20. data/doc/testing.rdoc +106 -0
  21. data/doc/transactions.rdoc +97 -0
  22. data/doc/virtual_rows.rdoc +2 -2
  23. data/lib/sequel/adapters/ado.rb +12 -1
  24. data/lib/sequel/adapters/amalgalite.rb +4 -0
  25. data/lib/sequel/adapters/db2.rb +95 -58
  26. data/lib/sequel/adapters/do.rb +12 -0
  27. data/lib/sequel/adapters/firebird.rb +25 -203
  28. data/lib/sequel/adapters/ibmdb.rb +440 -0
  29. data/lib/sequel/adapters/informix.rb +4 -19
  30. data/lib/sequel/adapters/jdbc/as400.rb +0 -7
  31. data/lib/sequel/adapters/jdbc/db2.rb +49 -0
  32. data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
  33. data/lib/sequel/adapters/jdbc/h2.rb +16 -5
  34. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  35. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  36. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  37. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  38. data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
  39. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
  40. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  41. data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
  42. data/lib/sequel/adapters/jdbc.rb +62 -29
  43. data/lib/sequel/adapters/mysql.rb +22 -139
  44. data/lib/sequel/adapters/mysql2.rb +9 -14
  45. data/lib/sequel/adapters/odbc/db2.rb +21 -0
  46. data/lib/sequel/adapters/odbc.rb +15 -3
  47. data/lib/sequel/adapters/oracle.rb +17 -1
  48. data/lib/sequel/adapters/postgres.rb +111 -16
  49. data/lib/sequel/adapters/shared/access.rb +21 -0
  50. data/lib/sequel/adapters/shared/db2.rb +290 -0
  51. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  52. data/lib/sequel/adapters/shared/informix.rb +45 -0
  53. data/lib/sequel/adapters/shared/mssql.rb +85 -47
  54. data/lib/sequel/adapters/shared/mysql.rb +50 -7
  55. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
  56. data/lib/sequel/adapters/shared/oracle.rb +0 -4
  57. data/lib/sequel/adapters/shared/postgres.rb +75 -43
  58. data/lib/sequel/adapters/shared/sqlite.rb +56 -8
  59. data/lib/sequel/adapters/sqlite.rb +12 -11
  60. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  61. data/lib/sequel/adapters/tinytds.rb +139 -7
  62. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
  63. data/lib/sequel/ast_transformer.rb +190 -0
  64. data/lib/sequel/connection_pool/threaded.rb +3 -2
  65. data/lib/sequel/connection_pool.rb +1 -1
  66. data/lib/sequel/core.rb +6 -5
  67. data/lib/sequel/database/connecting.rb +5 -5
  68. data/lib/sequel/database/dataset.rb +1 -1
  69. data/lib/sequel/database/dataset_defaults.rb +1 -1
  70. data/lib/sequel/database/logging.rb +1 -1
  71. data/lib/sequel/database/misc.rb +38 -17
  72. data/lib/sequel/database/query.rb +50 -19
  73. data/lib/sequel/database/schema_generator.rb +8 -5
  74. data/lib/sequel/database/schema_methods.rb +52 -27
  75. data/lib/sequel/dataset/actions.rb +167 -48
  76. data/lib/sequel/dataset/features.rb +57 -8
  77. data/lib/sequel/dataset/graph.rb +1 -1
  78. data/lib/sequel/dataset/misc.rb +39 -20
  79. data/lib/sequel/dataset/mutation.rb +3 -3
  80. data/lib/sequel/dataset/prepared_statements.rb +29 -14
  81. data/lib/sequel/dataset/query.rb +182 -32
  82. data/lib/sequel/dataset/sql.rb +31 -58
  83. data/lib/sequel/dataset.rb +8 -0
  84. data/lib/sequel/exceptions.rb +4 -0
  85. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  86. data/lib/sequel/extensions/migration.rb +6 -4
  87. data/lib/sequel/extensions/to_dot.rb +95 -83
  88. data/lib/sequel/model/associations.rb +893 -309
  89. data/lib/sequel/model/base.rb +302 -105
  90. data/lib/sequel/model/errors.rb +1 -1
  91. data/lib/sequel/model/exceptions.rb +5 -1
  92. data/lib/sequel/model.rb +13 -7
  93. data/lib/sequel/plugins/association_pks.rb +22 -4
  94. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  95. data/lib/sequel/plugins/identity_map.rb +113 -6
  96. data/lib/sequel/plugins/many_through_many.rb +67 -5
  97. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  98. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  99. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  100. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  101. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  102. data/lib/sequel/plugins/sharding.rb +12 -20
  103. data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
  104. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  105. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  106. data/lib/sequel/sql.rb +107 -51
  107. data/lib/sequel/timezones.rb +12 -3
  108. data/lib/sequel/version.rb +1 -1
  109. data/spec/adapters/db2_spec.rb +146 -0
  110. data/spec/adapters/mssql_spec.rb +36 -0
  111. data/spec/adapters/mysql_spec.rb +36 -19
  112. data/spec/adapters/postgres_spec.rb +115 -28
  113. data/spec/adapters/spec_helper.rb +6 -0
  114. data/spec/adapters/sqlite_spec.rb +11 -0
  115. data/spec/core/connection_pool_spec.rb +62 -77
  116. data/spec/core/database_spec.rb +244 -287
  117. data/spec/core/dataset_spec.rb +383 -34
  118. data/spec/core/expression_filters_spec.rb +159 -41
  119. data/spec/core/schema_spec.rb +326 -3
  120. data/spec/core/spec_helper.rb +45 -0
  121. data/spec/extensions/association_pks_spec.rb +38 -0
  122. data/spec/extensions/columns_introspection_spec.rb +91 -0
  123. data/spec/extensions/defaults_setter_spec.rb +64 -0
  124. data/spec/extensions/identity_map_spec.rb +162 -0
  125. data/spec/extensions/many_through_many_spec.rb +195 -20
  126. data/spec/extensions/migration_spec.rb +17 -17
  127. data/spec/extensions/nested_attributes_spec.rb +1 -0
  128. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  129. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  130. data/spec/extensions/prepared_statements_spec.rb +72 -0
  131. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  132. data/spec/extensions/schema_dumper_spec.rb +2 -2
  133. data/spec/extensions/schema_spec.rb +12 -20
  134. data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
  135. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  136. data/spec/extensions/spec_helper.rb +3 -1
  137. data/spec/extensions/to_dot_spec.rb +3 -5
  138. data/spec/extensions/xml_serializer_spec.rb +16 -4
  139. data/spec/integration/associations_test.rb +405 -15
  140. data/spec/integration/database_test.rb +4 -2
  141. data/spec/integration/dataset_test.rb +240 -20
  142. data/spec/integration/plugin_test.rb +142 -5
  143. data/spec/integration/prepared_statement_test.rb +174 -95
  144. data/spec/integration/schema_test.rb +128 -16
  145. data/spec/integration/spec_helper.rb +15 -0
  146. data/spec/integration/transaction_test.rb +40 -0
  147. data/spec/integration/type_test.rb +16 -2
  148. data/spec/model/association_reflection_spec.rb +91 -0
  149. data/spec/model/associations_spec.rb +476 -5
  150. data/spec/model/base_spec.rb +91 -1
  151. data/spec/model/eager_loading_spec.rb +519 -31
  152. data/spec/model/hooks_spec.rb +161 -0
  153. data/spec/model/model_spec.rb +89 -2
  154. data/spec/model/plugins_spec.rb +17 -0
  155. data/spec/model/record_spec.rb +184 -12
  156. data/spec/model/spec_helper.rb +5 -0
  157. data/spec/model/validations_spec.rb +11 -0
  158. metadata +85 -34
@@ -1,109 +1,64 @@
1
1
  require 'fb'
2
+ Sequel.require 'adapters/shared/firebird'
2
3
 
3
4
  module Sequel
4
5
  # The Sequel Firebird adapter requires the ruby fb driver located at
5
6
  # http://github.com/wishdev/fb.
6
7
  module Firebird
7
8
  class Database < Sequel::Database
9
+ include Sequel::Firebird::DatabaseMethods
10
+
8
11
  set_adapter_scheme :firebird
9
12
 
10
- AUTO_INCREMENT = ''.freeze
11
- TEMPORARY = 'GLOBAL TEMPORARY '.freeze
13
+ DISCONNECT_ERRORS = /Unsuccessful execution caused by a system error that precludes successful execution of subsequent statements/
12
14
 
13
- # Add the primary_keys and primary_key_sequences instance variables,
15
+ # Add the primary_keys instance variables.
14
16
  # so we can get the correct return values for inserted rows.
15
17
  def initialize(*args)
16
18
  super
17
19
  @primary_keys = {}
18
- @primary_key_sequences = {}
19
20
  end
20
21
 
21
22
  def connect(server)
22
23
  opts = server_opts(server)
23
24
 
24
- db = Fb::Database.new(
25
+ Fb::Database.new(
25
26
  :database => "#{opts[:host]}:#{opts[:database]}",
26
27
  :username => opts[:user],
27
- :password => opts[:password])
28
- conn = db.connect
29
- conn.downcase_names = true
30
- conn
31
- end
32
-
33
- def create_trigger(*args)
34
- self << create_trigger_sql(*args)
28
+ :password => opts[:password]).connect
35
29
  end
36
30
 
37
31
  def dataset(opts = nil)
38
32
  Firebird::Dataset.new(self, opts)
39
33
  end
40
34
 
41
- def drop_sequence(name)
42
- self << drop_sequence_sql(name)
43
- end
44
-
45
35
  def execute(sql, opts={})
46
36
  begin
47
37
  synchronize(opts[:server]) do |conn|
38
+ if conn.transaction_started && !@transactions.include?(Thread.current)
39
+ conn.rollback
40
+ raise DatabaseDisconnectError, "transaction accidently left open, rolling back and disconnecting"
41
+ end
48
42
  r = log_yield(sql){conn.execute(sql)}
49
43
  yield(r) if block_given?
50
44
  r
51
45
  end
52
- rescue => e
53
- raise_error(e, :classes=>[Fb::Error])
46
+ rescue Fb::Error => e
47
+ raise_error(e, :disconnect=>DISCONNECT_ERRORS.match(e.message))
54
48
  end
55
49
  end
56
50
 
57
- # Return primary key for the given table.
58
- def primary_key(table, server=nil)
59
- synchronize(server){|conn| primary_key_for_table(conn, table)}
60
- end
61
-
62
- # Returns primary key for the given table. This information is
63
- # cached, and if the primary key for a table is changed, the
64
- # @primary_keys instance variable should be reset manually.
65
- def primary_key_for_table(conn, table)
66
- @primary_keys[quote_identifier(table)] ||= conn.table_primary_key(quote_identifier(table))
67
- end
68
-
69
- def restart_sequence(*args)
70
- self << restart_sequence_sql(*args)
71
- end
72
-
73
- def sequences(opts={})
74
- ds = self[:"rdb$generators"].server(opts[:server]).filter(:"rdb$system_flag" => 0).select(:"rdb$generator_name")
75
- block_given? ? yield(ds) : ds.map{|r| ds.send(:output_identifier, r[:"rdb$generator_name"])}
76
- end
77
-
78
- def tables(opts={})
79
- ds = self[:"rdb$relations"].server(opts[:server]).filter(:"rdb$view_blr" => nil, Sequel::SQL::Function.new(:COALESCE, :"rdb$system_flag", 0) => 0).select(:"rdb$relation_name")
80
- block_given? ? yield(ds) : ds.map{|r| ds.send(:output_identifier, r[:"rdb$relation_name"])}
81
- end
82
-
83
51
  private
84
52
 
85
- # Use Firebird specific syntax for add column
86
- def alter_table_sql(table, op)
87
- case op[:op]
88
- when :add_column
89
- "ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
90
- when :drop_column
91
- "ALTER TABLE #{quote_schema_table(table)} DROP #{column_definition_sql(op)}"
92
- when :rename_column
93
- "ALTER TABLE #{quote_schema_table(table)} ALTER #{quote_identifier(op[:name])} TO #{quote_identifier(op[:new_name])}"
94
- when :set_column_type
95
- "ALTER TABLE #{quote_schema_table(table)} ALTER #{quote_identifier(op[:name])} TYPE #{type_literal(op)}"
96
- else
97
- super(table, op)
98
- end
99
- end
100
-
101
- def auto_increment_sql()
102
- AUTO_INCREMENT
103
- end
104
-
105
53
  def begin_transaction(conn, opts={})
106
- log_yield(TRANSACTION_BEGIN){conn.transaction}
54
+ log_yield(TRANSACTION_BEGIN) do
55
+ begin
56
+ conn.transaction
57
+ rescue Fb::Error => e
58
+ conn.rollback
59
+ raise_error(e, :disconnect=>true)
60
+ end
61
+ end
107
62
  conn
108
63
  end
109
64
 
@@ -111,60 +66,6 @@ module Sequel
111
66
  log_yield(TRANSACTION_COMMIT){conn.commit}
112
67
  end
113
68
 
114
- def create_sequence_sql(name, opts={})
115
- "CREATE SEQUENCE #{quote_identifier(name)}"
116
- end
117
-
118
- # Firebird gets an override because of the mess of creating a
119
- # sequence and trigger for auto-incrementing primary keys.
120
- def create_table_from_generator(name, generator, options)
121
- drop_statement, create_statements = create_table_sql_list(name, generator, options)
122
- (execute_ddl(drop_statement) rescue nil) if drop_statement
123
- create_statements.each{|sql| execute_ddl(sql)}
124
- end
125
-
126
- def create_table_sql_list(name, generator, options={})
127
- statements = [create_table_sql(name, generator, options)]
128
- drop_seq_statement = nil
129
- generator.columns.each do |c|
130
- if c[:auto_increment]
131
- c[:sequence_name] ||= "seq_#{name}_#{c[:name]}"
132
- unless c[:create_sequence] == false
133
- drop_seq_statement = drop_sequence_sql(c[:sequence_name])
134
- statements << create_sequence_sql(c[:sequence_name])
135
- statements << restart_sequence_sql(c[:sequence_name], {:restart_position => c[:sequence_start_position]}) if c[:sequence_start_position]
136
- end
137
- unless c[:create_trigger] == false
138
- c[:trigger_name] ||= "BI_#{name}_#{c[:name]}"
139
- c[:quoted_name] = quote_identifier(c[:name])
140
- trigger_definition = <<-END
141
- begin
142
- if ((new.#{c[:quoted_name]} is null) or (new.#{c[:quoted_name]} = 0)) then
143
- begin
144
- new.#{c[:quoted_name]} = next value for #{c[:sequence_name]};
145
- end
146
- end
147
- END
148
- statements << create_trigger_sql(name, c[:trigger_name], trigger_definition, {:events => [:insert]})
149
- end
150
- end
151
- end
152
- [drop_seq_statement, statements]
153
- end
154
-
155
- def create_trigger_sql(table, name, definition, opts={})
156
- events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
157
- whence = opts[:after] ? 'AFTER' : 'BEFORE'
158
- inactive = opts[:inactive] ? 'INACTIVE' : 'ACTIVE'
159
- position = opts.fetch(:position, 0)
160
- sql = <<-end_sql
161
- CREATE TRIGGER #{quote_identifier(name)} for #{quote_identifier(table)}
162
- #{inactive} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} position #{position}
163
- as #{definition}
164
- end_sql
165
- sql
166
- end
167
-
168
69
  def database_error_classes
169
70
  [Fb::Error]
170
71
  end
@@ -173,41 +74,24 @@ module Sequel
173
74
  c.close
174
75
  end
175
76
 
176
- def drop_sequence_sql(name)
177
- "DROP SEQUENCE #{quote_identifier(name)}"
178
- end
179
-
180
- def restart_sequence_sql(name, opts={})
181
- seq_name = quote_identifier(name)
182
- "ALTER SEQUENCE #{seq_name} RESTART WITH #{opts[:restart_position]}"
183
- end
184
-
185
77
  def rollback_transaction(conn, opts={})
186
78
  log_yield(TRANSACTION_ROLLBACK){conn.rollback}
187
79
  end
188
-
189
- def type_literal_generic_string(column)
190
- column[:text] ? :"BLOB SUB_TYPE TEXT" : super
191
- end
192
80
  end
193
81
 
194
82
  # Dataset class for Firebird datasets
195
83
  class Dataset < Sequel::Dataset
196
- BOOL_TRUE = '1'.freeze
197
- BOOL_FALSE = '0'.freeze
198
- NULL = LiteralString.new('NULL').freeze
199
- COMMA_SEPARATOR = ', '.freeze
200
- SELECT_CLAUSE_METHODS = clause_methods(:select, %w'with distinct limit columns from join where group having compounds order')
84
+ include Sequel::Firebird::DatasetMethods
201
85
 
202
86
  # Yield all rows returned by executing the given SQL and converting
203
87
  # the types.
204
88
  def fetch_rows(sql)
205
89
  execute(sql) do |s|
206
90
  begin
207
- @columns = s.fields.map{|c| output_identifier(c.name)}
208
- s.fetchall(:symbols_hash).each do |r|
91
+ @columns = columns = s.fields.map{|c| output_identifier(c.name)}
92
+ s.fetchall.each do |r|
209
93
  h = {}
210
- r.each{|k,v| h[output_identifier(k)] = v}
94
+ r.zip(columns).each{|v, c| h[c] = v}
211
95
  yield h
212
96
  end
213
97
  ensure
@@ -216,68 +100,6 @@ module Sequel
216
100
  end
217
101
  self
218
102
  end
219
-
220
- # Insert given values into the database.
221
- def insert(*values)
222
- if !@opts[:sql]
223
- clone(default_server_opts(:sql=>insert_returning_pk_sql(*values))).single_value
224
- else
225
- execute_insert(insert_sql(*values), :table=>opts[:from].first,
226
- :values=>values.size == 1 ? values.first : values)
227
- end
228
- end
229
-
230
- # Use the RETURNING clause to return the primary key of the inserted record, if it exists
231
- def insert_returning_pk_sql(*values)
232
- pk = db.primary_key(opts[:from].first)
233
- insert_returning_sql(pk ? Sequel::SQL::Identifier.new(pk) : NULL, *values)
234
- end
235
-
236
- # Use the RETURNING clause to return the columns listed in returning.
237
- def insert_returning_sql(returning, *values)
238
- "#{insert_sql(*values)} RETURNING #{column_list(Array(returning))}"
239
- end
240
-
241
- # Insert a record returning the record inserted
242
- def insert_select(*values)
243
- naked.clone(default_server_opts(:sql=>insert_returning_sql(nil, *values))).single_record
244
- end
245
-
246
- def requires_sql_standard_datetimes?
247
- true
248
- end
249
-
250
- # The order of clauses in the SELECT SQL statement
251
- def select_clause_methods
252
- SELECT_CLAUSE_METHODS
253
- end
254
-
255
- def select_limit_sql(sql)
256
- sql << " FIRST #{@opts[:limit]}" if @opts[:limit]
257
- sql << " SKIP #{@opts[:offset]}" if @opts[:offset]
258
- end
259
-
260
- # Firebird does not support INTERSECT or EXCEPT
261
- def supports_intersect_except?
262
- false
263
- end
264
-
265
- private
266
-
267
- def hash_row(stmt, row)
268
- @columns.inject({}) do |m, c|
269
- m[c] = row.shift
270
- m
271
- end
272
- end
273
-
274
- def literal_false
275
- BOOL_FALSE
276
- end
277
-
278
- def literal_true
279
- BOOL_TRUE
280
- end
281
103
  end
282
104
  end
283
105
  end