activerecord 3.2.22.4 → 4.0.13

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2799 -617
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +23 -32
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +40 -34
  7. data/lib/active_record/association_relation.rb +22 -0
  8. data/lib/active_record/associations/alias_tracker.rb +4 -2
  9. data/lib/active_record/associations/association.rb +60 -46
  10. data/lib/active_record/associations/association_scope.rb +46 -40
  11. data/lib/active_record/associations/belongs_to_association.rb +17 -4
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +73 -56
  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 +130 -96
  21. data/lib/active_record/associations/collection_proxy.rb +916 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
  23. data/lib/active_record/associations/has_many_association.rb +35 -8
  24. data/lib/active_record/associations/has_many_through_association.rb +37 -17
  25. data/lib/active_record/associations/has_one_association.rb +42 -19
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
  28. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  29. data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
  30. data/lib/active_record/associations/join_dependency.rb +30 -9
  31. data/lib/active_record/associations/join_helper.rb +1 -11
  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 +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  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/preloader.rb +20 -43
  39. data/lib/active_record/associations/singular_association.rb +11 -11
  40. data/lib/active_record/associations/through_association.rb +3 -3
  41. data/lib/active_record/associations.rb +223 -282
  42. data/lib/active_record/attribute_assignment.rb +134 -154
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  44. data/lib/active_record/attribute_methods/dirty.rb +36 -29
  45. data/lib/active_record/attribute_methods/primary_key.rb +45 -31
  46. data/lib/active_record/attribute_methods/query.rb +5 -4
  47. data/lib/active_record/attribute_methods/read.rb +67 -90
  48. data/lib/active_record/attribute_methods/serialization.rb +133 -70
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
  50. data/lib/active_record/attribute_methods/write.rb +34 -39
  51. data/lib/active_record/attribute_methods.rb +268 -108
  52. data/lib/active_record/autosave_association.rb +80 -73
  53. data/lib/active_record/base.rb +54 -451
  54. data/lib/active_record/callbacks.rb +60 -22
  55. data/lib/active_record/coders/yaml_column.rb +18 -21
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
  67. data/lib/active_record/connection_adapters/column.rb +67 -36
  68. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
  70. data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
  71. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
  72. data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
  75. data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
  76. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
  79. data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
  80. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
  81. data/lib/active_record/connection_handling.rb +98 -0
  82. data/lib/active_record/core.rb +472 -0
  83. data/lib/active_record/counter_cache.rb +107 -108
  84. data/lib/active_record/dynamic_matchers.rb +115 -63
  85. data/lib/active_record/errors.rb +36 -18
  86. data/lib/active_record/explain.rb +15 -63
  87. data/lib/active_record/explain_registry.rb +30 -0
  88. data/lib/active_record/explain_subscriber.rb +8 -4
  89. data/lib/active_record/fixture_set/file.rb +55 -0
  90. data/lib/active_record/fixtures.rb +159 -155
  91. data/lib/active_record/inheritance.rb +93 -59
  92. data/lib/active_record/integration.rb +8 -8
  93. data/lib/active_record/locale/en.yml +8 -1
  94. data/lib/active_record/locking/optimistic.rb +39 -43
  95. data/lib/active_record/locking/pessimistic.rb +4 -4
  96. data/lib/active_record/log_subscriber.rb +19 -9
  97. data/lib/active_record/migration/command_recorder.rb +102 -33
  98. data/lib/active_record/migration/join_table.rb +15 -0
  99. data/lib/active_record/migration.rb +411 -173
  100. data/lib/active_record/model_schema.rb +81 -94
  101. data/lib/active_record/nested_attributes.rb +173 -131
  102. data/lib/active_record/null_relation.rb +67 -0
  103. data/lib/active_record/persistence.rb +254 -106
  104. data/lib/active_record/query_cache.rb +18 -36
  105. data/lib/active_record/querying.rb +19 -15
  106. data/lib/active_record/railtie.rb +113 -38
  107. data/lib/active_record/railties/console_sandbox.rb +3 -4
  108. data/lib/active_record/railties/controller_runtime.rb +4 -3
  109. data/lib/active_record/railties/databases.rake +115 -368
  110. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  111. data/lib/active_record/readonly_attributes.rb +7 -3
  112. data/lib/active_record/reflection.rb +110 -61
  113. data/lib/active_record/relation/batches.rb +29 -29
  114. data/lib/active_record/relation/calculations.rb +155 -125
  115. data/lib/active_record/relation/delegation.rb +94 -18
  116. data/lib/active_record/relation/finder_methods.rb +151 -203
  117. data/lib/active_record/relation/merger.rb +188 -0
  118. data/lib/active_record/relation/predicate_builder.rb +85 -42
  119. data/lib/active_record/relation/query_methods.rb +793 -146
  120. data/lib/active_record/relation/spawn_methods.rb +43 -150
  121. data/lib/active_record/relation.rb +293 -173
  122. data/lib/active_record/result.rb +48 -7
  123. data/lib/active_record/runtime_registry.rb +17 -0
  124. data/lib/active_record/sanitization.rb +41 -54
  125. data/lib/active_record/schema.rb +19 -12
  126. data/lib/active_record/schema_dumper.rb +41 -41
  127. data/lib/active_record/schema_migration.rb +46 -0
  128. data/lib/active_record/scoping/default.rb +56 -52
  129. data/lib/active_record/scoping/named.rb +78 -103
  130. data/lib/active_record/scoping.rb +54 -124
  131. data/lib/active_record/serialization.rb +6 -2
  132. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  133. data/lib/active_record/statement_cache.rb +26 -0
  134. data/lib/active_record/store.rb +131 -15
  135. data/lib/active_record/tasks/database_tasks.rb +204 -0
  136. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
  138. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  140. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  141. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  142. data/lib/active_record/test_case.rb +67 -38
  143. data/lib/active_record/timestamp.rb +16 -11
  144. data/lib/active_record/transactions.rb +73 -51
  145. data/lib/active_record/validations/associated.rb +19 -13
  146. data/lib/active_record/validations/presence.rb +65 -0
  147. data/lib/active_record/validations/uniqueness.rb +110 -57
  148. data/lib/active_record/validations.rb +18 -17
  149. data/lib/active_record/version.rb +7 -6
  150. data/lib/active_record.rb +63 -45
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
  152. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  154. data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
  155. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  156. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  157. data/lib/rails/generators/active_record.rb +3 -5
  158. metadata +43 -29
  159. data/examples/associations.png +0 -0
  160. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  161. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  162. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  163. data/lib/active_record/dynamic_finder_match.rb +0 -68
  164. data/lib/active_record/dynamic_scope_match.rb +0 -23
  165. data/lib/active_record/fixtures/file.rb +0 -65
  166. data/lib/active_record/identity_map.rb +0 -162
  167. data/lib/active_record/observer.rb +0 -121
  168. data/lib/active_record/session_store.rb +0 -360
  169. data/lib/rails/generators/active_record/migration.rb +0 -15
  170. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  171. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  172. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  173. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -0,0 +1,98 @@
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
+ return if !quoted && current_item.length == 0
88
+
89
+ if !quoted && current_item == 'NULL'
90
+ array.push nil
91
+ else
92
+ array.push current_item
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,160 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLColumn < Column
4
+ module Cast
5
+ def point_to_string(point)
6
+ "(#{point[0]},#{point[1]})"
7
+ end
8
+
9
+ def string_to_point(string)
10
+ if string[0] == '(' && string[-1] == ')'
11
+ string = string[1...-1]
12
+ end
13
+ string.split(',').map{ |v| Float(v) }
14
+ end
15
+
16
+ def string_to_time(string)
17
+ return string unless String === string
18
+
19
+ case string
20
+ when 'infinity'; 1.0 / 0.0
21
+ when '-infinity'; -1.0 / 0.0
22
+ when / BC$/
23
+ super("-" + string.sub(/ BC$/, ""))
24
+ else
25
+ super
26
+ end
27
+ end
28
+
29
+ def string_to_bit(value)
30
+ case value
31
+ when /^0x/i
32
+ value[2..-1].hex.to_s(2) # Hexadecimal notation
33
+ else
34
+ value # Bit-string notation
35
+ end
36
+ end
37
+
38
+ def hstore_to_string(object, array_member = false)
39
+ if Hash === object
40
+ string = object.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(',')
41
+ string = escape_hstore(string) if array_member
42
+ string
43
+ else
44
+ object
45
+ end
46
+ end
47
+
48
+ def string_to_hstore(string)
49
+ if string.nil?
50
+ nil
51
+ elsif String === string
52
+ Hash[string.scan(HstorePair).map { |k, v|
53
+ v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
54
+ k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
55
+ [k, v]
56
+ }]
57
+ else
58
+ string
59
+ end
60
+ end
61
+
62
+ def json_to_string(object)
63
+ if Hash === object || Array === object
64
+ ActiveSupport::JSON.encode(object)
65
+ else
66
+ object
67
+ end
68
+ end
69
+
70
+ def array_to_string(value, column, adapter, should_be_quoted = false)
71
+ casted_values = value.map do |val|
72
+ if String === val
73
+ if val == "NULL"
74
+ "\"#{val}\""
75
+ else
76
+ quote_and_escape(adapter.type_cast(val, column, true))
77
+ end
78
+ else
79
+ adapter.type_cast(val, column, true)
80
+ end
81
+ end
82
+ "{#{casted_values.join(',')}}"
83
+ end
84
+
85
+ def range_to_string(object)
86
+ from = object.begin.respond_to?(:infinite?) && object.begin.infinite? ? '' : object.begin
87
+ to = object.end.respond_to?(:infinite?) && object.end.infinite? ? '' : object.end
88
+ "[#{from},#{to}#{object.exclude_end? ? ')' : ']'}"
89
+ end
90
+
91
+ def string_to_json(string)
92
+ if String === string
93
+ ActiveSupport::JSON.decode(string)
94
+ else
95
+ string
96
+ end
97
+ end
98
+
99
+ def string_to_cidr(string)
100
+ if string.nil?
101
+ nil
102
+ elsif String === string
103
+ begin
104
+ IPAddr.new(string)
105
+ rescue ArgumentError
106
+ nil
107
+ end
108
+ else
109
+ string
110
+ end
111
+ end
112
+
113
+ def cidr_to_string(object)
114
+ if IPAddr === object
115
+ "#{object.to_s}/#{object.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
116
+ else
117
+ object
118
+ end
119
+ end
120
+
121
+ def string_to_array(string, oid)
122
+ parse_pg_array(string).map{|val| oid.type_cast val}
123
+ end
124
+
125
+ private
126
+
127
+ HstorePair = begin
128
+ quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
129
+ unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
130
+ /(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
131
+ end
132
+
133
+ def escape_hstore(value)
134
+ if value.nil?
135
+ 'NULL'
136
+ else
137
+ if value == ""
138
+ '""'
139
+ else
140
+ '"%s"' % value.to_s.gsub(/(["\\])/, '\\\\\1')
141
+ end
142
+ end
143
+ end
144
+
145
+ ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays
146
+
147
+ def quote_and_escape(value)
148
+ case value
149
+ when "NULL", Numeric
150
+ value
151
+ else
152
+ value = value.gsub(/\\/, ARRAY_ESCAPE)
153
+ value.gsub!(/"/,"\\\"")
154
+ "\"#{value}\""
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,240 @@
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, binds = [])
50
+ exec_query(sql, name, binds).rows
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 = without_prepared_statement?(binds) ? exec_no_cache(sql, binds) :
139
+ exec_cache(sql, binds)
140
+
141
+ types = {}
142
+ fields = result.fields
143
+ fields.each_with_index do |fname, i|
144
+ ftype = result.ftype i
145
+ fmod = result.fmod i
146
+ types[fname] = get_oid_type(ftype, fmod, fname)
147
+ end
148
+
149
+ ret = ActiveRecord::Result.new(fields, result.values, types)
150
+ result.clear
151
+ return ret
152
+ end
153
+ end
154
+
155
+ def exec_delete(sql, name = 'SQL', binds = [])
156
+ log(sql, name, binds) do
157
+ result = without_prepared_statement?(binds) ? exec_no_cache(sql, binds) :
158
+ exec_cache(sql, binds)
159
+ affected = result.cmd_tuples
160
+ result.clear
161
+ affected
162
+ end
163
+ end
164
+ alias :exec_update :exec_delete
165
+
166
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
167
+ unless pk
168
+ # Extract the table from the insert sql. Yuck.
169
+ table_ref = extract_table_ref_from_insert_sql(sql)
170
+ pk = primary_key(table_ref) if table_ref
171
+ end
172
+
173
+ if pk && use_insert_returning?
174
+ sql = "#{sql} RETURNING #{quote_column_name(pk)}"
175
+ end
176
+
177
+ [sql, binds]
178
+ end
179
+
180
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
181
+ val = exec_query(sql, name, binds)
182
+ if !use_insert_returning? && pk
183
+ unless sequence_name
184
+ table_ref = extract_table_ref_from_insert_sql(sql)
185
+ sequence_name = default_sequence_name(table_ref, pk)
186
+ return val unless sequence_name
187
+ end
188
+ last_insert_id_result(sequence_name)
189
+ else
190
+ val
191
+ end
192
+ end
193
+
194
+ # Executes an UPDATE query and returns the number of affected tuples.
195
+ def update_sql(sql, name = nil)
196
+ super.cmd_tuples
197
+ end
198
+
199
+ # Begins a transaction.
200
+ def begin_db_transaction
201
+ execute "BEGIN"
202
+ end
203
+
204
+ def begin_isolated_db_transaction(isolation)
205
+ begin_db_transaction
206
+ execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
207
+ end
208
+
209
+ # Commits a transaction.
210
+ def commit_db_transaction
211
+ execute "COMMIT"
212
+ end
213
+
214
+ # Aborts a transaction.
215
+ def rollback_db_transaction
216
+ execute "ROLLBACK"
217
+ end
218
+
219
+ def outside_transaction?
220
+ message = "#outside_transaction? is deprecated. This method was only really used " \
221
+ "internally, but you can use #transaction_open? instead."
222
+ ActiveSupport::Deprecation.warn message
223
+ @connection.transaction_status == PGconn::PQTRANS_IDLE
224
+ end
225
+
226
+ def create_savepoint
227
+ execute("SAVEPOINT #{current_savepoint_name}")
228
+ end
229
+
230
+ def rollback_to_savepoint
231
+ execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
232
+ end
233
+
234
+ def release_savepoint
235
+ execute("RELEASE SAVEPOINT #{current_savepoint_name}")
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end