globe-composite_primary_keys 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
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