activerecord 3.1.10 → 4.2.11

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.

Potentially problematic release.


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

Files changed (237) hide show
  1. checksums.yaml +6 -6
  2. data/CHANGELOG.md +1837 -338
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +39 -43
  5. data/examples/performance.rb +51 -20
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +57 -43
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -39
  10. data/lib/active_record/associations/association.rb +71 -85
  11. data/lib/active_record/associations/association_scope.rb +138 -89
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
  14. data/lib/active_record/associations/builder/association.rb +125 -29
  15. data/lib/active_record/associations/builder/belongs_to.rb +91 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +69 -49
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
  18. data/lib/active_record/associations/builder/has_many.rb +8 -64
  19. data/lib/active_record/associations/builder/has_one.rb +12 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +22 -29
  21. data/lib/active_record/associations/collection_association.rb +294 -187
  22. data/lib/active_record/associations/collection_proxy.rb +961 -94
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +118 -23
  25. data/lib/active_record/associations/has_many_through_association.rb +115 -45
  26. data/lib/active_record/associations/has_one_association.rb +57 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +76 -102
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +230 -156
  32. data/lib/active_record/associations/preloader/association.rb +96 -55
  33. data/lib/active_record/associations/preloader/collection_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +61 -32
  38. data/lib/active_record/associations/preloader.rb +113 -87
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +37 -19
  41. data/lib/active_record/associations.rb +505 -371
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +212 -0
  44. data/lib/active_record/attribute_decorators.rb +66 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  46. data/lib/active_record/attribute_methods/dirty.rb +141 -51
  47. data/lib/active_record/attribute_methods/primary_key.rb +87 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +74 -117
  50. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
  52. data/lib/active_record/attribute_methods/write.rb +60 -21
  53. data/lib/active_record/attribute_methods.rb +409 -48
  54. data/lib/active_record/attribute_set/builder.rb +106 -0
  55. data/lib/active_record/attribute_set.rb +81 -0
  56. data/lib/active_record/attributes.rb +147 -0
  57. data/lib/active_record/autosave_association.rb +279 -232
  58. data/lib/active_record/base.rb +84 -1969
  59. data/lib/active_record/callbacks.rb +66 -28
  60. data/lib/active_record/coders/json.rb +13 -0
  61. data/lib/active_record/coders/yaml_column.rb +18 -21
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +422 -243
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +273 -170
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
  75. data/lib/active_record/connection_adapters/column.rb +33 -221
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
  79. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  80. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  81. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
  112. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  113. data/lib/active_record/connection_adapters/postgresql_adapter.rb +445 -902
  114. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
  116. data/lib/active_record/connection_handling.rb +132 -0
  117. data/lib/active_record/core.rb +579 -0
  118. data/lib/active_record/counter_cache.rb +159 -102
  119. data/lib/active_record/dynamic_matchers.rb +140 -0
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +102 -34
  122. data/lib/active_record/explain.rb +38 -0
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +29 -0
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +318 -260
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +247 -0
  129. data/lib/active_record/integration.rb +113 -0
  130. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  131. data/lib/active_record/locale/en.yml +8 -1
  132. data/lib/active_record/locking/optimistic.rb +80 -52
  133. data/lib/active_record/locking/pessimistic.rb +27 -5
  134. data/lib/active_record/log_subscriber.rb +25 -18
  135. data/lib/active_record/migration/command_recorder.rb +130 -38
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +532 -201
  138. data/lib/active_record/model_schema.rb +342 -0
  139. data/lib/active_record/nested_attributes.rb +229 -139
  140. data/lib/active_record/no_touching.rb +52 -0
  141. data/lib/active_record/null_relation.rb +81 -0
  142. data/lib/active_record/persistence.rb +304 -99
  143. data/lib/active_record/query_cache.rb +25 -43
  144. data/lib/active_record/querying.rb +68 -0
  145. data/lib/active_record/railtie.rb +86 -45
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +7 -4
  148. data/lib/active_record/railties/databases.rake +198 -377
  149. data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
  150. data/lib/active_record/readonly_attributes.rb +23 -0
  151. data/lib/active_record/reflection.rb +516 -165
  152. data/lib/active_record/relation/batches.rb +96 -45
  153. data/lib/active_record/relation/calculations.rb +221 -144
  154. data/lib/active_record/relation/delegation.rb +140 -0
  155. data/lib/active_record/relation/finder_methods.rb +362 -243
  156. data/lib/active_record/relation/merger.rb +193 -0
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  159. data/lib/active_record/relation/predicate_builder.rb +135 -41
  160. data/lib/active_record/relation/query_methods.rb +982 -155
  161. data/lib/active_record/relation/spawn_methods.rb +50 -110
  162. data/lib/active_record/relation.rb +371 -180
  163. data/lib/active_record/result.rb +109 -12
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +191 -0
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +111 -61
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +135 -0
  170. data/lib/active_record/scoping/named.rb +164 -0
  171. data/lib/active_record/scoping.rb +87 -0
  172. data/lib/active_record/serialization.rb +7 -45
  173. data/lib/active_record/serializers/xml_serializer.rb +14 -65
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +205 -0
  176. data/lib/active_record/tasks/database_tasks.rb +299 -0
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  180. data/lib/active_record/timestamp.rb +35 -14
  181. data/lib/active_record/transactions.rb +141 -74
  182. data/lib/active_record/translation.rb +22 -0
  183. data/lib/active_record/type/big_integer.rb +13 -0
  184. data/lib/active_record/type/binary.rb +50 -0
  185. data/lib/active_record/type/boolean.rb +31 -0
  186. data/lib/active_record/type/date.rb +50 -0
  187. data/lib/active_record/type/date_time.rb +54 -0
  188. data/lib/active_record/type/decimal.rb +64 -0
  189. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  190. data/lib/active_record/type/decorator.rb +14 -0
  191. data/lib/active_record/type/float.rb +19 -0
  192. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  193. data/lib/active_record/type/integer.rb +59 -0
  194. data/lib/active_record/type/mutable.rb +16 -0
  195. data/lib/active_record/type/numeric.rb +36 -0
  196. data/lib/active_record/type/serialized.rb +62 -0
  197. data/lib/active_record/type/string.rb +40 -0
  198. data/lib/active_record/type/text.rb +11 -0
  199. data/lib/active_record/type/time.rb +26 -0
  200. data/lib/active_record/type/time_value.rb +38 -0
  201. data/lib/active_record/type/type_map.rb +64 -0
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type/value.rb +110 -0
  204. data/lib/active_record/type.rb +23 -0
  205. data/lib/active_record/validations/associated.rb +27 -18
  206. data/lib/active_record/validations/presence.rb +67 -0
  207. data/lib/active_record/validations/uniqueness.rb +125 -66
  208. data/lib/active_record/validations.rb +37 -30
  209. data/lib/active_record/version.rb +5 -7
  210. data/lib/active_record.rb +80 -25
  211. data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
  212. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  213. data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
  214. data/lib/rails/generators/active_record/migration.rb +11 -8
  215. data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
  216. data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
  217. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  218. data/lib/rails/generators/active_record.rb +3 -11
  219. metadata +132 -53
  220. data/examples/associations.png +0 -0
  221. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
  222. data/lib/active_record/associations/join_helper.rb +0 -55
  223. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -135
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
  226. data/lib/active_record/dynamic_finder_match.rb +0 -56
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/identity_map.rb +0 -163
  229. data/lib/active_record/named_scope.rb +0 -200
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -358
  232. data/lib/active_record/test_case.rb +0 -69
  233. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
  234. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  235. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  236. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  237. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -0,0 +1,93 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module ArrayParser # :nodoc:
5
+
6
+ DOUBLE_QUOTE = '"'
7
+ BACKSLASH = "\\"
8
+ COMMA = ','
9
+ BRACKET_OPEN = '{'
10
+ BRACKET_CLOSE = '}'
11
+
12
+ def parse_pg_array(string) # :nodoc:
13
+ local_index = 0
14
+ array = []
15
+ while(local_index < string.length)
16
+ case string[local_index]
17
+ when BRACKET_OPEN
18
+ local_index,array = parse_array_contents(array, string, local_index + 1)
19
+ when BRACKET_CLOSE
20
+ return array
21
+ end
22
+ local_index += 1
23
+ end
24
+
25
+ array
26
+ end
27
+
28
+ private
29
+
30
+ def parse_array_contents(array, string, index)
31
+ is_escaping = false
32
+ is_quoted = false
33
+ was_quoted = false
34
+ current_item = ''
35
+
36
+ local_index = index
37
+ while local_index
38
+ token = string[local_index]
39
+ if is_escaping
40
+ current_item << token
41
+ is_escaping = false
42
+ else
43
+ if is_quoted
44
+ case token
45
+ when DOUBLE_QUOTE
46
+ is_quoted = false
47
+ was_quoted = true
48
+ when BACKSLASH
49
+ is_escaping = true
50
+ else
51
+ current_item << token
52
+ end
53
+ else
54
+ case token
55
+ when BACKSLASH
56
+ is_escaping = true
57
+ when COMMA
58
+ add_item_to_array(array, current_item, was_quoted)
59
+ current_item = ''
60
+ was_quoted = false
61
+ when DOUBLE_QUOTE
62
+ is_quoted = true
63
+ when BRACKET_OPEN
64
+ internal_items = []
65
+ local_index,internal_items = parse_array_contents(internal_items, string, local_index + 1)
66
+ array.push(internal_items)
67
+ when BRACKET_CLOSE
68
+ add_item_to_array(array, current_item, was_quoted)
69
+ return local_index,array
70
+ else
71
+ current_item << token
72
+ end
73
+ end
74
+ end
75
+
76
+ local_index += 1
77
+ end
78
+ return local_index,array
79
+ end
80
+
81
+ def add_item_to_array(array, current_item, quoted)
82
+ return if !quoted && current_item.length == 0
83
+
84
+ if !quoted && current_item == 'NULL'
85
+ array.push nil
86
+ else
87
+ array.push current_item
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ # PostgreSQL-specific extensions to column definitions in a table.
4
+ class PostgreSQLColumn < Column #:nodoc:
5
+ attr_accessor :array
6
+
7
+ def initialize(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
8
+ if sql_type =~ /\[\]$/
9
+ @array = true
10
+ super(name, default, cast_type, sql_type[0..sql_type.length - 3], null)
11
+ else
12
+ @array = false
13
+ super(name, default, cast_type, sql_type, null)
14
+ end
15
+
16
+ @default_function = default_function
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,232 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
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
+ def select_value(arel, name = nil, binds = [])
48
+ arel, binds = binds_from_relation arel, binds
49
+ sql = to_sql(arel, binds)
50
+ execute_and_clear(sql, name, binds) do |result|
51
+ result.getvalue(0, 0) if result.ntuples > 0 && result.nfields > 0
52
+ end
53
+ end
54
+
55
+ def select_values(arel, name = nil)
56
+ arel, binds = binds_from_relation arel, []
57
+ sql = to_sql(arel, binds)
58
+ execute_and_clear(sql, name, binds) do |result|
59
+ if result.nfields > 0
60
+ result.column_values(0)
61
+ else
62
+ []
63
+ end
64
+ end
65
+ end
66
+
67
+ # Executes a SELECT query and returns an array of rows. Each row is an
68
+ # array of field values.
69
+ def select_rows(sql, name = nil, binds = [])
70
+ execute_and_clear(sql, name, binds) do |result|
71
+ result.values
72
+ end
73
+ end
74
+
75
+ # Executes an INSERT query and returns the new record's ID
76
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
77
+ unless pk
78
+ # Extract the table from the insert sql. Yuck.
79
+ table_ref = extract_table_ref_from_insert_sql(sql)
80
+ pk = primary_key(table_ref) if table_ref
81
+ end
82
+
83
+ if pk && use_insert_returning?
84
+ select_value("#{sql} RETURNING #{quote_column_name(pk)}")
85
+ elsif pk
86
+ super
87
+ last_insert_id_value(sequence_name || default_sequence_name(table_ref, pk))
88
+ else
89
+ super
90
+ end
91
+ end
92
+
93
+ def create
94
+ super.insert
95
+ end
96
+
97
+ # The internal PostgreSQL identifier of the money data type.
98
+ MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
99
+ # The internal PostgreSQL identifier of the BYTEA data type.
100
+ BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
101
+
102
+ # create a 2D array representing the result set
103
+ def result_as_array(res) #:nodoc:
104
+ # check if we have any binary column and if they need escaping
105
+ ftypes = Array.new(res.nfields) do |i|
106
+ [i, res.ftype(i)]
107
+ end
108
+
109
+ rows = res.values
110
+ return rows unless ftypes.any? { |_, x|
111
+ x == BYTEA_COLUMN_TYPE_OID || x == MONEY_COLUMN_TYPE_OID
112
+ }
113
+
114
+ typehash = ftypes.group_by { |_, type| type }
115
+ binaries = typehash[BYTEA_COLUMN_TYPE_OID] || []
116
+ monies = typehash[MONEY_COLUMN_TYPE_OID] || []
117
+
118
+ rows.each do |row|
119
+ # unescape string passed BYTEA field (OID == 17)
120
+ binaries.each do |index, _|
121
+ row[index] = unescape_bytea(row[index])
122
+ end
123
+
124
+ # If this is a money type column and there are any currency symbols,
125
+ # then strip them off. Indeed it would be prettier to do this in
126
+ # PostgreSQLColumn.string_to_decimal but would break form input
127
+ # fields that call value_before_type_cast.
128
+ monies.each do |index, _|
129
+ data = row[index]
130
+ # Because money output is formatted according to the locale, there are two
131
+ # cases to consider (note the decimal separators):
132
+ # (1) $12,345,678.12
133
+ # (2) $12.345.678,12
134
+ case data
135
+ when /^-?\D+[\d,]+\.\d{2}$/ # (1)
136
+ data.gsub!(/[^-\d.]/, '')
137
+ when /^-?\D+[\d.]+,\d{2}$/ # (2)
138
+ data.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ # Queries the database and returns the results in an Array-like object
145
+ def query(sql, name = nil) #:nodoc:
146
+ log(sql, name) do
147
+ result_as_array @connection.async_exec(sql)
148
+ end
149
+ end
150
+
151
+ # Executes an SQL statement, returning a PGresult object on success
152
+ # or raising a PGError exception otherwise.
153
+ def execute(sql, name = nil)
154
+ log(sql, name) do
155
+ @connection.async_exec(sql)
156
+ end
157
+ end
158
+
159
+ def exec_query(sql, name = 'SQL', binds = [])
160
+ execute_and_clear(sql, name, binds) do |result|
161
+ types = {}
162
+ fields = result.fields
163
+ fields.each_with_index do |fname, i|
164
+ ftype = result.ftype i
165
+ fmod = result.fmod i
166
+ types[fname] = get_oid_type(ftype, fmod, fname)
167
+ end
168
+ ActiveRecord::Result.new(fields, result.values, types)
169
+ end
170
+ end
171
+
172
+ def exec_delete(sql, name = 'SQL', binds = [])
173
+ execute_and_clear(sql, name, binds) {|result| result.cmd_tuples }
174
+ end
175
+ alias :exec_update :exec_delete
176
+
177
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
178
+ unless pk
179
+ # Extract the table from the insert sql. Yuck.
180
+ table_ref = extract_table_ref_from_insert_sql(sql)
181
+ pk = primary_key(table_ref) if table_ref
182
+ end
183
+
184
+ if pk && use_insert_returning?
185
+ sql = "#{sql} RETURNING #{quote_column_name(pk)}"
186
+ end
187
+
188
+ [sql, binds]
189
+ end
190
+
191
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
192
+ val = exec_query(sql, name, binds)
193
+ if !use_insert_returning? && pk
194
+ unless sequence_name
195
+ table_ref = extract_table_ref_from_insert_sql(sql)
196
+ sequence_name = default_sequence_name(table_ref, pk)
197
+ return val unless sequence_name
198
+ end
199
+ last_insert_id_result(sequence_name)
200
+ else
201
+ val
202
+ end
203
+ end
204
+
205
+ # Executes an UPDATE query and returns the number of affected tuples.
206
+ def update_sql(sql, name = nil)
207
+ super.cmd_tuples
208
+ end
209
+
210
+ # Begins a transaction.
211
+ def begin_db_transaction
212
+ execute "BEGIN"
213
+ end
214
+
215
+ def begin_isolated_db_transaction(isolation)
216
+ begin_db_transaction
217
+ execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
218
+ end
219
+
220
+ # Commits a transaction.
221
+ def commit_db_transaction
222
+ execute "COMMIT"
223
+ end
224
+
225
+ # Aborts a transaction.
226
+ def exec_rollback_db_transaction
227
+ execute "ROLLBACK"
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,100 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Array < Type::Value # :nodoc:
6
+ include Type::Mutable
7
+
8
+ # Loads pg_array_parser if available. String parsing can be
9
+ # performed quicker by a native extension, which will not create
10
+ # a large amount of Ruby objects that will need to be garbage
11
+ # collected. pg_array_parser has a C and Java extension
12
+ begin
13
+ require 'pg_array_parser'
14
+ include PgArrayParser
15
+ rescue LoadError
16
+ require 'active_record/connection_adapters/postgresql/array_parser'
17
+ include PostgreSQL::ArrayParser
18
+ end
19
+
20
+ attr_reader :subtype, :delimiter
21
+ delegate :type, :limit, to: :subtype
22
+
23
+ def initialize(subtype, delimiter = ',')
24
+ @subtype = subtype
25
+ @delimiter = delimiter
26
+ end
27
+
28
+ def type_cast_from_database(value)
29
+ if value.is_a?(::String)
30
+ type_cast_array(parse_pg_array(value), :type_cast_from_database)
31
+ else
32
+ super
33
+ end
34
+ end
35
+
36
+ def type_cast_from_user(value)
37
+ if value.is_a?(::String)
38
+ value = parse_pg_array(value)
39
+ end
40
+ type_cast_array(value, :type_cast_from_user)
41
+ end
42
+
43
+ def type_cast_for_database(value)
44
+ if value.is_a?(::Array)
45
+ cast_value_for_database(value)
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def type_cast_array(value, method)
54
+ if value.is_a?(::Array)
55
+ value.map { |item| type_cast_array(item, method) }
56
+ else
57
+ @subtype.public_send(method, value)
58
+ end
59
+ end
60
+
61
+ def cast_value_for_database(value)
62
+ if value.is_a?(::Array)
63
+ casted_values = value.map { |item| cast_value_for_database(item) }
64
+ "{#{casted_values.join(delimiter)}}"
65
+ else
66
+ quote_and_escape(subtype.type_cast_for_database(value))
67
+ end
68
+ end
69
+
70
+ ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays
71
+
72
+ def quote_and_escape(value)
73
+ case value
74
+ when ::String
75
+ if string_requires_quoting?(value)
76
+ value = value.gsub(/\\/, ARRAY_ESCAPE)
77
+ value.gsub!(/"/,"\\\"")
78
+ %("#{value}")
79
+ else
80
+ value
81
+ end
82
+ when nil then "NULL"
83
+ when ::Date, ::DateTime, ::Time then subtype.type_cast_for_schema(value)
84
+ else value
85
+ end
86
+ end
87
+
88
+ # See http://www.postgresql.org/docs/9.2/static/arrays.html#ARRAYS-IO
89
+ # for a list of all cases in which strings will be quoted.
90
+ def string_requires_quoting?(string)
91
+ string.empty? ||
92
+ string == "NULL" ||
93
+ string =~ /[\{\}"\\\s]/ ||
94
+ string.include?(delimiter)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,52 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Bit < Type::Value # :nodoc:
6
+ def type
7
+ :bit
8
+ end
9
+
10
+ def type_cast(value)
11
+ if ::String === value
12
+ case value
13
+ when /^0x/i
14
+ value[2..-1].hex.to_s(2) # Hexadecimal notation
15
+ else
16
+ value # Bit-string notation
17
+ end
18
+ else
19
+ value
20
+ end
21
+ end
22
+
23
+ def type_cast_for_database(value)
24
+ Data.new(super) if value
25
+ end
26
+
27
+ class Data
28
+ def initialize(value)
29
+ @value = value
30
+ end
31
+
32
+ def to_s
33
+ value
34
+ end
35
+
36
+ def binary?
37
+ /\A[01]*\Z/ === value
38
+ end
39
+
40
+ def hex?
41
+ /\A[0-9A-F]*\Z/i === value
42
+ end
43
+
44
+ protected
45
+
46
+ attr_reader :value
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class BitVarying < OID::Bit # :nodoc:
6
+ def type
7
+ :bit_varying
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Bytea < Type::Binary # :nodoc:
6
+ def type_cast_from_database(value)
7
+ return if value.nil?
8
+ return value.to_s if value.is_a?(Type::Binary::Data)
9
+ PGconn.unescape_bytea(super)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Cidr < Type::Value # :nodoc:
6
+ def type
7
+ :cidr
8
+ end
9
+
10
+ def type_cast_for_schema(value)
11
+ subnet_mask = value.instance_variable_get(:@mask_addr)
12
+
13
+ # If the subnet mask is equal to /32, don't output it
14
+ if subnet_mask == (2**32 - 1)
15
+ "\"#{value}\""
16
+ else
17
+ "\"#{value}/#{subnet_mask.to_s(2).count('1')}\""
18
+ end
19
+ end
20
+
21
+ def type_cast_for_database(value)
22
+ if IPAddr === value
23
+ "#{value}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
24
+ else
25
+ value
26
+ end
27
+ end
28
+
29
+ def cast_value(value)
30
+ if value.nil?
31
+ nil
32
+ elsif String === value
33
+ begin
34
+ IPAddr.new(value)
35
+ rescue ArgumentError
36
+ nil
37
+ end
38
+ else
39
+ value
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Date < Type::Date # :nodoc:
6
+ include Infinity
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,36 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class DateTime < Type::DateTime # :nodoc:
6
+ include Infinity
7
+
8
+ def type_cast_for_database(value)
9
+ if has_precision? && value.acts_like?(:time) && value.year <= 0
10
+ bce_year = format("%04d", -value.year + 1)
11
+ super.sub(/^-?\d+/, bce_year) + " BC"
12
+ else
13
+ super
14
+ end
15
+ end
16
+
17
+ def cast_value(value)
18
+ if value.is_a?(::String)
19
+ case value
20
+ when 'infinity' then ::Float::INFINITY
21
+ when '-infinity' then -::Float::INFINITY
22
+ when / BC$/
23
+ astronomical_year = format("%04d", -value[/^\d+/].to_i + 1)
24
+ super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
25
+ else
26
+ super
27
+ end
28
+ else
29
+ value
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Decimal < Type::Decimal # :nodoc:
6
+ def infinity(options = {})
7
+ BigDecimal.new("Infinity") * (options[:negative] ? -1 : 1)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Enum < Type::Value # :nodoc:
6
+ def type
7
+ :enum
8
+ end
9
+
10
+ private
11
+
12
+ def cast_value(value)
13
+ value.to_s
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID # :nodoc:
5
+ class Float < Type::Float # :nodoc:
6
+ include Infinity
7
+
8
+ def cast_value(value)
9
+ case value
10
+ when ::Float then value
11
+ when 'Infinity' then ::Float::INFINITY
12
+ when '-Infinity' then -::Float::INFINITY
13
+ when 'NaN' then ::Float::NAN
14
+ else value.to_f
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end