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
@@ -6,16 +6,10 @@ module Sequel
6
6
  def database_type
7
7
  :progress
8
8
  end
9
-
10
- def dataset(opts = nil)
11
- ds = super
12
- ds.extend(DatasetMethods)
13
- ds
14
- end
15
9
  end
16
10
 
17
11
  module DatasetMethods
18
- SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where group order having compounds')
12
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select limit distinct columns from join where group order having compounds')
19
13
 
20
14
  # Progress requires SQL standard datetimes
21
15
  def requires_sql_standard_datetimes?
@@ -37,7 +31,10 @@ module Sequel
37
31
  # The Progress adapter targets Progress 9, so it silently ignores the option.
38
32
  def select_limit_sql(sql)
39
33
  raise(Error, "OFFSET not supported") if @opts[:offset]
40
- #sql << " TOP #{@opts[:limit]}" if @opts[:limit]
34
+ # if l = @opts[:limit]
35
+ # sql << " TOP "
36
+ # literal_append(sql, l)
37
+ # end
41
38
  end
42
39
  end
43
40
  end
@@ -7,16 +7,13 @@ module Sequel
7
7
  AUTO_VACUUM = [:none, :full, :incremental].freeze
8
8
  PRIMARY_KEY_INDEX_RE = /\Asqlite_autoindex_/.freeze
9
9
  SYNCHRONOUS = [:off, :normal, :full].freeze
10
- TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'"
10
+ TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'".freeze
11
11
  TEMP_STORE = [:default, :file, :memory].freeze
12
-
13
- # Run all alter_table commands in a transaction. This is technically only
14
- # needed for drop column.
15
- def alter_table(name, generator=nil, &block)
16
- remove_cached_schema(name)
17
- generator ||= Schema::AlterTableGenerator.new(self, &block)
18
- transaction{generator.operations.each{|op| alter_table_sql_list(name, [op]).flatten.each{|sql| execute_ddl(sql)}}}
19
- end
12
+ VIEWS_FILTER = "type = 'view'".freeze
13
+
14
+ # Whether to use integers for booleans in the database. SQLite recommends
15
+ # booleans be stored as integers, but historically Sequel has used 't'/'f'.
16
+ attr_accessor :integer_booleans
20
17
 
21
18
  # A symbol signifying the value of the auto_vacuum PRAGMA.
22
19
  def auto_vacuum
@@ -30,7 +27,14 @@ module Sequel
30
27
  value = AUTO_VACUUM.index(value) || (raise Error, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
31
28
  pragma_set(:auto_vacuum, value)
32
29
  end
33
-
30
+
31
+ # Set the case_sensitive_like PRAGMA using the given boolean value, if using
32
+ # SQLite 3.2.3+. If not using 3.2.3+, no error is raised. See pragma_set.
33
+ # Consider using the :case_sensitive_like Database option instead.
34
+ def case_sensitive_like=(value)
35
+ pragma_set(:case_sensitive_like, !!value ? 'on' : 'off') if sqlite_version >= 30203
36
+ end
37
+
34
38
  # SQLite uses the :sqlite database type.
35
39
  def database_type
36
40
  :sqlite
@@ -49,22 +53,33 @@ module Sequel
49
53
  pragma_set(:foreign_keys, !!value ? 'on' : 'off') if sqlite_version >= 30619
50
54
  end
51
55
 
56
+ # Return the array of foreign key info hashes using the foreign_key_list PRAGMA,
57
+ # including information for the :on_update and :on_delete entries.
58
+ def foreign_key_list(table, opts={})
59
+ m = output_identifier_meth
60
+ h = {}
61
+ metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
62
+ if r = h[row[:id]]
63
+ r[:columns] << m.call(row[:from])
64
+ r[:key] << m.call(row[:to]) if r[:key]
65
+ else
66
+ h[row[:id]] = {:columns=>[m.call(row[:from])], :table=>m.call(row[:table]), :key=>([m.call(row[:to])] if row[:to]), :on_update=>on_delete_sql_to_sym(row[:on_update]), :on_delete=>on_delete_sql_to_sym(row[:on_delete])}
67
+ end
68
+ end
69
+ h.values
70
+ end
71
+
52
72
  # Use the index_list and index_info PRAGMAs to determine the indexes on the table.
53
73
  def indexes(table, opts={})
54
74
  m = output_identifier_meth
55
75
  im = input_identifier_meth
56
76
  indexes = {}
57
- begin
58
- metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
59
- next if r[:name] =~ PRIMARY_KEY_INDEX_RE
60
- indexes[m.call(r[:name])] = {:unique=>r[:unique].to_i==1}
61
- end
62
- rescue Sequel::DatabaseError
63
- nil
64
- else
65
- indexes.each do |k, v|
66
- v[:columns] = metadata_dataset.with_sql("PRAGMA index_info(?)", im.call(k)).map(:name).map{|x| m.call(x)}
67
- end
77
+ metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
78
+ next if r[:name] =~ PRIMARY_KEY_INDEX_RE
79
+ indexes[m.call(r[:name])] = {:unique=>r[:unique].to_i==1}
80
+ end
81
+ indexes.each do |k, v|
82
+ v[:columns] = metadata_dataset.with_sql("PRAGMA index_info(?)", im.call(k)).map(:name).map{|x| m.call(x)}
68
83
  end
69
84
  indexes
70
85
  end
@@ -83,6 +98,11 @@ module Sequel
83
98
  def pragma_set(name, value)
84
99
  execute_ddl("PRAGMA #{name} = #{value}")
85
100
  end
101
+
102
+ # Set the integer_booleans option using the passed in :integer_boolean option.
103
+ def set_integer_booleans
104
+ @integer_booleans = typecast_value_boolean(@opts[:integer_booleans])
105
+ end
86
106
 
87
107
  # The version of the server as an integer, where 3.6.19 = 30619.
88
108
  # If the server version can't be determined, 0 is used.
@@ -96,11 +116,29 @@ module Sequel
96
116
  end
97
117
  end
98
118
 
119
+ # SQLite supports CREATE TABLE IF NOT EXISTS syntax since 3.3.0.
120
+ def supports_create_table_if_not_exists?
121
+ sqlite_version >= 30300
122
+ end
123
+
99
124
  # SQLite 3.6.8+ supports savepoints.
100
125
  def supports_savepoints?
101
126
  sqlite_version >= 30608
102
127
  end
103
128
 
129
+ # Override the default setting for whether to use timezones in timestamps.
130
+ # For backwards compatibility, it is set to +true+ by default.
131
+ # Anyone wanting to use SQLite's datetime functions should set it to +false+
132
+ # using this method. It's possible that the default will change in a future version,
133
+ # so anyone relying on timezones in timestamps should set this to +true+.
134
+ attr_writer :use_timestamp_timezones
135
+
136
+ # SQLite supports timezones in timestamps, since it just stores them as strings,
137
+ # but it breaks the usage of SQLite's datetime functions.
138
+ def use_timestamp_timezones?
139
+ defined?(@use_timestamp_timezones) ? @use_timestamp_timezones : (@use_timestamp_timezones = true)
140
+ end
141
+
104
142
  # A symbol signifying the value of the synchronous PRAGMA.
105
143
  def synchronous
106
144
  SYNCHRONOUS[pragma_get(:synchronous).to_i]
@@ -118,8 +156,7 @@ module Sequel
118
156
  # Options:
119
157
  # * :server - Set the server to use.
120
158
  def tables(opts={})
121
- m = output_identifier_meth
122
- metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(TABLES_FILTER).map{|r| m.call(r[:name])}
159
+ tables_and_views(TABLES_FILTER, opts)
123
160
  end
124
161
 
125
162
  # A symbol signifying the value of the temp_store PRAGMA.
@@ -134,8 +171,35 @@ module Sequel
134
171
  pragma_set(:temp_store, value)
135
172
  end
136
173
 
174
+ # Array of symbols specifying the view names in the current database.
175
+ #
176
+ # Options:
177
+ # * :server - Set the server to use.
178
+ def views(opts={})
179
+ tables_and_views(VIEWS_FILTER, opts)
180
+ end
181
+
137
182
  private
138
183
 
184
+ # Run all alter_table commands in a transaction. This is technically only
185
+ # needed for drop column.
186
+ def apply_alter_table(table, ops)
187
+ fks = foreign_keys
188
+ self.foreign_keys = false if fks
189
+ transaction do
190
+ if ops.length > 1 && ops.all?{|op| op[:op] == :add_constraint}
191
+ # If you are just doing constraints, apply all of them at the same time,
192
+ # as otherwise all but the last one get lost.
193
+ alter_table_sql_list(table, [{:op=>:add_constraints, :ops=>ops}]).flatten.each{|sql| execute_ddl(sql)}
194
+ else
195
+ # Run each operation separately, as later operations may depend on the
196
+ # results of earlier operations.
197
+ ops.each{|op| alter_table_sql_list(table, [op]).flatten.each{|sql| execute_ddl(sql)}}
198
+ end
199
+ end
200
+ self.foreign_keys = true if fks
201
+ end
202
+
139
203
  # SQLite supports limited table modification. You can add a column
140
204
  # or an index. Dropping columns is supported by copying the table into
141
205
  # a temporary table, dropping the table, and creating a new table without
@@ -161,13 +225,28 @@ module Sequel
161
225
  when :set_column_null
162
226
  duplicate_table(table){|columns| columns.each{|s| s[:null] = op[:null] if s[:name].to_s == op[:name].to_s}}
163
227
  when :set_column_type
164
- duplicate_table(table){|columns| columns.each{|s| s[:type] = op[:type] if s[:name].to_s == op[:name].to_s}}
228
+ duplicate_table(table){|columns| columns.each{|s| s.merge!(op) if s[:name].to_s == op[:name].to_s}}
229
+ when :drop_constraint
230
+ case op[:type]
231
+ when :primary_key
232
+ duplicate_table(table){|columns| columns.each{|s| s[:primary_key] = nil}}
233
+ when :foreign_key
234
+ duplicate_table(table, :no_foreign_keys=>true)
235
+ when :unique
236
+ duplicate_table(table)
237
+ else
238
+ raise Error, "Unsupported :type option for drop_constraint: #{op[:type].inspect}"
239
+ end
240
+ when :add_constraint
241
+ duplicate_table(table, :constraints=>[op])
242
+ when :add_constraints
243
+ duplicate_table(table, :constraints=>op[:ops])
165
244
  else
166
- raise Error, "Unsupported ALTER TABLE operation"
245
+ raise Error, "Unsupported ALTER TABLE operation: #{op[:op].inspect}"
167
246
  end
168
247
  end
169
-
170
- # The array of column symbols in the table, except for ones given in opts[:except]
248
+
249
+ # A name to use for the backup table
171
250
  def backup_table_name(table, opts={})
172
251
  table = table.gsub('`', '')
173
252
  (opts[:times]||1000).times do |i|
@@ -176,17 +255,19 @@ module Sequel
176
255
  end
177
256
  end
178
257
 
179
- # Allow use without a generator, needed for the alter table hackery that Sequel allows.
180
- def column_list_sql(generator)
181
- generator.is_a?(Schema::Generator) ? super : generator.map{|c| column_definition_sql(c)}.join(', ')
258
+ # Surround default with parens to appease SQLite
259
+ def column_definition_default_sql(sql, column)
260
+ sql << " DEFAULT (#{literal(column[:default])})" if column.include?(:default)
182
261
  end
183
-
262
+
184
263
  # Array of PRAGMA SQL statements based on the Database options that should be applied to
185
264
  # new connections.
186
265
  def connection_pragmas
187
266
  ps = []
188
267
  v = typecast_value_boolean(opts.fetch(:foreign_keys, 1))
189
268
  ps << "PRAGMA foreign_keys = #{v ? 1 : 0}"
269
+ v = typecast_value_boolean(opts.fetch(:case_sensitive_like, 1))
270
+ ps << "PRAGMA case_sensitive_like = #{v ? 1 : 0}"
190
271
  [[:auto_vacuum, AUTO_VACUUM], [:synchronous, SYNCHRONOUS], [:temp_store, TEMP_STORE]].each do |prag, con|
191
272
  if v = opts[prag]
192
273
  raise(Error, "Value for PRAGMA #{prag} not supported, should be one of #{con.join(', ')}") unless v = con.index(v.to_sym)
@@ -196,28 +277,13 @@ module Sequel
196
277
  ps
197
278
  end
198
279
 
199
- # The array of column schema hashes, except for the ones given in opts[:except]
200
- def defined_columns_for(table, opts={})
280
+ # The array of column schema hashes for the current columns in the table
281
+ def defined_columns_for(table)
201
282
  cols = parse_pragma(table, {})
202
283
  cols.each do |c|
203
284
  c[:default] = LiteralString.new(c[:default]) if c[:default]
204
285
  c[:type] = c[:db_type]
205
286
  end
206
- if opts[:except]
207
- nono= Array(opts[:except]).compact.map{|n| n.to_s}
208
- cols.reject!{|c| nono.include? c[:name] }
209
- end
210
-
211
- if foreign_keys
212
- metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
213
- c = cols.find {|co| co[:name] == row[:from] } or next
214
- c[:table] = row[:table]
215
- c[:key] = row[:to]
216
- c[:on_update] = on_delete_sql_to_sym(row[:on_update])
217
- c[:on_delete] = on_delete_sql_to_sym(row[:on_delete])
218
- # is there any way to get deferrable status?
219
- end
220
- end
221
287
  cols
222
288
  end
223
289
 
@@ -231,17 +297,40 @@ module Sequel
231
297
  opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
232
298
 
233
299
  yield def_columns if block_given?
234
- def_columns_str = column_list_sql(def_columns)
300
+
301
+ constraints = (opts[:constraints] || []).dup
302
+ pks = []
303
+ def_columns.each{|c| pks << c[:name] if c[:primary_key]}
304
+ if pks.length > 1
305
+ constraints << {:type=>:primary_key, :columns=>pks}
306
+ def_columns.each{|c| c[:primary_key] = false if c[:primary_key]}
307
+ end
308
+
309
+ # If dropping a foreign key constraint, drop all foreign key constraints,
310
+ # as there is no way to determine which one to drop.
311
+ unless opts[:no_foreign_keys]
312
+ fks = foreign_key_list(table)
313
+
314
+ # If dropping a column, if there is a foreign key with that
315
+ # column, don't include it when building a copy of the table.
316
+ if ocp = opts[:old_columns_proc]
317
+ fks.delete_if{|c| ocp.call(c[:columns].dup) != c[:columns]}
318
+ end
319
+
320
+ constraints.concat(fks.each{|h| h[:type] = :foreign_key})
321
+ end
322
+
323
+ def_columns_str = (def_columns.map{|c| column_definition_sql(c)} + constraints.map{|c| constraint_definition_sql(c)}).join(', ')
235
324
  new_columns = old_columns.dup
236
325
  opts[:new_columns_proc].call(new_columns) if opts[:new_columns_proc]
237
326
 
238
327
  qt = quote_schema_table(table)
239
328
  bt = quote_identifier(backup_table_name(qt))
240
329
  a = [
241
- "CREATE TABLE #{bt}(#{def_columns_str})",
242
- "INSERT INTO #{bt}(#{dataset.send(:identifier_list, new_columns)}) SELECT #{dataset.send(:identifier_list, old_columns)} FROM #{qt}",
243
- "DROP TABLE #{qt}",
244
- "ALTER TABLE #{bt} RENAME TO #{qt}"
330
+ "ALTER TABLE #{qt} RENAME TO #{bt}",
331
+ "CREATE TABLE #{qt}(#{def_columns_str})",
332
+ "INSERT INTO #{qt}(#{dataset.send(:identifier_list, new_columns)}) SELECT #{dataset.send(:identifier_list, old_columns)} FROM #{bt}",
333
+ "DROP TABLE #{bt}"
245
334
  ]
246
335
  indexes(table).each do |name, h|
247
336
  if (h[:columns].map{|x| x.to_s} - new_columns).empty?
@@ -280,7 +369,7 @@ module Sequel
280
369
 
281
370
  # Parse the output of the table_info pragma
282
371
  def parse_pragma(table_name, opts)
283
- metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth.call(table_name)).map do |row|
372
+ metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth(opts[:dataset]).call(table_name)).map do |row|
284
373
  row.delete(:cid)
285
374
  row[:allow_null] = row.delete(:notnull).to_i == 0
286
375
  row[:default] = row.delete(:dflt_value)
@@ -300,59 +389,102 @@ module Sequel
300
389
  # SQLite supports schema parsing using the table_info PRAGMA, so
301
390
  # parse the output of that into the format Sequel expects.
302
391
  def schema_parse_table(table_name, opts)
303
- m = output_identifier_meth
392
+ m = output_identifier_meth(opts[:dataset])
304
393
  parse_pragma(table_name, opts).map do |row|
305
394
  [m.call(row.delete(:name)), row]
306
395
  end
307
396
  end
308
397
 
309
- # SQLite uses the integer data type even for bignums. This is because they
310
- # are both stored internally as text, and converted when returned from
311
- # the database. Using an integer type instead of bigint makes it more likely
312
- # that software will automatically return the column as an integer.
398
+ # Backbone of the tables and views support.
399
+ def tables_and_views(filter, opts)
400
+ m = output_identifier_meth
401
+ metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(filter).map{|r| m.call(r[:name])}
402
+ end
403
+
404
+ # SQLite only supports AUTOINCREMENT on integer columns, not
405
+ # bigint columns, so use integer instead of bigint for those
406
+ # columns.
313
407
  def type_literal_generic_bignum(column)
314
- :integer
408
+ column[:auto_increment] ? :integer : super
315
409
  end
316
410
  end
317
411
 
318
412
  # Instance methods for datasets that connect to an SQLite database
319
413
  module DatasetMethods
320
- SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
321
- COMMA_SEPARATOR = ', '.freeze
414
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit')
322
415
  CONSTANT_MAP = {:CURRENT_DATE=>"date(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIMESTAMP=>"datetime(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIME=>"time(CURRENT_TIMESTAMP, 'localtime')".freeze}
323
-
416
+ EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}
417
+ NOT_SPACE = Dataset::NOT_SPACE
418
+ COMMA = Dataset::COMMA
419
+ PAREN_CLOSE = Dataset::PAREN_CLOSE
420
+ AS = Dataset::AS
421
+ APOS = Dataset::APOS
422
+ EXTRACT_OPEN = "CAST(strftime(".freeze
423
+ EXTRACT_CLOSE = ') AS '.freeze
424
+ NUMERIC = 'NUMERIC'.freeze
425
+ INTEGER = 'INTEGER'.freeze
426
+ BACKTICK = '`'.freeze
427
+ BACKTICK_RE = /`/.freeze
428
+ DOUBLE_BACKTICK = '``'.freeze
429
+ BLOB_START = "X'".freeze
430
+ HSTAR = "H*".freeze
431
+
324
432
  # SQLite does not support pattern matching via regular expressions.
325
433
  # SQLite is case insensitive (depending on pragma), so use LIKE for
326
434
  # ILIKE.
327
- def complex_expression_sql(op, args)
435
+ def complex_expression_sql_append(sql, op, args)
328
436
  case op
329
437
  when :~, :'!~', :'~*', :'!~*'
330
438
  raise Error, "SQLite does not support pattern matching via regular expressions"
331
- when :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
332
- # SQLite is case insensitive for ASCII, and non case sensitive for other character sets
333
- "#{'NOT ' if [:'NOT LIKE', :'NOT ILIKE'].include?(op)}(#{literal(args.at(0))} LIKE #{literal(args.at(1))})"
439
+ when :ILIKE
440
+ super(sql, :LIKE, args.map{|a| SQL::Function.new(:upper, a)})
441
+ when :"NOT LIKE", :"NOT ILIKE"
442
+ sql << NOT_SPACE
443
+ complex_expression_sql_append(sql, (op == :"NOT ILIKE" ? :ILIKE : :LIKE), args)
444
+ when :^
445
+ sql << complex_expression_arg_pairs(args) do |a, b|
446
+ a = literal(a)
447
+ b = literal(b)
448
+ "((~(#{a} & #{b})) & (#{a} | #{b}))"
449
+ end
450
+ when :extract
451
+ part = args.at(0)
452
+ raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
453
+ sql << EXTRACT_OPEN << format << COMMA
454
+ literal_append(sql, args.at(1))
455
+ sql << EXTRACT_CLOSE << (part == :second ? NUMERIC : INTEGER) << PAREN_CLOSE
334
456
  else
335
- super(op, args)
457
+ super
336
458
  end
337
459
  end
338
460
 
339
- # MSSQL doesn't support the SQL standard CURRENT_DATE or CURRENT_TIME
340
- def constant_sql(constant)
341
- CONSTANT_MAP[constant] || super
461
+ # SQLite has CURRENT_TIMESTAMP and related constants in UTC instead
462
+ # of in localtime, so convert those constants to local time.
463
+ def constant_sql_append(sql, constant)
464
+ if c = CONSTANT_MAP[constant]
465
+ sql << c
466
+ else
467
+ super
468
+ end
342
469
  end
343
470
 
344
471
  # SQLite performs a TRUNCATE style DELETE if no filter is specified.
345
472
  # Since we want to always return the count of records, add a condition
346
473
  # that is always true and then delete.
347
474
  def delete
348
- @opts[:where] ? super : filter(1=>1).delete
475
+ @opts[:where] ? super : where(1=>1).delete
349
476
  end
350
477
 
351
478
  # Return an array of strings specifying a query explanation for a SELECT of the
352
- # current dataset.
353
- def explain
354
- db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}").
355
- map{|x| "#{x[:addr]}|#{x[:opcode]}|#{(1..5).map{|i| x[:"p#{i}"]}.join('|')}|#{x[:comment]}"}
479
+ # current dataset. Currently, the options are ignore, but it accepts options
480
+ # to be compatible with other adapters.
481
+ def explain(opts=nil)
482
+ # Load the PrettyTable class, needed for explain output
483
+ Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
484
+
485
+ ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
486
+ rows = ds.all
487
+ Sequel::PrettyTable.string(rows, ds.columns)
356
488
  end
357
489
 
358
490
  # HAVING requires GROUP BY on SQLite
@@ -362,8 +494,20 @@ module Sequel
362
494
  end
363
495
 
364
496
  # SQLite uses the nonstandard ` (backtick) for quoting identifiers.
365
- def quoted_identifier(c)
366
- "`#{c}`"
497
+ def quoted_identifier_append(sql, c)
498
+ sql << BACKTICK << c.to_s.gsub(BACKTICK_RE, DOUBLE_BACKTICK) << BACKTICK
499
+ end
500
+
501
+ # When a qualified column is selected on SQLite and the qualifier
502
+ # is a subselect, the column name used is the full qualified name
503
+ # (including the qualifier) instead of just the column name. To
504
+ # get correct column names, you must use an alias.
505
+ def select(*cols)
506
+ if ((f = @opts[:from]) && f.any?{|t| t.is_a?(Dataset) || (t.is_a?(SQL::AliasedExpression) && t.expression.is_a?(Dataset))}) || ((j = @opts[:join]) && j.any?{|t| t.table.is_a?(Dataset)})
507
+ super(*cols.map{|c| alias_qualified_column(c)})
508
+ else
509
+ super
510
+ end
367
511
  end
368
512
 
369
513
  # SQLite does not support INTERSECT ALL or EXCEPT ALL
@@ -382,31 +526,63 @@ module Sequel
382
526
  end
383
527
 
384
528
  # SQLite supports timezones in literal timestamps, since it stores them
385
- # as text.
529
+ # as text. But using timezones in timestamps breaks SQLite datetime
530
+ # functions, so we allow the user to override the default per database.
386
531
  def supports_timestamp_timezones?
387
- true
532
+ db.use_timestamp_timezones?
388
533
  end
389
534
 
535
+ # SQLite cannot use WHERE 't'.
536
+ def supports_where_true?
537
+ false
538
+ end
539
+
390
540
  private
391
541
 
392
542
  # SQLite uses string literals instead of identifiers in AS clauses.
393
- def as_sql(expression, aliaz)
543
+ def as_sql_append(sql, aliaz)
394
544
  aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
395
- "#{expression} AS #{literal(aliaz.to_s)}"
545
+ sql << AS
546
+ literal_append(sql, aliaz.to_s)
547
+ end
548
+
549
+ # If col is a qualified column, alias it to the same as the column name
550
+ def alias_qualified_column(col)
551
+ case col
552
+ when Symbol
553
+ t, c, a = split_symbol(col)
554
+ if t && !a
555
+ alias_qualified_column(SQL::QualifiedIdentifier.new(t, c))
556
+ else
557
+ col
558
+ end
559
+ when SQL::QualifiedIdentifier
560
+ SQL::AliasedExpression.new(col, col.column)
561
+ else
562
+ col
563
+ end
396
564
  end
397
565
 
398
566
  # SQL fragment specifying a list of identifiers
399
567
  def identifier_list(columns)
400
- columns.map{|i| quote_identifier(i)}.join(COMMA_SEPARATOR)
568
+ columns.map{|i| quote_identifier(i)}.join(COMMA)
401
569
  end
402
570
 
403
571
  # SQLite uses a preceding X for hex escaping strings
404
- def literal_blob(v)
405
- blob = ''
406
- v.each_byte{|x| blob << sprintf('%02x', x)}
407
- "X'#{blob}'"
572
+ def literal_blob_append(sql, v)
573
+ sql << BLOB_START << v.unpack(HSTAR).first << APOS
408
574
  end
409
-
575
+
576
+ # Respect the database integer_booleans setting, using 0 or 'f'.
577
+ def literal_false
578
+ @db.integer_booleans ? '0' : "'f'"
579
+ end
580
+
581
+ # Respect the database integer_booleans setting, using 1 or 't'.
582
+ def literal_true
583
+ @db.integer_booleans ? '1' : "'t'"
584
+ end
585
+
410
586
  # SQLite does not support the SQL WITH clause
411
587
  def select_clause_methods
412
588
  SELECT_CLAUSE_METHODS
@@ -416,7 +592,7 @@ module Sequel
416
592
  def select_lock_sql(sql)
417
593
  super unless @opts[:lock] == :update
418
594
  end
419
-
595
+
420
596
  # SQLite treats a DELETE with no WHERE clause as a TRUNCATE
421
597
  def _truncate_sql(table)
422
598
  "DELETE FROM #{table}"