globe-composite_primary_keys 3.0.1

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 (150) hide show
  1. data/History.txt +203 -0
  2. data/Manifest.txt +121 -0
  3. data/README.txt +41 -0
  4. data/README_DB2.txt +33 -0
  5. data/Rakefile +30 -0
  6. data/composite_primary_keys.gemspec +17 -0
  7. data/lib/adapter_helper/base.rb +63 -0
  8. data/lib/adapter_helper/mysql.rb +13 -0
  9. data/lib/adapter_helper/oracle.rb +12 -0
  10. data/lib/adapter_helper/oracle_enhanced.rb +12 -0
  11. data/lib/adapter_helper/postgresql.rb +13 -0
  12. data/lib/adapter_helper/sqlite3.rb +13 -0
  13. data/lib/composite_primary_keys.rb +63 -0
  14. data/lib/composite_primary_keys/association_preload.rb +162 -0
  15. data/lib/composite_primary_keys/associations.rb +159 -0
  16. data/lib/composite_primary_keys/attribute_methods.rb +84 -0
  17. data/lib/composite_primary_keys/base.rb +200 -0
  18. data/lib/composite_primary_keys/composite_arrays.rb +29 -0
  19. data/lib/composite_primary_keys/connection_adapters/abstract_adapter.rb +9 -0
  20. data/lib/composite_primary_keys/connection_adapters/ibm_db_adapter.rb +21 -0
  21. data/lib/composite_primary_keys/connection_adapters/oracle_adapter.rb +15 -0
  22. data/lib/composite_primary_keys/connection_adapters/oracle_enhanced_adapter.rb +17 -0
  23. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +53 -0
  24. data/lib/composite_primary_keys/connection_adapters/sqlite3_adapter.rb +15 -0
  25. data/lib/composite_primary_keys/finder_methods.rb +68 -0
  26. data/lib/composite_primary_keys/fixtures.rb +8 -0
  27. data/lib/composite_primary_keys/read.rb +25 -0
  28. data/lib/composite_primary_keys/reflection.rb +39 -0
  29. data/lib/composite_primary_keys/relation.rb +31 -0
  30. data/lib/composite_primary_keys/through_association_scope.rb +212 -0
  31. data/lib/composite_primary_keys/validations/uniqueness.rb +118 -0
  32. data/lib/composite_primary_keys/version.rb +9 -0
  33. data/loader.rb +24 -0
  34. data/local/database_connections.rb.sample +12 -0
  35. data/local/paths.rb.sample +2 -0
  36. data/local/tasks.rb.sample +2 -0
  37. data/scripts/console.rb +48 -0
  38. data/scripts/txt2html +67 -0
  39. data/scripts/txt2js +59 -0
  40. data/tasks/activerecord_selection.rake +43 -0
  41. data/tasks/databases.rake +12 -0
  42. data/tasks/databases/mysql.rake +30 -0
  43. data/tasks/databases/oracle.rake +25 -0
  44. data/tasks/databases/postgresql.rake +25 -0
  45. data/tasks/databases/sqlite3.rake +28 -0
  46. data/tasks/deployment.rake +22 -0
  47. data/tasks/local_setup.rake +13 -0
  48. data/tasks/website.rake +18 -0
  49. data/test/README_tests.txt +67 -0
  50. data/test/abstract_unit.rb +103 -0
  51. data/test/connections/native_ibm_db/connection.rb +23 -0
  52. data/test/connections/native_mysql/connection.rb +13 -0
  53. data/test/connections/native_oracle/connection.rb +14 -0
  54. data/test/connections/native_oracle_enhanced/connection.rb +20 -0
  55. data/test/connections/native_postgresql/connection.rb +8 -0
  56. data/test/connections/native_sqlite/connection.rb +9 -0
  57. data/test/fixtures/article.rb +5 -0
  58. data/test/fixtures/article_group.rb +4 -0
  59. data/test/fixtures/article_groups.yml +7 -0
  60. data/test/fixtures/articles.yml +6 -0
  61. data/test/fixtures/comment.rb +6 -0
  62. data/test/fixtures/comments.yml +16 -0
  63. data/test/fixtures/db_definitions/db2-create-tables.sql +113 -0
  64. data/test/fixtures/db_definitions/db2-drop-tables.sql +16 -0
  65. data/test/fixtures/db_definitions/mysql.sql +181 -0
  66. data/test/fixtures/db_definitions/oracle.drop.sql +39 -0
  67. data/test/fixtures/db_definitions/oracle.sql +188 -0
  68. data/test/fixtures/db_definitions/postgresql.sql +206 -0
  69. data/test/fixtures/db_definitions/sqlite.sql +166 -0
  70. data/test/fixtures/department.rb +5 -0
  71. data/test/fixtures/departments.yml +3 -0
  72. data/test/fixtures/dorm.rb +3 -0
  73. data/test/fixtures/dorms.yml +2 -0
  74. data/test/fixtures/employee.rb +4 -0
  75. data/test/fixtures/employees.yml +9 -0
  76. data/test/fixtures/group.rb +3 -0
  77. data/test/fixtures/groups.yml +3 -0
  78. data/test/fixtures/hack.rb +6 -0
  79. data/test/fixtures/hacks.yml +2 -0
  80. data/test/fixtures/kitchen_sink.rb +3 -0
  81. data/test/fixtures/kitchen_sinks.yml +5 -0
  82. data/test/fixtures/membership.rb +10 -0
  83. data/test/fixtures/membership_status.rb +3 -0
  84. data/test/fixtures/membership_statuses.yml +10 -0
  85. data/test/fixtures/memberships.yml +6 -0
  86. data/test/fixtures/product.rb +7 -0
  87. data/test/fixtures/product_tariff.rb +5 -0
  88. data/test/fixtures/product_tariffs.yml +12 -0
  89. data/test/fixtures/products.yml +6 -0
  90. data/test/fixtures/reading.rb +4 -0
  91. data/test/fixtures/readings.yml +10 -0
  92. data/test/fixtures/reference_code.rb +7 -0
  93. data/test/fixtures/reference_codes.yml +28 -0
  94. data/test/fixtures/reference_type.rb +7 -0
  95. data/test/fixtures/reference_types.yml +9 -0
  96. data/test/fixtures/restaurant.rb +6 -0
  97. data/test/fixtures/restaurants.yml +5 -0
  98. data/test/fixtures/restaurants_suburbs.yml +11 -0
  99. data/test/fixtures/room.rb +10 -0
  100. data/test/fixtures/room_assignment.rb +4 -0
  101. data/test/fixtures/room_assignments.yml +4 -0
  102. data/test/fixtures/room_attribute.rb +3 -0
  103. data/test/fixtures/room_attribute_assignment.rb +5 -0
  104. data/test/fixtures/room_attribute_assignments.yml +4 -0
  105. data/test/fixtures/room_attributes.yml +3 -0
  106. data/test/fixtures/rooms.yml +3 -0
  107. data/test/fixtures/seat.rb +5 -0
  108. data/test/fixtures/seats.yml +4 -0
  109. data/test/fixtures/street.rb +3 -0
  110. data/test/fixtures/streets.yml +15 -0
  111. data/test/fixtures/student.rb +4 -0
  112. data/test/fixtures/students.yml +2 -0
  113. data/test/fixtures/suburb.rb +6 -0
  114. data/test/fixtures/suburbs.yml +9 -0
  115. data/test/fixtures/tariff.rb +6 -0
  116. data/test/fixtures/tariffs.yml +13 -0
  117. data/test/fixtures/user.rb +10 -0
  118. data/test/fixtures/users.yml +6 -0
  119. data/test/hash_tricks.rb +34 -0
  120. data/test/plugins/pagination.rb +405 -0
  121. data/test/plugins/pagination_helper.rb +135 -0
  122. data/test/test_associations.rb +178 -0
  123. data/test/test_attribute_methods.rb +22 -0
  124. data/test/test_attributes.rb +80 -0
  125. data/test/test_clone.rb +34 -0
  126. data/test/test_composite_arrays.rb +32 -0
  127. data/test/test_create.rb +68 -0
  128. data/test/test_delete.rb +83 -0
  129. data/test/test_exists.rb +25 -0
  130. data/test/test_find.rb +73 -0
  131. data/test/test_ids.rb +90 -0
  132. data/test/test_miscellaneous.rb +39 -0
  133. data/test/test_pagination.rb +38 -0
  134. data/test/test_polymorphic.rb +32 -0
  135. data/test/test_santiago.rb +27 -0
  136. data/test/test_suite.rb +19 -0
  137. data/test/test_tutorial_example.rb +26 -0
  138. data/test/test_update.rb +40 -0
  139. data/test/test_validations.rb +11 -0
  140. data/website/index.html +195 -0
  141. data/website/index.txt +159 -0
  142. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  143. data/website/stylesheets/screen.css +126 -0
  144. data/website/template.js +3 -0
  145. data/website/template.rhtml +53 -0
  146. data/website/version-raw.js +3 -0
  147. data/website/version-raw.txt +2 -0
  148. data/website/version.js +4 -0
  149. data/website/version.txt +3 -0
  150. metadata +339 -0
@@ -0,0 +1,84 @@
1
+ module CompositePrimaryKeys
2
+ module ActiveRecord
3
+ module AttributeMethods #:nodoc:
4
+ def self.append_features(base)
5
+ super
6
+ base.send(:extend, ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ # Define an attribute reader method. Cope with nil column.
11
+ def define_read_method(symbol, attr_name, column)
12
+ cast_code = column.type_cast_code('v') if column
13
+ cast_code = "::#{cast_code}" if cast_code && cast_code.match('ActiveRecord::.*')
14
+ access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
15
+
16
+ unless self.primary_keys.include?(attr_name.to_sym)
17
+ access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
18
+ end
19
+
20
+ if cache_attribute?(attr_name)
21
+ access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
22
+ end
23
+
24
+ evaluate_attribute_method attr_name, "def #{symbol}; #{access_code}; end"
25
+ end
26
+
27
+ # Evaluate the definition for an attribute related method
28
+ def evaluate_attribute_method(attr_name, method_definition, method_name=attr_name)
29
+ unless primary_keys.include?(method_name.to_sym)
30
+ generated_methods << method_name
31
+ end
32
+
33
+ begin
34
+ class_eval(method_definition, __FILE__, __LINE__)
35
+ rescue SyntaxError => err
36
+ generated_methods.delete(attr_name)
37
+ if logger
38
+ logger.warn "Exception occurred during reader method compilation."
39
+ logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
40
+ logger.warn "#{err.message}"
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ # Allows access to the object attributes, which are held in the @attributes hash, as though they
47
+ # were first-class methods. So a Person class with a name attribute can use Person#name and
48
+ # Person#name= and never directly use the attributes hash -- except for multiple assigns with
49
+ # ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that
50
+ # the completed attribute is not nil or 0.
51
+ #
52
+ # It's also possible to instantiate related objects, so a Client class belonging to the clients
53
+ # table with a master_id foreign key can instantiate master through Client#master.
54
+ def method_missing(method_id, *args, &block)
55
+ method_name = method_id.to_s
56
+
57
+ # If we haven't generated any methods yet, generate them, then
58
+ # see if we've created the method we're looking for.
59
+ if !self.class.generated_methods?
60
+ self.class.define_attribute_methods
61
+
62
+ if self.class.generated_methods.include?(method_name)
63
+ return self.send(method_id, *args, &block)
64
+ end
65
+ end
66
+
67
+ if self.class.primary_keys.include?(method_name.to_sym)
68
+ ids[self.class.primary_keys.index(method_name.to_sym)]
69
+ elsif md = self.class.match_attribute_method?(method_name)
70
+ attribute_name, method_type = md.pre_match, md.to_s
71
+ if @attributes.include?(attribute_name)
72
+ __send__("attribute#{method_type}", attribute_name, *args, &block)
73
+ else
74
+ super
75
+ end
76
+ elsif @attributes.include?(method_name)
77
+ read_attribute(method_name)
78
+ else
79
+ super
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,200 @@
1
+ module ActiveRecord
2
+ class CompositeKeyError < StandardError #:nodoc:
3
+ end
4
+
5
+ class Base
6
+ INVALID_FOR_COMPOSITE_KEYS = 'Not appropriate for composite primary keys'
7
+ NOT_IMPLEMENTED_YET = 'Not implemented for composite primary keys yet'
8
+
9
+ class << self
10
+ def set_primary_keys(*keys)
11
+ keys = keys.first if keys.first.is_a?(Array)
12
+
13
+ if keys.length == 1
14
+ set_primary_key(keys.first)
15
+ return
16
+ end
17
+
18
+ cattr_accessor :primary_keys
19
+ self.primary_keys = keys.map { |k| k.to_sym }
20
+
21
+ class_eval <<-EOV
22
+ extend CompositeClassMethods
23
+ include CompositeInstanceMethods
24
+ include CompositePrimaryKeys::ActiveRecord::AssociationPreload
25
+ EOV
26
+
27
+ class << unscoped
28
+ include CompositePrimaryKeys::ActiveRecord::FinderMethods::InstanceMethods
29
+ include CompositePrimaryKeys::ActiveRecord::Relation::InstanceMethods
30
+ end
31
+ end
32
+
33
+ def composite?
34
+ false
35
+ end
36
+ end
37
+
38
+ def composite?
39
+ self.class.composite?
40
+ end
41
+
42
+ def [](attr_name)
43
+ # CPK
44
+ if attr_name.is_a?(String) and attr_name != attr_name.split(CompositePrimaryKeys::ID_SEP).first
45
+ attr_name = attr_name.split(CompositePrimaryKeys::ID_SEP)
46
+ end
47
+
48
+ # CPK
49
+ if attr_name.is_a?(Array)
50
+ attr_name.map {|name| read_attribute(name)}
51
+ else
52
+ read_attribute(attr_name)
53
+ end
54
+ end
55
+
56
+ def []=(attr_name, value)
57
+ # CPK
58
+ if attr_name.is_a?(String) and attr_name != attr_name.split(CompositePrimaryKeys::ID_SEP).first
59
+ attr_name = attr_name.split(CompositePrimaryKeys::ID_SEP)
60
+ end
61
+
62
+ if attr_name.is_a? Array
63
+ unless value.length == attr_name.length
64
+ raise "Number of attr_names and values do not match"
65
+ end
66
+ [attr_name, value].transpose.map {|name,val| write_attribute(name, val)}
67
+ value
68
+ else
69
+ write_attribute(attr_name, value)
70
+ end
71
+ end
72
+
73
+ module CompositeClassMethods
74
+ def primary_key
75
+ primary_keys
76
+ end
77
+
78
+ def primary_key=(keys)
79
+ primary_keys = keys
80
+ end
81
+
82
+ def composite?
83
+ true
84
+ end
85
+
86
+ #ids_to_s([[1,2],[7,3]]) -> "(1,2),(7,3)"
87
+ #ids_to_s([[1,2],[7,3]], ',', ';') -> "1,2;7,3"
88
+ def ids_to_s(many_ids, id_sep = CompositePrimaryKeys::ID_SEP, list_sep = ',', left_bracket = '(', right_bracket = ')')
89
+ many_ids.map {|ids| "#{left_bracket}#{ids}#{right_bracket}"}.join(list_sep)
90
+ end
91
+ end
92
+
93
+ module CompositeInstanceMethods
94
+ # A model instance's primary keys is always available as model.ids
95
+ # whether you name it the default 'id' or set it to something else.
96
+ def id
97
+ attr_names = self.class.primary_keys
98
+ CompositePrimaryKeys::CompositeKeys.new(attr_names.map { |attr_name| read_attribute(attr_name) })
99
+ end
100
+ alias_method :ids, :id
101
+
102
+ def ids_hash
103
+ self.class.primary_key.zip(ids).inject(Hash.new) do |hash, (key, value)|
104
+ hash[key] = value
105
+ hash
106
+ end
107
+ end
108
+
109
+ def to_param
110
+ id.to_s
111
+ end
112
+
113
+ def id_before_type_cast #:nodoc:
114
+ raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::NOT_IMPLEMENTED_YET
115
+ end
116
+
117
+ def quoted_id #:nodoc:
118
+ [self.class.primary_keys, ids].
119
+ transpose.
120
+ map {|attr_name,id| quote_value(id, column_for_attribute(attr_name))}.
121
+ to_composite_ids
122
+ end
123
+
124
+ # Sets the primary ID.
125
+ def id=(ids)
126
+ ids = ids.split(CompositePrimaryKeys::ID_SEP) if ids.is_a?(String)
127
+ ids.flatten!
128
+ unless ids.is_a?(Array) and ids.length == self.class.primary_keys.length
129
+ raise "#{self.class}.id= requires #{self.class.primary_keys.length} ids"
130
+ end
131
+ [primary_keys, ids].transpose.each {|key, an_id| write_attribute(key , an_id)}
132
+ id
133
+ end
134
+
135
+ def update_attribute(name, value)
136
+ raise ActiveRecordError, "#{name.to_s} is marked as readonly" if self.class.readonly_attributes.include? name.to_s
137
+
138
+ changes = {}
139
+
140
+ if name
141
+ name = name.to_s
142
+ send("#{name}=", value)
143
+ changes[name] = read_attribute(name)
144
+ end
145
+
146
+ @changed_attributes.except!(*changes.keys)
147
+ self.class.update_all(changes, ids_hash) == 1
148
+ end
149
+
150
+ # Cloned objects have no id assigned and are treated as new records. Note that this is a "shallow" clone
151
+ # as it copies the object's attributes only, not its associations. The extent of a "deep" clone is
152
+ # application specific and is therefore left to the application to implement according to its need.
153
+ def initialize_copy(other)
154
+ # Think the assertion which fails if the after_initialize callback goes at the end of the method is wrong. The
155
+ # deleted clone method called new which therefore called the after_initialize callback. It then went on to copy
156
+ # over the attributes. But if it's copying the attributes afterwards then it hasn't finished initializing right?
157
+ # For example in the test suite the topic model's after_initialize method sets the author_email_address to
158
+ # test@test.com. I would have thought this would mean that all cloned models would have an author email address
159
+ # of test@test.com. However the test_clone test method seems to test that this is not the case. As a result the
160
+ # after_initialize callback has to be run *before* the copying of the atrributes rather than afterwards in order
161
+ # for all tests to pass. This makes no sense to me.
162
+ callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
163
+ cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
164
+ # CPK
165
+ #cloned_attributes.delete(self.class.primary_key)
166
+ self.class.primary_key.each {|key| cloned_attributes.delete(key.to_s)}
167
+
168
+ @attributes = cloned_attributes
169
+ clear_aggregation_cache
170
+ @attributes_cache = {}
171
+ @new_record = true
172
+ ensure_proper_type
173
+
174
+ if scope = self.class.send(:current_scoped_methods)
175
+ create_with = scope.scope_for_create
176
+ create_with.each { |att,value| self.send("#{att}=", value) } if create_with
177
+ end
178
+ end
179
+
180
+ def destroy
181
+ if persisted?
182
+ # CPK
183
+ # self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all
184
+ self.class.unscoped.where(ids_hash).delete_all
185
+ end
186
+
187
+ @destroyed = true
188
+ freeze
189
+ end
190
+
191
+ def update(attribute_names = @attributes.keys)
192
+ attributes_with_values = arel_attributes_values(false, false, attribute_names)
193
+ return 0 if attributes_with_values.empty?
194
+ # CPK
195
+ # self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values)
196
+ self.class.unscoped.where(ids_hash).arel.update(attributes_with_values)
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,29 @@
1
+ module CompositePrimaryKeys
2
+ ID_SEP = ','
3
+ ID_SET_SEP = ';'
4
+
5
+ module ArrayExtension
6
+ def to_composite_keys
7
+ CompositeKeys.new(self)
8
+ end
9
+
10
+ def to_composite_ids
11
+ CompositeIds.new(self)
12
+ end
13
+ end
14
+
15
+ class CompositeKeys < Array
16
+ def to_s
17
+ # Doing this makes it easier to parse Base#[](attr_name)
18
+ join(ID_SEP)
19
+ end
20
+ end
21
+
22
+ class CompositeIds < Array
23
+ def to_s
24
+ join(ID_SEP)
25
+ end
26
+ end
27
+ end
28
+
29
+ Array.send(:include, CompositePrimaryKeys::ArrayExtension)
@@ -0,0 +1,9 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ class AbstractAdapter
4
+ def concat(*columns)
5
+ "CONCAT(#{columns.join(',')})"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class IBM_DBAdapter < AbstractAdapter
4
+
5
+ # This mightn't be in Core, but count(distinct x,y) doesn't work for me
6
+ def supports_count_distinct? #:nodoc:
7
+ false
8
+ end
9
+
10
+ alias_method :quote_original, :quote
11
+ def quote(value, column = nil)
12
+ if value.kind_of?(String) && column && [:integer, :float].include?(column.type)
13
+ value = column.type == :integer ? value.to_i : value.to_f
14
+ value.to_s
15
+ else
16
+ quote_original(value, column)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class OracleAdapter < AbstractAdapter
4
+
5
+ # This mightn't be in Core, but count(distinct x,y) doesn't work for me
6
+ def supports_count_distinct? #:nodoc:
7
+ false
8
+ end
9
+
10
+ def concat(*columns)
11
+ "(#{columns.join('||')})"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # Added to OracleEnhancedAdapter version 1.1.4
2
+ #
3
+ # module ActiveRecord
4
+ # module ConnectionAdapters
5
+ # class OracleEnhancedAdapter < AbstractAdapter
6
+ #
7
+ # # This mightn't be in Core, but count(distinct x,y) doesn't work for me
8
+ # def supports_count_distinct? #:nodoc:
9
+ # false
10
+ # end
11
+ #
12
+ # def concat(*columns)
13
+ # "(#{columns.join('||')})"
14
+ # end
15
+ # end
16
+ # end
17
+ # end
@@ -0,0 +1,53 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLAdapter < AbstractAdapter
4
+
5
+ # This mightn't be in Core, but count(distinct x,y) doesn't work for me
6
+ def supports_count_distinct? #:nodoc:
7
+ false
8
+ end
9
+
10
+ def concat(*columns)
11
+ columns = columns.map { |c| "CAST(#{c} AS varchar)" }
12
+ "(#{columns.join('||')})"
13
+ end
14
+
15
+ # Executes an INSERT query and returns the new record's ID
16
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
17
+ # Extract the table from the insert sql. Yuck.
18
+ table = sql.split(" ", 4)[2].gsub('"', '')
19
+
20
+ # Try an insert with 'returning id' if available (PG >= 8.2)
21
+ if supports_insert_with_returning?
22
+ pk, sequence_name = *pk_and_sequence_for(table) unless pk
23
+ if pk
24
+ quoted_pk = if pk.is_a?(Array)
25
+ pk.map { |col| quote_column_name(col) }.join(CompositePrimaryKeys::ID_SEP)
26
+ else
27
+ quote_column_name(pk)
28
+ end
29
+ id = select_value("#{sql} RETURNING #{quoted_pk}")
30
+ clear_query_cache
31
+ return id
32
+ end
33
+ end
34
+
35
+ # Otherwise, insert then grab last_insert_id.
36
+ if insert_id = super
37
+ insert_id
38
+ else
39
+ # If neither pk nor sequence name is given, look them up.
40
+ unless pk || sequence_name
41
+ pk, sequence_name = *pk_and_sequence_for(table)
42
+ end
43
+
44
+ # If a pk is given, fallback to default sequence name.
45
+ # Don't fetch last insert id for a table without a pk.
46
+ if pk && sequence_name ||= default_sequence_name(table, pk)
47
+ last_insert_id(table, sequence_name)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,15 @@
1
+ require 'active_record/connection_adapters/sqlite_adapter'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters #:nodoc:
5
+ class SQLite3Adapter < SQLiteAdapter # :nodoc:
6
+ def supports_count_distinct? #:nodoc:
7
+ false
8
+ end
9
+
10
+ def concat(*columns)
11
+ "(#{columns.join('||')})"
12
+ end
13
+ end
14
+ end
15
+ end