sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -1,27 +1,25 @@
1
1
  module Sequel
2
2
  class Database
3
3
  # ---------------------
4
- # :section: Methods that modify the database schema
4
+ # :section: 2 - Methods that modify the database schema
5
5
  # These methods execute code on the database that modifies the database's schema.
6
6
  # ---------------------
7
7
 
8
8
  AUTOINCREMENT = 'AUTOINCREMENT'.freeze
9
- CASCADE = 'CASCADE'.freeze
10
9
  COMMA_SEPARATOR = ', '.freeze
11
- NO_ACTION = 'NO ACTION'.freeze
12
10
  NOT_NULL = ' NOT NULL'.freeze
13
11
  NULL = ' NULL'.freeze
14
12
  PRIMARY_KEY = ' PRIMARY KEY'.freeze
15
- RESTRICT = 'RESTRICT'.freeze
16
- SET_DEFAULT = 'SET DEFAULT'.freeze
17
- SET_NULL = 'SET NULL'.freeze
18
13
  TEMPORARY = 'TEMPORARY '.freeze
19
14
  UNDERSCORE = '_'.freeze
20
15
  UNIQUE = ' UNIQUE'.freeze
21
16
  UNSIGNED = ' UNSIGNED'.freeze
22
17
 
23
18
  # The order of column modifiers to use when defining a column.
24
- COLUMN_DEFINITION_ORDER = [:default, :null, :unique, :primary_key, :auto_increment, :references]
19
+ COLUMN_DEFINITION_ORDER = [:collate, :default, :null, :unique, :primary_key, :auto_increment, :references]
20
+
21
+ # The default options for join table columns.
22
+ DEFAULT_JOIN_TABLE_COLUMN_OPTIONS = {:null=>false}
25
23
 
26
24
  # Adds a column to the specified table. This method expects a column name,
27
25
  # a datatype and optionally a hash with additional constraints and options:
@@ -29,7 +27,7 @@ module Sequel
29
27
  # DB.add_column :items, :name, :text, :unique => true, :null => false
30
28
  # DB.add_column :items, :category, :text, :default => 'ruby'
31
29
  #
32
- # See alter_table.
30
+ # See <tt>alter_table</tt>.
33
31
  def add_column(table, *args)
34
32
  alter_table(table) {add_column(*args)}
35
33
  end
@@ -40,9 +38,9 @@ module Sequel
40
38
  # DB.add_index :posts, [:author, :title], :unique => true
41
39
  #
42
40
  # Options:
43
- # * :ignore_errors - Ignore any DatabaseErrors that are raised
41
+ # :ignore_errors :: Ignore any DatabaseErrors that are raised
44
42
  #
45
- # See alter_table.
43
+ # See <tt>alter_table</tt>.
46
44
  def add_index(table, columns, options={})
47
45
  e = options[:ignore_errors]
48
46
  begin
@@ -65,17 +63,65 @@ module Sequel
65
63
  # end
66
64
  #
67
65
  # Note that +add_column+ accepts all the options available for column
68
- # definitions using create_table, and +add_index+ accepts all the options
66
+ # definitions using <tt>create_table</tt>, and +add_index+ accepts all the options
69
67
  # available for index definition.
70
68
  #
71
- # See Schema::AlterTableGenerator and the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
69
+ # See <tt>Schema::AlterTableGenerator</tt> and the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
72
70
  def alter_table(name, generator=nil, &block)
73
71
  generator ||= Schema::AlterTableGenerator.new(self, &block)
74
- alter_table_sql_list(name, generator.operations).flatten.each {|sql| execute_ddl(sql)}
75
72
  remove_cached_schema(name)
73
+ apply_alter_table(name, generator.operations)
76
74
  nil
77
75
  end
78
-
76
+
77
+ # Create a join table using a hash of foreign keys to referenced
78
+ # table names. Example:
79
+ #
80
+ # create_join_table(:cat_id=>:cats, :dog_id=>:dogs)
81
+ # # CREATE TABLE cats_dogs (
82
+ # # cat_id integer NOT NULL REFERENCES cats,
83
+ # # dog_id integer NOT NULL REFERENCES dogs,
84
+ # # PRIMARY KEY (cat_id, dog_id)
85
+ # # )
86
+ # # CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs(dog_id, cat_id)
87
+ #
88
+ # The primary key and index are used so that almost all operations
89
+ # on the table can benefit from one of the two indexes, and the primary
90
+ # key ensures that entries in the table are unique, which is the typical
91
+ # desire for a join table.
92
+ #
93
+ # You can provide column options by making the values in the hash
94
+ # be option hashes, so long as the option hashes have a :table
95
+ # entry giving the table referenced:
96
+ #
97
+ # create_join_table(:cat_id=>{:table=>:cats, :type=>Bignum}, :dog_id=>:dogs)
98
+ #
99
+ # You can provide a second argument which is a table options hash:
100
+ #
101
+ # create_join_table({:cat_id=>:cats, :dog_id=>:dogs}, :temp=>true)
102
+ #
103
+ # Some table options are handled specially:
104
+ #
105
+ # :index_options :: The options to pass to the index
106
+ # :name :: The name of the table to create
107
+ # :no_index :: Set to true not to create the second index.
108
+ # :no_primary_key :: Set to true to not create the primary key.
109
+ def create_join_table(hash, options={})
110
+ keys = hash.keys.sort_by{|k| k.to_s}
111
+ create_table(join_table_name(hash, options), options) do
112
+ keys.each do |key|
113
+ v = hash[key]
114
+ unless v.is_a?(Hash)
115
+ v = {:table=>v}
116
+ end
117
+ v = DEFAULT_JOIN_TABLE_COLUMN_OPTIONS.merge(v)
118
+ foreign_key(key, v)
119
+ end
120
+ primary_key(keys) unless options[:no_primary_key]
121
+ index(keys.reverse, options[:index_options] || {}) unless options[:no_index]
122
+ end
123
+ end
124
+
79
125
  # Creates a table with the columns given in the provided block:
80
126
  #
81
127
  # DB.create_table :posts do
@@ -85,33 +131,55 @@ module Sequel
85
131
  # index :title
86
132
  # end
87
133
  #
88
- # Options:
89
- # :temp :: Create the table as a temporary table.
134
+ # General options:
135
+ # :as :: Create the table using the value, which should be either a
136
+ # dataset or a literal SQL string. If this option is used,
137
+ # a block should not be given to the method.
90
138
  # :ignore_index_errors :: Ignore any errors when creating indexes.
139
+ # :temp :: Create the table as a temporary table.
140
+ #
141
+ # MySQL specific options:
142
+ # :charset :: The character set to use for the table.
143
+ # :collate :: The collation to use for the table.
144
+ # :engine :: The table engine to use for the table.
91
145
  #
92
- # See Schema::Generator and the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html].
146
+ # See <tt>Schema::Generator</tt> and the {"Schema Modification" guide}[link:files/doc/schema_modification_rdoc.html].
93
147
  def create_table(name, options={}, &block)
94
148
  remove_cached_schema(name)
95
149
  options = {:generator=>options} if options.is_a?(Schema::Generator)
96
- generator = options[:generator] || Schema::Generator.new(self, &block)
97
- create_table_from_generator(name, generator, options)
98
- create_table_indexes_from_generator(name, generator, options)
99
- nil
150
+ if sql = options[:as]
151
+ raise(Error, "can't provide both :as option and block to create_table") if block
152
+ create_table_as(name, sql, options)
153
+ else
154
+ generator = options[:generator] || Schema::Generator.new(self, &block)
155
+ create_table_from_generator(name, generator, options)
156
+ create_table_indexes_from_generator(name, generator, options)
157
+ nil
158
+ end
100
159
  end
101
-
102
- # Forcibly creates a table, attempting to drop it unconditionally (and catching any errors), then creating it.
160
+
161
+ # Forcibly create a table, attempting to drop it if it already exists, then creating it.
103
162
  #
104
163
  # DB.create_table!(:a){Integer :a}
105
- # # DROP TABLE a
164
+ # # SELECT NULL FROM a LIMIT 1 -- check existence
165
+ # # DROP TABLE a -- drop table if already exists
106
166
  # # CREATE TABLE a (a integer)
107
167
  def create_table!(name, options={}, &block)
108
- drop_table(name) rescue nil
168
+ drop_table?(name)
109
169
  create_table(name, options, &block)
110
170
  end
111
171
 
112
- # Creates the table unless the table already exists
172
+ # Creates the table unless the table already exists.
173
+ #
174
+ # DB.create_table?(:a){Integer :a}
175
+ # # SELECT NULL FROM a LIMIT 1 -- check existence
176
+ # # CREATE TABLE a (a integer) -- if it doesn't already exist
113
177
  def create_table?(name, options={}, &block)
114
- create_table(name, options, &block) unless table_exists?(name)
178
+ if supports_create_table_if_not_exists?
179
+ create_table(name, options.merge(:if_not_exists=>true), &block)
180
+ elsif !table_exists?(name)
181
+ create_table(name, options, &block)
182
+ end
115
183
  end
116
184
 
117
185
  # Creates a view, replacing it if it already exists:
@@ -138,7 +206,7 @@ module Sequel
138
206
  #
139
207
  # DB.drop_column :items, :category
140
208
  #
141
- # See alter_table.
209
+ # See <tt>alter_table</tt>.
142
210
  def drop_column(table, *args)
143
211
  alter_table(table) {drop_column(*args)}
144
212
  end
@@ -148,28 +216,63 @@ module Sequel
148
216
  # DB.drop_index :posts, :title
149
217
  # DB.drop_index :posts, [:author, :title]
150
218
  #
151
- # See alter_table.
219
+ # See <tt>alter_table</tt>.
152
220
  def drop_index(table, columns, options={})
153
221
  alter_table(table){drop_index(columns, options)}
154
222
  end
223
+
224
+ # Drop the join table that would have been created with the
225
+ # same arguments to create_join_table:
226
+ #
227
+ # drop_join_table(:cat_id=>:cats, :dog_id=>:dogs)
228
+ # # DROP TABLE cats_dogs
229
+ def drop_join_table(hash, options={})
230
+ drop_table(join_table_name(hash, options), options)
231
+ end
155
232
 
156
233
  # Drops one or more tables corresponding to the given names:
157
234
  #
235
+ # DB.drop_table(:posts) # DROP TABLE posts
158
236
  # DB.drop_table(:posts, :comments)
237
+ # DB.drop_table(:posts, :comments, :cascade=>true)
159
238
  def drop_table(*names)
239
+ options = names.last.is_a?(Hash) ? names.pop : {}
160
240
  names.each do |n|
161
- execute_ddl(drop_table_sql(n))
241
+ execute_ddl(drop_table_sql(n, options))
162
242
  remove_cached_schema(n)
163
243
  end
164
244
  nil
165
245
  end
166
246
 
247
+ # Drops the table if it already exists. If it doesn't exist,
248
+ # does nothing.
249
+ #
250
+ # DB.drop_table?(:a)
251
+ # # SELECT NULL FROM a LIMIT 1 -- check existence
252
+ # # DROP TABLE a -- if it already exists
253
+ def drop_table?(*names)
254
+ options = names.last.is_a?(Hash) ? names.pop : {}
255
+ if supports_drop_table_if_exists?
256
+ options = options.merge(:if_exists=>true)
257
+ names.each do |name|
258
+ drop_table(name, options)
259
+ end
260
+ else
261
+ names.each do |name|
262
+ drop_table(name, options) if table_exists?(name)
263
+ end
264
+ end
265
+ end
266
+
167
267
  # Drops one or more views corresponding to the given names:
168
268
  #
169
269
  # DB.drop_view(:cheap_items)
270
+ # DB.drop_view(:cheap_items, :pricey_items)
271
+ # DB.drop_view(:cheap_items, :pricey_items, :cascade=>true)
170
272
  def drop_view(*names)
273
+ options = names.last.is_a?(Hash) ? names.pop : {}
171
274
  names.each do |n|
172
- execute_ddl("DROP VIEW #{quote_schema_table(n)}")
275
+ execute_ddl(drop_view_sql(n, options))
173
276
  remove_cached_schema(n)
174
277
  end
175
278
  nil
@@ -191,7 +294,7 @@ module Sequel
191
294
  #
192
295
  # DB.rename_column :items, :cntr, :counter
193
296
  #
194
- # See alter_table.
297
+ # See <tt>alter_table</tt>.
195
298
  def rename_column(table, *args)
196
299
  alter_table(table) {rename_column(*args)}
197
300
  end
@@ -200,7 +303,7 @@ module Sequel
200
303
  #
201
304
  # DB.set_column_default :items, :category, 'perl!'
202
305
  #
203
- # See alter_table.
306
+ # See <tt>alter_table</tt>.
204
307
  def set_column_default(table, *args)
205
308
  alter_table(table) {set_column_default(*args)}
206
309
  end
@@ -209,13 +312,18 @@ module Sequel
209
312
  #
210
313
  # DB.set_column_type :items, :price, :float
211
314
  #
212
- # See alter_table.
315
+ # See <tt>alter_table</tt>.
213
316
  def set_column_type(table, *args)
214
317
  alter_table(table) {set_column_type(*args)}
215
318
  end
216
319
 
217
320
  private
218
321
 
322
+ # Apply the changes in the given alter table ops to the table given by name.
323
+ def apply_alter_table(name, ops)
324
+ alter_table_sql_list(name, ops).flatten.each{|sql| execute_ddl(sql)}
325
+ end
326
+
219
327
  # The SQL to execute to modify the DDL for the given table name. op
220
328
  # should be one of the operations returned by the AlterTableGenerator.
221
329
  def alter_table_sql(table, op)
@@ -224,7 +332,7 @@ module Sequel
224
332
  when :add_column
225
333
  "ADD COLUMN #{column_definition_sql(op)}"
226
334
  when :drop_column
227
- "DROP COLUMN #{quoted_name}"
335
+ "DROP COLUMN #{quoted_name}#{' CASCADE' if op[:cascade]}"
228
336
  when :rename_column
229
337
  "RENAME COLUMN #{quoted_name} TO #{quote_identifier(op[:new_name])}"
230
338
  when :set_column_type
@@ -240,7 +348,7 @@ module Sequel
240
348
  when :add_constraint
241
349
  "ADD #{constraint_definition_sql(op)}"
242
350
  when :drop_constraint
243
- "DROP CONSTRAINT #{quoted_name}"
351
+ "DROP CONSTRAINT #{quoted_name}#{' CASCADE' if op[:cascade]}"
244
352
  else
245
353
  raise Error, "Unsupported ALTER TABLE operation"
246
354
  end
@@ -275,7 +383,12 @@ module Sequel
275
383
  def column_definition_auto_increment_sql(sql, column)
276
384
  sql << " #{auto_increment_sql}" if column[:auto_increment]
277
385
  end
278
-
386
+
387
+ # Add collate SQL fragment to column creation SQL.
388
+ def column_definition_collate_sql(sql, column)
389
+ sql << " COLLATE #{column[:collate]}" if column[:collate]
390
+ end
391
+
279
392
  # Add default SQL fragment to column creation SQL.
280
393
  def column_definition_default_sql(sql, column)
281
394
  sql << " DEFAULT #{literal(column[:default])}" if column.include?(:default)
@@ -318,7 +431,7 @@ module Sequel
318
431
  sql = " REFERENCES #{quote_schema_table(column[:table])}"
319
432
  sql << "(#{Array(column[:key]).map{|x| quote_identifier(x)}.join(COMMA_SEPARATOR)})" if column[:key]
320
433
  sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
321
- sql << " ON UPDATE #{on_delete_clause(column[:on_update])}" if column[:on_update]
434
+ sql << " ON UPDATE #{on_update_clause(column[:on_update])}" if column[:on_update]
322
435
  sql << " DEFERRABLE INITIALLY DEFERRED" if column[:deferrable]
323
436
  sql
324
437
  end
@@ -354,7 +467,7 @@ module Sequel
354
467
 
355
468
  # Execute the create index statements using the generator.
356
469
  def create_table_indexes_from_generator(name, generator, options)
357
- e = options[:ignore_index_errors]
470
+ e = options[:ignore_index_errors] || options[:if_not_exists]
358
471
  generator.indexes.each do |index|
359
472
  begin
360
473
  index_sql_list(name, [index]).each{|sql| execute_ddl(sql)}
@@ -366,7 +479,25 @@ module Sequel
366
479
 
367
480
  # DDL statement for creating a table with the given name, columns, and options
368
481
  def create_table_sql(name, generator, options)
369
- "CREATE #{temporary_table_sql if options[:temp]}TABLE #{quote_schema_table(name)} (#{column_list_sql(generator)})"
482
+ "#{create_table_prefix_sql(name, options)} (#{column_list_sql(generator)})"
483
+ end
484
+
485
+ # Run a command to create the table with the given name from the given
486
+ # SELECT sql statement.
487
+ def create_table_as(name, sql, options)
488
+ sql = sql.sql if sql.is_a?(Sequel::Dataset)
489
+ run(create_table_as_sql(name, sql, options))
490
+ end
491
+
492
+ # DDL statement for creating a table from the result of a SELECT statement.
493
+ # +sql+ should be a string representing a SELECT query.
494
+ def create_table_as_sql(name, sql, options)
495
+ "#{create_table_prefix_sql(name, options)} AS #{sql}"
496
+ end
497
+
498
+ # DDL statement for creating a table with the given name, columns, and options
499
+ def create_table_prefix_sql(name, options)
500
+ "CREATE #{temporary_table_sql if options[:temp]}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)}"
370
501
  end
371
502
 
372
503
  # Default index name for the table and columns, may be too long
@@ -382,10 +513,15 @@ module Sequel
382
513
  end
383
514
 
384
515
  # SQL DDL statement to drop the table with the given name.
385
- def drop_table_sql(name)
386
- "DROP TABLE #{quote_schema_table(name)}"
516
+ def drop_table_sql(name, options)
517
+ "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_schema_table(name)}#{' CASCADE' if options[:cascade]}"
387
518
  end
388
519
 
520
+ # SQL DDL statement to drop a view with the given name.
521
+ def drop_view_sql(name, options)
522
+ "DROP VIEW #{quote_schema_table(name)}#{' CASCADE' if options[:cascade]}"
523
+ end
524
+
389
525
  # Proxy the filter_expr call to the dataset, used for creating constraints.
390
526
  def filter_expr(*args, &block)
391
527
  schema_utility_dataset.literal(schema_utility_dataset.send(:filter_expr, *args, &block))
@@ -410,6 +546,32 @@ module Sequel
410
546
  indexes.map{|i| index_definition_sql(table_name, i)}
411
547
  end
412
548
 
549
+ # Extract the join table name from the arguments given to create_join_table.
550
+ # Also does argument validation for the create_join_table method.
551
+ def join_table_name(hash, options)
552
+ entries = hash.values
553
+ raise Error, "must have 2 entries in hash given to (create|drop)_join_table" unless entries.length == 2
554
+ if options[:name]
555
+ options[:name]
556
+ else
557
+ table_names = entries.map{|e| join_table_name_extract(e)}
558
+ table_names.map{|t| t.to_s}.sort.join('_')
559
+ end
560
+ end
561
+
562
+ # Extract an individual join table name, which should either be a string
563
+ # or symbol, or a hash containing one of those as the value for :table.
564
+ def join_table_name_extract(entry)
565
+ case entry
566
+ when Symbol, String
567
+ entry
568
+ when Hash
569
+ join_table_name_extract(entry[:table])
570
+ else
571
+ raise Error, "can't extract table name from #{entry.inspect}"
572
+ end
573
+ end
574
+
413
575
  # SQL DDL ON DELETE fragment to use, based on the given action.
414
576
  # The following actions are recognized:
415
577
  #
@@ -420,19 +582,15 @@ module Sequel
420
582
  # but do not allow deferring the integrity check.
421
583
  # * :set_default - Set columns referencing this row to their default value.
422
584
  # * :set_null - Set columns referencing this row to NULL.
585
+ #
586
+ # Any other object given is just converted to a string, with "_" converted to " " and upcased.
423
587
  def on_delete_clause(action)
424
- case action
425
- when :restrict
426
- RESTRICT
427
- when :cascade
428
- CASCADE
429
- when :set_null
430
- SET_NULL
431
- when :set_default
432
- SET_DEFAULT
433
- else
434
- NO_ACTION
435
- end
588
+ action.to_s.gsub("_", " ").upcase
589
+ end
590
+
591
+ # Alias of #on_delete_clause, since the two usually behave the same.
592
+ def on_update_clause(action)
593
+ on_delete_clause(action)
436
594
  end
437
595
 
438
596
  # Proxy the quote_schema_table method to the dataset