masamune 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +54 -0
  4. data/Rakefile +15 -0
  5. data/bin/masamune-elastic-mapreduce +4 -0
  6. data/bin/masamune-hive +4 -0
  7. data/bin/masamune-psql +4 -0
  8. data/bin/masamune-shell +4 -0
  9. data/lib/masamune.rb +56 -0
  10. data/lib/masamune/accumulate.rb +60 -0
  11. data/lib/masamune/actions.rb +38 -0
  12. data/lib/masamune/actions/data_flow.rb +131 -0
  13. data/lib/masamune/actions/date_parse.rb +75 -0
  14. data/lib/masamune/actions/elastic_mapreduce.rb +68 -0
  15. data/lib/masamune/actions/execute.rb +52 -0
  16. data/lib/masamune/actions/filesystem.rb +37 -0
  17. data/lib/masamune/actions/hadoop_filesystem.rb +40 -0
  18. data/lib/masamune/actions/hadoop_streaming.rb +41 -0
  19. data/lib/masamune/actions/hive.rb +74 -0
  20. data/lib/masamune/actions/postgres.rb +76 -0
  21. data/lib/masamune/actions/postgres_admin.rb +34 -0
  22. data/lib/masamune/actions/s3cmd.rb +44 -0
  23. data/lib/masamune/actions/transform.rb +89 -0
  24. data/lib/masamune/after_initialize_callbacks.rb +55 -0
  25. data/lib/masamune/cached_filesystem.rb +110 -0
  26. data/lib/masamune/commands.rb +37 -0
  27. data/lib/masamune/commands/elastic_mapreduce.rb +119 -0
  28. data/lib/masamune/commands/hadoop_filesystem.rb +57 -0
  29. data/lib/masamune/commands/hadoop_streaming.rb +116 -0
  30. data/lib/masamune/commands/hive.rb +178 -0
  31. data/lib/masamune/commands/interactive.rb +37 -0
  32. data/lib/masamune/commands/postgres.rb +128 -0
  33. data/lib/masamune/commands/postgres_admin.rb +72 -0
  34. data/lib/masamune/commands/postgres_common.rb +33 -0
  35. data/lib/masamune/commands/retry_with_backoff.rb +60 -0
  36. data/lib/masamune/commands/s3cmd.rb +70 -0
  37. data/lib/masamune/commands/shell.rb +202 -0
  38. data/lib/masamune/configuration.rb +195 -0
  39. data/lib/masamune/data_plan.rb +31 -0
  40. data/lib/masamune/data_plan/builder.rb +66 -0
  41. data/lib/masamune/data_plan/elem.rb +190 -0
  42. data/lib/masamune/data_plan/engine.rb +162 -0
  43. data/lib/masamune/data_plan/rule.rb +292 -0
  44. data/lib/masamune/data_plan/set.rb +176 -0
  45. data/lib/masamune/environment.rb +164 -0
  46. data/lib/masamune/filesystem.rb +567 -0
  47. data/lib/masamune/has_environment.rb +40 -0
  48. data/lib/masamune/helpers.rb +27 -0
  49. data/lib/masamune/helpers/postgres.rb +84 -0
  50. data/lib/masamune/io.rb +33 -0
  51. data/lib/masamune/last_element.rb +53 -0
  52. data/lib/masamune/method_logger.rb +41 -0
  53. data/lib/masamune/multi_io.rb +39 -0
  54. data/lib/masamune/schema.rb +36 -0
  55. data/lib/masamune/schema/catalog.rb +233 -0
  56. data/lib/masamune/schema/column.rb +527 -0
  57. data/lib/masamune/schema/dimension.rb +133 -0
  58. data/lib/masamune/schema/event.rb +121 -0
  59. data/lib/masamune/schema/fact.rb +133 -0
  60. data/lib/masamune/schema/map.rb +265 -0
  61. data/lib/masamune/schema/row.rb +133 -0
  62. data/lib/masamune/schema/store.rb +115 -0
  63. data/lib/masamune/schema/table.rb +308 -0
  64. data/lib/masamune/schema/table_reference.rb +76 -0
  65. data/lib/masamune/spec_helper.rb +23 -0
  66. data/lib/masamune/string_format.rb +34 -0
  67. data/lib/masamune/tasks/elastic_mapreduce_thor.rb +60 -0
  68. data/lib/masamune/tasks/hive_thor.rb +55 -0
  69. data/lib/masamune/tasks/postgres_thor.rb +47 -0
  70. data/lib/masamune/tasks/shell_thor.rb +63 -0
  71. data/lib/masamune/template.rb +77 -0
  72. data/lib/masamune/thor.rb +186 -0
  73. data/lib/masamune/thor_loader.rb +38 -0
  74. data/lib/masamune/topological_hash.rb +34 -0
  75. data/lib/masamune/transform.rb +47 -0
  76. data/lib/masamune/transform/bulk_upsert.psql.erb +64 -0
  77. data/lib/masamune/transform/bulk_upsert.rb +52 -0
  78. data/lib/masamune/transform/consolidate_dimension.rb +54 -0
  79. data/lib/masamune/transform/deduplicate_dimension.psql.erb +52 -0
  80. data/lib/masamune/transform/deduplicate_dimension.rb +53 -0
  81. data/lib/masamune/transform/define_event_view.hql.erb +51 -0
  82. data/lib/masamune/transform/define_event_view.rb +60 -0
  83. data/lib/masamune/transform/define_index.psql.erb +34 -0
  84. data/lib/masamune/transform/define_schema.hql.erb +23 -0
  85. data/lib/masamune/transform/define_schema.psql.erb +79 -0
  86. data/lib/masamune/transform/define_schema.rb +56 -0
  87. data/lib/masamune/transform/define_table.hql.erb +34 -0
  88. data/lib/masamune/transform/define_table.psql.erb +95 -0
  89. data/lib/masamune/transform/define_table.rb +40 -0
  90. data/lib/masamune/transform/define_unique.psql.erb +30 -0
  91. data/lib/masamune/transform/insert_reference_values.psql.erb +43 -0
  92. data/lib/masamune/transform/insert_reference_values.rb +64 -0
  93. data/lib/masamune/transform/load_dimension.rb +47 -0
  94. data/lib/masamune/transform/load_fact.rb +45 -0
  95. data/lib/masamune/transform/operator.rb +96 -0
  96. data/lib/masamune/transform/relabel_dimension.psql.erb +76 -0
  97. data/lib/masamune/transform/relabel_dimension.rb +39 -0
  98. data/lib/masamune/transform/rollup_fact.psql.erb +79 -0
  99. data/lib/masamune/transform/rollup_fact.rb +149 -0
  100. data/lib/masamune/transform/snapshot_dimension.psql.erb +75 -0
  101. data/lib/masamune/transform/snapshot_dimension.rb +74 -0
  102. data/lib/masamune/transform/stage_dimension.psql.erb +39 -0
  103. data/lib/masamune/transform/stage_dimension.rb +83 -0
  104. data/lib/masamune/transform/stage_fact.psql.erb +80 -0
  105. data/lib/masamune/transform/stage_fact.rb +111 -0
  106. data/lib/masamune/version.rb +25 -0
  107. data/spec/fixtures/aggregate.sql.erb +25 -0
  108. data/spec/fixtures/comment.sql.erb +27 -0
  109. data/spec/fixtures/invalid.sql.erb +23 -0
  110. data/spec/fixtures/relative.sql.erb +23 -0
  111. data/spec/fixtures/simple.sql.erb +28 -0
  112. data/spec/fixtures/whitespace.sql.erb +30 -0
  113. data/spec/masamune/actions/elastic_mapreduce_spec.rb +108 -0
  114. data/spec/masamune/actions/execute_spec.rb +50 -0
  115. data/spec/masamune/actions/hadoop_filesystem_spec.rb +44 -0
  116. data/spec/masamune/actions/hadoop_streaming_spec.rb +74 -0
  117. data/spec/masamune/actions/hive_spec.rb +117 -0
  118. data/spec/masamune/actions/postgres_admin_spec.rb +58 -0
  119. data/spec/masamune/actions/postgres_spec.rb +134 -0
  120. data/spec/masamune/actions/s3cmd_spec.rb +44 -0
  121. data/spec/masamune/actions/transform_spec.rb +144 -0
  122. data/spec/masamune/after_initialization_callbacks_spec.rb +61 -0
  123. data/spec/masamune/cached_filesystem_spec.rb +167 -0
  124. data/spec/masamune/commands/hadoop_filesystem_spec.rb +50 -0
  125. data/spec/masamune/commands/hadoop_streaming_spec.rb +106 -0
  126. data/spec/masamune/commands/hive_spec.rb +117 -0
  127. data/spec/masamune/commands/postgres_admin_spec.rb +69 -0
  128. data/spec/masamune/commands/postgres_spec.rb +100 -0
  129. data/spec/masamune/commands/retry_with_backoff_spec.rb +116 -0
  130. data/spec/masamune/commands/s3cmd_spec.rb +50 -0
  131. data/spec/masamune/commands/shell_spec.rb +101 -0
  132. data/spec/masamune/configuration_spec.rb +102 -0
  133. data/spec/masamune/data_plan/builder_spec.rb +91 -0
  134. data/spec/masamune/data_plan/elem_spec.rb +102 -0
  135. data/spec/masamune/data_plan/engine_spec.rb +356 -0
  136. data/spec/masamune/data_plan/rule_spec.rb +407 -0
  137. data/spec/masamune/data_plan/set_spec.rb +517 -0
  138. data/spec/masamune/environment_spec.rb +65 -0
  139. data/spec/masamune/filesystem_spec.rb +1421 -0
  140. data/spec/masamune/helpers/postgres_spec.rb +95 -0
  141. data/spec/masamune/schema/catalog_spec.rb +613 -0
  142. data/spec/masamune/schema/column_spec.rb +696 -0
  143. data/spec/masamune/schema/dimension_spec.rb +137 -0
  144. data/spec/masamune/schema/event_spec.rb +75 -0
  145. data/spec/masamune/schema/fact_spec.rb +117 -0
  146. data/spec/masamune/schema/map_spec.rb +593 -0
  147. data/spec/masamune/schema/row_spec.rb +28 -0
  148. data/spec/masamune/schema/store_spec.rb +49 -0
  149. data/spec/masamune/schema/table_spec.rb +395 -0
  150. data/spec/masamune/string_format_spec.rb +60 -0
  151. data/spec/masamune/tasks/elastic_mapreduce_thor_spec.rb +57 -0
  152. data/spec/masamune/tasks/hive_thor_spec.rb +75 -0
  153. data/spec/masamune/tasks/postgres_thor_spec.rb +42 -0
  154. data/spec/masamune/tasks/shell_thor_spec.rb +51 -0
  155. data/spec/masamune/template_spec.rb +77 -0
  156. data/spec/masamune/thor_spec.rb +238 -0
  157. data/spec/masamune/transform/bulk_upsert.dimension_spec.rb +200 -0
  158. data/spec/masamune/transform/consolidate_dimension_spec.rb +62 -0
  159. data/spec/masamune/transform/deduplicate_dimension_spec.rb +84 -0
  160. data/spec/masamune/transform/define_event_view_spec.rb +84 -0
  161. data/spec/masamune/transform/define_schema_spec.rb +83 -0
  162. data/spec/masamune/transform/define_table.dimension_spec.rb +306 -0
  163. data/spec/masamune/transform/define_table.fact_spec.rb +291 -0
  164. data/spec/masamune/transform/define_table.table_spec.rb +525 -0
  165. data/spec/masamune/transform/insert_reference_values.dimension_spec.rb +111 -0
  166. data/spec/masamune/transform/insert_reference_values.fact_spec.rb +149 -0
  167. data/spec/masamune/transform/load_dimension_spec.rb +76 -0
  168. data/spec/masamune/transform/load_fact_spec.rb +89 -0
  169. data/spec/masamune/transform/relabel_dimension_spec.rb +102 -0
  170. data/spec/masamune/transform/rollup_fact_spec.rb +333 -0
  171. data/spec/masamune/transform/snapshot_dimension_spec.rb +103 -0
  172. data/spec/masamune/transform/stage_dimension_spec.rb +115 -0
  173. data/spec/masamune/transform/stage_fact_spec.rb +204 -0
  174. data/spec/masamune_spec.rb +32 -0
  175. data/spec/spec_helper.rb +41 -0
  176. data/spec/support/masamune/example_group.rb +36 -0
  177. data/spec/support/masamune/mock_command.rb +99 -0
  178. data/spec/support/masamune/mock_delegate.rb +51 -0
  179. data/spec/support/masamune/mock_filesystem.rb +96 -0
  180. data/spec/support/masamune/thor_mute.rb +35 -0
  181. data/spec/support/rspec/example/action_example_group.rb +34 -0
  182. data/spec/support/rspec/example/task_example_group.rb +80 -0
  183. data/spec/support/rspec/example/transform_example_group.rb +36 -0
  184. data/spec/support/shared_examples/postgres_common_examples.rb +53 -0
  185. metadata +462 -0
@@ -0,0 +1,527 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2014-2015, VMware, Inc. All Rights Reserved.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require 'json'
24
+
25
+ module Masamune::Schema
26
+ class Column
27
+ DEFAULT_ATTRIBUTES =
28
+ {
29
+ id: nil,
30
+ type: :integer,
31
+ sub_type: nil,
32
+ array: false,
33
+ values: [],
34
+ null: false,
35
+ strict: true,
36
+ default: nil,
37
+ auto: false,
38
+ index: Set.new,
39
+ unique: Set.new,
40
+ ignore: false,
41
+ sequence_offset: 1,
42
+ surrogate_key: false,
43
+ natural_key: false,
44
+ measure: false,
45
+ partition: false,
46
+ aggregate: nil,
47
+ reference: nil,
48
+ parent: nil,
49
+ debug: false
50
+ }
51
+
52
+ DEFAULT_ATTRIBUTES.keys.each do |attr|
53
+ attr_accessor attr
54
+ end
55
+
56
+ def initialize(opts = {})
57
+ opts.symbolize_keys!
58
+ raise ArgumentError, 'required parameter id: missing' unless opts.key?(:id)
59
+ DEFAULT_ATTRIBUTES.merge(opts).each do |name, value|
60
+ public_send("#{name}=", value)
61
+ end
62
+ end
63
+
64
+ def id=(id)
65
+ @id = id.to_sym
66
+ end
67
+
68
+ def name
69
+ if reference && reference.columns.include?(id)
70
+ [reference.label, reference.name, id].compact.join('_').to_sym
71
+ else
72
+ id
73
+ end
74
+ end
75
+
76
+ def default
77
+ return @default unless @default.nil?
78
+ case type
79
+ when :uuid
80
+ 'uuid_generate_v4()'
81
+ when :sequence
82
+ "nextval('#{sequence_id}')"
83
+ when :enum
84
+ values.first
85
+ end
86
+ end
87
+
88
+ def index=(value)
89
+ @index ||= Set.new
90
+ @index.clear
91
+ @index +=
92
+ case value
93
+ when true
94
+ [id]
95
+ when false
96
+ []
97
+ when String, Symbol
98
+ [value.to_sym]
99
+ when Array, Set
100
+ value.map(&:to_sym)
101
+ else
102
+ raise ArgumentError
103
+ end
104
+ end
105
+
106
+ def unique
107
+ self.unique = 'natural' if natural_key
108
+ @unique
109
+ end
110
+
111
+ def unique=(value)
112
+ @unique ||= Set.new
113
+ @unique.clear
114
+ @unique +=
115
+ case value
116
+ when true
117
+ [id]
118
+ when false
119
+ []
120
+ when String, Symbol
121
+ [value.to_sym]
122
+ when Array, Set
123
+ value.map(&:to_sym)
124
+ else
125
+ raise ArgumentError
126
+ end
127
+ end
128
+
129
+ def foreign_key_name
130
+ "#{reference.name}.#{@id}".to_sym if reference
131
+ end
132
+
133
+ def compact_name
134
+ if reference
135
+ # XXX once columns only reference columns, this can be cleaned up
136
+ if @id == reference.surrogate_key.reference_name(reference.label)
137
+ "#{reference.id}.#{reference.surrogate_key.id}".to_sym
138
+ else
139
+ "#{reference.id}.#{@id}".to_sym
140
+ end
141
+ else
142
+ @id
143
+ end
144
+ end
145
+
146
+ def qualified_name(label = nil)
147
+ [label, (parent ? "#{parent.name}.#{name}" : name)].compact.join('_').to_sym
148
+ end
149
+
150
+ def reference_name(label = nil)
151
+ qualified_name(label).to_s.gsub(/\./, '_').to_sym
152
+ end
153
+
154
+ def sql_type(for_surrogate_key = false)
155
+ elem =
156
+ case type
157
+ when :integer
158
+ for_surrogate_key ? 'SERIAL' : 'INTEGER'
159
+ when :money
160
+ 'MONEY'
161
+ when :string
162
+ 'VARCHAR'
163
+ when :uuid
164
+ 'UUID'
165
+ when :date
166
+ 'DATE'
167
+ when :timestamp
168
+ 'TIMESTAMP'
169
+ when :boolean
170
+ 'BOOLEAN'
171
+ when :sequence
172
+ 'INTEGER'
173
+ when :enum
174
+ "#{sub_type || id}_TYPE".upcase
175
+ when :key_value
176
+ if parent.type == :stage && !parent.inherit
177
+ 'JSON'
178
+ else
179
+ 'HSTORE'
180
+ end
181
+ when :json, :yaml
182
+ 'JSON'
183
+ end
184
+ array_value? ? "#{elem}[]" : elem
185
+ end
186
+
187
+ def hql_type
188
+ elem =
189
+ case type
190
+ when :integer
191
+ 'INT'
192
+ when :string
193
+ 'STRING'
194
+ else
195
+ sql_type
196
+ end
197
+ array_value? ? "ARRAY<#{elem}>" : elem
198
+ end
199
+
200
+ def sql_value(value)
201
+ return value if sql_function?(value)
202
+ return 'NULL' if value == :null
203
+ case type
204
+ when :boolean
205
+ value ? 'TRUE' : 'FALSE'
206
+ when :string
207
+ "'#{value}'"
208
+ when :enum
209
+ "'#{value}'::#{sql_type}"
210
+ else
211
+ value
212
+ end
213
+ end
214
+
215
+ def csv_value(value)
216
+ return value if sql_function?(value)
217
+ return csv_array(value) if array_value?
218
+ return nil if value.nil?
219
+ case type
220
+ when :boolean
221
+ value ? 'TRUE' : (hive_encoding? ? nil : 'FALSE')
222
+ when :yaml
223
+ value.to_hash.to_yaml
224
+ when :json, :key_value
225
+ value.to_hash.to_json
226
+ when :date
227
+ value.to_s
228
+ when :timestamp
229
+ value.to_time.utc.iso8601(3)
230
+ when :string
231
+ value.empty? ? nil : value
232
+ else
233
+ value
234
+ end
235
+ rescue
236
+ raise ArgumentError, "Could not coerce '#{value}' into :#{type} for column '#{name}'"
237
+ end
238
+
239
+ def ruby_value(value, recursive = true)
240
+ value = nil if null_value?(value)
241
+ return value if sql_function?(value)
242
+ return ruby_array(value) if recursive && array_value?
243
+ case type
244
+ when :boolean
245
+ case value
246
+ when false, 0, '0', "'0'", /\Afalse\z/i
247
+ false
248
+ when true, 1, '1', "'1'", /\Atrue\z/i
249
+ true
250
+ end
251
+ when :date
252
+ case value
253
+ when Date
254
+ value
255
+ when String
256
+ Date.parse(value.to_s)
257
+ when nil
258
+ nil
259
+ end
260
+ when :timestamp
261
+ case value
262
+ when Time
263
+ value
264
+ when Date, DateTime
265
+ value.to_time
266
+ when String
267
+ if value =~ /\A\d+\z/
268
+ Time.at(value.to_i)
269
+ else
270
+ Time.parse(value)
271
+ end
272
+ when Integer
273
+ Time.at(value)
274
+ when nil
275
+ nil
276
+ end
277
+ when :integer
278
+ value.nil? ? nil : value.to_i
279
+ when :yaml
280
+ case value
281
+ when Hash
282
+ value
283
+ when String
284
+ ruby_key_value(YAML.load(value))
285
+ when nil
286
+ {}
287
+ end
288
+ when :json
289
+ case value
290
+ when Hash
291
+ value
292
+ when String
293
+ ruby_key_value(JSON.load(value))
294
+ when nil
295
+ {}
296
+ end
297
+ when :string
298
+ value.to_s
299
+ else
300
+ value
301
+ end
302
+ rescue
303
+ raise ArgumentError, "Could not coerce '#{value}' into :#{type} for column '#{name}'"
304
+ end
305
+
306
+ def default_ruby_value
307
+ return [] if array_value?
308
+ return {} if hash_value?
309
+ case type
310
+ when :date
311
+ Date.new(0)
312
+ when :timestamp
313
+ Time.new(0)
314
+ else
315
+ nil
316
+ end
317
+ end
318
+
319
+ def aggregate_value
320
+ return qualified_name unless aggregate
321
+ case aggregate
322
+ when :min
323
+ "MIN(#{qualified_name})"
324
+ when :max
325
+ "MAX(#{qualified_name})"
326
+ when :sum
327
+ "SUM(#{qualified_name})"
328
+ when :average
329
+ "AVG(#{qualified_name})"
330
+ end
331
+ end
332
+
333
+ def null_value?(value)
334
+ if type == :json || array_value?
335
+ return true if value == 'NULL'
336
+ end
337
+ return false unless value
338
+ if hive_encoding?
339
+ value.to_s == '\N'
340
+ else
341
+ false
342
+ end
343
+ end
344
+
345
+ def hive_encoding?
346
+ if parent && parent.store
347
+ parent.store.type == :hive
348
+ else
349
+ false
350
+ end
351
+ end
352
+
353
+ def sql_function?(value)
354
+ value =~ /\(\)\Z/
355
+ end
356
+
357
+ def array_value?
358
+ !!(array || (reference && reference.respond_to?(:multiple) && reference.multiple))
359
+ end
360
+
361
+ def hash_value?
362
+ [:key_value, :yaml, :json].include?(type)
363
+ end
364
+
365
+ def as_psql
366
+ [name, sql_type(surrogate_key), *sql_constraints, reference_constraint, sql_default].compact.join(' ')
367
+ end
368
+
369
+ def as_hash
370
+ {id: id}.tap do |hash|
371
+ DEFAULT_ATTRIBUTES.keys.each do |attr|
372
+ hash[attr] = public_send(attr)
373
+ end
374
+ end
375
+ end
376
+
377
+ # TODO: Add ELEMENT REFERENCES
378
+ def reference_constraint
379
+ return if parent.temporary?
380
+ return if degenerate?
381
+ return if array_value?
382
+ if reference && reference.surrogate_key.type == type
383
+ "REFERENCES #{reference.name}(#{reference.surrogate_key.name})"
384
+ end
385
+ end
386
+
387
+ class << self
388
+ def dereference_column_name(name)
389
+ return unless name
390
+ if name.to_s =~ /\./
391
+ reference_name, column_name = name.to_s.split('.')
392
+ [reference_name.to_sym, column_name.to_sym]
393
+ else
394
+ [nil, name.to_sym]
395
+ end
396
+ end
397
+ end
398
+
399
+ def ==(other)
400
+ return false unless other
401
+ id == other.id &&
402
+ typecast?(other.type) &&
403
+ (!reference || reference.id == other.reference.try(:id) || reference.id == other.parent.try(:id)) &&
404
+ (!other.reference || other.reference.id == reference.try(:id) || other.reference.id == parent.try(:id))
405
+ end
406
+
407
+ def eql?(other)
408
+ self == other
409
+ end
410
+
411
+ def hash
412
+ [id, type].hash
413
+ end
414
+
415
+ def typecast?(other_type)
416
+ return true if type == other_type
417
+ case [type, other_type]
418
+ when [:key_value, :yaml]
419
+ true
420
+ when [:key_value, :json]
421
+ true
422
+ when [:yaml, :json]
423
+ true
424
+ else
425
+ false
426
+ end
427
+ end
428
+
429
+ def auto_reference
430
+ reference && reference.surrogate_key.auto && !reference.insert
431
+ end
432
+
433
+ # XXX hack to work around columns not being able to reference columns
434
+ def references?(other)
435
+ return false unless other
436
+ if reference && other.reference && reference.id == other.reference.id
437
+ true
438
+ elsif parent && other.parent && parent.id == other.parent.id
439
+ self == other
440
+ elsif parent && other.parent && other.parent.parent && parent.id == other.parent.parent.id
441
+ self == other
442
+ elsif reference && other.parent && reference.id == other.parent.id
443
+ self == other
444
+ elsif natural_key || other.natural_key
445
+ self == other
446
+ else
447
+ false
448
+ end
449
+ end
450
+
451
+ def degenerate?
452
+ reference && reference.respond_to?(:degenerate) && reference.degenerate
453
+ end
454
+
455
+ def adjacent
456
+ return unless reference
457
+ reference.columns[id]
458
+ end
459
+
460
+ def sequence_id
461
+ "#{reference_name}_seq" if type == :sequence
462
+ end
463
+
464
+ def required_value?
465
+ if reference
466
+ !(reference.null || reference.default)
467
+ else
468
+ surrogate_key || natural_key
469
+ end
470
+ end
471
+
472
+ private
473
+
474
+ def sql_constraints
475
+ [].tap do |constraints|
476
+ constraints << 'NOT NULL' unless null || surrogate_key || !strict || parent.temporary? || degenerate?
477
+ constraints << "PRIMARY KEY" if surrogate_key
478
+ end
479
+ end
480
+
481
+ def sql_default
482
+ return if default.nil?
483
+ return if !strict
484
+ "DEFAULT #{sql_value(default)}"
485
+ end
486
+
487
+ def ruby_array(value)
488
+ case value
489
+ when Array
490
+ value.map { |elem| ruby_value(elem, false) }
491
+ when String
492
+ Array.wrap(JSON.load(value)).map { |elem| ruby_value(elem, false) }
493
+ when nil
494
+ []
495
+ end
496
+ end
497
+
498
+ def ruby_key_value(hash)
499
+ case sub_type
500
+ when :boolean
501
+ Hash[hash.map { |key, value| ruby_boolean_key_value(key, value) }.compact]
502
+ else
503
+ hash
504
+ end
505
+ end
506
+
507
+ def ruby_boolean_key_value(key, value)
508
+ case value
509
+ when true, '1', 1
510
+ [key, true]
511
+ when false, '0', 0
512
+ [key, false]
513
+ end
514
+ end
515
+
516
+ def csv_array(value)
517
+ case value
518
+ when Array
519
+ ruby_value(value).to_json
520
+ when nil
521
+ [].to_json
522
+ else
523
+ [ruby_value(value, false)].to_json
524
+ end
525
+ end
526
+ end
527
+ end