viking-sequel 3.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,44 @@
1
+ module Sequel
2
+ module Progress
3
+ module DatabaseMethods
4
+
5
+ # Progress uses the :progress database type.
6
+ def database_type
7
+ :progress
8
+ end
9
+
10
+ def dataset(opts = nil)
11
+ ds = super
12
+ ds.extend(DatasetMethods)
13
+ ds
14
+ end
15
+ end
16
+
17
+ module DatasetMethods
18
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where group order having compounds')
19
+
20
+ # Progress requires SQL standard datetimes
21
+ def requires_sql_standard_datetimes?
22
+ true
23
+ end
24
+
25
+ # Progress does not support INTERSECT or EXCEPT
26
+ def supports_intersect_except?
27
+ false
28
+ end
29
+
30
+ private
31
+
32
+ def select_clause_methods
33
+ SELECT_CLAUSE_METHODS
34
+ end
35
+
36
+ # Progress uses TOP for limit, but it is only supported in Progress 10.
37
+ # The Progress adapter targets Progress 9, so it silently ignores the option.
38
+ def select_limit_sql(sql)
39
+ raise(Error, "OFFSET not supported") if @opts[:offset]
40
+ #sql << " TOP #{@opts[:limit]}" if @opts[:limit]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,389 @@
1
+ module Sequel
2
+ module SQLite
3
+ # No matter how you connect to SQLite, the following Database options
4
+ # can be used to set PRAGMAs on connections in a thread-safe manner:
5
+ # :auto_vacuum, :foreign_keys, :synchronous, and :temp_store.
6
+ module DatabaseMethods
7
+ AUTO_VACUUM = [:none, :full, :incremental].freeze
8
+ PRIMARY_KEY_INDEX_RE = /\Asqlite_autoindex_/.freeze
9
+ SYNCHRONOUS = [:off, :normal, :full].freeze
10
+ TABLES_FILTER = "type = 'table' AND NOT name = 'sqlite_sequence'"
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
20
+
21
+ # A symbol signifying the value of the auto_vacuum PRAGMA.
22
+ def auto_vacuum
23
+ AUTO_VACUUM[pragma_get(:auto_vacuum).to_i]
24
+ end
25
+
26
+ # Set the auto_vacuum PRAGMA using the given symbol (:none, :full, or
27
+ # :incremental). See pragma_set. Consider using the :auto_vacuum
28
+ # Database option instead.
29
+ def auto_vacuum=(value)
30
+ value = AUTO_VACUUM.index(value) || (raise Error, "Invalid value for auto_vacuum option. Please specify one of :none, :full, :incremental.")
31
+ pragma_set(:auto_vacuum, value)
32
+ end
33
+
34
+ # SQLite uses the :sqlite database type.
35
+ def database_type
36
+ :sqlite
37
+ end
38
+
39
+ # Boolean signifying the value of the foreign_keys PRAGMA, or nil
40
+ # if not using SQLite 3.6.19+.
41
+ def foreign_keys
42
+ pragma_get(:foreign_keys).to_i == 1 if sqlite_version >= 30619
43
+ end
44
+
45
+ # Set the foreign_keys PRAGMA using the given boolean value, if using
46
+ # SQLite 3.6.19+. If not using 3.6.19+, no error is raised. See pragma_set.
47
+ # Consider using the :foreign_keys Database option instead.
48
+ def foreign_keys=(value)
49
+ pragma_set(:foreign_keys, !!value ? 'on' : 'off') if sqlite_version >= 30619
50
+ end
51
+
52
+ # Return a hash containing index information. Hash keys are index name symbols.
53
+ # Values are subhashes with two keys, :columns and :unique. The value of :columns
54
+ # is an array of symbols of column names. The value of :unique is true or false
55
+ # depending on if the index is unique.
56
+ def indexes(table)
57
+ m = output_identifier_meth
58
+ im = input_identifier_meth
59
+ indexes = {}
60
+ begin
61
+ metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
62
+ next if r[:name] =~ PRIMARY_KEY_INDEX_RE
63
+ indexes[m.call(r[:name])] = {:unique=>r[:unique].to_i==1}
64
+ end
65
+ rescue Sequel::DatabaseError
66
+ nil
67
+ else
68
+ indexes.each do |k, v|
69
+ v[:columns] = metadata_dataset.with_sql("PRAGMA index_info(?)", im.call(k)).map(:name).map{|x| m.call(x)}
70
+ end
71
+ end
72
+ indexes
73
+ end
74
+
75
+ # Get the value of the given PRAGMA.
76
+ def pragma_get(name)
77
+ self["PRAGMA #{name}"].single_value
78
+ end
79
+
80
+ # Set the value of the given PRAGMA to value.
81
+ #
82
+ # This method is not thread safe, and will not work correctly if there
83
+ # are multiple connections in the Database's connection pool. PRAGMA
84
+ # modifications should be done when the connection is created, using
85
+ # an option provided when creating the Database object.
86
+ def pragma_set(name, value)
87
+ execute_ddl("PRAGMA #{name} = #{value}")
88
+ end
89
+
90
+ # The version of the server as an integer, where 3.6.19 = 30619.
91
+ # If the server version can't be determined, 0 is used.
92
+ def sqlite_version
93
+ return @sqlite_version if defined?(@sqlite_version)
94
+ @sqlite_version = begin
95
+ v = get{sqlite_version{}}
96
+ [10000, 100, 1].zip(v.split('.')).inject(0){|a, m| a + m[0] * Integer(m[1])}
97
+ rescue
98
+ 0
99
+ end
100
+ end
101
+
102
+ # SQLite 3.6.8+ supports savepoints.
103
+ def supports_savepoints?
104
+ sqlite_version >= 30608
105
+ end
106
+
107
+ # A symbol signifying the value of the synchronous PRAGMA.
108
+ def synchronous
109
+ SYNCHRONOUS[pragma_get(:synchronous).to_i]
110
+ end
111
+
112
+ # Set the synchronous PRAGMA using the given symbol (:off, :normal, or :full). See pragma_set.
113
+ # Consider using the :synchronous Database option instead.
114
+ def synchronous=(value)
115
+ value = SYNCHRONOUS.index(value) || (raise Error, "Invalid value for synchronous option. Please specify one of :off, :normal, :full.")
116
+ pragma_set(:synchronous, value)
117
+ end
118
+
119
+ # Array of symbols specifying the table names in the current database.
120
+ #
121
+ # Options:
122
+ # * :server - Set the server to use.
123
+ def tables(opts={})
124
+ m = output_identifier_meth
125
+ metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(TABLES_FILTER).map{|r| m.call(r[:name])}
126
+ end
127
+
128
+ # A symbol signifying the value of the temp_store PRAGMA.
129
+ def temp_store
130
+ TEMP_STORE[pragma_get(:temp_store).to_i]
131
+ end
132
+
133
+ # Set the temp_store PRAGMA using the given symbol (:default, :file, or :memory). See pragma_set.
134
+ # Consider using the :temp_store Database option instead.
135
+ def temp_store=(value)
136
+ value = TEMP_STORE.index(value) || (raise Error, "Invalid value for temp_store option. Please specify one of :default, :file, :memory.")
137
+ pragma_set(:temp_store, value)
138
+ end
139
+
140
+ private
141
+
142
+ # SQLite supports limited table modification. You can add a column
143
+ # or an index. Dropping columns is supported by copying the table into
144
+ # a temporary table, dropping the table, and creating a new table without
145
+ # the column inside of a transaction.
146
+ def alter_table_sql(table, op)
147
+ case op[:op]
148
+ when :add_index, :drop_index
149
+ super
150
+ when :add_column
151
+ if op[:unique] || op[:primary_key]
152
+ duplicate_table(table){|columns| columns.push(op)}
153
+ else
154
+ super
155
+ end
156
+ when :drop_column
157
+ ocp = lambda{|oc| oc.delete_if{|c| c.to_s == op[:name].to_s}}
158
+ duplicate_table(table, :old_columns_proc=>ocp){|columns| columns.delete_if{|s| s[:name].to_s == op[:name].to_s}}
159
+ when :rename_column
160
+ ncp = lambda{|nc| nc.map!{|c| c.to_s == op[:name].to_s ? op[:new_name] : c}}
161
+ duplicate_table(table, :new_columns_proc=>ncp){|columns| columns.each{|s| s[:name] = op[:new_name] if s[:name].to_s == op[:name].to_s}}
162
+ when :set_column_default
163
+ duplicate_table(table){|columns| columns.each{|s| s[:default] = op[:default] if s[:name].to_s == op[:name].to_s}}
164
+ when :set_column_null
165
+ duplicate_table(table){|columns| columns.each{|s| s[:null] = op[:null] if s[:name].to_s == op[:name].to_s}}
166
+ when :set_column_type
167
+ duplicate_table(table){|columns| columns.each{|s| s[:type] = op[:type] if s[:name].to_s == op[:name].to_s}}
168
+ else
169
+ raise Error, "Unsupported ALTER TABLE operation"
170
+ end
171
+ end
172
+
173
+ # The array of column symbols in the table, except for ones given in opts[:except]
174
+ def backup_table_name(table, opts={})
175
+ table = table.gsub('`', '')
176
+ (opts[:times]||1000).times do |i|
177
+ table_name = "#{table}_backup#{i}"
178
+ return table_name unless table_exists?(table_name)
179
+ end
180
+ end
181
+
182
+ # Allow use without a generator, needed for the alter table hackery that Sequel allows.
183
+ def column_list_sql(generator)
184
+ generator.is_a?(Schema::Generator) ? super : generator.map{|c| column_definition_sql(c)}.join(', ')
185
+ end
186
+
187
+ # The array of column schema hashes, except for the ones given in opts[:except]
188
+ def defined_columns_for(table, opts={})
189
+ cols = parse_pragma(table, {})
190
+ cols.each do |c|
191
+ c[:default] = LiteralString.new(c[:default]) if c[:default]
192
+ c[:type] = c[:db_type]
193
+ end
194
+ if opts[:except]
195
+ nono= Array(opts[:except]).compact.map{|n| n.to_s}
196
+ cols.reject!{|c| nono.include? c[:name] }
197
+ end
198
+ cols
199
+ end
200
+
201
+ # Duplicate an existing table by creating a new table, copying all records
202
+ # from the existing table into the new table, deleting the existing table
203
+ # and renaming the new table to the existing table's name.
204
+ def duplicate_table(table, opts={})
205
+ remove_cached_schema(table)
206
+ def_columns = defined_columns_for(table)
207
+ old_columns = def_columns.map{|c| c[:name]}
208
+ opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
209
+
210
+ yield def_columns if block_given?
211
+ def_columns_str = column_list_sql(def_columns)
212
+ new_columns = old_columns.dup
213
+ opts[:new_columns_proc].call(new_columns) if opts[:new_columns_proc]
214
+
215
+ qt = quote_schema_table(table)
216
+ bt = quote_identifier(backup_table_name(qt))
217
+ [
218
+ "CREATE TABLE #{bt}(#{def_columns_str})",
219
+ "INSERT INTO #{bt}(#{dataset.send(:identifier_list, new_columns)}) SELECT #{dataset.send(:identifier_list, old_columns)} FROM #{qt}",
220
+ "DROP TABLE #{qt}",
221
+ "ALTER TABLE #{bt} RENAME TO #{qt}"
222
+ ]
223
+ end
224
+
225
+ # SQLite folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
226
+ def identifier_input_method_default
227
+ nil
228
+ end
229
+
230
+ # SQLite folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
231
+ def identifier_output_method_default
232
+ nil
233
+ end
234
+
235
+ # Array of PRAGMA SQL statements based on the Database options that should be applied to
236
+ # new connections.
237
+ def connection_pragmas
238
+ ps = []
239
+ v = typecast_value_boolean(opts.fetch(:foreign_keys, 1))
240
+ ps << "PRAGMA foreign_keys = #{v ? 1 : 0}"
241
+ [[:auto_vacuum, AUTO_VACUUM], [:synchronous, SYNCHRONOUS], [:temp_store, TEMP_STORE]].each do |prag, con|
242
+ if v = opts[prag]
243
+ raise(Error, "Value for PRAGMA #{prag} not supported, should be one of #{con.join(', ')}") unless v = con.index(v.to_sym)
244
+ ps << "PRAGMA #{prag} = #{v}"
245
+ end
246
+ end
247
+ ps
248
+ end
249
+
250
+ # Parse the output of the table_info pragma
251
+ def parse_pragma(table_name, opts)
252
+ metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth.call(table_name)).map do |row|
253
+ row.delete(:cid)
254
+ row[:allow_null] = row.delete(:notnull).to_i == 0
255
+ row[:default] = row.delete(:dflt_value)
256
+ row[:primary_key] = row.delete(:pk).to_i == 1
257
+ row[:default] = nil if blank_object?(row[:default]) || row[:default] == 'NULL'
258
+ row[:db_type] = row.delete(:type)
259
+ row[:type] = schema_column_type(row[:db_type])
260
+ row
261
+ end
262
+ end
263
+
264
+ # SQLite treats integer primary keys as autoincrementing (alias of rowid).
265
+ def schema_autoincrementing_primary_key?(schema)
266
+ super and schema[:db_type].downcase == 'integer'
267
+ end
268
+
269
+ # SQLite supports schema parsing using the table_info PRAGMA, so
270
+ # parse the output of that into the format Sequel expects.
271
+ def schema_parse_table(table_name, opts)
272
+ m = output_identifier_meth
273
+ parse_pragma(table_name, opts).map do |row|
274
+ [m.call(row.delete(:name)), row]
275
+ end
276
+ end
277
+
278
+ # SQLite uses the integer data type even for bignums. This is because they
279
+ # are both stored internally as text, and converted when returned from
280
+ # the database. Using an integer type instead of bigint makes it more likely
281
+ # that software will automatically return the column as an integer.
282
+ def type_literal_generic_bignum(column)
283
+ :integer
284
+ end
285
+ end
286
+
287
+ # Instance methods for datasets that connect to an SQLite database
288
+ module DatasetMethods
289
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
290
+ CONSTANT_MAP = {:CURRENT_DATE=>"date(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIMESTAMP=>"datetime(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIME=>"time(CURRENT_TIMESTAMP, 'localtime')".freeze}
291
+
292
+ # SQLite does not support pattern matching via regular expressions.
293
+ # SQLite is case insensitive (depending on pragma), so use LIKE for
294
+ # ILIKE.
295
+ def complex_expression_sql(op, args)
296
+ case op
297
+ when :~, :'!~', :'~*', :'!~*'
298
+ raise Error, "SQLite does not support pattern matching via regular expressions"
299
+ when :LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'
300
+ # SQLite is case insensitive for ASCII, and non case sensitive for other character sets
301
+ "#{'NOT ' if [:'NOT LIKE', :'NOT ILIKE'].include?(op)}(#{literal(args.at(0))} LIKE #{literal(args.at(1))})"
302
+ else
303
+ super(op, args)
304
+ end
305
+ end
306
+
307
+ # MSSQL doesn't support the SQL standard CURRENT_DATE or CURRENT_TIME
308
+ def constant_sql(constant)
309
+ CONSTANT_MAP[constant] || super
310
+ end
311
+
312
+ # SQLite performs a TRUNCATE style DELETE if no filter is specified.
313
+ # Since we want to always return the count of records, add a condition
314
+ # that is always true and then delete.
315
+ def delete
316
+ @opts[:where] ? super : filter(1=>1).delete
317
+ end
318
+
319
+ # Return an array of strings specifying a query explanation for a SELECT of the
320
+ # current dataset.
321
+ def explain
322
+ db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}").
323
+ map{|x| "#{x[:addr]}|#{x[:opcode]}|#{(1..5).map{|i| x[:"p#{i}"]}.join('|')}|#{x[:comment]}"}
324
+ end
325
+
326
+ # HAVING requires GROUP BY on SQLite
327
+ def having(*cond, &block)
328
+ raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") unless @opts[:group]
329
+ super
330
+ end
331
+
332
+ # SQLite uses the nonstandard ` (backtick) for quoting identifiers.
333
+ def quoted_identifier(c)
334
+ "`#{c}`"
335
+ end
336
+
337
+ # SQLite does not support INTERSECT ALL or EXCEPT ALL
338
+ def supports_intersect_except_all?
339
+ false
340
+ end
341
+
342
+ # SQLite does not support IS TRUE
343
+ def supports_is_true?
344
+ false
345
+ end
346
+
347
+ # SQLite does not support multiple columns for the IN/NOT IN operators
348
+ def supports_multiple_column_in?
349
+ false
350
+ end
351
+
352
+ # SQLite supports timezones in literal timestamps, since it stores them
353
+ # as text.
354
+ def supports_timestamp_timezones?
355
+ true
356
+ end
357
+
358
+ private
359
+
360
+ # SQLite uses string literals instead of identifiers in AS clauses.
361
+ def as_sql(expression, aliaz)
362
+ aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
363
+ "#{expression} AS #{literal(aliaz.to_s)}"
364
+ end
365
+
366
+ # SQLite uses a preceding X for hex escaping strings
367
+ def literal_blob(v)
368
+ blob = ''
369
+ v.each_byte{|x| blob << sprintf('%02x', x)}
370
+ "X'#{blob}'"
371
+ end
372
+
373
+ # SQLite does not support the SQL WITH clause
374
+ def select_clause_methods
375
+ SELECT_CLAUSE_METHODS
376
+ end
377
+
378
+ # Support FOR SHARE locking when using the :share lock style.
379
+ def select_lock_sql(sql)
380
+ super unless @opts[:lock] == :update
381
+ end
382
+
383
+ # SQLite treats a DELETE with no WHERE clause as a TRUNCATE
384
+ def _truncate_sql(table)
385
+ "DELETE FROM #{table}"
386
+ end
387
+ end
388
+ end
389
+ end
@@ -0,0 +1,224 @@
1
+ require 'sqlite3'
2
+ begin
3
+ SQLite3::Database.instance_method(:type_translation)
4
+ rescue
5
+ raise(Sequel::Error, "SQLite3::Database#type_translation is not defined. If you are using the sqlite3 gem, please install the sqlite3-ruby gem.")
6
+ end
7
+ Sequel.require 'adapters/shared/sqlite'
8
+
9
+ module Sequel
10
+ # Top level module for holding all SQLite-related modules and classes
11
+ # for Sequel.
12
+ module SQLite
13
+ # Database class for SQLite databases used with Sequel and the
14
+ # ruby-sqlite3 driver.
15
+ class Database < Sequel::Database
16
+ UNIX_EPOCH_TIME_FORMAT = /\A\d+\z/.freeze
17
+ include ::Sequel::SQLite::DatabaseMethods
18
+
19
+ set_adapter_scheme :sqlite
20
+
21
+ # Mimic the file:// uri, by having 2 preceding slashes specify a relative
22
+ # path, and 3 preceding slashes specify an absolute path.
23
+ def self.uri_to_options(uri) # :nodoc:
24
+ { :database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}" }
25
+ end
26
+
27
+ private_class_method :uri_to_options
28
+
29
+ # Connect to the database. Since SQLite is a file based database,
30
+ # the only options available are :database (to specify the database
31
+ # name), and :timeout, to specify how long to wait for the database to
32
+ # be available if it is locked, given in milliseconds (default is 5000).
33
+ def connect(server)
34
+ opts = server_opts(server)
35
+ opts[:database] = ':memory:' if blank_object?(opts[:database])
36
+ db = ::SQLite3::Database.new(opts[:database])
37
+ db.busy_timeout(opts.fetch(:timeout, 5000))
38
+ db.type_translation = true
39
+
40
+ connection_pragmas.each{|s| log_yield(s){db.execute_batch(s)}}
41
+
42
+ # Handle datetimes with Sequel.datetime_class
43
+ prok = proc do |t,v|
44
+ v = Time.at(v.to_i).iso8601 if UNIX_EPOCH_TIME_FORMAT.match(v)
45
+ Sequel.database_to_application_timestamp(v)
46
+ end
47
+ db.translator.add_translator("timestamp", &prok)
48
+ db.translator.add_translator("datetime", &prok)
49
+
50
+ # Handle numeric values with BigDecimal
51
+ prok = proc{|t,v| BigDecimal.new(v) rescue v}
52
+ db.translator.add_translator("numeric", &prok)
53
+ db.translator.add_translator("decimal", &prok)
54
+ db.translator.add_translator("money", &prok)
55
+
56
+ # Handle floating point values with Float
57
+ prok = proc{|t,v| Float(v) rescue v}
58
+ db.translator.add_translator("float", &prok)
59
+ db.translator.add_translator("real", &prok)
60
+ db.translator.add_translator("double precision", &prok)
61
+
62
+ # Handle blob values with Sequel::SQL::Blob
63
+ db.translator.add_translator("blob"){|t,v| ::Sequel::SQL::Blob.new(v)}
64
+
65
+ db
66
+ end
67
+
68
+ # Return instance of Sequel::SQLite::Dataset with the given options.
69
+ def dataset(opts = nil)
70
+ SQLite::Dataset.new(self, opts)
71
+ end
72
+
73
+ # Run the given SQL with the given arguments and return the number of changed rows.
74
+ def execute_dui(sql, opts={})
75
+ _execute(opts){|conn| log_yield(sql, opts[:arguments]){conn.execute_batch(sql, opts[:arguments])}; conn.changes}
76
+ end
77
+
78
+ # Run the given SQL with the given arguments and return the last inserted row id.
79
+ def execute_insert(sql, opts={})
80
+ _execute(opts){|conn| log_yield(sql, opts[:arguments]){conn.execute(sql, opts[:arguments])}; conn.last_insert_row_id}
81
+ end
82
+
83
+ # Run the given SQL with the given arguments and yield each row.
84
+ def execute(sql, opts={})
85
+ _execute(opts) do |conn|
86
+ begin
87
+ yield(result = log_yield(sql, opts[:arguments]){conn.query(sql, opts[:arguments])})
88
+ ensure
89
+ result.close if result
90
+ end
91
+ end
92
+ end
93
+
94
+ # Run the given SQL with the given arguments and return the first value of the first row.
95
+ def single_value(sql, opts={})
96
+ _execute(opts){|conn| log_yield(sql, opts[:arguments]){conn.get_first_value(sql, opts[:arguments])}}
97
+ end
98
+
99
+ private
100
+
101
+ # Yield an available connection. Rescue
102
+ # any SQLite3::Exceptions and turn them into DatabaseErrors.
103
+ def _execute(opts, &block)
104
+ begin
105
+ synchronize(opts[:server], &block)
106
+ rescue SQLite3::Exception => e
107
+ raise_error(e)
108
+ end
109
+ end
110
+
111
+ # The SQLite adapter does not need the pool to convert exceptions.
112
+ # Also, force the max connections to 1 if a memory database is being
113
+ # used, as otherwise each connection gets a separate database.
114
+ def connection_pool_default_options
115
+ o = super.dup
116
+ # Default to only a single connection if a memory database is used,
117
+ # because otherwise each connection will get a separate database
118
+ o[:max_connections] = 1 if @opts[:database] == ':memory:' || blank_object?(@opts[:database])
119
+ o
120
+ end
121
+
122
+ # The main error class that SQLite3 raises
123
+ def database_error_classes
124
+ [SQLite3::Exception]
125
+ end
126
+
127
+ # Disconnect given connections from the database.
128
+ def disconnect_connection(c)
129
+ c.close
130
+ end
131
+ end
132
+
133
+ # Dataset class for SQLite datasets that use the ruby-sqlite3 driver.
134
+ class Dataset < Sequel::Dataset
135
+ include ::Sequel::SQLite::DatasetMethods
136
+
137
+ PREPARED_ARG_PLACEHOLDER = ':'.freeze
138
+
139
+ # SQLite already supports named bind arguments, so use directly.
140
+ module ArgumentMapper
141
+ include Sequel::Dataset::ArgumentMapper
142
+
143
+ protected
144
+
145
+ # Return a hash with the same values as the given hash,
146
+ # but with the keys converted to strings.
147
+ def map_to_prepared_args(hash)
148
+ args = {}
149
+ hash.each{|k,v| args[k.to_s] = v}
150
+ args
151
+ end
152
+
153
+ private
154
+
155
+ # SQLite uses a : before the name of the argument for named
156
+ # arguments.
157
+ def prepared_arg(k)
158
+ LiteralString.new("#{prepared_arg_placeholder}#{k}")
159
+ end
160
+ end
161
+
162
+ # SQLite prepared statement uses a new prepared statement each time
163
+ # it is called, but it does use the bind arguments.
164
+ module PreparedStatementMethods
165
+ include ArgumentMapper
166
+
167
+ private
168
+
169
+ # Run execute_select on the database with the given SQL and the stored
170
+ # bind arguments.
171
+ def execute(sql, opts={}, &block)
172
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
173
+ end
174
+
175
+ # Same as execute, explicit due to intricacies of alias and super.
176
+ def execute_dui(sql, opts={}, &block)
177
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
178
+ end
179
+
180
+ # Same as execute, explicit due to intricacies of alias and super.
181
+ def execute_insert(sql, opts={}, &block)
182
+ super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
183
+ end
184
+ end
185
+
186
+ # Yield a hash for each row in the dataset.
187
+ def fetch_rows(sql)
188
+ execute(sql) do |result|
189
+ i = -1
190
+ cols = result.columns.map{|c| [output_identifier(c), i+=1]}
191
+ @columns = cols.map{|c| c.first}
192
+ result.each do |values|
193
+ row = {}
194
+ cols.each{|n,i| row[n] = values[i]}
195
+ yield row
196
+ end
197
+ end
198
+ end
199
+
200
+ # Prepare the given type of query with the given name and store
201
+ # it in the database. Note that a new native prepared statement is
202
+ # created on each call to this prepared statement.
203
+ def prepare(type, name=nil, *values)
204
+ ps = to_prepared_statement(type, values)
205
+ ps.extend(PreparedStatementMethods)
206
+ db.prepared_statements[name] = ps if name
207
+ ps.prepared_sql
208
+ ps
209
+ end
210
+
211
+ private
212
+
213
+ # Quote the string using the adapter class method.
214
+ def literal_string(v)
215
+ "'#{::SQLite3::Database.quote(v)}'"
216
+ end
217
+
218
+ # SQLite uses a : before the name of the argument as a placeholder.
219
+ def prepared_arg_placeholder
220
+ PREPARED_ARG_PLACEHOLDER
221
+ end
222
+ end
223
+ end
224
+ end