activerecord 3.2.22.5 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -0,0 +1,97 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLColumn < Column
4
+ module ArrayParser
5
+ private
6
+ # Loads pg_array_parser if available. String parsing can be
7
+ # performed quicker by a native extension, which will not create
8
+ # a large amount of Ruby objects that will need to be garbage
9
+ # collected. pg_array_parser has a C and Java extension
10
+ begin
11
+ require 'pg_array_parser'
12
+ include PgArrayParser
13
+ rescue LoadError
14
+ def parse_pg_array(string)
15
+ parse_data(string, 0)
16
+ end
17
+ end
18
+
19
+ def parse_data(string, index)
20
+ local_index = index
21
+ array = []
22
+ while(local_index < string.length)
23
+ case string[local_index]
24
+ when '{'
25
+ local_index,array = parse_array_contents(array, string, local_index + 1)
26
+ when '}'
27
+ return array
28
+ end
29
+ local_index += 1
30
+ end
31
+
32
+ array
33
+ end
34
+
35
+ def parse_array_contents(array, string, index)
36
+ is_escaping = false
37
+ is_quoted = false
38
+ was_quoted = false
39
+ current_item = ''
40
+
41
+ local_index = index
42
+ while local_index
43
+ token = string[local_index]
44
+ if is_escaping
45
+ current_item << token
46
+ is_escaping = false
47
+ else
48
+ if is_quoted
49
+ case token
50
+ when '"'
51
+ is_quoted = false
52
+ was_quoted = true
53
+ when "\\"
54
+ is_escaping = true
55
+ else
56
+ current_item << token
57
+ end
58
+ else
59
+ case token
60
+ when "\\"
61
+ is_escaping = true
62
+ when ','
63
+ add_item_to_array(array, current_item, was_quoted)
64
+ current_item = ''
65
+ was_quoted = false
66
+ when '"'
67
+ is_quoted = true
68
+ when '{'
69
+ internal_items = []
70
+ local_index,internal_items = parse_array_contents(internal_items, string, local_index + 1)
71
+ array.push(internal_items)
72
+ when '}'
73
+ add_item_to_array(array, current_item, was_quoted)
74
+ return local_index,array
75
+ else
76
+ current_item << token
77
+ end
78
+ end
79
+ end
80
+
81
+ local_index += 1
82
+ end
83
+ return local_index,array
84
+ end
85
+
86
+ def add_item_to_array(array, current_item, quoted)
87
+ if current_item.length == 0
88
+ elsif !quoted && current_item == 'NULL'
89
+ array.push nil
90
+ else
91
+ array.push current_item
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,132 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLColumn < Column
4
+ module Cast
5
+ def string_to_time(string)
6
+ return string unless String === string
7
+
8
+ case string
9
+ when 'infinity'; 1.0 / 0.0
10
+ when '-infinity'; -1.0 / 0.0
11
+ when / BC$/
12
+ super("-" + string.sub(/ BC$/, ""))
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+ def hstore_to_string(object)
19
+ if Hash === object
20
+ object.map { |k,v|
21
+ "#{escape_hstore(k)}=>#{escape_hstore(v)}"
22
+ }.join ','
23
+ else
24
+ object
25
+ end
26
+ end
27
+
28
+ def string_to_hstore(string)
29
+ if string.nil?
30
+ nil
31
+ elsif String === string
32
+ Hash[string.scan(HstorePair).map { |k,v|
33
+ v = v.upcase == 'NULL' ? nil : v.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
34
+ k = k.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
35
+ [k,v]
36
+ }]
37
+ else
38
+ string
39
+ end
40
+ end
41
+
42
+ def json_to_string(object)
43
+ if Hash === object
44
+ ActiveSupport::JSON.encode(object)
45
+ else
46
+ object
47
+ end
48
+ end
49
+
50
+ def array_to_string(value, column, adapter, should_be_quoted = false)
51
+ casted_values = value.map do |val|
52
+ if String === val
53
+ if val == "NULL"
54
+ "\"#{val}\""
55
+ else
56
+ quote_and_escape(adapter.type_cast(val, column, true))
57
+ end
58
+ else
59
+ adapter.type_cast(val, column, true)
60
+ end
61
+ end
62
+ "{#{casted_values.join(',')}}"
63
+ end
64
+
65
+ def range_to_string(object)
66
+ from = object.begin.respond_to?(:infinite?) && object.begin.infinite? ? '' : object.begin
67
+ to = object.end.respond_to?(:infinite?) && object.end.infinite? ? '' : object.end
68
+ "[#{from},#{to}#{object.exclude_end? ? ')' : ']'}"
69
+ end
70
+
71
+ def string_to_json(string)
72
+ if String === string
73
+ ActiveSupport::JSON.decode(string)
74
+ else
75
+ string
76
+ end
77
+ end
78
+
79
+ def string_to_cidr(string)
80
+ if string.nil?
81
+ nil
82
+ elsif String === string
83
+ IPAddr.new(string)
84
+ else
85
+ string
86
+ end
87
+ end
88
+
89
+ def cidr_to_string(object)
90
+ if IPAddr === object
91
+ "#{object.to_s}/#{object.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
92
+ else
93
+ object
94
+ end
95
+ end
96
+
97
+ def string_to_array(string, oid)
98
+ parse_pg_array(string).map{|val| oid.type_cast val}
99
+ end
100
+
101
+ private
102
+
103
+ HstorePair = begin
104
+ quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
105
+ unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
106
+ /(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
107
+ end
108
+
109
+ def escape_hstore(value)
110
+ if value.nil?
111
+ 'NULL'
112
+ else
113
+ if value == ""
114
+ '""'
115
+ else
116
+ '"%s"' % value.to_s.gsub(/(["\\])/, '\\\\\1')
117
+ end
118
+ end
119
+ end
120
+
121
+ def quote_and_escape(value)
122
+ case value
123
+ when "NULL"
124
+ value
125
+ else
126
+ "\"#{value.gsub(/"/,"\\\"")}\""
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,242 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLAdapter < AbstractAdapter
4
+ module DatabaseStatements
5
+ def explain(arel, binds = [])
6
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
7
+ ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
8
+ end
9
+
10
+ class ExplainPrettyPrinter # :nodoc:
11
+ # Pretty prints the result of a EXPLAIN in a way that resembles the output of the
12
+ # PostgreSQL shell:
13
+ #
14
+ # QUERY PLAN
15
+ # ------------------------------------------------------------------------------
16
+ # Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
17
+ # Join Filter: (posts.user_id = users.id)
18
+ # -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
19
+ # Index Cond: (id = 1)
20
+ # -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
21
+ # Filter: (posts.user_id = 1)
22
+ # (6 rows)
23
+ #
24
+ def pp(result)
25
+ header = result.columns.first
26
+ lines = result.rows.map(&:first)
27
+
28
+ # We add 2 because there's one char of padding at both sides, note
29
+ # the extra hyphens in the example above.
30
+ width = [header, *lines].map(&:length).max + 2
31
+
32
+ pp = []
33
+
34
+ pp << header.center(width).rstrip
35
+ pp << '-' * width
36
+
37
+ pp += lines.map {|line| " #{line}"}
38
+
39
+ nrows = result.rows.length
40
+ rows_label = nrows == 1 ? 'row' : 'rows'
41
+ pp << "(#{nrows} #{rows_label})"
42
+
43
+ pp.join("\n") + "\n"
44
+ end
45
+ end
46
+
47
+ # Executes a SELECT query and returns an array of rows. Each row is an
48
+ # array of field values.
49
+ def select_rows(sql, name = nil)
50
+ select_raw(sql, name).last
51
+ end
52
+
53
+ # Executes an INSERT query and returns the new record's ID
54
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
55
+ unless pk
56
+ # Extract the table from the insert sql. Yuck.
57
+ table_ref = extract_table_ref_from_insert_sql(sql)
58
+ pk = primary_key(table_ref) if table_ref
59
+ end
60
+
61
+ if pk && use_insert_returning?
62
+ select_value("#{sql} RETURNING #{quote_column_name(pk)}")
63
+ elsif pk
64
+ super
65
+ last_insert_id_value(sequence_name || default_sequence_name(table_ref, pk))
66
+ else
67
+ super
68
+ end
69
+ end
70
+
71
+ def create
72
+ super.insert
73
+ end
74
+
75
+ # create a 2D array representing the result set
76
+ def result_as_array(res) #:nodoc:
77
+ # check if we have any binary column and if they need escaping
78
+ ftypes = Array.new(res.nfields) do |i|
79
+ [i, res.ftype(i)]
80
+ end
81
+
82
+ rows = res.values
83
+ return rows unless ftypes.any? { |_, x|
84
+ x == BYTEA_COLUMN_TYPE_OID || x == MONEY_COLUMN_TYPE_OID
85
+ }
86
+
87
+ typehash = ftypes.group_by { |_, type| type }
88
+ binaries = typehash[BYTEA_COLUMN_TYPE_OID] || []
89
+ monies = typehash[MONEY_COLUMN_TYPE_OID] || []
90
+
91
+ rows.each do |row|
92
+ # unescape string passed BYTEA field (OID == 17)
93
+ binaries.each do |index, _|
94
+ row[index] = unescape_bytea(row[index])
95
+ end
96
+
97
+ # If this is a money type column and there are any currency symbols,
98
+ # then strip them off. Indeed it would be prettier to do this in
99
+ # PostgreSQLColumn.string_to_decimal but would break form input
100
+ # fields that call value_before_type_cast.
101
+ monies.each do |index, _|
102
+ data = row[index]
103
+ # Because money output is formatted according to the locale, there are two
104
+ # cases to consider (note the decimal separators):
105
+ # (1) $12,345,678.12
106
+ # (2) $12.345.678,12
107
+ case data
108
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
109
+ data.gsub!(/[^-\d.]/, '')
110
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
111
+ data.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ # Queries the database and returns the results in an Array-like object
118
+ def query(sql, name = nil) #:nodoc:
119
+ log(sql, name) do
120
+ result_as_array @connection.async_exec(sql)
121
+ end
122
+ end
123
+
124
+ # Executes an SQL statement, returning a PGresult object on success
125
+ # or raising a PGError exception otherwise.
126
+ def execute(sql, name = nil)
127
+ log(sql, name) do
128
+ @connection.async_exec(sql)
129
+ end
130
+ end
131
+
132
+ def substitute_at(column, index)
133
+ Arel::Nodes::BindParam.new "$#{index + 1}"
134
+ end
135
+
136
+ def exec_query(sql, name = 'SQL', binds = [])
137
+ log(sql, name, binds) do
138
+ result = binds.empty? ? exec_no_cache(sql, binds) :
139
+ exec_cache(sql, binds)
140
+
141
+ types = {}
142
+ result.fields.each_with_index do |fname, i|
143
+ ftype = result.ftype i
144
+ fmod = result.fmod i
145
+ types[fname] = OID::TYPE_MAP.fetch(ftype, fmod) { |oid, mod|
146
+ warn "unknown OID: #{fname}(#{oid}) (#{sql})"
147
+ OID::Identity.new
148
+ }
149
+ end
150
+
151
+ ret = ActiveRecord::Result.new(result.fields, result.values, types)
152
+ result.clear
153
+ return ret
154
+ end
155
+ end
156
+
157
+ def exec_delete(sql, name = 'SQL', binds = [])
158
+ log(sql, name, binds) do
159
+ result = binds.empty? ? exec_no_cache(sql, binds) :
160
+ exec_cache(sql, binds)
161
+ affected = result.cmd_tuples
162
+ result.clear
163
+ affected
164
+ end
165
+ end
166
+ alias :exec_update :exec_delete
167
+
168
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
169
+ unless pk
170
+ # Extract the table from the insert sql. Yuck.
171
+ table_ref = extract_table_ref_from_insert_sql(sql)
172
+ pk = primary_key(table_ref) if table_ref
173
+ end
174
+
175
+ if pk && use_insert_returning?
176
+ sql = "#{sql} RETURNING #{quote_column_name(pk)}"
177
+ end
178
+
179
+ [sql, binds]
180
+ end
181
+
182
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
183
+ val = exec_query(sql, name, binds)
184
+ if !use_insert_returning? && pk
185
+ unless sequence_name
186
+ table_ref = extract_table_ref_from_insert_sql(sql)
187
+ sequence_name = default_sequence_name(table_ref, pk)
188
+ return val unless sequence_name
189
+ end
190
+ last_insert_id_result(sequence_name)
191
+ else
192
+ val
193
+ end
194
+ end
195
+
196
+ # Executes an UPDATE query and returns the number of affected tuples.
197
+ def update_sql(sql, name = nil)
198
+ super.cmd_tuples
199
+ end
200
+
201
+ # Begins a transaction.
202
+ def begin_db_transaction
203
+ execute "BEGIN"
204
+ end
205
+
206
+ def begin_isolated_db_transaction(isolation)
207
+ begin_db_transaction
208
+ execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
209
+ end
210
+
211
+ # Commits a transaction.
212
+ def commit_db_transaction
213
+ execute "COMMIT"
214
+ end
215
+
216
+ # Aborts a transaction.
217
+ def rollback_db_transaction
218
+ execute "ROLLBACK"
219
+ end
220
+
221
+ def outside_transaction?
222
+ message = "#outside_transaction? is deprecated. This method was only really used " \
223
+ "internally, but you can use #transaction_open? instead."
224
+ ActiveSupport::Deprecation.warn message
225
+ @connection.transaction_status == PGconn::PQTRANS_IDLE
226
+ end
227
+
228
+ def create_savepoint
229
+ execute("SAVEPOINT #{current_savepoint_name}")
230
+ end
231
+
232
+ def rollback_to_savepoint
233
+ execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
234
+ end
235
+
236
+ def release_savepoint
237
+ execute("RELEASE SAVEPOINT #{current_savepoint_name}")
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end