viking-sequel 3.10.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 (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