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,996 @@
1
+ module Sequel
2
+ class Dataset
3
+ # ---------------------
4
+ # :section: User Methods relating to SQL Creation
5
+ # These are methods you can call to see what SQL will be generated by the dataset.
6
+ # ---------------------
7
+
8
+ # Formats a DELETE statement using the given options and dataset options.
9
+ #
10
+ # dataset.filter{|o| o.price >= 100}.delete_sql #=>
11
+ # "DELETE FROM items WHERE (price >= 100)"
12
+ def delete_sql
13
+ return static_sql(opts[:sql]) if opts[:sql]
14
+ check_modification_allowed!
15
+ clause_sql(:delete)
16
+ end
17
+
18
+ # Returns an EXISTS clause for the dataset as a LiteralString.
19
+ #
20
+ # DB.select(1).where(DB[:items].exists).sql
21
+ # #=> "SELECT 1 WHERE (EXISTS (SELECT * FROM items))"
22
+ def exists
23
+ LiteralString.new("EXISTS (#{select_sql})")
24
+ end
25
+
26
+ # Formats an INSERT statement using the given values. The API is a little
27
+ # complex, and best explained by example:
28
+ #
29
+ # # Default values
30
+ # DB[:items].insert_sql #=> 'INSERT INTO items DEFAULT VALUES'
31
+ # DB[:items].insert_sql({}) #=> 'INSERT INTO items DEFAULT VALUES'
32
+ # # Values without columns
33
+ # DB[:items].insert_sql(1,2,3) #=> 'INSERT INTO items VALUES (1, 2, 3)'
34
+ # DB[:items].insert_sql([1,2,3]) #=> 'INSERT INTO items VALUES (1, 2, 3)'
35
+ # # Values with columns
36
+ # DB[:items].insert_sql([:a, :b], [1,2]) #=> 'INSERT INTO items (a, b) VALUES (1, 2)'
37
+ # DB[:items].insert_sql(:a => 1, :b => 2) #=> 'INSERT INTO items (a, b) VALUES (1, 2)'
38
+ # # Using a subselect
39
+ # DB[:items].insert_sql(DB[:old_items]) #=> 'INSERT INTO items SELECT * FROM old_items
40
+ # # Using a subselect with columns
41
+ # DB[:items].insert_sql([:a, :b], DB[:old_items]) #=> 'INSERT INTO items (a, b) SELECT * FROM old_items
42
+ def insert_sql(*values)
43
+ return static_sql(@opts[:sql]) if @opts[:sql]
44
+
45
+ check_modification_allowed!
46
+
47
+ columns = []
48
+
49
+ case values.size
50
+ when 0
51
+ return insert_sql({})
52
+ when 1
53
+ case vals = values.at(0)
54
+ when Hash
55
+ vals = @opts[:defaults].merge(vals) if @opts[:defaults]
56
+ vals = vals.merge(@opts[:overrides]) if @opts[:overrides]
57
+ values = []
58
+ vals.each do |k,v|
59
+ columns << k
60
+ values << v
61
+ end
62
+ when Dataset, Array, LiteralString
63
+ values = vals
64
+ else
65
+ if vals.respond_to?(:values) && (v = vals.values).is_a?(Hash)
66
+ return insert_sql(v)
67
+ end
68
+ end
69
+ when 2
70
+ if (v0 = values.at(0)).is_a?(Array) && ((v1 = values.at(1)).is_a?(Array) || v1.is_a?(Dataset) || v1.is_a?(LiteralString))
71
+ columns, values = v0, v1
72
+ raise(Error, "Different number of values and columns given to insert_sql") if values.is_a?(Array) and columns.length != values.length
73
+ end
74
+ end
75
+
76
+ columns = columns.map{|k| literal(String === k ? k.to_sym : k)}
77
+ clone(:columns=>columns, :values=>values)._insert_sql
78
+ end
79
+
80
+ # Returns a literal representation of a value to be used as part
81
+ # of an SQL expression.
82
+ #
83
+ # dataset.literal("abc'def\\") #=> "'abc''def\\\\'"
84
+ # dataset.literal(:items__id) #=> "items.id"
85
+ # dataset.literal([1, 2, 3]) => "(1, 2, 3)"
86
+ # dataset.literal(DB[:items]) => "(SELECT * FROM items)"
87
+ # dataset.literal(:x + 1 > :y) => "((x + 1) > y)"
88
+ #
89
+ # If an unsupported object is given, an exception is raised.
90
+ def literal(v)
91
+ case v
92
+ when String
93
+ return v if v.is_a?(LiteralString)
94
+ v.is_a?(SQL::Blob) ? literal_blob(v) : literal_string(v)
95
+ when Symbol
96
+ literal_symbol(v)
97
+ when Integer
98
+ literal_integer(v)
99
+ when Hash
100
+ literal_hash(v)
101
+ when SQL::Expression
102
+ literal_expression(v)
103
+ when Float
104
+ literal_float(v)
105
+ when BigDecimal
106
+ literal_big_decimal(v)
107
+ when NilClass
108
+ literal_nil
109
+ when TrueClass
110
+ literal_true
111
+ when FalseClass
112
+ literal_false
113
+ when Array
114
+ literal_array(v)
115
+ when Time
116
+ literal_time(v)
117
+ when DateTime
118
+ literal_datetime(v)
119
+ when Date
120
+ literal_date(v)
121
+ when Dataset
122
+ literal_dataset(v)
123
+ else
124
+ literal_other(v)
125
+ end
126
+ end
127
+
128
+ # Returns an array of insert statements for inserting multiple records.
129
+ # This method is used by #multi_insert to format insert statements and
130
+ # expects a keys array and and an array of value arrays.
131
+ #
132
+ # This method should be overridden by descendants if the support
133
+ # inserting multiple records in a single SQL statement.
134
+ def multi_insert_sql(columns, values)
135
+ values.map{|r| insert_sql(columns, r)}
136
+ end
137
+
138
+ # Formats a SELECT statement
139
+ #
140
+ # dataset.select_sql # => "SELECT * FROM items"
141
+ def select_sql
142
+ return static_sql(@opts[:sql]) if @opts[:sql]
143
+ clause_sql(:select)
144
+ end
145
+
146
+ # Same as select_sql, not aliased directly to make subclassing simpler.
147
+ def sql
148
+ select_sql
149
+ end
150
+
151
+ # SQL query to truncate the table
152
+ def truncate_sql
153
+ if opts[:sql]
154
+ static_sql(opts[:sql])
155
+ else
156
+ check_modification_allowed!
157
+ raise(InvalidOperation, "Can't truncate filtered datasets") if opts[:where]
158
+ _truncate_sql(source_list(opts[:from]))
159
+ end
160
+ end
161
+
162
+ # Formats an UPDATE statement using the given values.
163
+ #
164
+ # dataset.update_sql(:price => 100, :category => 'software') #=>
165
+ # "UPDATE items SET price = 100, category = 'software'"
166
+ #
167
+ # Raises an error if the dataset is grouped or includes more
168
+ # than one table.
169
+ def update_sql(values = {})
170
+ return static_sql(opts[:sql]) if opts[:sql]
171
+ check_modification_allowed!
172
+ clone(:values=>values)._update_sql
173
+ end
174
+
175
+ # ---------------------
176
+ # :section: Internal Methods relating to SQL Creation
177
+ # These methods, while public, are not designed to be used directly by the end user.
178
+ # ---------------------
179
+
180
+ # Given a type (e.g. select) and an array of clauses,
181
+ # return an array of methods to call to build the SQL string.
182
+ def self.clause_methods(type, clauses)
183
+ clauses.map{|clause| :"#{type}_#{clause}_sql"}.freeze
184
+ end
185
+
186
+ AND_SEPARATOR = " AND ".freeze
187
+ BOOL_FALSE = "'f'".freeze
188
+ BOOL_TRUE = "'t'".freeze
189
+ COMMA_SEPARATOR = ', '.freeze
190
+ COLUMN_REF_RE1 = /\A([\w ]+)__([\w ]+)___([\w ]+)\z/.freeze
191
+ COLUMN_REF_RE2 = /\A([\w ]+)___([\w ]+)\z/.freeze
192
+ COLUMN_REF_RE3 = /\A([\w ]+)__([\w ]+)\z/.freeze
193
+ COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
194
+ COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, LiteralString.new('*'.freeze)).as(:count)
195
+ DATASET_ALIAS_BASE_NAME = 't'.freeze
196
+ FOR_UPDATE = ' FOR UPDATE'.freeze
197
+ IS_LITERALS = {nil=>'NULL'.freeze, true=>'TRUE'.freeze, false=>'FALSE'.freeze}.freeze
198
+ IS_OPERATORS = ::Sequel::SQL::ComplexExpression::IS_OPERATORS
199
+ N_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::N_ARITY_OPERATORS
200
+ NULL = "NULL".freeze
201
+ QUALIFY_KEYS = [:select, :where, :having, :order, :group]
202
+ QUESTION_MARK = '?'.freeze
203
+ DELETE_CLAUSE_METHODS = clause_methods(:delete, %w'from where')
204
+ INSERT_CLAUSE_METHODS = clause_methods(:insert, %w'into columns values')
205
+ SELECT_CLAUSE_METHODS = clause_methods(:select, %w'with distinct columns from join where group having compounds order limit lock')
206
+ UPDATE_CLAUSE_METHODS = clause_methods(:update, %w'table set where')
207
+ TIMESTAMP_FORMAT = "'%Y-%m-%d %H:%M:%S%N%z'".freeze
208
+ STANDARD_TIMESTAMP_FORMAT = "TIMESTAMP #{TIMESTAMP_FORMAT}".freeze
209
+ TWO_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::TWO_ARITY_OPERATORS
210
+ WILDCARD = LiteralString.new('*').freeze
211
+ SQL_WITH = "WITH ".freeze
212
+
213
+ # SQL fragment for the aliased expression
214
+ def aliased_expression_sql(ae)
215
+ as_sql(literal(ae.expression), ae.aliaz)
216
+ end
217
+
218
+ # SQL fragment for the SQL array.
219
+ def array_sql(a)
220
+ a.empty? ? '(NULL)' : "(#{expression_list(a)})"
221
+ end
222
+
223
+ # SQL fragment for BooleanConstants
224
+ def boolean_constant_sql(constant)
225
+ literal(constant)
226
+ end
227
+
228
+ # SQL fragment for specifying given CaseExpression.
229
+ def case_expression_sql(ce)
230
+ sql = '(CASE '
231
+ sql << "#{literal(ce.expression)} " if ce.expression
232
+ ce.conditions.collect{ |c,r|
233
+ sql << "WHEN #{literal(c)} THEN #{literal(r)} "
234
+ }
235
+ sql << "ELSE #{literal(ce.default)} END)"
236
+ end
237
+
238
+ # SQL fragment for the SQL CAST expression.
239
+ def cast_sql(expr, type)
240
+ "CAST(#{literal(expr)} AS #{db.cast_type_literal(type)})"
241
+ end
242
+
243
+ # SQL fragment for specifying all columns in a given table.
244
+ def column_all_sql(ca)
245
+ "#{quote_schema_table(ca.table)}.*"
246
+ end
247
+
248
+ # SQL fragment for complex expressions
249
+ def complex_expression_sql(op, args)
250
+ case op
251
+ when *IS_OPERATORS
252
+ r = args.at(1)
253
+ if r.nil? || supports_is_true?
254
+ raise(InvalidOperation, 'Invalid argument used for IS operator') unless v = IS_LITERALS[r]
255
+ "(#{literal(args.at(0))} #{op} #{v})"
256
+ elsif op == :IS
257
+ complex_expression_sql(:"=", args)
258
+ else
259
+ complex_expression_sql(:OR, [SQL::BooleanExpression.new(:"!=", *args), SQL::BooleanExpression.new(:IS, args.at(0), nil)])
260
+ end
261
+ when :IN, :"NOT IN"
262
+ cols = args.at(0)
263
+ vals = args.at(1)
264
+ col_array = true if cols.is_a?(Array) || cols.is_a?(SQL::SQLArray)
265
+ if vals.is_a?(Array) || vals.is_a?(SQL::SQLArray)
266
+ val_array = true
267
+ empty_val_array = vals.to_a == []
268
+ end
269
+ if col_array
270
+ if empty_val_array
271
+ if op == :IN
272
+ literal(SQL::BooleanExpression.from_value_pairs(cols.to_a.map{|x| [x, x]}, :AND, true))
273
+ else
274
+ literal(1=>1)
275
+ end
276
+ elsif !supports_multiple_column_in?
277
+ if val_array
278
+ expr = SQL::BooleanExpression.new(:OR, *vals.to_a.map{|vs| SQL::BooleanExpression.from_value_pairs(cols.to_a.zip(vs).map{|c, v| [c, v]})})
279
+ literal(op == :IN ? expr : ~expr)
280
+ else
281
+ old_vals = vals
282
+ vals = vals.to_a
283
+ val_cols = old_vals.columns
284
+ complex_expression_sql(op, [cols, vals.map!{|x| x.values_at(*val_cols)}])
285
+ end
286
+ else
287
+ "(#{literal(cols)} #{op} #{literal(vals)})"
288
+ end
289
+ else
290
+ if empty_val_array
291
+ if op == :IN
292
+ literal(SQL::BooleanExpression.from_value_pairs([[cols, cols]], :AND, true))
293
+ else
294
+ literal(1=>1)
295
+ end
296
+ else
297
+ "(#{literal(cols)} #{op} #{literal(vals)})"
298
+ end
299
+ end
300
+ when *TWO_ARITY_OPERATORS
301
+ "(#{literal(args.at(0))} #{op} #{literal(args.at(1))})"
302
+ when *N_ARITY_OPERATORS
303
+ "(#{args.collect{|a| literal(a)}.join(" #{op} ")})"
304
+ when :NOT
305
+ "NOT #{literal(args.at(0))}"
306
+ when :NOOP
307
+ literal(args.at(0))
308
+ when :'B~'
309
+ "~#{literal(args.at(0))}"
310
+ else
311
+ raise(InvalidOperation, "invalid operator #{op}")
312
+ end
313
+ end
314
+
315
+ # SQL fragment for constants
316
+ def constant_sql(constant)
317
+ constant.to_s
318
+ end
319
+
320
+ # SQL fragment specifying an SQL function call
321
+ def function_sql(f)
322
+ args = f.args
323
+ "#{f.f}#{args.empty? ? '()' : literal(args)}"
324
+ end
325
+
326
+ # SQL fragment specifying a JOIN clause without ON or USING.
327
+ def join_clause_sql(jc)
328
+ table = jc.table
329
+ table_alias = jc.table_alias
330
+ table_alias = nil if table == table_alias
331
+ tref = table_ref(table)
332
+ " #{join_type_sql(jc.join_type)} #{table_alias ? as_sql(tref, table_alias) : tref}"
333
+ end
334
+
335
+ # SQL fragment specifying a JOIN clause with ON.
336
+ def join_on_clause_sql(jc)
337
+ "#{join_clause_sql(jc)} ON #{literal(filter_expr(jc.on))}"
338
+ end
339
+
340
+ # SQL fragment specifying a JOIN clause with USING.
341
+ def join_using_clause_sql(jc)
342
+ "#{join_clause_sql(jc)} USING (#{column_list(jc.using)})"
343
+ end
344
+
345
+ # SQL fragment for NegativeBooleanConstants
346
+ def negative_boolean_constant_sql(constant)
347
+ "NOT #{boolean_constant_sql(constant)}"
348
+ end
349
+
350
+ # SQL fragment for the ordered expression, used in the ORDER BY
351
+ # clause.
352
+ def ordered_expression_sql(oe)
353
+ "#{literal(oe.expression)} #{oe.descending ? 'DESC' : 'ASC'}"
354
+ end
355
+
356
+ # SQL fragment for a literal string with placeholders
357
+ def placeholder_literal_string_sql(pls)
358
+ args = pls.args
359
+ s = if args.is_a?(Hash)
360
+ re = /:(#{args.keys.map{|k| Regexp.escape(k.to_s)}.join('|')})\b/
361
+ pls.str.gsub(re){literal(args[$1.to_sym])}
362
+ else
363
+ i = -1
364
+ pls.str.gsub(QUESTION_MARK){literal(args.at(i+=1))}
365
+ end
366
+ s = "(#{s})" if pls.parens
367
+ s
368
+ end
369
+
370
+ # SQL fragment for the qualifed identifier, specifying
371
+ # a table and a column (or schema and table).
372
+ def qualified_identifier_sql(qcr)
373
+ [qcr.table, qcr.column].map{|x| [SQL::QualifiedIdentifier, SQL::Identifier, Symbol].any?{|c| x.is_a?(c)} ? literal(x) : quote_identifier(x)}.join('.')
374
+ end
375
+
376
+ # Adds quoting to identifiers (columns and tables). If identifiers are not
377
+ # being quoted, returns name as a string. If identifiers are being quoted
378
+ # quote the name with quoted_identifier.
379
+ def quote_identifier(name)
380
+ return name if name.is_a?(LiteralString)
381
+ name = name.value if name.is_a?(SQL::Identifier)
382
+ name = input_identifier(name)
383
+ name = quoted_identifier(name) if quote_identifiers?
384
+ name
385
+ end
386
+
387
+ # Separates the schema from the table and returns a string with them
388
+ # quoted (if quoting identifiers)
389
+ def quote_schema_table(table)
390
+ schema, table = schema_and_table(table)
391
+ "#{"#{quote_identifier(schema)}." if schema}#{quote_identifier(table)}"
392
+ end
393
+
394
+ # This method quotes the given name with the SQL standard double quote.
395
+ # should be overridden by subclasses to provide quoting not matching the
396
+ # SQL standard, such as backtick (used by MySQL and SQLite).
397
+ def quoted_identifier(name)
398
+ "\"#{name.to_s.gsub('"', '""')}\""
399
+ end
400
+
401
+ # Split the schema information from the table
402
+ def schema_and_table(table_name)
403
+ sch = db.default_schema if db
404
+ case table_name
405
+ when Symbol
406
+ s, t, a = split_symbol(table_name)
407
+ [s||sch, t]
408
+ when SQL::QualifiedIdentifier
409
+ [table_name.table, table_name.column]
410
+ when SQL::Identifier
411
+ [sch, table_name.value]
412
+ when String
413
+ [sch, table_name]
414
+ else
415
+ raise Error, 'table_name should be a Symbol, SQL::QualifiedIdentifier, SQL::Identifier, or String'
416
+ end
417
+ end
418
+
419
+ # SQL fragment for specifying subscripts (SQL arrays)
420
+ def subscript_sql(s)
421
+ "#{literal(s.f)}[#{expression_list(s.sub)}]"
422
+ end
423
+
424
+ # The SQL fragment for the given window's options.
425
+ def window_sql(opts)
426
+ raise(Error, 'This dataset does not support window functions') unless supports_window_functions?
427
+ window = literal(opts[:window]) if opts[:window]
428
+ partition = "PARTITION BY #{expression_list(Array(opts[:partition]))}" if opts[:partition]
429
+ order = "ORDER BY #{expression_list(Array(opts[:order]))}" if opts[:order]
430
+ frame = case opts[:frame]
431
+ when nil
432
+ nil
433
+ when :all
434
+ "ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING"
435
+ when :rows
436
+ "ROWS UNBOUNDED PRECEDING"
437
+ else
438
+ raise Error, "invalid window frame clause, should be :all, :rows, or nil"
439
+ end
440
+ "(#{[window, partition, order, frame].compact.join(' ')})"
441
+ end
442
+
443
+ # The SQL fragment for the given window function's function and window.
444
+ def window_function_sql(function, window)
445
+ "#{literal(function)} OVER #{literal(window)}"
446
+ end
447
+
448
+ protected
449
+
450
+ # Formats in INSERT statement using the stored columns and values.
451
+ def _insert_sql
452
+ clause_sql(:insert)
453
+ end
454
+
455
+ # Formats an UPDATE statement using the stored values.
456
+ def _update_sql
457
+ clause_sql(:update)
458
+ end
459
+
460
+ # Return a from_self dataset if an order or limit is specified, so it works as expected
461
+ # with UNION, EXCEPT, and INTERSECT clauses.
462
+ def compound_from_self
463
+ (@opts[:limit] || @opts[:order]) ? from_self : self
464
+ end
465
+
466
+ private
467
+
468
+ # Formats the truncate statement. Assumes the table given has already been
469
+ # literalized.
470
+ def _truncate_sql(table)
471
+ "TRUNCATE TABLE #{table}"
472
+ end
473
+
474
+ # Returns an appropriate symbol for the alias represented by s.
475
+ def alias_alias_symbol(s)
476
+ case s
477
+ when Symbol
478
+ s
479
+ when String
480
+ s.to_sym
481
+ when SQL::Identifier
482
+ s.value.to_s.to_sym
483
+ else
484
+ raise Error, "Invalid alias for alias_alias_symbol: #{s.inspect}"
485
+ end
486
+ end
487
+
488
+ # Returns an appropriate alias symbol for the given object, which can be
489
+ # a Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier, or
490
+ # SQL::AliasedExpression.
491
+ def alias_symbol(sym)
492
+ case sym
493
+ when Symbol
494
+ s, t, a = split_symbol(sym)
495
+ a || s ? (a || t).to_sym : sym
496
+ when String
497
+ sym.to_sym
498
+ when SQL::Identifier
499
+ sym.value.to_s.to_sym
500
+ when SQL::QualifiedIdentifier
501
+ alias_symbol(sym.column)
502
+ when SQL::AliasedExpression
503
+ alias_alias_symbol(sym.aliaz)
504
+ else
505
+ raise Error, "Invalid alias for alias_symbol: #{sym.inspect}"
506
+ end
507
+ end
508
+
509
+ # Clone of this dataset usable in aggregate operations. Does
510
+ # a from_self if dataset contains any parameters that would
511
+ # affect normal aggregation, or just removes an existing
512
+ # order if not.
513
+ def aggregate_dataset
514
+ options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
515
+ end
516
+
517
+ # Do a simple join of the arguments (which should be strings or symbols) separated by commas
518
+ def argument_list(args)
519
+ args.join(COMMA_SEPARATOR)
520
+ end
521
+
522
+ # SQL fragment for specifying an alias. expression should already be literalized.
523
+ def as_sql(expression, aliaz)
524
+ "#{expression} AS #{quote_identifier(aliaz)}"
525
+ end
526
+
527
+ # Raise an InvalidOperation exception if deletion is not allowed
528
+ # for this dataset
529
+ def check_modification_allowed!
530
+ raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
531
+ raise(InvalidOperation, "Joined datasets cannot be modified") if !supports_modifying_joins? && joined_dataset?
532
+ end
533
+
534
+ # Prepare an SQL statement by calling all clause methods for the given statement type.
535
+ def clause_sql(type)
536
+ sql = type.to_s.upcase
537
+ send("#{type}_clause_methods").each{|x| send(x, sql)}
538
+ sql
539
+ end
540
+
541
+ # Converts an array of column names into a comma seperated string of
542
+ # column names. If the array is empty, a wildcard (*) is returned.
543
+ def column_list(columns)
544
+ (columns.nil? || columns.empty?) ? WILDCARD : expression_list(columns)
545
+ end
546
+
547
+ # The alias to use for datasets, takes a number to make sure the name is unique.
548
+ def dataset_alias(number)
549
+ :"#{DATASET_ALIAS_BASE_NAME}#{number}"
550
+ end
551
+
552
+ # The order of methods to call to build the DELETE SQL statement
553
+ def delete_clause_methods
554
+ DELETE_CLAUSE_METHODS
555
+ end
556
+
557
+ # Converts an array of expressions into a comma separated string of
558
+ # expressions.
559
+ def expression_list(columns)
560
+ columns.map{|i| literal(i)}.join(COMMA_SEPARATOR)
561
+ end
562
+
563
+ # The strftime format to use when literalizing the time.
564
+ def default_timestamp_format
565
+ requires_sql_standard_datetimes? ? STANDARD_TIMESTAMP_FORMAT : TIMESTAMP_FORMAT
566
+ end
567
+
568
+ # Format the timestamp based on the default_timestamp_format, with a couple
569
+ # of modifiers. First, allow %N to be used for fractions seconds (if the
570
+ # database supports them), and override %z to always use a numeric offset
571
+ # of hours and minutes.
572
+ def format_timestamp(v)
573
+ v2 = Sequel.application_to_database_timestamp(v)
574
+ fmt = default_timestamp_format.gsub(/%[Nz]/) do |m|
575
+ if m == '%N'
576
+ format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*86400000000 : v.usec) if supports_timestamp_usecs?
577
+ else
578
+ if supports_timestamp_timezones?
579
+ # Would like to just use %z format, but it doesn't appear to work on Windows
580
+ # Instead, the offset fragment is constructed manually
581
+ minutes = (v2.is_a?(DateTime) ? v2.offset * 1440 : v2.utc_offset/60).to_i
582
+ format_timestamp_offset(*minutes.divmod(60))
583
+ end
584
+ end
585
+ end
586
+ v2.strftime(fmt)
587
+ end
588
+
589
+ # Return the SQL timestamp fragment to use for the timezone offset.
590
+ def format_timestamp_offset(hour, minute)
591
+ sprintf("%+03i%02i", hour, minute)
592
+ end
593
+
594
+ # Return the SQL timestamp fragment to use for the fractional time part.
595
+ # Should start with the decimal point. Uses 6 decimal places by default.
596
+ def format_timestamp_usec(usec)
597
+ sprintf(".%06d", usec)
598
+ end
599
+
600
+ # SQL fragment specifying a list of identifiers
601
+ # SQL fragment specifying a list of identifiers
602
+ def identifier_list(columns)
603
+ columns.map{|i| quote_identifier(i)}.join(COMMA_SEPARATOR)
604
+ end
605
+
606
+ # Modify the identifier returned from the database based on the
607
+ # identifier_output_method.
608
+ def input_identifier(v)
609
+ (i = identifier_input_method) ? v.to_s.send(i) : v.to_s
610
+ end
611
+
612
+ # SQL fragment specifying the table to insert INTO
613
+ def insert_into_sql(sql)
614
+ sql << " INTO #{source_list(@opts[:from])}"
615
+ end
616
+
617
+ # The order of methods to call to build the INSERT SQL statement
618
+ def insert_clause_methods
619
+ INSERT_CLAUSE_METHODS
620
+ end
621
+
622
+ # SQL fragment specifying the columns to insert into
623
+ def insert_columns_sql(sql)
624
+ columns = opts[:columns]
625
+ sql << " (#{columns.join(COMMA_SEPARATOR)})" if columns && !columns.empty?
626
+ end
627
+
628
+ # SQL fragment specifying the values to insert.
629
+ def insert_values_sql(sql)
630
+ case values = opts[:values]
631
+ when Array
632
+ sql << (values.empty? ? " DEFAULT VALUES" : " VALUES #{literal(values)}")
633
+ when Dataset
634
+ sql << " #{subselect_sql(values)}"
635
+ when LiteralString
636
+ sql << " #{values}"
637
+ else
638
+ raise Error, "Unsupported INSERT values type, should be an Array or Dataset: #{values.inspect}"
639
+ end
640
+ end
641
+
642
+ # SQL fragment specifying a JOIN type, converts underscores to
643
+ # spaces and upcases.
644
+ def join_type_sql(join_type)
645
+ "#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
646
+ end
647
+
648
+ # Whether this dataset is a joined dataset
649
+ def joined_dataset?
650
+ (opts[:from].is_a?(Array) && opts[:from].size > 1) || opts[:join]
651
+ end
652
+
653
+ # SQL fragment for Array. Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
654
+ def literal_array(v)
655
+ Sequel.condition_specifier?(v) ? literal_expression(SQL::BooleanExpression.from_value_pairs(v)) : array_sql(v)
656
+ end
657
+
658
+ # SQL fragment for BigDecimal
659
+ def literal_big_decimal(v)
660
+ d = v.to_s("F")
661
+ v.nan? || v.infinite? ? "'#{d}'" : d
662
+ end
663
+
664
+ # SQL fragment for SQL::Blob
665
+ def literal_blob(v)
666
+ literal_string(v)
667
+ end
668
+
669
+ # SQL fragment for Dataset. Does a subselect inside parantheses.
670
+ def literal_dataset(v)
671
+ "(#{subselect_sql(v)})"
672
+ end
673
+
674
+ # SQL fragment for Date, using the ISO8601 format.
675
+ def literal_date(v)
676
+ v.strftime("#{'DATE ' if requires_sql_standard_datetimes?}'%Y-%m-%d'")
677
+ end
678
+
679
+ # SQL fragment for DateTime
680
+ def literal_datetime(v)
681
+ format_timestamp(v)
682
+ end
683
+
684
+ # SQL fragment for SQL::Expression, result depends on the specific type of expression.
685
+ def literal_expression(v)
686
+ v.to_s(self)
687
+ end
688
+
689
+ # SQL fragment for false
690
+ def literal_false
691
+ BOOL_FALSE
692
+ end
693
+
694
+ # SQL fragment for Float
695
+ def literal_float(v)
696
+ v.to_s
697
+ end
698
+
699
+ # SQL fragment for Hash, treated as an expression
700
+ def literal_hash(v)
701
+ literal_expression(SQL::BooleanExpression.from_value_pairs(v))
702
+ end
703
+
704
+ # SQL fragment for Integer
705
+ def literal_integer(v)
706
+ v.to_s
707
+ end
708
+
709
+ # SQL fragment for nil
710
+ def literal_nil
711
+ NULL
712
+ end
713
+
714
+ # SQL fragment for a type of object not handled by Dataset#literal.
715
+ # Calls sql_literal if object responds to it, otherwise raises an error.
716
+ # Classes implementing sql_literal should call a class-specific method on the dataset
717
+ # provided and should add that method to Sequel::Dataset, allowing for adapters
718
+ # to provide customized literalizations.
719
+ # If a database specific type is allowed, this should be overriden in a subclass.
720
+ def literal_other(v)
721
+ if v.respond_to?(:sql_literal)
722
+ v.sql_literal(self)
723
+ else
724
+ raise Error, "can't express #{v.inspect} as a SQL literal"
725
+ end
726
+ end
727
+
728
+ # SQL fragment for String. Doubles \ and ' by default.
729
+ def literal_string(v)
730
+ "'#{v.gsub(/\\/, "\\\\\\\\").gsub(/'/, "''")}'"
731
+ end
732
+
733
+ # Converts a symbol into a column name. This method supports underscore
734
+ # notation in order to express qualified (two underscores) and aliased
735
+ # (three underscores) columns:
736
+ #
737
+ # dataset.literal(:abc) #=> "abc"
738
+ # dataset.literal(:abc___a) #=> "abc AS a"
739
+ # dataset.literal(:items__abc) #=> "items.abc"
740
+ # dataset.literal(:items__abc___a) #=> "items.abc AS a"
741
+ def literal_symbol(v)
742
+ c_table, column, c_alias = split_symbol(v)
743
+ qc = "#{"#{quote_identifier(c_table)}." if c_table}#{quote_identifier(column)}"
744
+ c_alias ? as_sql(qc, c_alias) : qc
745
+ end
746
+
747
+ # SQL fragment for Time
748
+ def literal_time(v)
749
+ format_timestamp(v)
750
+ end
751
+
752
+ # SQL fragment for true
753
+ def literal_true
754
+ BOOL_TRUE
755
+ end
756
+
757
+ # Returns a qualified column name (including a table name) if the column
758
+ # name isn't already qualified.
759
+ def qualified_column_name(column, table)
760
+ if Symbol === column
761
+ c_table, column, c_alias = split_symbol(column)
762
+ unless c_table
763
+ case table
764
+ when Symbol
765
+ schema, table, t_alias = split_symbol(table)
766
+ t_alias ||= Sequel::SQL::QualifiedIdentifier.new(schema, table) if schema
767
+ when Sequel::SQL::AliasedExpression
768
+ t_alias = table.aliaz
769
+ end
770
+ c_table = t_alias || table
771
+ end
772
+ ::Sequel::SQL::QualifiedIdentifier.new(c_table, column)
773
+ else
774
+ column
775
+ end
776
+ end
777
+
778
+ # Qualify the given expression e to the given table.
779
+ def qualified_expression(e, table)
780
+ case e
781
+ when Symbol
782
+ t, column, aliaz = split_symbol(e)
783
+ if t
784
+ e
785
+ elsif aliaz
786
+ SQL::AliasedExpression.new(SQL::QualifiedIdentifier.new(table, SQL::Identifier.new(column)), aliaz)
787
+ else
788
+ SQL::QualifiedIdentifier.new(table, e)
789
+ end
790
+ when Array
791
+ e.map{|a| qualified_expression(a, table)}
792
+ when Hash
793
+ h = {}
794
+ e.each{|k,v| h[qualified_expression(k, table)] = qualified_expression(v, table)}
795
+ h
796
+ when SQL::Identifier
797
+ SQL::QualifiedIdentifier.new(table, e)
798
+ when SQL::OrderedExpression
799
+ SQL::OrderedExpression.new(qualified_expression(e.expression, table), e.descending)
800
+ when SQL::AliasedExpression
801
+ SQL::AliasedExpression.new(qualified_expression(e.expression, table), e.aliaz)
802
+ when SQL::CaseExpression
803
+ SQL::CaseExpression.new(qualified_expression(e.conditions, table), qualified_expression(e.default, table), qualified_expression(e.expression, table))
804
+ when SQL::Cast
805
+ SQL::Cast.new(qualified_expression(e.expr, table), e.type)
806
+ when SQL::Function
807
+ SQL::Function.new(e.f, *qualified_expression(e.args, table))
808
+ when SQL::ComplexExpression
809
+ SQL::ComplexExpression.new(e.op, *qualified_expression(e.args, table))
810
+ when SQL::SQLArray
811
+ SQL::SQLArray.new(qualified_expression(e.array, table))
812
+ when SQL::Subscript
813
+ SQL::Subscript.new(qualified_expression(e.f, table), qualified_expression(e.sub, table))
814
+ when SQL::WindowFunction
815
+ SQL::WindowFunction.new(qualified_expression(e.function, table), qualified_expression(e.window, table))
816
+ when SQL::Window
817
+ o = e.opts.dup
818
+ o[:partition] = qualified_expression(o[:partition], table) if o[:partition]
819
+ o[:order] = qualified_expression(o[:order], table) if o[:order]
820
+ SQL::Window.new(o)
821
+ when SQL::PlaceholderLiteralString
822
+ args = if e.args.is_a?(Hash)
823
+ h = {}
824
+ e.args.each{|k,v| h[k] = qualified_expression(v, table)}
825
+ h
826
+ else
827
+ qualified_expression(e.args, table)
828
+ end
829
+ SQL::PlaceholderLiteralString.new(e.str, args, e.parens)
830
+ else
831
+ e
832
+ end
833
+ end
834
+
835
+ # The order of methods to call to build the SELECT SQL statement
836
+ def select_clause_methods
837
+ SELECT_CLAUSE_METHODS
838
+ end
839
+
840
+ # Modify the sql to add the columns selected
841
+ def select_columns_sql(sql)
842
+ sql << " #{column_list(@opts[:select])}"
843
+ end
844
+
845
+ # Modify the sql to add the DISTINCT modifier
846
+ def select_distinct_sql(sql)
847
+ if distinct = @opts[:distinct]
848
+ sql << " DISTINCT#{" ON (#{expression_list(distinct)})" unless distinct.empty?}"
849
+ end
850
+ end
851
+
852
+ # Modify the sql to add a dataset to the via an EXCEPT, INTERSECT, or UNION clause.
853
+ # This uses a subselect for the compound datasets used, because using parantheses doesn't
854
+ # work on all databases. I consider this an ugly hack, but can't I think of a better default.
855
+ def select_compounds_sql(sql)
856
+ return unless @opts[:compounds]
857
+ @opts[:compounds].each do |type, dataset, all|
858
+ compound_sql = subselect_sql(dataset)
859
+ sql << " #{type.to_s.upcase}#{' ALL' if all} #{compound_sql}"
860
+ end
861
+ end
862
+
863
+ # Modify the sql to add the list of tables to select FROM
864
+ def select_from_sql(sql)
865
+ sql << " FROM #{source_list(@opts[:from])}" if @opts[:from]
866
+ end
867
+ alias delete_from_sql select_from_sql
868
+
869
+ # Modify the sql to add the expressions to GROUP BY
870
+ def select_group_sql(sql)
871
+ sql << " GROUP BY #{expression_list(@opts[:group])}" if @opts[:group]
872
+ end
873
+
874
+ # Modify the sql to add the filter criteria in the HAVING clause
875
+ def select_having_sql(sql)
876
+ sql << " HAVING #{literal(@opts[:having])}" if @opts[:having]
877
+ end
878
+
879
+ # Modify the sql to add the list of tables to JOIN to
880
+ def select_join_sql(sql)
881
+ @opts[:join].each{|j| sql << literal(j)} if @opts[:join]
882
+ end
883
+
884
+ # Modify the sql to limit the number of rows returned and offset
885
+ def select_limit_sql(sql)
886
+ sql << " LIMIT #{literal(@opts[:limit])}" if @opts[:limit]
887
+ sql << " OFFSET #{literal(@opts[:offset])}" if @opts[:offset]
888
+ end
889
+
890
+ # Modify the sql to support the different types of locking modes.
891
+ def select_lock_sql(sql)
892
+ case @opts[:lock]
893
+ when :update
894
+ sql << FOR_UPDATE
895
+ when String
896
+ sql << " #{@opts[:lock]}"
897
+ end
898
+ end
899
+
900
+ # Modify the sql to add the expressions to ORDER BY
901
+ def select_order_sql(sql)
902
+ sql << " ORDER BY #{expression_list(@opts[:order])}" if @opts[:order]
903
+ end
904
+ alias delete_order_sql select_order_sql
905
+ alias update_order_sql select_order_sql
906
+
907
+ # Modify the sql to add the filter criteria in the WHERE clause
908
+ def select_where_sql(sql)
909
+ sql << " WHERE #{literal(@opts[:where])}" if @opts[:where]
910
+ end
911
+ alias delete_where_sql select_where_sql
912
+ alias update_where_sql select_where_sql
913
+
914
+ # SQL Fragment specifying the WITH clause
915
+ def select_with_sql(sql)
916
+ ws = opts[:with]
917
+ return if !ws || ws.empty?
918
+ sql.replace("#{select_with_sql_base}#{ws.map{|w| "#{quote_identifier(w[:name])}#{"(#{argument_list(w[:args])})" if w[:args]} AS #{literal_dataset(w[:dataset])}"}.join(COMMA_SEPARATOR)} #{sql}")
919
+ end
920
+
921
+ # The base keyword to use for the SQL WITH clause
922
+ def select_with_sql_base
923
+ SQL_WITH
924
+ end
925
+
926
+ # Converts an array of source names into into a comma separated list.
927
+ def source_list(source)
928
+ raise(Error, 'No source specified for query') if source.nil? || source.empty?
929
+ source.map{|s| table_ref(s)}.join(COMMA_SEPARATOR)
930
+ end
931
+
932
+ # Splits the symbol into three parts. Each part will
933
+ # either be a string or nil.
934
+ #
935
+ # For columns, these parts are the table, column, and alias.
936
+ # For tables, these parts are the schema, table, and alias.
937
+ def split_symbol(sym)
938
+ s = sym.to_s
939
+ if m = COLUMN_REF_RE1.match(s)
940
+ m[1..3]
941
+ elsif m = COLUMN_REF_RE2.match(s)
942
+ [nil, m[1], m[2]]
943
+ elsif m = COLUMN_REF_RE3.match(s)
944
+ [m[1], m[2], nil]
945
+ else
946
+ [nil, s, nil]
947
+ end
948
+ end
949
+
950
+ # SQL to use if this dataset uses static SQL. Since static SQL
951
+ # can be a PlaceholderLiteralString in addition to a String,
952
+ # we literalize nonstrings.
953
+ def static_sql(sql)
954
+ sql.is_a?(String) ? sql : literal(sql)
955
+ end
956
+
957
+ # SQL fragment for a subselect using the given database's SQL.
958
+ def subselect_sql(ds)
959
+ ds.sql
960
+ end
961
+
962
+ # SQL fragment specifying a table name.
963
+ def table_ref(t)
964
+ t.is_a?(String) ? quote_identifier(t) : literal(t)
965
+ end
966
+
967
+ # The order of methods to call to build the UPDATE SQL statement
968
+ def update_clause_methods
969
+ UPDATE_CLAUSE_METHODS
970
+ end
971
+
972
+ # SQL fragment specifying the tables from with to delete.
973
+ # Includes join table if modifying joins is allowed.
974
+ def update_table_sql(sql)
975
+ sql << " #{source_list(@opts[:from])}"
976
+ select_join_sql(sql) if supports_modifying_joins?
977
+ end
978
+
979
+ # The SQL fragment specifying the columns and values to SET.
980
+ def update_set_sql(sql)
981
+ values = opts[:values]
982
+ set = if values.is_a?(Hash)
983
+ values = opts[:defaults].merge(values) if opts[:defaults]
984
+ values = values.merge(opts[:overrides]) if opts[:overrides]
985
+ # get values from hash
986
+ values.map do |k, v|
987
+ "#{k.is_a?(String) && !k.is_a?(LiteralString) ? quote_identifier(k) : literal(k)} = #{literal(v)}"
988
+ end.join(COMMA_SEPARATOR)
989
+ else
990
+ # copy values verbatim
991
+ values
992
+ end
993
+ sql << " SET #{set}"
994
+ end
995
+ end
996
+ end