activerecord 4.2.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1372 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +218 -0
- data/examples/performance.rb +184 -0
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +173 -0
- data/lib/active_record/aggregations.rb +266 -0
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations.rb +1724 -0
- data/lib/active_record/associations/alias_tracker.rb +87 -0
- data/lib/active_record/associations/association.rb +253 -0
- data/lib/active_record/associations/association_scope.rb +194 -0
- data/lib/active_record/associations/belongs_to_association.rb +111 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
- data/lib/active_record/associations/builder/association.rb +149 -0
- data/lib/active_record/associations/builder/belongs_to.rb +116 -0
- data/lib/active_record/associations/builder/collection_association.rb +91 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
- data/lib/active_record/associations/builder/has_many.rb +15 -0
- data/lib/active_record/associations/builder/has_one.rb +23 -0
- data/lib/active_record/associations/builder/singular_association.rb +38 -0
- data/lib/active_record/associations/collection_association.rb +634 -0
- data/lib/active_record/associations/collection_proxy.rb +1027 -0
- data/lib/active_record/associations/has_many_association.rb +184 -0
- data/lib/active_record/associations/has_many_through_association.rb +238 -0
- data/lib/active_record/associations/has_one_association.rb +105 -0
- data/lib/active_record/associations/has_one_through_association.rb +36 -0
- data/lib/active_record/associations/join_dependency.rb +282 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/preloader.rb +203 -0
- data/lib/active_record/associations/preloader/association.rb +162 -0
- data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
- data/lib/active_record/associations/preloader/collection_association.rb +24 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
- data/lib/active_record/associations/preloader/has_one.rb +23 -0
- data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
- data/lib/active_record/associations/preloader/singular_association.rb +21 -0
- data/lib/active_record/associations/preloader/through_association.rb +96 -0
- data/lib/active_record/associations/singular_association.rb +86 -0
- data/lib/active_record/associations/through_association.rb +96 -0
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +212 -0
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +439 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
- data/lib/active_record/attribute_methods/dirty.rb +181 -0
- data/lib/active_record/attribute_methods/primary_key.rb +128 -0
- data/lib/active_record/attribute_methods/query.rb +40 -0
- data/lib/active_record/attribute_methods/read.rb +103 -0
- data/lib/active_record/attribute_methods/serialization.rb +70 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
- data/lib/active_record/attribute_methods/write.rb +83 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +439 -0
- data/lib/active_record/base.rb +317 -0
- data/lib/active_record/callbacks.rb +313 -0
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +38 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
- data/lib/active_record/connection_adapters/column.rb +82 -0
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +566 -0
- data/lib/active_record/counter_cache.rb +175 -0
- data/lib/active_record/dynamic_matchers.rb +140 -0
- data/lib/active_record/enum.rb +198 -0
- data/lib/active_record/errors.rb +252 -0
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +1007 -0
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +247 -0
- data/lib/active_record/integration.rb +113 -0
- data/lib/active_record/locale/en.yml +47 -0
- data/lib/active_record/locking/optimistic.rb +204 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/log_subscriber.rb +75 -0
- data/lib/active_record/migration.rb +1051 -0
- data/lib/active_record/migration/command_recorder.rb +197 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +340 -0
- data/lib/active_record/nested_attributes.rb +548 -0
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +532 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/querying.rb +68 -0
- data/lib/active_record/railtie.rb +162 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +50 -0
- data/lib/active_record/railties/databases.rake +391 -0
- data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
- data/lib/active_record/readonly_attributes.rb +23 -0
- data/lib/active_record/reflection.rb +881 -0
- data/lib/active_record/relation.rb +681 -0
- data/lib/active_record/relation/batches.rb +138 -0
- data/lib/active_record/relation/calculations.rb +403 -0
- data/lib/active_record/relation/delegation.rb +140 -0
- data/lib/active_record/relation/finder_methods.rb +528 -0
- data/lib/active_record/relation/merger.rb +170 -0
- data/lib/active_record/relation/predicate_builder.rb +126 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +1176 -0
- data/lib/active_record/relation/spawn_methods.rb +75 -0
- data/lib/active_record/result.rb +131 -0
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +191 -0
- data/lib/active_record/schema.rb +64 -0
- data/lib/active_record/schema_dumper.rb +251 -0
- data/lib/active_record/schema_migration.rb +56 -0
- data/lib/active_record/scoping.rb +87 -0
- data/lib/active_record/scoping/default.rb +134 -0
- data/lib/active_record/scoping/named.rb +164 -0
- data/lib/active_record/serialization.rb +22 -0
- data/lib/active_record/serializers/xml_serializer.rb +193 -0
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +205 -0
- data/lib/active_record/tasks/database_tasks.rb +296 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +121 -0
- data/lib/active_record/transactions.rb +417 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +56 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/validations.rb +90 -0
- data/lib/active_record/validations/associated.rb +51 -0
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +229 -0
- data/lib/active_record/version.rb +8 -0
- data/lib/rails/generators/active_record.rb +17 -0
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
- metadata +309 -0
@@ -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 rollback_db_transaction
|
227
|
+
execute "ROLLBACK"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'active_record/connection_adapters/postgresql/oid/infinity'
|
2
|
+
|
3
|
+
require 'active_record/connection_adapters/postgresql/oid/array'
|
4
|
+
require 'active_record/connection_adapters/postgresql/oid/bit'
|
5
|
+
require 'active_record/connection_adapters/postgresql/oid/bit_varying'
|
6
|
+
require 'active_record/connection_adapters/postgresql/oid/bytea'
|
7
|
+
require 'active_record/connection_adapters/postgresql/oid/cidr'
|
8
|
+
require 'active_record/connection_adapters/postgresql/oid/date'
|
9
|
+
require 'active_record/connection_adapters/postgresql/oid/date_time'
|
10
|
+
require 'active_record/connection_adapters/postgresql/oid/decimal'
|
11
|
+
require 'active_record/connection_adapters/postgresql/oid/enum'
|
12
|
+
require 'active_record/connection_adapters/postgresql/oid/float'
|
13
|
+
require 'active_record/connection_adapters/postgresql/oid/hstore'
|
14
|
+
require 'active_record/connection_adapters/postgresql/oid/inet'
|
15
|
+
require 'active_record/connection_adapters/postgresql/oid/integer'
|
16
|
+
require 'active_record/connection_adapters/postgresql/oid/json'
|
17
|
+
require 'active_record/connection_adapters/postgresql/oid/jsonb'
|
18
|
+
require 'active_record/connection_adapters/postgresql/oid/money'
|
19
|
+
require 'active_record/connection_adapters/postgresql/oid/point'
|
20
|
+
require 'active_record/connection_adapters/postgresql/oid/range'
|
21
|
+
require 'active_record/connection_adapters/postgresql/oid/specialized_string'
|
22
|
+
require 'active_record/connection_adapters/postgresql/oid/time'
|
23
|
+
require 'active_record/connection_adapters/postgresql/oid/uuid'
|
24
|
+
require 'active_record/connection_adapters/postgresql/oid/vector'
|
25
|
+
require 'active_record/connection_adapters/postgresql/oid/xml'
|
26
|
+
|
27
|
+
require 'active_record/connection_adapters/postgresql/oid/type_map_initializer'
|
28
|
+
|
29
|
+
module ActiveRecord
|
30
|
+
module ConnectionAdapters
|
31
|
+
module PostgreSQL
|
32
|
+
module OID # :nodoc:
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,99 @@
|
|
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, 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
|
+
else value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# See http://www.postgresql.org/docs/9.2/static/arrays.html#ARRAYS-IO
|
88
|
+
# for a list of all cases in which strings will be quoted.
|
89
|
+
def string_requires_quoting?(string)
|
90
|
+
string.empty? ||
|
91
|
+
string == "NULL" ||
|
92
|
+
string =~ /[\{\}"\\\s]/ ||
|
93
|
+
string.include?(delimiter)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|