sequel 3.21.0 → 3.32.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 (234) hide show
  1. data/CHANGELOG +539 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +20 -6
  4. data/Rakefile +37 -19
  5. data/bin/sequel +1 -5
  6. data/doc/advanced_associations.rdoc +17 -0
  7. data/doc/association_basics.rdoc +293 -43
  8. data/doc/dataset_basics.rdoc +4 -4
  9. data/doc/dataset_filtering.rdoc +8 -0
  10. data/doc/mass_assignment.rdoc +54 -0
  11. data/doc/migration.rdoc +15 -538
  12. data/doc/model_hooks.rdoc +72 -27
  13. data/doc/opening_databases.rdoc +89 -50
  14. data/doc/prepared_statements.rdoc +40 -13
  15. data/doc/reflection.rdoc +8 -2
  16. data/doc/release_notes/3.22.0.txt +39 -0
  17. data/doc/release_notes/3.23.0.txt +172 -0
  18. data/doc/release_notes/3.24.0.txt +420 -0
  19. data/doc/release_notes/3.25.0.txt +88 -0
  20. data/doc/release_notes/3.26.0.txt +88 -0
  21. data/doc/release_notes/3.27.0.txt +82 -0
  22. data/doc/release_notes/3.28.0.txt +304 -0
  23. data/doc/release_notes/3.29.0.txt +459 -0
  24. data/doc/release_notes/3.30.0.txt +135 -0
  25. data/doc/release_notes/3.31.0.txt +146 -0
  26. data/doc/release_notes/3.32.0.txt +202 -0
  27. data/doc/schema_modification.rdoc +547 -0
  28. data/doc/sharding.rdoc +7 -1
  29. data/doc/testing.rdoc +129 -0
  30. data/doc/transactions.rdoc +137 -0
  31. data/doc/virtual_rows.rdoc +2 -2
  32. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  33. data/lib/sequel/adapters/ado.rb +40 -18
  34. data/lib/sequel/adapters/amalgalite.rb +17 -9
  35. data/lib/sequel/adapters/db2.rb +175 -86
  36. data/lib/sequel/adapters/dbi.rb +15 -15
  37. data/lib/sequel/adapters/do/mysql.rb +5 -7
  38. data/lib/sequel/adapters/do/postgres.rb +0 -5
  39. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  40. data/lib/sequel/adapters/do.rb +17 -36
  41. data/lib/sequel/adapters/firebird.rb +27 -208
  42. data/lib/sequel/adapters/ibmdb.rb +448 -0
  43. data/lib/sequel/adapters/informix.rb +6 -23
  44. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  45. data/lib/sequel/adapters/jdbc/db2.rb +52 -0
  46. data/lib/sequel/adapters/jdbc/derby.rb +260 -0
  47. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  48. data/lib/sequel/adapters/jdbc/h2.rb +66 -30
  49. data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -0
  50. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  51. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  52. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  53. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  54. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  55. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  56. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  57. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  58. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  59. data/lib/sequel/adapters/jdbc.rb +231 -78
  60. data/lib/sequel/adapters/mock.rb +339 -0
  61. data/lib/sequel/adapters/mysql.rb +85 -184
  62. data/lib/sequel/adapters/mysql2.rb +26 -25
  63. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  64. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  65. data/lib/sequel/adapters/odbc.rb +28 -9
  66. data/lib/sequel/adapters/openbase.rb +3 -5
  67. data/lib/sequel/adapters/oracle.rb +355 -53
  68. data/lib/sequel/adapters/postgres.rb +194 -33
  69. data/lib/sequel/adapters/shared/access.rb +32 -9
  70. data/lib/sequel/adapters/shared/db2.rb +328 -0
  71. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  72. data/lib/sequel/adapters/shared/informix.rb +53 -0
  73. data/lib/sequel/adapters/shared/mssql.rb +278 -107
  74. data/lib/sequel/adapters/shared/mysql.rb +203 -65
  75. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  76. data/lib/sequel/adapters/shared/oracle.rb +237 -46
  77. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  78. data/lib/sequel/adapters/shared/progress.rb +5 -8
  79. data/lib/sequel/adapters/shared/sqlite.rb +200 -50
  80. data/lib/sequel/adapters/sqlite.rb +29 -21
  81. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  82. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  83. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  84. data/lib/sequel/adapters/swift.rb +5 -5
  85. data/lib/sequel/adapters/tinytds.rb +160 -19
  86. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  87. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  88. data/lib/sequel/ast_transformer.rb +190 -0
  89. data/lib/sequel/connection_pool/threaded.rb +3 -2
  90. data/lib/sequel/connection_pool.rb +1 -1
  91. data/lib/sequel/core.rb +68 -5
  92. data/lib/sequel/database/connecting.rb +5 -6
  93. data/lib/sequel/database/dataset.rb +4 -4
  94. data/lib/sequel/database/dataset_defaults.rb +59 -1
  95. data/lib/sequel/database/logging.rb +1 -1
  96. data/lib/sequel/database/misc.rb +104 -19
  97. data/lib/sequel/database/query.rb +163 -59
  98. data/lib/sequel/database/schema_generator.rb +17 -7
  99. data/lib/sequel/database/schema_methods.rb +59 -29
  100. data/lib/sequel/dataset/actions.rb +240 -76
  101. data/lib/sequel/dataset/features.rb +91 -8
  102. data/lib/sequel/dataset/graph.rb +38 -11
  103. data/lib/sequel/dataset/misc.rb +50 -23
  104. data/lib/sequel/dataset/mutation.rb +12 -7
  105. data/lib/sequel/dataset/prepared_statements.rb +59 -27
  106. data/lib/sequel/dataset/query.rb +236 -43
  107. data/lib/sequel/dataset/sql.rb +676 -267
  108. data/lib/sequel/dataset.rb +8 -0
  109. data/lib/sequel/exceptions.rb +4 -0
  110. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  111. data/lib/sequel/extensions/migration.rb +6 -4
  112. data/lib/sequel/extensions/named_timezones.rb +5 -0
  113. data/lib/sequel/extensions/query.rb +1 -1
  114. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  115. data/lib/sequel/extensions/to_dot.rb +95 -83
  116. data/lib/sequel/model/associations.rb +1081 -344
  117. data/lib/sequel/model/base.rb +420 -133
  118. data/lib/sequel/model/errors.rb +1 -1
  119. data/lib/sequel/model/exceptions.rb +5 -1
  120. data/lib/sequel/model.rb +20 -10
  121. data/lib/sequel/plugins/active_model.rb +13 -1
  122. data/lib/sequel/plugins/association_pks.rb +22 -4
  123. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  124. data/lib/sequel/plugins/composition.rb +43 -10
  125. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  126. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  127. data/lib/sequel/plugins/force_encoding.rb +6 -6
  128. data/lib/sequel/plugins/identity_map.rb +114 -7
  129. data/lib/sequel/plugins/many_through_many.rb +68 -7
  130. data/lib/sequel/plugins/nested_attributes.rb +65 -10
  131. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  132. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  133. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  134. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  135. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  136. data/lib/sequel/plugins/serialization.rb +98 -49
  137. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  138. data/lib/sequel/plugins/sharding.rb +21 -54
  139. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  140. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  141. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  142. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  143. data/lib/sequel/sql.rb +109 -52
  144. data/lib/sequel/timezones.rb +57 -38
  145. data/lib/sequel/version.rb +1 -1
  146. data/spec/adapters/db2_spec.rb +146 -0
  147. data/spec/adapters/mssql_spec.rb +135 -30
  148. data/spec/adapters/mysql_spec.rb +244 -212
  149. data/spec/adapters/oracle_spec.rb +67 -77
  150. data/spec/adapters/postgres_spec.rb +267 -78
  151. data/spec/adapters/spec_helper.rb +6 -5
  152. data/spec/adapters/sqlite_spec.rb +35 -21
  153. data/spec/core/connection_pool_spec.rb +71 -92
  154. data/spec/core/core_sql_spec.rb +21 -30
  155. data/spec/core/database_spec.rb +789 -512
  156. data/spec/core/dataset_spec.rb +1176 -1061
  157. data/spec/core/expression_filters_spec.rb +159 -42
  158. data/spec/core/mock_adapter_spec.rb +419 -0
  159. data/spec/core/object_graph_spec.rb +86 -114
  160. data/spec/core/schema_generator_spec.rb +3 -3
  161. data/spec/core/schema_spec.rb +298 -38
  162. data/spec/core/spec_helper.rb +6 -48
  163. data/spec/extensions/active_model_spec.rb +13 -0
  164. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  165. data/spec/extensions/association_dependencies_spec.rb +2 -12
  166. data/spec/extensions/association_pks_spec.rb +66 -32
  167. data/spec/extensions/caching_spec.rb +23 -50
  168. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  169. data/spec/extensions/columns_introspection_spec.rb +91 -0
  170. data/spec/extensions/composition_spec.rb +18 -13
  171. data/spec/extensions/dataset_associations_spec.rb +199 -0
  172. data/spec/extensions/defaults_setter_spec.rb +64 -0
  173. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  174. data/spec/extensions/identity_map_spec.rb +114 -22
  175. data/spec/extensions/instance_filters_spec.rb +11 -21
  176. data/spec/extensions/instance_hooks_spec.rb +53 -0
  177. data/spec/extensions/json_serializer_spec.rb +4 -5
  178. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  179. data/spec/extensions/list_spec.rb +17 -39
  180. data/spec/extensions/many_through_many_spec.rb +283 -250
  181. data/spec/extensions/migration_spec.rb +35 -32
  182. data/spec/extensions/named_timezones_spec.rb +23 -3
  183. data/spec/extensions/nested_attributes_spec.rb +97 -88
  184. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  185. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  186. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  187. data/spec/extensions/prepared_statements_spec.rb +53 -0
  188. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  189. data/spec/extensions/pretty_table_spec.rb +1 -6
  190. data/spec/extensions/query_spec.rb +6 -0
  191. data/spec/extensions/rcte_tree_spec.rb +41 -43
  192. data/spec/extensions/schema_dumper_spec.rb +5 -8
  193. data/spec/extensions/schema_spec.rb +13 -21
  194. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  195. data/spec/extensions/serialization_spec.rb +81 -43
  196. data/spec/extensions/sharding_spec.rb +66 -140
  197. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  198. data/spec/extensions/spec_helper.rb +13 -61
  199. data/spec/extensions/sql_expr_spec.rb +20 -60
  200. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  201. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  202. data/spec/extensions/timestamps_spec.rb +6 -6
  203. data/spec/extensions/to_dot_spec.rb +4 -7
  204. data/spec/extensions/touch_spec.rb +13 -14
  205. data/spec/extensions/tree_spec.rb +11 -26
  206. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  207. data/spec/extensions/update_primary_key_spec.rb +30 -24
  208. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  209. data/spec/extensions/validation_helpers_spec.rb +16 -35
  210. data/spec/extensions/xml_serializer_spec.rb +16 -4
  211. data/spec/integration/associations_test.rb +661 -21
  212. data/spec/integration/database_test.rb +4 -2
  213. data/spec/integration/dataset_test.rb +500 -67
  214. data/spec/integration/eager_loader_test.rb +19 -21
  215. data/spec/integration/model_test.rb +80 -1
  216. data/spec/integration/plugin_test.rb +367 -119
  217. data/spec/integration/prepared_statement_test.rb +222 -125
  218. data/spec/integration/schema_test.rb +217 -39
  219. data/spec/integration/spec_helper.rb +39 -30
  220. data/spec/integration/timezone_test.rb +38 -12
  221. data/spec/integration/transaction_test.rb +182 -5
  222. data/spec/integration/type_test.rb +17 -3
  223. data/spec/model/association_reflection_spec.rb +201 -7
  224. data/spec/model/associations_spec.rb +955 -662
  225. data/spec/model/base_spec.rb +275 -95
  226. data/spec/model/dataset_methods_spec.rb +7 -27
  227. data/spec/model/eager_loading_spec.rb +677 -676
  228. data/spec/model/hooks_spec.rb +349 -67
  229. data/spec/model/model_spec.rb +222 -168
  230. data/spec/model/plugins_spec.rb +24 -13
  231. data/spec/model/record_spec.rb +381 -218
  232. data/spec/model/spec_helper.rb +13 -71
  233. data/spec/model/validations_spec.rb +11 -0
  234. metadata +117 -73
@@ -0,0 +1,328 @@
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(opts[:dataset])
33
+ im = input_identifier_meth(opts[:dataset])
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
+ m = output_identifier_meth
64
+ indexes = {}
65
+ metadata_dataset.
66
+ from(:syscat__indexes).
67
+ select(:indname, :uniquerule, :colnames).
68
+ where(:tabname=>input_identifier_meth.call(table), :system_required=>0).
69
+ each do |r|
70
+ indexes[m.call(r[:indname])] = {:unique=>(r[:uniquerule]=='U'), :columns=>r[:colnames][1..-1].split('+').map{|v| m.call(v)}}
71
+ end
72
+ indexes
73
+ end
74
+
75
+ private
76
+
77
+ # Handle DB2 specific alter table operations.
78
+ def alter_table_sql(table, op)
79
+ case op[:op]
80
+ when :add_column
81
+ if op[:primary_key] && op[:auto_increment] && op[:type] == Integer
82
+ [
83
+ "ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op.merge(:auto_increment=>false, :primary_key=>false, :default=>0, :null=>false))}",
84
+ "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{literal(op[:name])} DROP DEFAULT",
85
+ "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{literal(op[:name])} SET #{AUTOINCREMENT}"
86
+ ]
87
+ else
88
+ "ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
89
+ end
90
+ when :drop_column
91
+ "ALTER TABLE #{quote_schema_table(table)} DROP #{column_definition_sql(op)}"
92
+ when :rename_column # renaming is only possible after db2 v9.7
93
+ "ALTER TABLE #{quote_schema_table(table)} RENAME COLUMN #{quote_identifier(op[:name])} TO #{quote_identifier(op[:new_name])}"
94
+ when :set_column_type
95
+ "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} SET DATA TYPE #{type_literal(op)}"
96
+ when :set_column_default
97
+ "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} SET DEFAULT #{literal(op[:default])}"
98
+ when :add_constraint
99
+ if op[:type] == :unique
100
+ sqls = op[:columns].map{|c| ["ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(c)} SET NOT NULL", reorg_sql(table)]}
101
+ sqls << super
102
+ sqls.flatten
103
+ else
104
+ super
105
+ end
106
+ else
107
+ super
108
+ end
109
+ end
110
+
111
+ # DB2 uses an identity column for autoincrement.
112
+ def auto_increment_sql
113
+ AUTOINCREMENT
114
+ end
115
+
116
+ # Add null/not null SQL fragment to column creation SQL.
117
+ def column_definition_null_sql(sql, column)
118
+ null = column.fetch(:null, column[:allow_null])
119
+ null = false if column[:primary_key]
120
+
121
+ sql << NOT_NULL if null == false
122
+ sql << NULL if null == true
123
+ end
124
+
125
+ # Supply columns with NOT NULL if they are part of a composite
126
+ # primary/foreign key
127
+ def column_list_sql(g)
128
+ ks = []
129
+ g.constraints.each{|c| ks = c[:columns] if [:primary_key, :foreign_key].include? c[:type]}
130
+ g.columns.each{|c| c[:null] = false if ks.include?(c[:name]) }
131
+ super
132
+ end
133
+
134
+ # Here we use DGTT which has most backward compatibility, which uses
135
+ # DECLARE instead of CREATE. CGTT can only be used after version 9.7.
136
+ # http://www.ibm.com/developerworks/data/library/techarticle/dm-0912globaltemptable/
137
+ def create_table_sql(name, generator, options)
138
+ if options[:temp]
139
+ "DECLARE GLOBAL TEMPORARY TABLE #{quote_identifier(name)} (#{column_list_sql(generator)})"
140
+ else
141
+ super
142
+ end
143
+ end
144
+
145
+ # DB2 has issues with quoted identifiers, so
146
+ # turn off database quoting by default.
147
+ def quote_identifiers_default
148
+ false
149
+ end
150
+
151
+ # DB2 uses RENAME TABLE to rename tables.
152
+ def rename_table_sql(name, new_name)
153
+ "RENAME TABLE #{quote_schema_table(name)} TO #{quote_schema_table(new_name)}"
154
+ end
155
+
156
+ # Run the REORG TABLE command for the table, necessary when
157
+ # the table has been altered.
158
+ def reorg(table)
159
+ synchronize(opts[:server]){|c| c.execute(reorg_sql(table))}
160
+ end
161
+
162
+ # The SQL to use for REORGing a table.
163
+ def reorg_sql(table)
164
+ "CALL ADMIN_CMD(#{literal("REORG TABLE #{table}")})"
165
+ end
166
+
167
+ # We uses the clob type by default for Files.
168
+ # Note: if user select to use blob, then insert statement should use
169
+ # use this for blob value:
170
+ # cast(X'fffefdfcfbfa' as blob(2G))
171
+ def type_literal_generic_file(column)
172
+ ::Sequel::DB2::use_clob_as_blob ? :clob : :blob
173
+ end
174
+
175
+ # DB2 uses smallint to store booleans.
176
+ def type_literal_generic_trueclass(column)
177
+ :smallint
178
+ end
179
+ alias type_literal_generic_falseclass type_literal_generic_trueclass
180
+ end
181
+
182
+ module DatasetMethods
183
+ include EmulateOffsetWithRowNumber
184
+
185
+ PAREN_CLOSE = Dataset::PAREN_CLOSE
186
+ PAREN_OPEN = Dataset::PAREN_OPEN
187
+ BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR, :'B~'=>:BITNOT}
188
+ BOOL_TRUE = '1'.freeze
189
+ BOOL_FALSE = '0'.freeze
190
+ CAST_STRING_OPEN = "RTRIM(CHAR(".freeze
191
+ CAST_STRING_CLOSE = "))".freeze
192
+ FETCH_FIRST_ROW_ONLY = " FETCH FIRST ROW ONLY".freeze
193
+ FETCH_FIRST = " FETCH FIRST ".freeze
194
+ ROWS_ONLY = " ROWS ONLY".freeze
195
+ EMPTY_FROM_TABLE = ' FROM "SYSIBM"."SYSDUMMY1"'.freeze
196
+
197
+ # DB2 casts strings using RTRIM and CHAR instead of VARCHAR.
198
+ def cast_sql_append(sql, expr, type)
199
+ if(type == String)
200
+ sql << CAST_STRING_OPEN
201
+ literal_append(sql, expr)
202
+ sql << CAST_STRING_CLOSE
203
+ else
204
+ super
205
+ end
206
+ end
207
+
208
+ # Handle DB2 specific LIKE and bitwise operator support, and
209
+ # emulate the extract method, which DB2 doesn't natively support.
210
+ def complex_expression_sql_append(sql, op, args)
211
+ case op
212
+ when :ILIKE
213
+ super(sql, :LIKE, [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
214
+ when :"NOT ILIKE"
215
+ super(sql, :"NOT LIKE", [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
216
+ when :&, :|, :^
217
+ # works with db2 v9.5 and after
218
+ op = BITWISE_METHOD_MAP[op]
219
+ sql << complex_expression_arg_pairs(args){|a, b| literal(SQL::Function.new(op, a, b))}
220
+ when :<<
221
+ sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * POWER(2, #{literal(b)}))"}
222
+ when :>>
223
+ sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / POWER(2, #{literal(b)}))"}
224
+ when :'B~'
225
+ literal_append(sql, SQL::Function.new(:BITNOT, *args))
226
+ when :extract
227
+ sql << args.at(0).to_s
228
+ sql << PAREN_OPEN
229
+ literal_append(sql, args.at(1))
230
+ sql << PAREN_CLOSE
231
+ else
232
+ super
233
+ end
234
+ end
235
+
236
+ # DB2 supports GROUP BY CUBE
237
+ def supports_group_cube?
238
+ true
239
+ end
240
+
241
+ # DB2 supports GROUP BY ROLLUP
242
+ def supports_group_rollup?
243
+ true
244
+ end
245
+
246
+ # DB2 does not support IS TRUE.
247
+ def supports_is_true?
248
+ false
249
+ end
250
+
251
+ # DB2 does not support multiple columns in IN.
252
+ def supports_multiple_column_in?
253
+ false
254
+ end
255
+
256
+ # DB2 only allows * in SELECT if it is the only thing being selected.
257
+ def supports_select_all_and_column?
258
+ false
259
+ end
260
+
261
+ # DB2 does not support fractional seconds in timestamps.
262
+ def supports_timestamp_usecs?
263
+ false
264
+ end
265
+
266
+ # DB2 supports window functions
267
+ def supports_window_functions?
268
+ true
269
+ end
270
+
271
+ # DB2 does not support WHERE 1.
272
+ def supports_where_true?
273
+ false
274
+ end
275
+
276
+ private
277
+
278
+ # DB2 needs the standard workaround to insert all default values into
279
+ # a table with more than one column.
280
+ def insert_supports_empty_values?
281
+ false
282
+ end
283
+
284
+ # Use 0 for false on DB2
285
+ def literal_false
286
+ BOOL_FALSE
287
+ end
288
+
289
+ # Use 1 for true on DB2
290
+ def literal_true
291
+ BOOL_TRUE
292
+ end
293
+
294
+ # Add a fallback table for empty from situation
295
+ def select_from_sql(sql)
296
+ @opts[:from] ? super : (sql << EMPTY_FROM_TABLE)
297
+ end
298
+
299
+ # Modify the sql to limit the number of rows returned
300
+ # Note:
301
+ #
302
+ # After db2 v9.7, MySQL flavored "LIMIT X OFFSET Y" can be enabled using
303
+ #
304
+ # db2set DB2_COMPATIBILITY_VECTOR=MYSQL
305
+ # db2stop
306
+ # db2start
307
+ #
308
+ # Support for this feature is not used in this adapter however.
309
+ def select_limit_sql(sql)
310
+ if l = @opts[:limit]
311
+ if l == 1
312
+ sql << FETCH_FIRST_ROW_ONLY
313
+ elsif l > 1
314
+ sql << FETCH_FIRST
315
+ literal_append(sql, l)
316
+ sql << ROWS_ONLY
317
+ end
318
+ end
319
+ end
320
+
321
+ def _truncate_sql(table)
322
+ # "TRUNCATE #{table} IMMEDIATE" is only for newer version of db2, so we
323
+ # use the following one
324
+ "ALTER TABLE #{quote_schema_table(table)} ACTIVATE NOT LOGGED INITIALLY WITH EMPTY TABLE"
325
+ end
326
+ end
327
+ end
328
+ end
@@ -0,0 +1,221 @@
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
+ # Return primary key for the given table.
24
+ def primary_key(table)
25
+ t = dataset.send(:input_identifier, table)
26
+ @primary_keys.fetch(t) do
27
+ 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
28
+ @primary_keys[t] = dataset.send(:output_identifier, pk.rstrip) if pk
29
+ end
30
+ end
31
+
32
+ def restart_sequence(*args)
33
+ self << restart_sequence_sql(*args)
34
+ end
35
+
36
+ def sequences(opts={})
37
+ ds = self[:"rdb$generators"].server(opts[:server]).filter(:"rdb$system_flag" => 0).select(:"rdb$generator_name")
38
+ block_given? ? yield(ds) : ds.map{|r| ds.send(:output_identifier, r[:"rdb$generator_name"])}
39
+ end
40
+
41
+ def tables(opts={})
42
+ tables_or_views(0, opts)
43
+ end
44
+
45
+ def views(opts={})
46
+ tables_or_views(1, opts)
47
+ end
48
+
49
+ private
50
+
51
+ # Use Firebird specific syntax for add column
52
+ def alter_table_sql(table, op)
53
+ case op[:op]
54
+ when :add_column
55
+ "ALTER TABLE #{quote_schema_table(table)} ADD #{column_definition_sql(op)}"
56
+ when :drop_column
57
+ "ALTER TABLE #{quote_schema_table(table)} DROP #{column_definition_sql(op)}"
58
+ when :rename_column
59
+ "ALTER TABLE #{quote_schema_table(table)} ALTER #{quote_identifier(op[:name])} TO #{quote_identifier(op[:new_name])}"
60
+ when :set_column_type
61
+ "ALTER TABLE #{quote_schema_table(table)} ALTER #{quote_identifier(op[:name])} TYPE #{type_literal(op)}"
62
+ else
63
+ super(table, op)
64
+ end
65
+ end
66
+
67
+ def auto_increment_sql()
68
+ AUTO_INCREMENT
69
+ end
70
+
71
+ def create_sequence_sql(name, opts={})
72
+ "CREATE SEQUENCE #{quote_identifier(name)}"
73
+ end
74
+
75
+ # Firebird gets an override because of the mess of creating a
76
+ # sequence and trigger for auto-incrementing primary keys.
77
+ def create_table_from_generator(name, generator, options)
78
+ drop_statement, create_statements = create_table_sql_list(name, generator, options)
79
+ (execute_ddl(drop_statement) rescue nil) if drop_statement
80
+ create_statements.each{|sql| execute_ddl(sql)}
81
+ end
82
+
83
+ def create_table_sql_list(name, generator, options={})
84
+ statements = [create_table_sql(name, generator, options)]
85
+ drop_seq_statement = nil
86
+ generator.columns.each do |c|
87
+ if c[:auto_increment]
88
+ c[:sequence_name] ||= "seq_#{name}_#{c[:name]}"
89
+ unless c[:create_sequence] == false
90
+ drop_seq_statement = drop_sequence_sql(c[:sequence_name])
91
+ statements << create_sequence_sql(c[:sequence_name])
92
+ statements << restart_sequence_sql(c[:sequence_name], {:restart_position => c[:sequence_start_position]}) if c[:sequence_start_position]
93
+ end
94
+ unless c[:create_trigger] == false
95
+ c[:trigger_name] ||= "BI_#{name}_#{c[:name]}"
96
+ c[:quoted_name] = quote_identifier(c[:name])
97
+ trigger_definition = <<-END
98
+ begin
99
+ if ((new.#{c[:quoted_name]} is null) or (new.#{c[:quoted_name]} = 0)) then
100
+ begin
101
+ new.#{c[:quoted_name]} = next value for #{c[:sequence_name]};
102
+ end
103
+ end
104
+ END
105
+ statements << create_trigger_sql(name, c[:trigger_name], trigger_definition, {:events => [:insert]})
106
+ end
107
+ end
108
+ end
109
+ [drop_seq_statement, statements]
110
+ end
111
+
112
+ def create_trigger_sql(table, name, definition, opts={})
113
+ events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
114
+ whence = opts[:after] ? 'AFTER' : 'BEFORE'
115
+ inactive = opts[:inactive] ? 'INACTIVE' : 'ACTIVE'
116
+ position = opts.fetch(:position, 0)
117
+ sql = <<-end_sql
118
+ CREATE TRIGGER #{quote_identifier(name)} for #{quote_identifier(table)}
119
+ #{inactive} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} position #{position}
120
+ as #{definition}
121
+ end_sql
122
+ sql
123
+ end
124
+
125
+ def drop_sequence_sql(name)
126
+ "DROP SEQUENCE #{quote_identifier(name)}"
127
+ end
128
+
129
+ def remove_cached_schema(table)
130
+ clear_primary_key(table)
131
+ super
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
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct limit columns from join where group having compounds order')
154
+ INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values returning')
155
+ FIRST = " FIRST ".freeze
156
+ SKIP = " SKIP ".freeze
157
+
158
+ # Insert given values into the database.
159
+ def insert(*values)
160
+ if @opts[:sql] || @opts[:returning]
161
+ super
162
+ else
163
+ returning(insert_pk).insert(*values){|r| return r.values.first}
164
+ end
165
+ end
166
+
167
+ # Insert a record returning the record inserted
168
+ def insert_select(*values)
169
+ returning.insert(*values){|r| return r}
170
+ end
171
+
172
+ def requires_sql_standard_datetimes?
173
+ true
174
+ end
175
+
176
+ def supports_insert_select?
177
+ true
178
+ end
179
+
180
+ # Firebird does not support INTERSECT or EXCEPT
181
+ def supports_intersect_except?
182
+ false
183
+ end
184
+
185
+ private
186
+
187
+ def insert_clause_methods
188
+ INSERT_CLAUSE_METHODS
189
+ end
190
+
191
+ def insert_pk(*values)
192
+ pk = db.primary_key(opts[:from].first)
193
+ pk ? Sequel::SQL::Identifier.new(pk) : NULL
194
+ end
195
+
196
+ def literal_false
197
+ BOOL_FALSE
198
+ end
199
+
200
+ def literal_true
201
+ BOOL_TRUE
202
+ end
203
+
204
+ # The order of clauses in the SELECT SQL statement
205
+ def select_clause_methods
206
+ SELECT_CLAUSE_METHODS
207
+ end
208
+
209
+ def select_limit_sql(sql)
210
+ if l = @opts[:limit]
211
+ sql << FIRST
212
+ literal_append(sql, l)
213
+ end
214
+ if o = @opts[:offset]
215
+ sql << SKIP
216
+ literal_append(sql, o)
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,53 @@
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'select limit distinct columns from join where having group compounds order')
27
+ FIRST = " FIRST ".freeze
28
+ SKIP = " SKIP ".freeze
29
+
30
+ private
31
+
32
+ # Informix does not support INTERSECT or EXCEPT
33
+ def supports_intersect_except?
34
+ false
35
+ end
36
+
37
+ def select_clause_methods
38
+ SELECT_CLAUSE_METHODS
39
+ end
40
+
41
+ def select_limit_sql(sql)
42
+ if o = @opts[:offset]
43
+ sql << SKIP
44
+ literal_append(sql, o)
45
+ end
46
+ if l = @opts[:limit]
47
+ sql << FIRST
48
+ literal_append(sql, l)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end