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
@@ -0,0 +1,290 @@
1
+ Sequel.require 'adapters/utils/emulate_offset_with_row_number'
2
+
3
+ module Sequel
4
+ module DB2
5
+ @use_clob_as_blob = true
6
+
7
+ class << self
8
+ # Whether to use clob as the generic File type, true by default.
9
+ attr_accessor :use_clob_as_blob
10
+ end
11
+
12
+ module DatabaseMethods
13
+ AUTOINCREMENT = 'GENERATED ALWAYS AS IDENTITY'.freeze
14
+ NOT_NULL = ' NOT NULL'.freeze
15
+ NULL = ''.freeze
16
+
17
+ # DB2 always uses :db2 as it's database type
18
+ def database_type
19
+ :db2
20
+ end
21
+
22
+ # Return the database version as a string. Don't rely on this,
23
+ # it may return an integer in the future.
24
+ def db2_version
25
+ return @db2_version if @db2_version
26
+ @db2_version = metadata_dataset.with_sql("select service_level from sysibmadm.env_inst_info").first[:service_level]
27
+ end
28
+ alias_method :server_version, :db2_version
29
+
30
+ # Use SYSIBM.SYSCOLUMNS to get the information on the tables.
31
+ def schema_parse_table(table, opts = {})
32
+ m = output_identifier_meth
33
+ im = input_identifier_meth
34
+ metadata_dataset.with_sql("SELECT * FROM SYSIBM.SYSCOLUMNS WHERE TBNAME = #{literal(im.call(table))} ORDER BY COLNO").
35
+ collect do |column|
36
+ column[:db_type] = column.delete(:typename)
37
+ if column[:db_type] == "DECIMAL"
38
+ column[:db_type] << "(#{column[:longlength]},#{column[:scale]})"
39
+ end
40
+ column[:allow_null] = column.delete(:nulls) == 'Y'
41
+ column[:primary_key] = column.delete(:identity) == 'Y' || !column[:keyseq].nil?
42
+ column[:type] = schema_column_type(column[:db_type])
43
+ [ m.call(column.delete(:name)), column]
44
+ end
45
+ end
46
+
47
+ # Use SYSCAT.TABLES to get the tables for the database
48
+ def tables
49
+ metadata_dataset.
50
+ with_sql("SELECT TABNAME FROM SYSCAT.TABLES WHERE TYPE='T' AND OWNER = #{literal(input_identifier_meth.call(opts[:user]))}").
51
+ all.map{|h| output_identifier_meth.call(h[:tabname]) }
52
+ end
53
+
54
+ # Use SYSCAT.TABLES to get the views for the database
55
+ def views
56
+ metadata_dataset.
57
+ with_sql("SELECT TABNAME FROM SYSCAT.TABLES WHERE TYPE='V' AND OWNER = #{literal(input_identifier_meth.call(opts[:user]))}").
58
+ all.map{|h| output_identifier_meth.call(h[:tabname]) }
59
+ end
60
+
61
+ # Use SYSCAT.INDEXES to get the indexes for the table
62
+ def indexes(table, opts = {})
63
+ metadata_dataset.
64
+ with_sql("SELECT INDNAME,UNIQUERULE,MADE_UNIQUE,SYSTEM_REQUIRED FROM SYSCAT.INDEXES WHERE TABNAME = #{literal(input_identifier_meth.call(table))}").
65
+ all.map{|h| Hash[ h.map{|k,v| [k.to_sym, v]} ] }
66
+ end
67
+
68
+ private
69
+
70
+ # Handle DB2 specific alter table operations.
71
+ def alter_table_sql(table, op)
72
+ case op[:op]
73
+ when :add_column
74
+ if op[:primary_key] && op[:auto_increment] && op[:type] == Integer
75
+ [
76
+ "ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op.merge(:auto_increment=>false, :primary_key=>false, :default=>0, :null=>false))}",
77
+ "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{literal(op[:name])} DROP DEFAULT",
78
+ "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{literal(op[:name])} SET #{AUTOINCREMENT}"
79
+ ]
80
+ else
81
+ "ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
82
+ end
83
+ when :drop_column
84
+ "ALTER TABLE #{quote_schema_table(table)} DROP #{column_definition_sql(op)}"
85
+ when :rename_column # renaming is only possible after db2 v9.7
86
+ "ALTER TABLE #{quote_schema_table(table)} RENAME COLUMN #{quote_identifier(op[:name])} TO #{quote_identifier(op[:new_name])}"
87
+ when :set_column_type
88
+ "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} SET DATA TYPE #{type_literal(op)}"
89
+ when :set_column_default
90
+ "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} SET DEFAULT #{literal(op[:default])}"
91
+ when :add_constraint
92
+ if op[:type] == :unique
93
+ sqls = op[:columns].map{|c| ["ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(c)} SET NOT NULL", reorg_sql(table)]}
94
+ sqls << super
95
+ sqls.flatten
96
+ else
97
+ super
98
+ end
99
+ else
100
+ super
101
+ end
102
+ end
103
+
104
+ # DB2 uses an identity column for autoincrement.
105
+ def auto_increment_sql
106
+ AUTOINCREMENT
107
+ end
108
+
109
+ # Add null/not null SQL fragment to column creation SQL.
110
+ def column_definition_null_sql(sql, column)
111
+ null = column.fetch(:null, column[:allow_null])
112
+ null = false if column[:primary_key]
113
+
114
+ sql << NOT_NULL if null == false
115
+ sql << NULL if null == true
116
+ end
117
+
118
+ # Supply columns with NOT NULL if they are part of a composite
119
+ # primary/foreign key
120
+ def column_list_sql(g)
121
+ ks = []
122
+ g.constraints.each{|c| ks = c[:columns] if [:primary_key, :foreign_key].include? c[:type]}
123
+ g.columns.each{|c| c[:null] = false if ks.include?(c[:name]) }
124
+ super
125
+ end
126
+
127
+ # Here we use DGTT which has most backward compatibility, which uses
128
+ # DECLARE instead of CREATE. CGTT can only be used after version 9.7.
129
+ # http://www.ibm.com/developerworks/data/library/techarticle/dm-0912globaltemptable/
130
+ def create_table_sql(name, generator, options)
131
+ if options[:temp]
132
+ "DECLARE GLOBAL TEMPORARY TABLE #{quote_identifier(name)} (#{column_list_sql(generator)})"
133
+ else
134
+ super
135
+ end
136
+ end
137
+
138
+ # DB2 has issues with quoted identifiers, so
139
+ # turn off database quoting by default.
140
+ def quote_identifiers_default
141
+ false
142
+ end
143
+
144
+ # DB2 uses RENAME TABLE to rename tables.
145
+ def rename_table_sql(name, new_name)
146
+ "RENAME TABLE #{quote_schema_table(name)} TO #{quote_schema_table(new_name)}"
147
+ end
148
+
149
+ # Run the REORG TABLE command for the table, necessary when
150
+ # the table has been altered.
151
+ def reorg(table)
152
+ synchronize(opts[:server]){|c| c.execute(reorg_sql(table))}
153
+ end
154
+
155
+ # The SQL to use for REORGing a table.
156
+ def reorg_sql(table)
157
+ "CALL ADMIN_CMD(#{literal("REORG TABLE #{table}")})"
158
+ end
159
+
160
+ # We uses the clob type by default for Files.
161
+ # Note: if user select to use blob, then insert statement should use
162
+ # use this for blob value:
163
+ # cast(X'fffefdfcfbfa' as blob(2G))
164
+ def type_literal_generic_file(column)
165
+ ::Sequel::DB2::use_clob_as_blob ? :clob : :blob
166
+ end
167
+
168
+ # DB2 uses smallint to store booleans.
169
+ def type_literal_generic_trueclass(column)
170
+ :smallint
171
+ end
172
+ alias type_literal_generic_falseclass type_literal_generic_trueclass
173
+ end
174
+
175
+ module DatasetMethods
176
+ include EmulateOffsetWithRowNumber
177
+
178
+ BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR, :'B~'=>:BITNOT}
179
+ BOOL_TRUE = '1'.freeze
180
+ BOOL_FALSE = '0'.freeze
181
+
182
+ # DB2 casts strings using RTRIM and CHAR instead of VARCHAR.
183
+ def cast_sql(expr, type)
184
+ type == String ? "RTRIM(CHAR(#{literal(expr)}))" : super
185
+ end
186
+
187
+ # Handle DB2 specific LIKE and bitwise operator support, and
188
+ # emulate the extract method, which DB2 doesn't natively support.
189
+ def complex_expression_sql(op, args)
190
+ case op
191
+ when :ILIKE
192
+ super(:LIKE, [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
193
+ when :"NOT ILIKE"
194
+ super(:"NOT LIKE", [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
195
+ when :&, :|, :^, :'B~'
196
+ # works with db2 v9.5 and after
197
+ literal(SQL::Function.new(BITWISE_METHOD_MAP[op], *args))
198
+ when :<<
199
+ "(#{literal(args[0])} * POWER(2, #{literal(args[1])}))"
200
+ when :>>
201
+ "(#{literal(args[0])} / POWER(2, #{literal(args[1])}))"
202
+ when :extract
203
+ "#{args.at(0)}(#{literal(args.at(1))})"
204
+ else
205
+ super
206
+ end
207
+ end
208
+
209
+ # Delete the row_number_column if offsets are being emulated with
210
+ # ROW_NUMBER.
211
+ def fetch_rows(sql, &block)
212
+ @opts[:offset] ? super(sql){|r| r.delete(row_number_column); yield r} : super(sql, &block)
213
+ end
214
+
215
+ # DB2 does not support IS TRUE.
216
+ def supports_is_true?
217
+ false
218
+ end
219
+
220
+ # DB2 does not support multiple columns in IN.
221
+ def supports_multiple_column_in?
222
+ false
223
+ end
224
+
225
+ # DB2 only allows * in SELECT if it is the only thing being selected.
226
+ def supports_select_all_and_column?
227
+ false
228
+ end
229
+
230
+ # DB2 does not support fractional seconds in timestamps.
231
+ def supports_timestamp_usecs?
232
+ false
233
+ end
234
+
235
+ # DB2 supports window functions
236
+ def supports_window_functions?
237
+ true
238
+ end
239
+
240
+ # DB2 does not support WHERE 1.
241
+ def supports_where_true?
242
+ false
243
+ end
244
+
245
+ private
246
+
247
+ # DB2 uses "INSERT INTO "ITEMS" VALUES DEFAULT" for a record with default values to be inserted
248
+ def insert_values_sql(sql)
249
+ opts[:values].empty? ? sql << " VALUES DEFAULT" : super
250
+ end
251
+
252
+ # Use 0 for false on DB2
253
+ def literal_false
254
+ BOOL_FALSE
255
+ end
256
+
257
+ # Use 1 for true on DB2
258
+ def literal_true
259
+ BOOL_TRUE
260
+ end
261
+
262
+ # Add a fallback table for empty from situation
263
+ def select_from_sql(sql)
264
+ @opts[:from] ? super : (sql << ' FROM "SYSIBM"."SYSDUMMY1"')
265
+ end
266
+
267
+ # Modify the sql to limit the number of rows returned
268
+ # Note:
269
+ #
270
+ # After db2 v9.7, MySQL flavored "LIMIT X OFFSET Y" can be enabled using
271
+ #
272
+ # db2set DB2_COMPATIBILITY_VECTOR=MYSQL
273
+ # db2stop
274
+ # db2start
275
+ #
276
+ # Support for this feature is not used in this adapter however.
277
+ def select_limit_sql(sql)
278
+ if l = @opts[:limit]
279
+ sql << " FETCH FIRST #{l == 1 ? 'ROW' : "#{literal(l)} ROWS"} ONLY"
280
+ end
281
+ end
282
+
283
+ def _truncate_sql(table)
284
+ # "TRUNCATE #{table} IMMEDIATE" is only for newer version of db2, so we
285
+ # use the following one
286
+ "ALTER TABLE #{quote_schema_table(table)} ACTIVATE NOT LOGGED INITIALLY WITH EMPTY TABLE"
287
+ end
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,214 @@
1
+ module Sequel
2
+ module Firebird
3
+ module DatabaseMethods
4
+ AUTO_INCREMENT = ''.freeze
5
+ TEMPORARY = 'GLOBAL TEMPORARY '.freeze
6
+
7
+ def clear_primary_key(*tables)
8
+ tables.each{|t| @primary_keys.delete(dataset.send(:input_identifier, t))}
9
+ end
10
+
11
+ def create_trigger(*args)
12
+ self << create_trigger_sql(*args)
13
+ end
14
+
15
+ def database_type
16
+ :firebird
17
+ end
18
+
19
+ def drop_sequence(name)
20
+ self << drop_sequence_sql(name)
21
+ end
22
+
23
+ def drop_table(*names)
24
+ clear_primary_key(*names)
25
+ super
26
+ end
27
+
28
+ # Return primary key for the given table.
29
+ def primary_key(table)
30
+ t = dataset.send(:input_identifier, table)
31
+ @primary_keys.fetch(t) do
32
+ pk = fetch("SELECT RDB$FIELD_NAME FROM RDB$INDEX_SEGMENTS NATURAL JOIN RDB$RELATION_CONSTRAINTS WHERE RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' AND RDB$RELATION_NAME = ?", t).single_value
33
+ @primary_keys[t] = dataset.send(:output_identifier, pk.rstrip) if pk
34
+ end
35
+ end
36
+
37
+ def restart_sequence(*args)
38
+ self << restart_sequence_sql(*args)
39
+ end
40
+
41
+ def sequences(opts={})
42
+ ds = self[:"rdb$generators"].server(opts[:server]).filter(:"rdb$system_flag" => 0).select(:"rdb$generator_name")
43
+ block_given? ? yield(ds) : ds.map{|r| ds.send(:output_identifier, r[:"rdb$generator_name"])}
44
+ end
45
+
46
+ def tables(opts={})
47
+ tables_or_views(0, opts)
48
+ end
49
+
50
+ def views(opts={})
51
+ tables_or_views(1, opts)
52
+ end
53
+
54
+ private
55
+
56
+ # Use Firebird specific syntax for add column
57
+ def alter_table_sql(table, op)
58
+ case op[:op]
59
+ when :add_column
60
+ "ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
61
+ when :drop_column
62
+ "ALTER TABLE #{quote_schema_table(table)} DROP #{column_definition_sql(op)}"
63
+ when :rename_column
64
+ "ALTER TABLE #{quote_schema_table(table)} ALTER #{quote_identifier(op[:name])} TO #{quote_identifier(op[:new_name])}"
65
+ when :set_column_type
66
+ "ALTER TABLE #{quote_schema_table(table)} ALTER #{quote_identifier(op[:name])} TYPE #{type_literal(op)}"
67
+ else
68
+ super(table, op)
69
+ end
70
+ end
71
+
72
+ def auto_increment_sql()
73
+ AUTO_INCREMENT
74
+ end
75
+
76
+ def create_sequence_sql(name, opts={})
77
+ "CREATE SEQUENCE #{quote_identifier(name)}"
78
+ end
79
+
80
+ # Firebird gets an override because of the mess of creating a
81
+ # sequence and trigger for auto-incrementing primary keys.
82
+ def create_table_from_generator(name, generator, options)
83
+ drop_statement, create_statements = create_table_sql_list(name, generator, options)
84
+ (execute_ddl(drop_statement) rescue nil) if drop_statement
85
+ create_statements.each{|sql| execute_ddl(sql)}
86
+ end
87
+
88
+ def create_table_sql_list(name, generator, options={})
89
+ statements = [create_table_sql(name, generator, options)]
90
+ drop_seq_statement = nil
91
+ generator.columns.each do |c|
92
+ if c[:auto_increment]
93
+ c[:sequence_name] ||= "seq_#{name}_#{c[:name]}"
94
+ unless c[:create_sequence] == false
95
+ drop_seq_statement = drop_sequence_sql(c[:sequence_name])
96
+ statements << create_sequence_sql(c[:sequence_name])
97
+ statements << restart_sequence_sql(c[:sequence_name], {:restart_position => c[:sequence_start_position]}) if c[:sequence_start_position]
98
+ end
99
+ unless c[:create_trigger] == false
100
+ c[:trigger_name] ||= "BI_#{name}_#{c[:name]}"
101
+ c[:quoted_name] = quote_identifier(c[:name])
102
+ trigger_definition = <<-END
103
+ begin
104
+ if ((new.#{c[:quoted_name]} is null) or (new.#{c[:quoted_name]} = 0)) then
105
+ begin
106
+ new.#{c[:quoted_name]} = next value for #{c[:sequence_name]};
107
+ end
108
+ end
109
+ END
110
+ statements << create_trigger_sql(name, c[:trigger_name], trigger_definition, {:events => [:insert]})
111
+ end
112
+ end
113
+ end
114
+ [drop_seq_statement, statements]
115
+ end
116
+
117
+ def create_trigger_sql(table, name, definition, opts={})
118
+ events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
119
+ whence = opts[:after] ? 'AFTER' : 'BEFORE'
120
+ inactive = opts[:inactive] ? 'INACTIVE' : 'ACTIVE'
121
+ position = opts.fetch(:position, 0)
122
+ sql = <<-end_sql
123
+ CREATE TRIGGER #{quote_identifier(name)} for #{quote_identifier(table)}
124
+ #{inactive} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} position #{position}
125
+ as #{definition}
126
+ end_sql
127
+ sql
128
+ end
129
+
130
+ def drop_sequence_sql(name)
131
+ "DROP SEQUENCE #{quote_identifier(name)}"
132
+ end
133
+
134
+ def restart_sequence_sql(name, opts={})
135
+ seq_name = quote_identifier(name)
136
+ "ALTER SEQUENCE #{seq_name} RESTART WITH #{opts[:restart_position]}"
137
+ end
138
+
139
+ def tables_or_views(type, opts)
140
+ ds = self[:"rdb$relations"].server(opts[:server]).filter(:"rdb$relation_type" => type, Sequel::SQL::Function.new(:COALESCE, :"rdb$system_flag", 0) => 0).select(:"rdb$relation_name")
141
+ ds.map{|r| ds.send(:output_identifier, r[:"rdb$relation_name"].rstrip)}
142
+ end
143
+
144
+ def type_literal_generic_string(column)
145
+ column[:text] ? :"BLOB SUB_TYPE TEXT" : super
146
+ end
147
+ end
148
+
149
+ module DatasetMethods
150
+ BOOL_TRUE = '1'.freeze
151
+ BOOL_FALSE = '0'.freeze
152
+ NULL = LiteralString.new('NULL').freeze
153
+ COMMA_SEPARATOR = ', '.freeze
154
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with distinct limit columns from join where group having compounds order')
155
+ INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'into columns values returning')
156
+
157
+ # Insert given values into the database.
158
+ def insert(*values)
159
+ if @opts[:sql] || @opts[:returning]
160
+ super
161
+ elsif supports_insert_select?
162
+ returning(insert_pk).insert(*values){|r| return r.values.first}
163
+ end
164
+ end
165
+
166
+ # Insert a record returning the record inserted
167
+ def insert_select(*values)
168
+ returning.insert(*values){|r| return r}
169
+ end
170
+
171
+ def requires_sql_standard_datetimes?
172
+ true
173
+ end
174
+
175
+ def supports_insert_select?
176
+ true
177
+ end
178
+
179
+ # Firebird does not support INTERSECT or EXCEPT
180
+ def supports_intersect_except?
181
+ false
182
+ end
183
+
184
+ private
185
+
186
+ def insert_clause_methods
187
+ INSERT_CLAUSE_METHODS
188
+ end
189
+
190
+ def insert_pk(*values)
191
+ pk = db.primary_key(opts[:from].first)
192
+ pk ? Sequel::SQL::Identifier.new(pk) : NULL
193
+ end
194
+
195
+ def literal_false
196
+ BOOL_FALSE
197
+ end
198
+
199
+ def literal_true
200
+ BOOL_TRUE
201
+ end
202
+
203
+ # The order of clauses in the SELECT SQL statement
204
+ def select_clause_methods
205
+ SELECT_CLAUSE_METHODS
206
+ end
207
+
208
+ def select_limit_sql(sql)
209
+ sql << " FIRST #{@opts[:limit]}" if @opts[:limit]
210
+ sql << " SKIP #{@opts[:offset]}" if @opts[:offset]
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,45 @@
1
+ module Sequel
2
+ module Informix
3
+ module DatabaseMethods
4
+ TEMPORARY = 'TEMP '.freeze
5
+
6
+ # Informix uses the :informix database type
7
+ def database_type
8
+ :informix
9
+ end
10
+
11
+ private
12
+
13
+ # Informix has issues with quoted identifiers, so
14
+ # turn off database quoting by default.
15
+ def quote_identifiers_default
16
+ false
17
+ end
18
+
19
+ # SQL fragment for showing a table is temporary
20
+ def temporary_table_sql
21
+ TEMPORARY
22
+ end
23
+ end
24
+
25
+ module DatasetMethods
26
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where having group compounds order')
27
+
28
+ private
29
+
30
+ # Informix does not support INTERSECT or EXCEPT
31
+ def supports_intersect_except?
32
+ false
33
+ end
34
+
35
+ def select_clause_methods
36
+ SELECT_CLAUSE_METHODS
37
+ end
38
+
39
+ def select_limit_sql(sql)
40
+ sql << " SKIP #{@opts[:offset]}" if @opts[:offset]
41
+ sql << " FIRST #{@opts[:limit]}" if @opts[:limit]
42
+ end
43
+ end
44
+ end
45
+ end