activerecord 1.0.0 → 4.0.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.

Files changed (255) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +213 -0
  5. data/examples/performance.rb +172 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +180 -84
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +248 -0
  10. data/lib/active_record/associations/association_scope.rb +135 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +92 -0
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +35 -0
  13. data/lib/active_record/associations/builder/association.rb +108 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  17. data/lib/active_record/associations/builder/has_many.rb +15 -0
  18. data/lib/active_record/associations/builder/has_one.rb +25 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +608 -0
  21. data/lib/active_record/associations/collection_proxy.rb +986 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +58 -39
  23. data/lib/active_record/associations/has_many_association.rb +116 -85
  24. data/lib/active_record/associations/has_many_through_association.rb +197 -0
  25. data/lib/active_record/associations/has_one_association.rb +102 -0
  26. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +235 -0
  31. data/lib/active_record/associations/join_helper.rb +45 -0
  32. data/lib/active_record/associations/preloader/association.rb +121 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  42. data/lib/active_record/associations/preloader.rb +178 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +1437 -431
  46. data/lib/active_record/attribute_assignment.rb +201 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +70 -0
  48. data/lib/active_record/attribute_methods/dirty.rb +118 -0
  49. data/lib/active_record/attribute_methods/primary_key.rb +122 -0
  50. data/lib/active_record/attribute_methods/query.rb +40 -0
  51. data/lib/active_record/attribute_methods/read.rb +107 -0
  52. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  53. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -0
  54. data/lib/active_record/attribute_methods/write.rb +63 -0
  55. data/lib/active_record/attribute_methods.rb +393 -0
  56. data/lib/active_record/autosave_association.rb +426 -0
  57. data/lib/active_record/base.rb +268 -930
  58. data/lib/active_record/callbacks.rb +203 -230
  59. data/lib/active_record/coders/yaml_column.rb +38 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +638 -0
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +390 -0
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +129 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +501 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +873 -0
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +389 -275
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  71. data/lib/active_record/connection_adapters/column.rb +318 -0
  72. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +517 -90
  75. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  76. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  77. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  79. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  80. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  81. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  82. data/lib/active_record/connection_adapters/postgresql_adapter.rb +911 -138
  83. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  84. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +624 -0
  85. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  86. data/lib/active_record/connection_handling.rb +98 -0
  87. data/lib/active_record/core.rb +463 -0
  88. data/lib/active_record/counter_cache.rb +122 -0
  89. data/lib/active_record/dynamic_matchers.rb +131 -0
  90. data/lib/active_record/errors.rb +213 -0
  91. data/lib/active_record/explain.rb +38 -0
  92. data/lib/active_record/explain_registry.rb +30 -0
  93. data/lib/active_record/explain_subscriber.rb +29 -0
  94. data/lib/active_record/fixture_set/file.rb +55 -0
  95. data/lib/active_record/fixtures.rb +892 -138
  96. data/lib/active_record/inheritance.rb +200 -0
  97. data/lib/active_record/integration.rb +60 -0
  98. data/lib/active_record/locale/en.yml +47 -0
  99. data/lib/active_record/locking/optimistic.rb +181 -0
  100. data/lib/active_record/locking/pessimistic.rb +77 -0
  101. data/lib/active_record/log_subscriber.rb +82 -0
  102. data/lib/active_record/migration/command_recorder.rb +164 -0
  103. data/lib/active_record/migration/join_table.rb +15 -0
  104. data/lib/active_record/migration.rb +1015 -0
  105. data/lib/active_record/model_schema.rb +345 -0
  106. data/lib/active_record/nested_attributes.rb +546 -0
  107. data/lib/active_record/null_relation.rb +65 -0
  108. data/lib/active_record/persistence.rb +509 -0
  109. data/lib/active_record/query_cache.rb +56 -0
  110. data/lib/active_record/querying.rb +62 -0
  111. data/lib/active_record/railtie.rb +205 -0
  112. data/lib/active_record/railties/console_sandbox.rb +5 -0
  113. data/lib/active_record/railties/controller_runtime.rb +50 -0
  114. data/lib/active_record/railties/databases.rake +402 -0
  115. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  116. data/lib/active_record/readonly_attributes.rb +30 -0
  117. data/lib/active_record/reflection.rb +544 -87
  118. data/lib/active_record/relation/batches.rb +93 -0
  119. data/lib/active_record/relation/calculations.rb +399 -0
  120. data/lib/active_record/relation/delegation.rb +125 -0
  121. data/lib/active_record/relation/finder_methods.rb +349 -0
  122. data/lib/active_record/relation/merger.rb +161 -0
  123. data/lib/active_record/relation/predicate_builder.rb +106 -0
  124. data/lib/active_record/relation/query_methods.rb +1044 -0
  125. data/lib/active_record/relation/spawn_methods.rb +73 -0
  126. data/lib/active_record/relation.rb +655 -0
  127. data/lib/active_record/result.rb +67 -0
  128. data/lib/active_record/runtime_registry.rb +17 -0
  129. data/lib/active_record/sanitization.rb +168 -0
  130. data/lib/active_record/schema.rb +65 -0
  131. data/lib/active_record/schema_dumper.rb +204 -0
  132. data/lib/active_record/schema_migration.rb +39 -0
  133. data/lib/active_record/scoping/default.rb +146 -0
  134. data/lib/active_record/scoping/named.rb +175 -0
  135. data/lib/active_record/scoping.rb +82 -0
  136. data/lib/active_record/serialization.rb +22 -0
  137. data/lib/active_record/serializers/xml_serializer.rb +197 -0
  138. data/lib/active_record/statement_cache.rb +26 -0
  139. data/lib/active_record/store.rb +156 -0
  140. data/lib/active_record/tasks/database_tasks.rb +203 -0
  141. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  142. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  143. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  146. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  147. data/lib/active_record/test_case.rb +96 -0
  148. data/lib/active_record/timestamp.rb +119 -0
  149. data/lib/active_record/transactions.rb +366 -69
  150. data/lib/active_record/translation.rb +22 -0
  151. data/lib/active_record/validations/associated.rb +49 -0
  152. data/lib/active_record/validations/presence.rb +65 -0
  153. data/lib/active_record/validations/uniqueness.rb +225 -0
  154. data/lib/active_record/validations.rb +64 -185
  155. data/lib/active_record/version.rb +11 -0
  156. data/lib/active_record.rb +149 -24
  157. data/lib/rails/generators/active_record/migration/migration_generator.rb +62 -0
  158. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  159. data/lib/rails/generators/active_record/migration/templates/migration.rb +39 -0
  160. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  161. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  162. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  163. data/lib/rails/generators/active_record.rb +23 -0
  164. metadata +261 -161
  165. data/CHANGELOG +0 -581
  166. data/README +0 -361
  167. data/RUNNING_UNIT_TESTS +0 -36
  168. data/dev-utils/eval_debugger.rb +0 -9
  169. data/examples/associations.png +0 -0
  170. data/examples/associations.rb +0 -87
  171. data/examples/shared_setup.rb +0 -15
  172. data/examples/validation.rb +0 -88
  173. data/install.rb +0 -60
  174. data/lib/active_record/associations/association_collection.rb +0 -70
  175. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -107
  176. data/lib/active_record/deprecated_associations.rb +0 -70
  177. data/lib/active_record/observer.rb +0 -71
  178. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  179. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  180. data/lib/active_record/support/clean_logger.rb +0 -10
  181. data/lib/active_record/support/inflector.rb +0 -70
  182. data/lib/active_record/vendor/mysql.rb +0 -1117
  183. data/lib/active_record/vendor/simple.rb +0 -702
  184. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  185. data/lib/active_record/wrappings.rb +0 -59
  186. data/rakefile +0 -122
  187. data/test/abstract_unit.rb +0 -16
  188. data/test/aggregations_test.rb +0 -34
  189. data/test/all.sh +0 -8
  190. data/test/associations_test.rb +0 -477
  191. data/test/base_test.rb +0 -513
  192. data/test/class_inheritable_attributes_test.rb +0 -33
  193. data/test/connections/native_mysql/connection.rb +0 -24
  194. data/test/connections/native_postgresql/connection.rb +0 -24
  195. data/test/connections/native_sqlite/connection.rb +0 -24
  196. data/test/deprecated_associations_test.rb +0 -336
  197. data/test/finder_test.rb +0 -67
  198. data/test/fixtures/accounts/signals37 +0 -3
  199. data/test/fixtures/accounts/unknown +0 -2
  200. data/test/fixtures/auto_id.rb +0 -4
  201. data/test/fixtures/column_name.rb +0 -3
  202. data/test/fixtures/companies/first_client +0 -6
  203. data/test/fixtures/companies/first_firm +0 -4
  204. data/test/fixtures/companies/second_client +0 -6
  205. data/test/fixtures/company.rb +0 -37
  206. data/test/fixtures/company_in_module.rb +0 -33
  207. data/test/fixtures/course.rb +0 -3
  208. data/test/fixtures/courses/java +0 -2
  209. data/test/fixtures/courses/ruby +0 -2
  210. data/test/fixtures/customer.rb +0 -30
  211. data/test/fixtures/customers/david +0 -6
  212. data/test/fixtures/db_definitions/mysql.sql +0 -96
  213. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  214. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  215. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  216. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  217. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  218. data/test/fixtures/default.rb +0 -2
  219. data/test/fixtures/developer.rb +0 -8
  220. data/test/fixtures/developers/david +0 -2
  221. data/test/fixtures/developers/jamis +0 -2
  222. data/test/fixtures/developers_projects/david_action_controller +0 -2
  223. data/test/fixtures/developers_projects/david_active_record +0 -2
  224. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  225. data/test/fixtures/entrant.rb +0 -3
  226. data/test/fixtures/entrants/first +0 -3
  227. data/test/fixtures/entrants/second +0 -3
  228. data/test/fixtures/entrants/third +0 -3
  229. data/test/fixtures/fixture_database.sqlite +0 -0
  230. data/test/fixtures/fixture_database_2.sqlite +0 -0
  231. data/test/fixtures/movie.rb +0 -5
  232. data/test/fixtures/movies/first +0 -2
  233. data/test/fixtures/movies/second +0 -2
  234. data/test/fixtures/project.rb +0 -3
  235. data/test/fixtures/projects/action_controller +0 -2
  236. data/test/fixtures/projects/active_record +0 -2
  237. data/test/fixtures/reply.rb +0 -21
  238. data/test/fixtures/subscriber.rb +0 -5
  239. data/test/fixtures/subscribers/first +0 -2
  240. data/test/fixtures/subscribers/second +0 -2
  241. data/test/fixtures/topic.rb +0 -20
  242. data/test/fixtures/topics/first +0 -9
  243. data/test/fixtures/topics/second +0 -8
  244. data/test/fixtures_test.rb +0 -20
  245. data/test/inflector_test.rb +0 -104
  246. data/test/inheritance_test.rb +0 -125
  247. data/test/lifecycle_test.rb +0 -110
  248. data/test/modules_test.rb +0 -21
  249. data/test/multiple_db_test.rb +0 -46
  250. data/test/pk_test.rb +0 -57
  251. data/test/reflection_test.rb +0 -78
  252. data/test/thread_safety_test.rb +0 -33
  253. data/test/transactions_test.rb +0 -83
  254. data/test/unconnected_test.rb +0 -24
  255. data/test/validations_test.rb +0 -126
@@ -1,177 +1,950 @@
1
+ require 'active_record/connection_adapters/abstract_adapter'
2
+ require 'active_record/connection_adapters/statement_pool'
3
+ require 'active_record/connection_adapters/postgresql/oid'
4
+ require 'active_record/connection_adapters/postgresql/cast'
5
+ require 'active_record/connection_adapters/postgresql/array_parser'
6
+ require 'active_record/connection_adapters/postgresql/quoting'
7
+ require 'active_record/connection_adapters/postgresql/schema_statements'
8
+ require 'active_record/connection_adapters/postgresql/database_statements'
9
+ require 'active_record/connection_adapters/postgresql/referential_integrity'
10
+ require 'arel/visitors/bind_visitor'
1
11
 
2
- # postgresql_adaptor.rb
3
- # author: Luke Holden <lholden@cablelan.net>
4
- # notes: Currently this adaptor does not pass the test_zero_date_fields
5
- # and test_zero_datetime_fields unit tests in the BasicsTest test
6
- # group.
7
- #
8
- # This is due to the fact that, in postgresql you can not have a
9
- # totally zero timestamp. Instead null/nil should be used to
10
- # represent no value.
11
- #
12
+ # Make sure we're using pg high enough for PGResult#values
13
+ gem 'pg', '~> 0.11'
14
+ require 'pg'
12
15
 
13
- require 'active_record/connection_adapters/abstract_adapter'
14
- require 'parsedate'
15
-
16
- begin
17
- # Only include the PostgreSQL driver if one hasn't already been loaded
18
- require 'postgres' unless self.class.const_defined?(:PGconn)
19
-
20
- module ActiveRecord
21
- class Base
22
- # Establishes a connection to the database that's used by all Active Record objects
23
- def self.postgresql_connection(config) # :nodoc:
24
- symbolize_strings_in_hash(config)
25
- host = config[:host]
26
- port = config[:port] || 5432 unless host.nil?
27
- username = config[:username] || ""
28
- password = config[:password] || ""
29
-
30
- if config.has_key?(:database)
31
- database = config[:database]
16
+ require 'ipaddr'
17
+
18
+ module ActiveRecord
19
+ module ConnectionHandling # :nodoc:
20
+ VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
21
+ :client_encoding, :options, :application_name, :fallback_application_name,
22
+ :keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
23
+ :tty, :sslmode, :requiressl, :sslcert, :sslkey, :sslrootcert, :sslcrl,
24
+ :requirepeer, :krbsrvname, :gsslib, :service]
25
+
26
+ # Establishes a connection to the database that's used by all Active Record objects
27
+ def postgresql_connection(config)
28
+ conn_params = config.symbolize_keys
29
+
30
+ conn_params.delete_if { |_, v| v.nil? }
31
+
32
+ # Map ActiveRecords param names to PGs.
33
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
34
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
35
+
36
+ # Forward only valid config params to PGconn.connect.
37
+ conn_params.keep_if { |k, _| VALID_CONN_PARAMS.include?(k) }
38
+
39
+ # The postgres drivers don't allow the creation of an unconnected PGconn object,
40
+ # so just pass a nil connection object for the time being.
41
+ ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
42
+ end
43
+ end
44
+
45
+ module ConnectionAdapters
46
+ # PostgreSQL-specific extensions to column definitions in a table.
47
+ class PostgreSQLColumn < Column #:nodoc:
48
+ attr_accessor :array
49
+ # Instantiates a new PostgreSQL column definition in a table.
50
+ def initialize(name, default, oid_type, sql_type = nil, null = true)
51
+ @oid_type = oid_type
52
+ if sql_type =~ /\[\]$/
53
+ @array = true
54
+ super(name, self.class.extract_value_from_default(default), sql_type[0..sql_type.length - 3], null)
32
55
  else
33
- raise ArgumentError, "No database specified. Missing argument: database."
56
+ @array = false
57
+ super(name, self.class.extract_value_from_default(default), sql_type, null)
34
58
  end
59
+ end
35
60
 
36
- ConnectionAdapters::PostgreSQLAdapter.new(
37
- PGconn.connect(host, port, "", "", database, username, password), logger
38
- )
61
+ # :stopdoc:
62
+ class << self
63
+ include ConnectionAdapters::PostgreSQLColumn::Cast
64
+ include ConnectionAdapters::PostgreSQLColumn::ArrayParser
65
+ attr_accessor :money_precision
66
+ end
67
+ # :startdoc:
68
+
69
+ # Extracts the value from a PostgreSQL column default definition.
70
+ def self.extract_value_from_default(default)
71
+ # This is a performance optimization for Ruby 1.9.2 in development.
72
+ # If the value is nil, we return nil straight away without checking
73
+ # the regular expressions. If we check each regular expression,
74
+ # Regexp#=== will call NilClass#to_str, which will trigger
75
+ # method_missing (defined by whiny nil in ActiveSupport) which
76
+ # makes this method very very slow.
77
+ return default unless default
78
+
79
+ case default
80
+ when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
81
+ $1
82
+ # Numeric types
83
+ when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
84
+ $1
85
+ # Character types
86
+ when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
87
+ $1
88
+ # Binary data types
89
+ when /\A'(.*)'::bytea\z/m
90
+ $1
91
+ # Date/time types
92
+ when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
93
+ $1
94
+ when /\A'(.*)'::interval\z/
95
+ $1
96
+ # Boolean type
97
+ when 'true'
98
+ true
99
+ when 'false'
100
+ false
101
+ # Geometric types
102
+ when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
103
+ $1
104
+ # Network address types
105
+ when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
106
+ $1
107
+ # Bit string types
108
+ when /\AB'(.*)'::"?bit(?: varying)?"?\z/
109
+ $1
110
+ # XML type
111
+ when /\A'(.*)'::xml\z/m
112
+ $1
113
+ # Arrays
114
+ when /\A'(.*)'::"?\D+"?\[\]\z/
115
+ $1
116
+ # Hstore
117
+ when /\A'(.*)'::hstore\z/
118
+ $1
119
+ # JSON
120
+ when /\A'(.*)'::json\z/
121
+ $1
122
+ # Object identifier types
123
+ when /\A-?\d+\z/
124
+ $1
125
+ else
126
+ # Anything else is blank, some user type, or some function
127
+ # and we can't know the value of that, so return nil.
128
+ nil
129
+ end
39
130
  end
40
- end
41
131
 
42
- module ConnectionAdapters
43
- class PostgreSQLAdapter < AbstractAdapter # :nodoc:
132
+ def type_cast(value)
133
+ return if value.nil?
134
+ return super if encoded?
44
135
 
45
- def select_all(sql, name = nil)
46
- select(sql, name)
136
+ @oid_type.type_cast value
137
+ end
138
+
139
+ private
140
+
141
+ def extract_limit(sql_type)
142
+ case sql_type
143
+ when /^bigint/i; 8
144
+ when /^smallint/i; 2
145
+ when /^timestamp/i; nil
146
+ else super
147
+ end
47
148
  end
48
149
 
49
- def select_one(sql, name = nil)
50
- result = select(sql, name)
51
- result.nil? ? nil : result.first
150
+ # Extracts the scale from PostgreSQL-specific data types.
151
+ def extract_scale(sql_type)
152
+ # Money type has a fixed scale of 2.
153
+ sql_type =~ /^money/ ? 2 : super
52
154
  end
53
155
 
54
- def columns(table_name, name = nil)
55
- table_structure(table_name).inject([]) do |columns, field|
56
- columns << Column.new(field[0], field[2], field[1])
57
- columns
156
+ # Extracts the precision from PostgreSQL-specific data types.
157
+ def extract_precision(sql_type)
158
+ if sql_type == 'money'
159
+ self.class.money_precision
160
+ elsif sql_type =~ /timestamp/i
161
+ $1.to_i if sql_type =~ /\((\d+)\)/
162
+ else
163
+ super
58
164
  end
59
165
  end
60
166
 
61
- def insert(sql, name = nil, pk = nil, id_value = nil)
62
- execute(sql, name = nil)
63
- table = sql.split(" ", 4)[2]
64
- return id_value || last_insert_id(table, pk)
167
+ # Maps PostgreSQL-specific data types to logical Rails types.
168
+ def simplified_type(field_type)
169
+ case field_type
170
+ # Numeric and monetary types
171
+ when /^(?:real|double precision)$/
172
+ :float
173
+ # Monetary types
174
+ when 'money'
175
+ :decimal
176
+ when 'hstore'
177
+ :hstore
178
+ when 'ltree'
179
+ :ltree
180
+ # Network address types
181
+ when 'inet'
182
+ :inet
183
+ when 'cidr'
184
+ :cidr
185
+ when 'macaddr'
186
+ :macaddr
187
+ # Character types
188
+ when /^(?:character varying|bpchar)(?:\(\d+\))?$/
189
+ :string
190
+ # Binary data types
191
+ when 'bytea'
192
+ :binary
193
+ # Date/time types
194
+ when /^timestamp with(?:out)? time zone$/
195
+ :datetime
196
+ when /^interval(?:|\(\d+\))$/
197
+ :string
198
+ # Geometric types
199
+ when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
200
+ :string
201
+ # Bit strings
202
+ when /^bit(?: varying)?(?:\(\d+\))?$/
203
+ :string
204
+ # XML type
205
+ when 'xml'
206
+ :xml
207
+ # tsvector type
208
+ when 'tsvector'
209
+ :tsvector
210
+ # Arrays
211
+ when /^\D+\[\]$/
212
+ :string
213
+ # Object identifier types
214
+ when 'oid'
215
+ :integer
216
+ # UUID type
217
+ when 'uuid'
218
+ :uuid
219
+ # JSON type
220
+ when 'json'
221
+ :json
222
+ # Small and big integer types
223
+ when /^(?:small|big)int$/
224
+ :integer
225
+ when /(num|date|tstz|ts|int4|int8)range$/
226
+ field_type.to_sym
227
+ # Pass through all types that are not specific to PostgreSQL.
228
+ else
229
+ super
230
+ end
231
+ end
232
+ end
233
+
234
+ # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
235
+ #
236
+ # Options:
237
+ #
238
+ # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
239
+ # the default is to connect to localhost.
240
+ # * <tt>:port</tt> - Defaults to 5432.
241
+ # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
242
+ # * <tt>:password</tt> - Password to be used if the server demands password authentication.
243
+ # * <tt>:database</tt> - Defaults to be the same as the user name.
244
+ # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
245
+ # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
246
+ # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
247
+ # <encoding></tt> call on the connection.
248
+ # * <tt>:min_messages</tt> - An optional client min messages that is used in a
249
+ # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
250
+ # * <tt>:variables</tt> - An optional hash of additional parameters that
251
+ # will be used in <tt>SET SESSION key = val</tt> calls on the connection.
252
+ # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
253
+ # defaults to true.
254
+ #
255
+ # Any further options are used as connection parameters to libpq. See
256
+ # http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
257
+ # list of parameters.
258
+ #
259
+ # In addition, default connection parameters of libpq can be set per environment variables.
260
+ # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
261
+ class PostgreSQLAdapter < AbstractAdapter
262
+ class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
263
+ attr_accessor :array
264
+ end
265
+
266
+ module ColumnMethods
267
+ def xml(*args)
268
+ options = args.extract_options!
269
+ column(args[0], 'xml', options)
270
+ end
271
+
272
+ def tsvector(*args)
273
+ options = args.extract_options!
274
+ column(args[0], 'tsvector', options)
275
+ end
276
+
277
+ def int4range(name, options = {})
278
+ column(name, 'int4range', options)
279
+ end
280
+
281
+ def int8range(name, options = {})
282
+ column(name, 'int8range', options)
283
+ end
284
+
285
+ def tsrange(name, options = {})
286
+ column(name, 'tsrange', options)
287
+ end
288
+
289
+ def tstzrange(name, options = {})
290
+ column(name, 'tstzrange', options)
291
+ end
292
+
293
+ def numrange(name, options = {})
294
+ column(name, 'numrange', options)
295
+ end
296
+
297
+ def daterange(name, options = {})
298
+ column(name, 'daterange', options)
299
+ end
300
+
301
+ def hstore(name, options = {})
302
+ column(name, 'hstore', options)
65
303
  end
66
304
 
67
- def execute(sql, name = nil)
68
- log(sql, name, @connection) { |connection| connection.query(sql) }
305
+ def ltree(name, options = {})
306
+ column(name, 'ltree', options)
69
307
  end
70
308
 
71
- alias_method :update, :execute
72
- alias_method :delete, :execute
309
+ def inet(name, options = {})
310
+ column(name, 'inet', options)
311
+ end
312
+
313
+ def cidr(name, options = {})
314
+ column(name, 'cidr', options)
315
+ end
316
+
317
+ def macaddr(name, options = {})
318
+ column(name, 'macaddr', options)
319
+ end
320
+
321
+ def uuid(name, options = {})
322
+ column(name, 'uuid', options)
323
+ end
73
324
 
74
- def begin_db_transaction() execute "BEGIN" end
75
- def commit_db_transaction() execute "COMMIT" end
76
- def rollback_db_transaction() execute "ROLLBACK" end
325
+ def json(name, options = {})
326
+ column(name, 'json', options)
327
+ end
328
+ end
329
+
330
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
331
+ include ColumnMethods
332
+
333
+ # Defines the primary key field.
334
+ # Use of the native PostgreSQL UUID type is supported, and can be used
335
+ # by defining your tables as such:
336
+ #
337
+ # create_table :stuffs, id: :uuid do |t|
338
+ # t.string :content
339
+ # t.timestamps
340
+ # end
341
+ #
342
+ # By default, this will use the +uuid_generate_v4()+ function from the
343
+ # +uuid-ossp+ extension, which MUST be enabled on your databse. To enable
344
+ # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
345
+ # migrations To use a UUID primary key without +uuid-ossp+ enabled, you can
346
+ # set the +:default+ option to nil:
347
+ #
348
+ # create_table :stuffs, id: false do |t|
349
+ # t.primary_key :id, :uuid, default: nil
350
+ # t.uuid :foo_id
351
+ # t.timestamps
352
+ # end
353
+ #
354
+ # You may also pass a different UUID generation function from +uuid-ossp+
355
+ # or another library.
356
+ #
357
+ # Note that setting the UUID primary key default value to +nil+
358
+ # will require you to assure that you always provide a UUID value
359
+ # before saving a record (as primary keys cannot be nil). This might be
360
+ # done via the SecureRandom.uuid method and a +before_save+ callback,
361
+ # for instance.
362
+ def primary_key(name, type = :primary_key, options = {})
363
+ return super unless type == :uuid
364
+ options[:default] = options.fetch(:default, 'uuid_generate_v4()')
365
+ options[:primary_key] = true
366
+ column name, type, options
367
+ end
368
+
369
+ def column(name, type = nil, options = {})
370
+ super
371
+ column = self[name]
372
+ column.array = options[:array]
373
+
374
+ self
375
+ end
77
376
 
78
- def quote_column_name(name)
79
- return "\"#{name}\""
377
+ def xml(options = {})
378
+ column(args[0], :text, options)
80
379
  end
81
380
 
82
381
  private
83
- def last_insert_id(table, column = "id")
84
- sequence_name = "#{table}_#{column || 'id'}_seq"
85
- @connection.exec("SELECT currval('#{sequence_name}')")[0][0].to_i
86
- end
87
-
88
- def select(sql, name = nil)
89
- res = nil
90
- log(sql, name, @connection) { |connection| res = connection.exec(sql) }
91
-
92
- results = res.result
93
- rows = []
94
- if results.length > 0
95
- fields = res.fields
96
- results.each do |row|
97
- hashed_row = {}
98
- row.each_index { |cel_index| hashed_row[fields[cel_index]] = row[cel_index] }
99
- rows << hashed_row
100
- end
101
- end
102
- return rows
382
+
383
+ def create_column_definition(name, type)
384
+ ColumnDefinition.new name, type
385
+ end
386
+ end
387
+
388
+ class Table < ActiveRecord::ConnectionAdapters::Table
389
+ include ColumnMethods
390
+ end
391
+
392
+ ADAPTER_NAME = 'PostgreSQL'
393
+
394
+ NATIVE_DATABASE_TYPES = {
395
+ primary_key: "serial primary key",
396
+ string: { name: "character varying", limit: 255 },
397
+ text: { name: "text" },
398
+ integer: { name: "integer" },
399
+ float: { name: "float" },
400
+ decimal: { name: "decimal" },
401
+ datetime: { name: "timestamp" },
402
+ timestamp: { name: "timestamp" },
403
+ time: { name: "time" },
404
+ date: { name: "date" },
405
+ daterange: { name: "daterange" },
406
+ numrange: { name: "numrange" },
407
+ tsrange: { name: "tsrange" },
408
+ tstzrange: { name: "tstzrange" },
409
+ int4range: { name: "int4range" },
410
+ int8range: { name: "int8range" },
411
+ binary: { name: "bytea" },
412
+ boolean: { name: "boolean" },
413
+ xml: { name: "xml" },
414
+ tsvector: { name: "tsvector" },
415
+ hstore: { name: "hstore" },
416
+ inet: { name: "inet" },
417
+ cidr: { name: "cidr" },
418
+ macaddr: { name: "macaddr" },
419
+ uuid: { name: "uuid" },
420
+ json: { name: "json" },
421
+ ltree: { name: "ltree" }
422
+ }
423
+
424
+ include Quoting
425
+ include ReferentialIntegrity
426
+ include SchemaStatements
427
+ include DatabaseStatements
428
+
429
+ # Returns 'PostgreSQL' as adapter name for identification purposes.
430
+ def adapter_name
431
+ ADAPTER_NAME
432
+ end
433
+
434
+ # Adds `:array` option to the default set provided by the
435
+ # AbstractAdapter
436
+ def prepare_column_options(column, types)
437
+ spec = super
438
+ spec[:array] = 'true' if column.respond_to?(:array) && column.array
439
+ spec
440
+ end
441
+
442
+ # Adds `:array` as a valid migration key
443
+ def migration_keys
444
+ super + [:array]
445
+ end
446
+
447
+ # Returns +true+, since this connection adapter supports prepared statement
448
+ # caching.
449
+ def supports_statement_cache?
450
+ true
451
+ end
452
+
453
+ def supports_index_sort_order?
454
+ true
455
+ end
456
+
457
+ def supports_partial_index?
458
+ true
459
+ end
460
+
461
+ def supports_transaction_isolation?
462
+ true
463
+ end
464
+
465
+ def index_algorithms
466
+ { concurrently: 'CONCURRENTLY' }
467
+ end
468
+
469
+ class StatementPool < ConnectionAdapters::StatementPool
470
+ def initialize(connection, max)
471
+ super
472
+ @counter = 0
473
+ @cache = Hash.new { |h,pid| h[pid] = {} }
474
+ end
475
+
476
+ def each(&block); cache.each(&block); end
477
+ def key?(key); cache.key?(key); end
478
+ def [](key); cache[key]; end
479
+ def length; cache.length; end
480
+
481
+ def next_key
482
+ "a#{@counter + 1}"
483
+ end
484
+
485
+ def []=(sql, key)
486
+ while @max <= cache.size
487
+ dealloc(cache.shift.last)
103
488
  end
489
+ @counter += 1
490
+ cache[sql] = key
491
+ end
104
492
 
105
- def split_table_schema(table_name)
106
- schema_split = table_name.split('.')
107
- schema_name = "public"
108
- if schema_split.length > 1
109
- schema_name = schema_split.first.strip
110
- table_name = schema_split.last.strip
111
- end
112
- return [schema_name, table_name]
113
- end
114
-
115
- def table_structure(table_name)
116
- database_name = @connection.db
117
- schema_name, table_name = split_table_schema(table_name)
118
-
119
- # Grab a list of all the default values for the columns.
120
- sql = "SELECT column_name, column_default, character_maximum_length, data_type "
121
- sql << " FROM information_schema.columns "
122
- sql << " WHERE table_catalog = '#{database_name}' "
123
- sql << " AND table_schema = '#{schema_name}' "
124
- sql << " AND table_name = '#{table_name}';"
125
-
126
- column_defaults = nil
127
- log(sql, nil, @connection) { |connection| column_defaults = connection.query(sql) }
128
- column_defaults.collect do |row|
129
- field = row[0]
130
- type = type_as_string(row[3], row[2])
131
- default = default_value(row[1])
132
- length = row[2]
133
-
134
- [field, type, default, length]
135
- end
493
+ def clear
494
+ cache.each_value do |stmt_key|
495
+ dealloc stmt_key
496
+ end
497
+ cache.clear
498
+ end
499
+
500
+ def delete(sql_key)
501
+ dealloc cache[sql_key]
502
+ cache.delete sql_key
503
+ end
504
+
505
+ private
506
+
507
+ def cache
508
+ @cache[Process.pid]
509
+ end
510
+
511
+ def dealloc(key)
512
+ @connection.query "DEALLOCATE #{key}" if connection_active?
513
+ end
514
+
515
+ def connection_active?
516
+ @connection.status == PGconn::CONNECTION_OK
517
+ rescue PGError
518
+ false
136
519
  end
520
+ end
521
+
522
+ class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
523
+ include Arel::Visitors::BindVisitor
524
+ end
525
+
526
+ # Initializes and connects a PostgreSQL adapter.
527
+ def initialize(connection, logger, connection_parameters, config)
528
+ super(connection, logger)
529
+
530
+ if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
531
+ @visitor = Arel::Visitors::PostgreSQL.new self
532
+ else
533
+ @visitor = unprepared_visitor
534
+ end
535
+
536
+ @connection_parameters, @config = connection_parameters, config
537
+
538
+ # @local_tz is initialized as nil to avoid warnings when connect tries to use it
539
+ @local_tz = nil
540
+ @table_alias_length = nil
541
+
542
+ connect
543
+ @statements = StatementPool.new @connection,
544
+ self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
545
+
546
+ if postgresql_version < 80200
547
+ raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
548
+ end
549
+
550
+ initialize_type_map
551
+ @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
552
+ @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
553
+ end
137
554
 
138
- def type_as_string(field_type, field_length)
139
- type = case field_type
140
- when 'numeric', 'real', 'money' then 'float'
141
- when 'character varying', 'interval' then 'string'
142
- when 'timestamp without time zone' then 'datetime'
143
- else field_type
555
+ # Clears the prepared statements cache.
556
+ def clear_cache!
557
+ @statements.clear
558
+ end
559
+
560
+ # Is this connection alive and ready for queries?
561
+ def active?
562
+ @connection.connect_poll != PG::PGRES_POLLING_FAILED
563
+ rescue PGError
564
+ false
565
+ end
566
+
567
+ # Close then reopen the connection.
568
+ def reconnect!
569
+ super
570
+ @connection.reset
571
+ configure_connection
572
+ end
573
+
574
+ def reset!
575
+ clear_cache!
576
+ super
577
+ end
578
+
579
+ # Disconnects from the database if already connected. Otherwise, this
580
+ # method does nothing.
581
+ def disconnect!
582
+ super
583
+ @connection.close rescue nil
584
+ end
585
+
586
+ def native_database_types #:nodoc:
587
+ NATIVE_DATABASE_TYPES
588
+ end
589
+
590
+ # Returns true, since this connection adapter supports migrations.
591
+ def supports_migrations?
592
+ true
593
+ end
594
+
595
+ # Does PostgreSQL support finding primary key on non-Active Record tables?
596
+ def supports_primary_key? #:nodoc:
597
+ true
598
+ end
599
+
600
+ # Enable standard-conforming strings if available.
601
+ def set_standard_conforming_strings
602
+ old, self.client_min_messages = client_min_messages, 'panic'
603
+ execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
604
+ ensure
605
+ self.client_min_messages = old
606
+ end
607
+
608
+ def supports_insert_with_returning?
609
+ true
610
+ end
611
+
612
+ def supports_ddl_transactions?
613
+ true
614
+ end
615
+
616
+ # Returns true, since this connection adapter supports savepoints.
617
+ def supports_savepoints?
618
+ true
619
+ end
620
+
621
+ # Returns true.
622
+ def supports_explain?
623
+ true
624
+ end
625
+
626
+ # Returns true if pg > 9.2
627
+ def supports_extensions?
628
+ postgresql_version >= 90200
629
+ end
630
+
631
+ # Range datatypes weren't introduced until PostgreSQL 9.2
632
+ def supports_ranges?
633
+ postgresql_version >= 90200
634
+ end
635
+
636
+ def enable_extension(name)
637
+ exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
638
+ reload_type_map
639
+ }
640
+ end
641
+
642
+ def disable_extension(name)
643
+ exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
644
+ reload_type_map
645
+ }
646
+ end
647
+
648
+ def extension_enabled?(name)
649
+ if supports_extensions?
650
+ res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL)",
651
+ 'SCHEMA'
652
+ res.column_types['exists'].type_cast res.rows.first.first
653
+ end
654
+ end
655
+
656
+ def extensions
657
+ if supports_extensions?
658
+ res = exec_query "SELECT extname from pg_extension", "SCHEMA"
659
+ res.rows.map { |r| res.column_types['extname'].type_cast r.first }
660
+ else
661
+ super
662
+ end
663
+ end
664
+
665
+ # Returns the configured supported identifier length supported by PostgreSQL
666
+ def table_alias_length
667
+ @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
668
+ end
669
+
670
+ # Set the authorized user for this session
671
+ def session_auth=(user)
672
+ clear_cache!
673
+ exec_query "SET SESSION AUTHORIZATION #{user}"
674
+ end
675
+
676
+ module Utils
677
+ extend self
678
+
679
+ # Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
680
+ # +schema_name+ is nil if not specified in +name+.
681
+ # +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
682
+ # +name+ supports the range of schema/table references understood by PostgreSQL, for example:
683
+ #
684
+ # * <tt>table_name</tt>
685
+ # * <tt>"table.name"</tt>
686
+ # * <tt>schema_name.table_name</tt>
687
+ # * <tt>schema_name."table.name"</tt>
688
+ # * <tt>"schema.name"."table name"</tt>
689
+ def extract_schema_and_table(name)
690
+ table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
691
+ [schema, table]
692
+ end
693
+ end
694
+
695
+ def use_insert_returning?
696
+ @use_insert_returning
697
+ end
698
+
699
+ def valid_type?(type)
700
+ !native_database_types[type].nil?
701
+ end
702
+
703
+ protected
704
+
705
+ # Returns the version of the connected PostgreSQL server.
706
+ def postgresql_version
707
+ @connection.server_version
708
+ end
709
+
710
+ # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
711
+ FOREIGN_KEY_VIOLATION = "23503"
712
+ UNIQUE_VIOLATION = "23505"
713
+
714
+ def translate_exception(exception, message)
715
+ return exception unless exception.respond_to?(:result)
716
+
717
+ case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
718
+ when UNIQUE_VIOLATION
719
+ RecordNotUnique.new(message, exception)
720
+ when FOREIGN_KEY_VIOLATION
721
+ InvalidForeignKey.new(message, exception)
722
+ else
723
+ super
724
+ end
725
+ end
726
+
727
+ private
728
+
729
+ def reload_type_map
730
+ OID::TYPE_MAP.clear
731
+ initialize_type_map
732
+ end
733
+
734
+ def initialize_type_map
735
+ result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
736
+ leaves, nodes = result.partition { |row| row['typelem'] == '0' }
737
+
738
+ # populate the leaf nodes
739
+ leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
740
+ OID::TYPE_MAP[row['oid'].to_i] = OID::NAMES[row['typname']]
741
+ end
742
+
743
+ arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
744
+
745
+ # populate composite types
746
+ nodes.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
747
+ if OID.registered_type? row['typname']
748
+ # this composite type is explicitly registered
749
+ vector = OID::NAMES[row['typname']]
750
+ else
751
+ # use the default for composite types
752
+ vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
144
753
  end
145
754
 
146
- size = field_length.nil? ? "" : "(#{field_length})"
755
+ OID::TYPE_MAP[row['oid'].to_i] = vector
756
+ end
147
757
 
148
- return type + size
758
+ # populate array types
759
+ arrays.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
760
+ array = OID::Array.new OID::TYPE_MAP[row['typelem'].to_i]
761
+ OID::TYPE_MAP[row['oid'].to_i] = array
149
762
  end
763
+ end
150
764
 
151
- def default_value(value)
152
- # Boolean types
153
- return "t" if value =~ /true/i
154
- return "f" if value =~ /false/i
155
-
156
- # Char/String type values
157
- return $1 if value =~ /^'(.*)'::(bpchar|text|character varying)$/
158
-
159
- # Numeric values
160
- return value if value =~ /^[0-9]+(\.[0-9]*)?/
765
+ FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
161
766
 
162
- # Date / Time magic values
163
- return Time.now.to_s if value =~ /^\('now'::text\)::(date|timestamp)/
767
+ def exec_no_cache(sql, binds)
768
+ @connection.async_exec(sql)
769
+ end
164
770
 
165
- # Fixed dates / times
166
- return $1 if value =~ /^'(.+)'::(date|timestamp)/
167
-
168
- # Anything else is blank, some user type, or some function
169
- # and we can't know the value of that, so return nil.
170
- return nil
771
+ def exec_cache(sql, binds)
772
+ stmt_key = prepare_statement sql
773
+
774
+ # Clear the queue
775
+ @connection.get_last_result
776
+ @connection.send_query_prepared(stmt_key, binds.map { |col, val|
777
+ type_cast(val, col)
778
+ })
779
+ @connection.block
780
+ @connection.get_last_result
781
+ rescue PGError => e
782
+ # Get the PG code for the failure. Annoyingly, the code for
783
+ # prepared statements whose return value may have changed is
784
+ # FEATURE_NOT_SUPPORTED. Check here for more details:
785
+ # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
786
+ begin
787
+ code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
788
+ rescue
789
+ raise e
171
790
  end
172
- end
791
+ if FEATURE_NOT_SUPPORTED == code
792
+ @statements.delete sql_key(sql)
793
+ retry
794
+ else
795
+ raise e
796
+ end
797
+ end
798
+
799
+ # Returns the statement identifier for the client side cache
800
+ # of statements
801
+ def sql_key(sql)
802
+ "#{schema_search_path}-#{sql}"
803
+ end
804
+
805
+ # Prepare the statement if it hasn't been prepared, return
806
+ # the statement key.
807
+ def prepare_statement(sql)
808
+ sql_key = sql_key(sql)
809
+ unless @statements.key? sql_key
810
+ nextkey = @statements.next_key
811
+ @connection.prepare nextkey, sql
812
+ @statements[sql_key] = nextkey
813
+ end
814
+ @statements[sql_key]
815
+ end
816
+
817
+ # The internal PostgreSQL identifier of the money data type.
818
+ MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
819
+ # The internal PostgreSQL identifier of the BYTEA data type.
820
+ BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
821
+
822
+ # Connects to a PostgreSQL server and sets up the adapter depending on the
823
+ # connected server's characteristics.
824
+ def connect
825
+ @connection = PGconn.connect(@connection_parameters)
826
+
827
+ # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
828
+ # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
829
+ # should know about this but can't detect it there, so deal with it here.
830
+ PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
831
+
832
+ configure_connection
833
+ end
834
+
835
+ # Configures the encoding, verbosity, schema search path, and time zone of the connection.
836
+ # This is called by #connect and should not be called manually.
837
+ def configure_connection
838
+ if @config[:encoding]
839
+ @connection.set_client_encoding(@config[:encoding])
840
+ end
841
+ self.client_min_messages = @config[:min_messages] || 'warning'
842
+ self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
843
+
844
+ # Use standard-conforming strings if available so we don't have to do the E'...' dance.
845
+ set_standard_conforming_strings
846
+
847
+ # If using Active Record's time zone support configure the connection to return
848
+ # TIMESTAMP WITH ZONE types in UTC.
849
+ # (SET TIME ZONE does not use an equals sign like other SET variables)
850
+ if ActiveRecord::Base.default_timezone == :utc
851
+ execute("SET time zone 'UTC'", 'SCHEMA')
852
+ elsif @local_tz
853
+ execute("SET time zone '#{@local_tz}'", 'SCHEMA')
854
+ end
855
+
856
+ # SET statements from :variables config hash
857
+ # http://www.postgresql.org/docs/8.3/static/sql-set.html
858
+ variables = @config[:variables] || {}
859
+ variables.map do |k, v|
860
+ if v == ':default' || v == :default
861
+ # Sets the value to the global or compile default
862
+ execute("SET SESSION #{k.to_s} TO DEFAULT", 'SCHEMA')
863
+ elsif !v.nil?
864
+ execute("SET SESSION #{k.to_s} TO #{quote(v)}", 'SCHEMA')
865
+ end
866
+ end
867
+ end
868
+
869
+ # Returns the current ID of a table's sequence.
870
+ def last_insert_id(sequence_name) #:nodoc:
871
+ Integer(last_insert_id_value(sequence_name))
872
+ end
873
+
874
+ def last_insert_id_value(sequence_name)
875
+ last_insert_id_result(sequence_name).rows.first.first
876
+ end
877
+
878
+ def last_insert_id_result(sequence_name) #:nodoc:
879
+ exec_query("SELECT currval('#{sequence_name}')", 'SQL')
880
+ end
881
+
882
+ # Executes a SELECT query and returns the results, performing any data type
883
+ # conversions that are required to be performed here instead of in PostgreSQLColumn.
884
+ def select(sql, name = nil, binds = [])
885
+ exec_query(sql, name, binds)
886
+ end
887
+
888
+ def select_raw(sql, name = nil)
889
+ res = execute(sql, name)
890
+ results = result_as_array(res)
891
+ fields = res.fields
892
+ res.clear
893
+ return fields, results
894
+ end
895
+
896
+ # Returns the list of a table's column names, data types, and default values.
897
+ #
898
+ # The underlying query is roughly:
899
+ # SELECT column.name, column.type, default.value
900
+ # FROM column LEFT JOIN default
901
+ # ON column.table_id = default.table_id
902
+ # AND column.num = default.column_num
903
+ # WHERE column.table_id = get_table_id('table_name')
904
+ # AND column.num > 0
905
+ # AND NOT column.is_dropped
906
+ # ORDER BY column.num
907
+ #
908
+ # If the table name is not prefixed with a schema, the database will
909
+ # take the first match from the schema search path.
910
+ #
911
+ # Query implementation notes:
912
+ # - format_type includes the column size constraint, e.g. varchar(50)
913
+ # - ::regclass is a function that gives the id for a table name
914
+ def column_definitions(table_name) #:nodoc:
915
+ exec_query(<<-end_sql, 'SCHEMA').rows
916
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
917
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
918
+ FROM pg_attribute a LEFT JOIN pg_attrdef d
919
+ ON a.attrelid = d.adrelid AND a.attnum = d.adnum
920
+ WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
921
+ AND a.attnum > 0 AND NOT a.attisdropped
922
+ ORDER BY a.attnum
923
+ end_sql
924
+ end
925
+
926
+ def extract_pg_identifier_from_name(name)
927
+ match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
928
+
929
+ if match_data
930
+ rest = name[match_data[0].length, name.length]
931
+ rest = rest[1, rest.length] if rest.start_with? "."
932
+ [match_data[1], (rest.length > 0 ? rest : nil)]
933
+ end
934
+ end
935
+
936
+ def extract_table_ref_from_insert_sql(sql)
937
+ sql[/into\s+([^\(]*).*values\s*\(/i]
938
+ $1.strip if $1
939
+ end
940
+
941
+ def create_table_definition(name, temporary, options)
942
+ TableDefinition.new native_database_types, name, temporary, options
943
+ end
944
+
945
+ def update_table_definition(table_name, base)
946
+ Table.new(table_name, base)
947
+ end
173
948
  end
174
949
  end
175
- rescue LoadError
176
- # PostgreSQL driver is not availible
177
950
  end