sequel 3.21.0 → 3.39.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 (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -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,26 @@ 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
+ else
236
+ duplicate_table(table)
237
+ end
238
+ when :add_constraint
239
+ duplicate_table(table, :constraints=>[op])
240
+ when :add_constraints
241
+ duplicate_table(table, :constraints=>op[:ops])
165
242
  else
166
- raise Error, "Unsupported ALTER TABLE operation"
243
+ raise Error, "Unsupported ALTER TABLE operation: #{op[:op].inspect}"
167
244
  end
168
245
  end
169
-
170
- # The array of column symbols in the table, except for ones given in opts[:except]
246
+
247
+ # A name to use for the backup table
171
248
  def backup_table_name(table, opts={})
172
249
  table = table.gsub('`', '')
173
250
  (opts[:times]||1000).times do |i|
@@ -176,17 +253,19 @@ module Sequel
176
253
  end
177
254
  end
178
255
 
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(', ')
256
+ # Surround default with parens to appease SQLite
257
+ def column_definition_default_sql(sql, column)
258
+ sql << " DEFAULT (#{literal(column[:default])})" if column.include?(:default)
182
259
  end
183
-
260
+
184
261
  # Array of PRAGMA SQL statements based on the Database options that should be applied to
185
262
  # new connections.
186
263
  def connection_pragmas
187
264
  ps = []
188
265
  v = typecast_value_boolean(opts.fetch(:foreign_keys, 1))
189
266
  ps << "PRAGMA foreign_keys = #{v ? 1 : 0}"
267
+ v = typecast_value_boolean(opts.fetch(:case_sensitive_like, 1))
268
+ ps << "PRAGMA case_sensitive_like = #{v ? 1 : 0}"
190
269
  [[:auto_vacuum, AUTO_VACUUM], [:synchronous, SYNCHRONOUS], [:temp_store, TEMP_STORE]].each do |prag, con|
191
270
  if v = opts[prag]
192
271
  raise(Error, "Value for PRAGMA #{prag} not supported, should be one of #{con.join(', ')}") unless v = con.index(v.to_sym)
@@ -196,28 +275,13 @@ module Sequel
196
275
  ps
197
276
  end
198
277
 
199
- # The array of column schema hashes, except for the ones given in opts[:except]
200
- def defined_columns_for(table, opts={})
278
+ # The array of column schema hashes for the current columns in the table
279
+ def defined_columns_for(table)
201
280
  cols = parse_pragma(table, {})
202
281
  cols.each do |c|
203
282
  c[:default] = LiteralString.new(c[:default]) if c[:default]
204
283
  c[:type] = c[:db_type]
205
284
  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
285
  cols
222
286
  end
223
287
 
@@ -231,17 +295,40 @@ module Sequel
231
295
  opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
232
296
 
233
297
  yield def_columns if block_given?
234
- def_columns_str = column_list_sql(def_columns)
298
+
299
+ constraints = (opts[:constraints] || []).dup
300
+ pks = []
301
+ def_columns.each{|c| pks << c[:name] if c[:primary_key]}
302
+ if pks.length > 1
303
+ constraints << {:type=>:primary_key, :columns=>pks}
304
+ def_columns.each{|c| c[:primary_key] = false if c[:primary_key]}
305
+ end
306
+
307
+ # If dropping a foreign key constraint, drop all foreign key constraints,
308
+ # as there is no way to determine which one to drop.
309
+ unless opts[:no_foreign_keys]
310
+ fks = foreign_key_list(table)
311
+
312
+ # If dropping a column, if there is a foreign key with that
313
+ # column, don't include it when building a copy of the table.
314
+ if ocp = opts[:old_columns_proc]
315
+ fks.delete_if{|c| ocp.call(c[:columns].dup) != c[:columns]}
316
+ end
317
+
318
+ constraints.concat(fks.each{|h| h[:type] = :foreign_key})
319
+ end
320
+
321
+ def_columns_str = (def_columns.map{|c| column_definition_sql(c)} + constraints.map{|c| constraint_definition_sql(c)}).join(', ')
235
322
  new_columns = old_columns.dup
236
323
  opts[:new_columns_proc].call(new_columns) if opts[:new_columns_proc]
237
324
 
238
325
  qt = quote_schema_table(table)
239
326
  bt = quote_identifier(backup_table_name(qt))
240
327
  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}"
328
+ "ALTER TABLE #{qt} RENAME TO #{bt}",
329
+ "CREATE TABLE #{qt}(#{def_columns_str})",
330
+ "INSERT INTO #{qt}(#{dataset.send(:identifier_list, new_columns)}) SELECT #{dataset.send(:identifier_list, old_columns)} FROM #{bt}",
331
+ "DROP TABLE #{bt}"
245
332
  ]
246
333
  indexes(table).each do |name, h|
247
334
  if (h[:columns].map{|x| x.to_s} - new_columns).empty?
@@ -280,7 +367,7 @@ module Sequel
280
367
 
281
368
  # Parse the output of the table_info pragma
282
369
  def parse_pragma(table_name, opts)
283
- metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth.call(table_name)).map do |row|
370
+ metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth(opts[:dataset]).call(table_name)).map do |row|
284
371
  row.delete(:cid)
285
372
  row[:allow_null] = row.delete(:notnull).to_i == 0
286
373
  row[:default] = row.delete(:dflt_value)
@@ -294,65 +381,123 @@ module Sequel
294
381
 
295
382
  # SQLite treats integer primary keys as autoincrementing (alias of rowid).
296
383
  def schema_autoincrementing_primary_key?(schema)
297
- super and schema[:db_type].downcase == 'integer'
384
+ super && schema[:db_type].downcase == 'integer'
298
385
  end
299
386
 
300
387
  # SQLite supports schema parsing using the table_info PRAGMA, so
301
388
  # parse the output of that into the format Sequel expects.
302
389
  def schema_parse_table(table_name, opts)
303
- m = output_identifier_meth
390
+ m = output_identifier_meth(opts[:dataset])
304
391
  parse_pragma(table_name, opts).map do |row|
305
392
  [m.call(row.delete(:name)), row]
306
393
  end
307
394
  end
308
395
 
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.
396
+ # Backbone of the tables and views support.
397
+ def tables_and_views(filter, opts)
398
+ m = output_identifier_meth
399
+ metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(filter).map{|r| m.call(r[:name])}
400
+ end
401
+
402
+ # SQLite only supports AUTOINCREMENT on integer columns, not
403
+ # bigint columns, so use integer instead of bigint for those
404
+ # columns.
313
405
  def type_literal_generic_bignum(column)
314
- :integer
406
+ column[:auto_increment] ? :integer : super
315
407
  end
316
408
  end
317
409
 
318
410
  # Instance methods for datasets that connect to an SQLite database
319
411
  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
412
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit')
322
413
  CONSTANT_MAP = {:CURRENT_DATE=>"date(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIMESTAMP=>"datetime(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIME=>"time(CURRENT_TIMESTAMP, 'localtime')".freeze}
323
-
324
- # SQLite does not support pattern matching via regular expressions.
325
- # SQLite is case insensitive (depending on pragma), so use LIKE for
326
- # ILIKE.
327
- def complex_expression_sql(op, args)
414
+ EMULATED_FUNCTION_MAP = {:char_length=>'length'.freeze}
415
+ EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}
416
+ NOT_SPACE = Dataset::NOT_SPACE
417
+ COMMA = Dataset::COMMA
418
+ PAREN_CLOSE = Dataset::PAREN_CLOSE
419
+ AS = Dataset::AS
420
+ APOS = Dataset::APOS
421
+ EXTRACT_OPEN = "CAST(strftime(".freeze
422
+ EXTRACT_CLOSE = ') AS '.freeze
423
+ NUMERIC = 'NUMERIC'.freeze
424
+ INTEGER = 'INTEGER'.freeze
425
+ BACKTICK = '`'.freeze
426
+ BACKTICK_RE = /`/.freeze
427
+ DOUBLE_BACKTICK = '``'.freeze
428
+ BLOB_START = "X'".freeze
429
+ HSTAR = "H*".freeze
430
+ DATE_OPEN = "date(".freeze
431
+ DATETIME_OPEN = "datetime(".freeze
432
+
433
+ def cast_sql_append(sql, expr, type)
434
+ if type == Time or type == DateTime
435
+ sql << DATETIME_OPEN
436
+ literal_append(sql, expr)
437
+ sql << PAREN_CLOSE
438
+ elsif type == Date
439
+ sql << DATE_OPEN
440
+ literal_append(sql, expr)
441
+ sql << PAREN_CLOSE
442
+ else
443
+ super
444
+ end
445
+ end
446
+
447
+ # SQLite is case insensitive (depending on pragma), so use LIKE for ILIKE.
448
+ # It also doesn't support a NOT LIKE b, you need to use NOT (a LIKE b).
449
+ # It doesn't support xor or the extract function natively, so those have to be emulated.
450
+ def complex_expression_sql_append(sql, op, args)
328
451
  case op
329
- when :~, :'!~', :'~*', :'!~*'
330
- 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))})"
452
+ when :ILIKE
453
+ super(sql, :LIKE, args.map{|a| SQL::Function.new(:upper, a)})
454
+ when :"NOT LIKE", :"NOT ILIKE"
455
+ sql << NOT_SPACE
456
+ complex_expression_sql_append(sql, (op == :"NOT ILIKE" ? :ILIKE : :LIKE), args)
457
+ when :^
458
+ sql << complex_expression_arg_pairs(args) do |a, b|
459
+ a = literal(a)
460
+ b = literal(b)
461
+ "((~(#{a} & #{b})) & (#{a} | #{b}))"
462
+ end
463
+ when :extract
464
+ part = args.at(0)
465
+ raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
466
+ sql << EXTRACT_OPEN << format << COMMA
467
+ literal_append(sql, args.at(1))
468
+ sql << EXTRACT_CLOSE << (part == :second ? NUMERIC : INTEGER) << PAREN_CLOSE
334
469
  else
335
- super(op, args)
470
+ super
336
471
  end
337
472
  end
338
473
 
339
- # MSSQL doesn't support the SQL standard CURRENT_DATE or CURRENT_TIME
340
- def constant_sql(constant)
341
- CONSTANT_MAP[constant] || super
474
+ # SQLite has CURRENT_TIMESTAMP and related constants in UTC instead
475
+ # of in localtime, so convert those constants to local time.
476
+ def constant_sql_append(sql, constant)
477
+ if c = CONSTANT_MAP[constant]
478
+ sql << c
479
+ else
480
+ super
481
+ end
342
482
  end
343
483
 
344
484
  # SQLite performs a TRUNCATE style DELETE if no filter is specified.
345
485
  # Since we want to always return the count of records, add a condition
346
486
  # that is always true and then delete.
347
487
  def delete
348
- @opts[:where] ? super : filter(1=>1).delete
488
+ @opts[:where] ? super : where(1=>1).delete
349
489
  end
350
490
 
351
491
  # 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]}"}
492
+ # current dataset. Currently, the options are ignore, but it accepts options
493
+ # to be compatible with other adapters.
494
+ def explain(opts=nil)
495
+ # Load the PrettyTable class, needed for explain output
496
+ Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
497
+
498
+ ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
499
+ rows = ds.all
500
+ Sequel::PrettyTable.string(rows, ds.columns)
356
501
  end
357
502
 
358
503
  # HAVING requires GROUP BY on SQLite
@@ -362,8 +507,20 @@ module Sequel
362
507
  end
363
508
 
364
509
  # SQLite uses the nonstandard ` (backtick) for quoting identifiers.
365
- def quoted_identifier(c)
366
- "`#{c}`"
510
+ def quoted_identifier_append(sql, c)
511
+ sql << BACKTICK << c.to_s.gsub(BACKTICK_RE, DOUBLE_BACKTICK) << BACKTICK
512
+ end
513
+
514
+ # When a qualified column is selected on SQLite and the qualifier
515
+ # is a subselect, the column name used is the full qualified name
516
+ # (including the qualifier) instead of just the column name. To
517
+ # get correct column names, you must use an alias.
518
+ def select(*cols)
519
+ 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)})
520
+ super(*cols.map{|c| alias_qualified_column(c)})
521
+ else
522
+ super
523
+ end
367
524
  end
368
525
 
369
526
  # SQLite does not support INTERSECT ALL or EXCEPT ALL
@@ -382,31 +539,63 @@ module Sequel
382
539
  end
383
540
 
384
541
  # SQLite supports timezones in literal timestamps, since it stores them
385
- # as text.
542
+ # as text. But using timezones in timestamps breaks SQLite datetime
543
+ # functions, so we allow the user to override the default per database.
386
544
  def supports_timestamp_timezones?
387
- true
545
+ db.use_timestamp_timezones?
388
546
  end
389
547
 
548
+ # SQLite cannot use WHERE 't'.
549
+ def supports_where_true?
550
+ false
551
+ end
552
+
390
553
  private
391
554
 
392
555
  # SQLite uses string literals instead of identifiers in AS clauses.
393
- def as_sql(expression, aliaz)
556
+ def as_sql_append(sql, aliaz)
394
557
  aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
395
- "#{expression} AS #{literal(aliaz.to_s)}"
558
+ sql << AS
559
+ literal_append(sql, aliaz.to_s)
396
560
  end
397
-
561
+
562
+ # If col is a qualified column, alias it to the same as the column name
563
+ def alias_qualified_column(col)
564
+ case col
565
+ when Symbol
566
+ t, c, a = split_symbol(col)
567
+ if t && !a
568
+ alias_qualified_column(SQL::QualifiedIdentifier.new(t, c))
569
+ else
570
+ col
571
+ end
572
+ when SQL::QualifiedIdentifier
573
+ SQL::AliasedExpression.new(col, col.column)
574
+ else
575
+ col
576
+ end
577
+ end
578
+
398
579
  # SQL fragment specifying a list of identifiers
399
580
  def identifier_list(columns)
400
- columns.map{|i| quote_identifier(i)}.join(COMMA_SEPARATOR)
581
+ columns.map{|i| quote_identifier(i)}.join(COMMA)
401
582
  end
402
583
 
403
584
  # 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}'"
585
+ def literal_blob_append(sql, v)
586
+ sql << BLOB_START << v.unpack(HSTAR).first << APOS
408
587
  end
409
-
588
+
589
+ # Respect the database integer_booleans setting, using 0 or 'f'.
590
+ def literal_false
591
+ @db.integer_booleans ? '0' : "'f'"
592
+ end
593
+
594
+ # Respect the database integer_booleans setting, using 1 or 't'.
595
+ def literal_true
596
+ @db.integer_booleans ? '1' : "'t'"
597
+ end
598
+
410
599
  # SQLite does not support the SQL WITH clause
411
600
  def select_clause_methods
412
601
  SELECT_CLAUSE_METHODS
@@ -416,7 +605,7 @@ module Sequel
416
605
  def select_lock_sql(sql)
417
606
  super unless @opts[:lock] == :update
418
607
  end
419
-
608
+
420
609
  # SQLite treats a DELETE with no WHERE clause as a TRUNCATE
421
610
  def _truncate_sql(table)
422
611
  "DELETE FROM #{table}"