activerecord 2.1.2 → 2.2.2
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.
- data/CHANGELOG +32 -6
- data/README +0 -0
- data/Rakefile +4 -5
- data/lib/active_record.rb +11 -10
- data/lib/active_record/aggregations.rb +110 -38
- data/lib/active_record/association_preload.rb +104 -15
- data/lib/active_record/associations.rb +427 -212
- data/lib/active_record/associations/association_collection.rb +101 -16
- data/lib/active_record/associations/association_proxy.rb +65 -13
- data/lib/active_record/associations/belongs_to_association.rb +2 -2
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -3
- data/lib/active_record/associations/has_many_association.rb +28 -28
- data/lib/active_record/associations/has_many_through_association.rb +21 -19
- data/lib/active_record/associations/has_one_association.rb +24 -7
- data/lib/active_record/associations/has_one_through_association.rb +3 -4
- data/lib/active_record/attribute_methods.rb +13 -5
- data/lib/active_record/base.rb +435 -212
- data/lib/active_record/calculations.rb +12 -5
- data/lib/active_record/callbacks.rb +28 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +355 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +42 -215
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -5
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +48 -7
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +10 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -26
- data/lib/active_record/connection_adapters/mysql_adapter.rb +71 -45
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +155 -84
- data/lib/active_record/dirty.rb +25 -7
- data/lib/active_record/dynamic_finder_match.rb +41 -0
- data/lib/active_record/fixtures.rb +10 -9
- data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
- data/lib/active_record/locale/en.yml +54 -0
- data/lib/active_record/migration.rb +47 -10
- data/lib/active_record/named_scope.rb +29 -16
- data/lib/active_record/reflection.rb +118 -54
- data/lib/active_record/schema_dumper.rb +13 -7
- data/lib/active_record/test_case.rb +18 -5
- data/lib/active_record/transactions.rb +89 -34
- data/lib/active_record/validations.rb +270 -180
- data/lib/active_record/version.rb +1 -1
- data/test/cases/active_schema_test_mysql.rb +5 -0
- data/test/cases/adapter_test.rb +6 -0
- data/test/cases/aggregations_test.rb +39 -0
- data/test/cases/associations/belongs_to_associations_test.rb +10 -0
- data/test/cases/associations/eager_load_nested_include_test.rb +30 -12
- data/test/cases/associations/eager_test.rb +54 -5
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +77 -10
- data/test/cases/associations/has_many_associations_test.rb +74 -7
- data/test/cases/associations/has_many_through_associations_test.rb +50 -3
- data/test/cases/associations/has_one_associations_test.rb +17 -0
- data/test/cases/associations/has_one_through_associations_test.rb +49 -1
- data/test/cases/associations_test.rb +0 -0
- data/test/cases/attribute_methods_test.rb +59 -4
- data/test/cases/base_test.rb +93 -21
- data/test/cases/binary_test.rb +1 -5
- data/test/cases/calculations_test.rb +5 -0
- data/test/cases/callbacks_observers_test.rb +38 -0
- data/test/cases/connection_test_mysql.rb +1 -1
- data/test/cases/defaults_test.rb +32 -1
- data/test/cases/deprecated_finder_test.rb +0 -0
- data/test/cases/dirty_test.rb +13 -0
- data/test/cases/finder_test.rb +162 -12
- data/test/cases/fixtures_test.rb +32 -3
- data/test/cases/helper.rb +15 -0
- data/test/cases/i18n_test.rb +41 -0
- data/test/cases/inheritance_test.rb +2 -2
- data/test/cases/lifecycle_test.rb +0 -0
- data/test/cases/locking_test.rb +4 -9
- data/test/cases/method_scoping_test.rb +109 -2
- data/test/cases/migration_test.rb +43 -8
- data/test/cases/multiple_db_test.rb +25 -0
- data/test/cases/named_scope_test.rb +74 -0
- data/test/cases/pooled_connections_test.rb +103 -0
- data/test/cases/readonly_test.rb +0 -0
- data/test/cases/reflection_test.rb +11 -3
- data/test/cases/reload_models_test.rb +20 -0
- data/test/cases/sanitize_test.rb +25 -0
- data/test/cases/schema_authorization_test_postgresql.rb +2 -2
- data/test/cases/transactions_test.rb +62 -12
- data/test/cases/unconnected_test.rb +0 -0
- data/test/cases/validations_i18n_test.rb +921 -0
- data/test/cases/validations_test.rb +44 -33
- data/test/connections/native_mysql/connection.rb +1 -3
- data/test/fixtures/companies.yml +1 -0
- data/test/fixtures/customers.yml +10 -1
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/fixtures/organizations.yml +5 -0
- data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
- data/test/models/author.rb +3 -0
- data/test/models/category.rb +3 -0
- data/test/models/club.rb +6 -0
- data/test/models/company.rb +25 -1
- data/test/models/customer.rb +19 -1
- data/test/models/member.rb +2 -0
- data/test/models/member_detail.rb +4 -0
- data/test/models/organization.rb +4 -0
- data/test/models/parrot.rb +1 -0
- data/test/models/post.rb +3 -0
- data/test/models/reply.rb +0 -0
- data/test/models/topic.rb +3 -0
- data/test/schema/schema.rb +12 -1
- metadata +22 -10
- data/lib/active_record/vendor/mysql.rb +0 -1214
- data/test/cases/adapter_test_sqlserver.rb +0 -95
- data/test/cases/table_name_test_sqlserver.rb +0 -23
- data/test/cases/threaded_connections_test.rb +0 -48
- data/test/schema/sqlserver_specific_schema.rb +0 -5
@@ -31,19 +31,25 @@ module ActiveRecord
|
|
31
31
|
# See the concrete implementation for details on the expected parameter values.
|
32
32
|
def columns(table_name, name = nil) end
|
33
33
|
|
34
|
-
# Creates a new table
|
34
|
+
# Creates a new table with the name +table_name+. +table_name+ may either
|
35
|
+
# be a String or a Symbol.
|
36
|
+
#
|
35
37
|
# There are two ways to work with +create_table+. You can use the block
|
36
38
|
# form or the regular form, like this:
|
37
39
|
#
|
38
40
|
# === Block form
|
39
|
-
# # create_table()
|
41
|
+
# # create_table() passes a TableDefinition object to the block.
|
42
|
+
# # This form will not only create the table, but also columns for the
|
43
|
+
# # table.
|
40
44
|
# create_table(:suppliers) do |t|
|
41
45
|
# t.column :name, :string, :limit => 60
|
42
46
|
# # Other fields here
|
43
47
|
# end
|
44
48
|
#
|
45
49
|
# === Regular form
|
50
|
+
# # Creates a table called 'suppliers' with no columns.
|
46
51
|
# create_table(:suppliers)
|
52
|
+
# # Add a column to 'suppliers'.
|
47
53
|
# add_column(:suppliers, :name, :string, {:limit => 60})
|
48
54
|
#
|
49
55
|
# The +options+ hash can include the following keys:
|
@@ -356,7 +362,7 @@ module ActiveRecord
|
|
356
362
|
|
357
363
|
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
|
358
364
|
if native = native_database_types[type]
|
359
|
-
column_type_sql = native.is_a?(Hash) ? native[:name] : native
|
365
|
+
column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
|
360
366
|
|
361
367
|
if type == :decimal # ignore limit, use precision and scale
|
362
368
|
scale ||= native[:scale]
|
@@ -371,7 +377,7 @@ module ActiveRecord
|
|
371
377
|
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
|
372
378
|
end
|
373
379
|
|
374
|
-
elsif limit ||= native.is_a?(Hash) && native[:limit]
|
380
|
+
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
|
375
381
|
column_type_sql << "(#{limit})"
|
376
382
|
end
|
377
383
|
|
@@ -7,23 +7,31 @@ require 'active_record/connection_adapters/abstract/schema_definitions'
|
|
7
7
|
require 'active_record/connection_adapters/abstract/schema_statements'
|
8
8
|
require 'active_record/connection_adapters/abstract/database_statements'
|
9
9
|
require 'active_record/connection_adapters/abstract/quoting'
|
10
|
+
require 'active_record/connection_adapters/abstract/connection_pool'
|
10
11
|
require 'active_record/connection_adapters/abstract/connection_specification'
|
11
12
|
require 'active_record/connection_adapters/abstract/query_cache'
|
12
13
|
|
13
14
|
module ActiveRecord
|
14
15
|
module ConnectionAdapters # :nodoc:
|
16
|
+
# ActiveRecord supports multiple database systems. AbstractAdapter and
|
17
|
+
# related classes form the abstraction layer which makes this possible.
|
18
|
+
# An AbstractAdapter represents a connection to a database, and provides an
|
19
|
+
# abstract interface for database-specific functionality such as establishing
|
20
|
+
# a connection, escaping values, building the right SQL fragments for ':offset'
|
21
|
+
# and ':limit' options, etc.
|
22
|
+
#
|
15
23
|
# All the concrete database adapters follow the interface laid down in this class.
|
16
|
-
#
|
17
|
-
#
|
24
|
+
# ActiveRecord::Base.connection returns an AbstractAdapter object, which
|
25
|
+
# you can use.
|
18
26
|
#
|
19
|
-
# Most of the methods in the adapter are useful during migrations.
|
20
|
-
# notably,
|
21
|
-
# SchemaStatements#add_index, SchemaStatements#remove_index,
|
22
|
-
# SchemaStatements#add_column, SchemaStatements#change_column and
|
23
|
-
# SchemaStatements#remove_column are very useful.
|
27
|
+
# Most of the methods in the adapter are useful during migrations. Most
|
28
|
+
# notably, the instance methods provided by SchemaStatement are very useful.
|
24
29
|
class AbstractAdapter
|
25
30
|
include Quoting, DatabaseStatements, SchemaStatements
|
26
31
|
include QueryCache
|
32
|
+
include ActiveSupport::Callbacks
|
33
|
+
define_callbacks :checkout, :checkin
|
34
|
+
|
27
35
|
@@row_even = true
|
28
36
|
|
29
37
|
def initialize(connection, logger = nil) #:nodoc:
|
@@ -51,6 +59,13 @@ module ActiveRecord
|
|
51
59
|
true
|
52
60
|
end
|
53
61
|
|
62
|
+
# Does this adapter support DDL rollbacks in transactions? That is, would
|
63
|
+
# CREATE TABLE or ALTER TABLE get rolled back by a transaction? PostgreSQL,
|
64
|
+
# SQL Server, and others support this. MySQL and others do not.
|
65
|
+
def supports_ddl_transactions?
|
66
|
+
false
|
67
|
+
end
|
68
|
+
|
54
69
|
# Should primary key values be selected from their corresponding
|
55
70
|
# sequence before the insert statement? If true, next_sequence_value
|
56
71
|
# is called before each insert to set the record's primary key.
|
@@ -80,48 +95,74 @@ module ActiveRecord
|
|
80
95
|
|
81
96
|
# CONNECTION MANAGEMENT ====================================
|
82
97
|
|
83
|
-
#
|
98
|
+
# Checks whether the connection to the database is still active. This includes
|
99
|
+
# checking whether the database is actually capable of responding, i.e. whether
|
100
|
+
# the connection isn't stale.
|
84
101
|
def active?
|
85
102
|
@active != false
|
86
103
|
end
|
87
104
|
|
88
|
-
#
|
105
|
+
# Disconnects from the database if already connected, and establishes a
|
106
|
+
# new connection with the database.
|
89
107
|
def reconnect!
|
90
108
|
@active = true
|
91
109
|
end
|
92
110
|
|
93
|
-
#
|
111
|
+
# Disconnects from the database if already connected. Otherwise, this
|
112
|
+
# method does nothing.
|
94
113
|
def disconnect!
|
95
114
|
@active = false
|
96
115
|
end
|
97
116
|
|
117
|
+
# Reset the state of this connection, directing the DBMS to clear
|
118
|
+
# transactions and other connection-related server-side state. Usually a
|
119
|
+
# database-dependent operation.
|
120
|
+
#
|
121
|
+
# The default implementation does nothing; the implementation should be
|
122
|
+
# overridden by concrete adapters.
|
123
|
+
def reset!
|
124
|
+
# this should be overridden by concrete adapters
|
125
|
+
end
|
126
|
+
|
98
127
|
# Returns true if its safe to reload the connection between requests for development mode.
|
99
|
-
# This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite.
|
100
128
|
def requires_reloading?
|
101
|
-
|
129
|
+
true
|
102
130
|
end
|
103
131
|
|
104
|
-
#
|
105
|
-
#
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
reconnect! unless active?
|
110
|
-
@last_verification = now
|
111
|
-
end
|
132
|
+
# Checks whether the connection to the database is still active (i.e. not stale).
|
133
|
+
# This is done under the hood by calling <tt>active?</tt>. If the connection
|
134
|
+
# is no longer active, then this method will reconnect to the database.
|
135
|
+
def verify!(*ignored)
|
136
|
+
reconnect! unless active?
|
112
137
|
end
|
113
138
|
|
114
|
-
# Provides access to the underlying database
|
115
|
-
#
|
116
|
-
#
|
139
|
+
# Provides access to the underlying database driver for this adapter. For
|
140
|
+
# example, this method returns a Mysql object in case of MysqlAdapter,
|
141
|
+
# and a PGconn object in case of PostgreSQLAdapter.
|
142
|
+
#
|
143
|
+
# This is useful for when you need to call a proprietary method such as
|
144
|
+
# PostgreSQL's lo_* methods.
|
117
145
|
def raw_connection
|
118
146
|
@connection
|
119
147
|
end
|
120
148
|
|
121
|
-
def
|
149
|
+
def open_transactions
|
150
|
+
@open_transactions ||= 0
|
151
|
+
end
|
152
|
+
|
153
|
+
def increment_open_transactions
|
154
|
+
@open_transactions ||= 0
|
155
|
+
@open_transactions += 1
|
156
|
+
end
|
157
|
+
|
158
|
+
def decrement_open_transactions
|
159
|
+
@open_transactions -= 1
|
160
|
+
end
|
161
|
+
|
162
|
+
def log_info(sql, name, seconds)
|
122
163
|
if @logger && @logger.debug?
|
123
|
-
name = "#{name.nil? ? "SQL" : name} (#{sprintf("
|
124
|
-
@logger.debug
|
164
|
+
name = "#{name.nil? ? "SQL" : name} (#{sprintf("%.1f", seconds * 1000)}ms)"
|
165
|
+
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
|
125
166
|
end
|
126
167
|
end
|
127
168
|
|
@@ -42,27 +42,6 @@ end
|
|
42
42
|
|
43
43
|
module ActiveRecord
|
44
44
|
class Base
|
45
|
-
def self.require_mysql
|
46
|
-
# Include the MySQL driver if one hasn't already been loaded
|
47
|
-
unless defined? Mysql
|
48
|
-
begin
|
49
|
-
require_library_or_gem 'mysql'
|
50
|
-
rescue LoadError => cannot_require_mysql
|
51
|
-
# Use the bundled Ruby/MySQL driver if no driver is already in place
|
52
|
-
begin
|
53
|
-
ActiveSupport::Deprecation.warn "You're using the Ruby-based MySQL library that ships with Rails. This library will be REMOVED FROM RAILS 2.2. Please switch to the offical mysql gem: `gem install mysql`", caller
|
54
|
-
|
55
|
-
require 'active_record/vendor/mysql'
|
56
|
-
rescue LoadError
|
57
|
-
raise cannot_require_mysql
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# Define Mysql::Result.all_hashes
|
63
|
-
MysqlCompat.define_all_hashes_method!
|
64
|
-
end
|
65
|
-
|
66
45
|
# Establishes a connection to the database that's used by all Active Record objects.
|
67
46
|
def self.mysql_connection(config) # :nodoc:
|
68
47
|
config = config.symbolize_keys
|
@@ -78,9 +57,19 @@ module ActiveRecord
|
|
78
57
|
raise ArgumentError, "No database specified. Missing argument: database."
|
79
58
|
end
|
80
59
|
|
81
|
-
|
60
|
+
# Require the MySQL driver and define Mysql::Result.all_hashes
|
61
|
+
unless defined? Mysql
|
62
|
+
begin
|
63
|
+
require_library_or_gem('mysql')
|
64
|
+
rescue LoadError
|
65
|
+
$stderr.puts '!!! The bundled mysql.rb driver has been removed from Rails 2.2. Please install the mysql gem and try again: gem install mysql.'
|
66
|
+
raise
|
67
|
+
end
|
68
|
+
end
|
69
|
+
MysqlCompat.define_all_hashes_method!
|
70
|
+
|
82
71
|
mysql = Mysql.init
|
83
|
-
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslkey]
|
72
|
+
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey]
|
84
73
|
|
85
74
|
ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config)
|
86
75
|
end
|
@@ -91,7 +80,7 @@ module ActiveRecord
|
|
91
80
|
def extract_default(default)
|
92
81
|
if type == :binary || type == :text
|
93
82
|
if default.blank?
|
94
|
-
nil
|
83
|
+
return null ? nil : ''
|
95
84
|
else
|
96
85
|
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
97
86
|
end
|
@@ -102,6 +91,11 @@ module ActiveRecord
|
|
102
91
|
end
|
103
92
|
end
|
104
93
|
|
94
|
+
def has_default?
|
95
|
+
return false if type == :binary || type == :text #mysql forbids defaults on blob and text columns
|
96
|
+
super
|
97
|
+
end
|
98
|
+
|
105
99
|
private
|
106
100
|
def simplified_type(field_type)
|
107
101
|
return :boolean if MysqlAdapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
|
@@ -156,6 +150,7 @@ module ActiveRecord
|
|
156
150
|
# * <tt>:password</tt> - Defaults to nothing.
|
157
151
|
# * <tt>:database</tt> - The name of the database. No default, must be provided.
|
158
152
|
# * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
|
153
|
+
# * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
|
159
154
|
# * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
|
160
155
|
# * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
|
161
156
|
# * <tt>:sslcapath</tt> - Necessary to use MySQL with an SSL connection.
|
@@ -168,8 +163,10 @@ module ActiveRecord
|
|
168
163
|
#
|
169
164
|
# ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans = false
|
170
165
|
class MysqlAdapter < AbstractAdapter
|
171
|
-
@@emulate_booleans = true
|
172
166
|
cattr_accessor :emulate_booleans
|
167
|
+
self.emulate_booleans = true
|
168
|
+
|
169
|
+
ADAPTER_NAME = 'MySQL'.freeze
|
173
170
|
|
174
171
|
LOST_CONNECTION_ERROR_MESSAGES = [
|
175
172
|
"Server shutdown in progress",
|
@@ -177,7 +174,22 @@ module ActiveRecord
|
|
177
174
|
"Lost connection to MySQL server during query",
|
178
175
|
"MySQL server has gone away" ]
|
179
176
|
|
180
|
-
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
|
177
|
+
QUOTED_TRUE, QUOTED_FALSE = '1'.freeze, '0'.freeze
|
178
|
+
|
179
|
+
NATIVE_DATABASE_TYPES = {
|
180
|
+
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze,
|
181
|
+
:string => { :name => "varchar", :limit => 255 },
|
182
|
+
:text => { :name => "text" },
|
183
|
+
:integer => { :name => "int", :limit => 4 },
|
184
|
+
:float => { :name => "float" },
|
185
|
+
:decimal => { :name => "decimal" },
|
186
|
+
:datetime => { :name => "datetime" },
|
187
|
+
:timestamp => { :name => "datetime" },
|
188
|
+
:time => { :name => "time" },
|
189
|
+
:date => { :name => "date" },
|
190
|
+
:binary => { :name => "blob" },
|
191
|
+
:boolean => { :name => "tinyint", :limit => 1 }
|
192
|
+
}
|
181
193
|
|
182
194
|
def initialize(connection, logger, connection_options, config)
|
183
195
|
super(connection, logger)
|
@@ -187,7 +199,7 @@ module ActiveRecord
|
|
187
199
|
end
|
188
200
|
|
189
201
|
def adapter_name #:nodoc:
|
190
|
-
|
202
|
+
ADAPTER_NAME
|
191
203
|
end
|
192
204
|
|
193
205
|
def supports_migrations? #:nodoc:
|
@@ -195,20 +207,7 @@ module ActiveRecord
|
|
195
207
|
end
|
196
208
|
|
197
209
|
def native_database_types #:nodoc:
|
198
|
-
|
199
|
-
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze,
|
200
|
-
:string => { :name => "varchar", :limit => 255 },
|
201
|
-
:text => { :name => "text" },
|
202
|
-
:integer => { :name => "int", :limit => 4 },
|
203
|
-
:float => { :name => "float" },
|
204
|
-
:decimal => { :name => "decimal" },
|
205
|
-
:datetime => { :name => "datetime" },
|
206
|
-
:timestamp => { :name => "datetime" },
|
207
|
-
:time => { :name => "time" },
|
208
|
-
:date => { :name => "date" },
|
209
|
-
:binary => { :name => "blob" },
|
210
|
-
:boolean => { :name => "tinyint", :limit => 1 }
|
211
|
-
}
|
210
|
+
NATIVE_DATABASE_TYPES
|
212
211
|
end
|
213
212
|
|
214
213
|
|
@@ -219,7 +218,7 @@ module ActiveRecord
|
|
219
218
|
s = column.class.string_to_binary(value).unpack("H*")[0]
|
220
219
|
"x'#{s}'"
|
221
220
|
elsif value.kind_of?(BigDecimal)
|
222
|
-
|
221
|
+
value.to_s("F")
|
223
222
|
else
|
224
223
|
super
|
225
224
|
end
|
@@ -286,6 +285,14 @@ module ActiveRecord
|
|
286
285
|
@connection.close rescue nil
|
287
286
|
end
|
288
287
|
|
288
|
+
def reset!
|
289
|
+
if @connection.respond_to?(:change_user)
|
290
|
+
# See http://bugs.mysql.com/bug.php?id=33540 -- the workaround way to
|
291
|
+
# reset the connection is to change the user to the same user.
|
292
|
+
@connection.change_user(@config[:username], @config[:password], @config[:database])
|
293
|
+
configure_connection
|
294
|
+
end
|
295
|
+
end
|
289
296
|
|
290
297
|
# DATABASE STATEMENTS ======================================
|
291
298
|
|
@@ -364,9 +371,9 @@ module ActiveRecord
|
|
364
371
|
end
|
365
372
|
end
|
366
373
|
|
367
|
-
def recreate_database(name) #:nodoc:
|
374
|
+
def recreate_database(name, options = {}) #:nodoc:
|
368
375
|
drop_database(name)
|
369
|
-
create_database(name)
|
376
|
+
create_database(name, options)
|
370
377
|
end
|
371
378
|
|
372
379
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
@@ -517,14 +524,33 @@ module ActiveRecord
|
|
517
524
|
keys.length == 1 ? [keys.first, nil] : nil
|
518
525
|
end
|
519
526
|
|
527
|
+
def case_sensitive_equality_operator
|
528
|
+
"= BINARY"
|
529
|
+
end
|
530
|
+
|
531
|
+
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
|
532
|
+
where_sql
|
533
|
+
end
|
534
|
+
|
520
535
|
private
|
521
536
|
def connect
|
537
|
+
@connection.reconnect = true if @connection.respond_to?(:reconnect=)
|
538
|
+
|
522
539
|
encoding = @config[:encoding]
|
523
540
|
if encoding
|
524
541
|
@connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
|
525
542
|
end
|
526
|
-
|
543
|
+
|
544
|
+
if @config[:sslca] || @config[:sslkey]
|
545
|
+
@connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
|
546
|
+
end
|
547
|
+
|
527
548
|
@connection.real_connect(*@connection_options)
|
549
|
+
configure_connection
|
550
|
+
end
|
551
|
+
|
552
|
+
def configure_connection
|
553
|
+
encoding = @config[:encoding]
|
528
554
|
execute("SET NAMES '#{encoding}'") if encoding
|
529
555
|
|
530
556
|
# By default, MySQL 'where id is null' selects the last inserted id.
|
@@ -68,72 +68,6 @@ module ActiveRecord
|
|
68
68
|
super
|
69
69
|
end
|
70
70
|
|
71
|
-
# Escapes binary strings for bytea input to the database.
|
72
|
-
def self.string_to_binary(value)
|
73
|
-
if PGconn.respond_to?(:escape_bytea)
|
74
|
-
self.class.module_eval do
|
75
|
-
define_method(:string_to_binary) do |value|
|
76
|
-
PGconn.escape_bytea(value) if value
|
77
|
-
end
|
78
|
-
end
|
79
|
-
else
|
80
|
-
self.class.module_eval do
|
81
|
-
define_method(:string_to_binary) do |value|
|
82
|
-
if value
|
83
|
-
result = ''
|
84
|
-
value.each_byte { |c| result << sprintf('\\\\%03o', c) }
|
85
|
-
result
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
self.class.string_to_binary(value)
|
91
|
-
end
|
92
|
-
|
93
|
-
# Unescapes bytea output from a database to the binary string it represents.
|
94
|
-
def self.binary_to_string(value)
|
95
|
-
# In each case, check if the value actually is escaped PostgreSQL bytea output
|
96
|
-
# or an unescaped Active Record attribute that was just written.
|
97
|
-
if PGconn.respond_to?(:unescape_bytea)
|
98
|
-
self.class.module_eval do
|
99
|
-
define_method(:binary_to_string) do |value|
|
100
|
-
if value =~ /\\\d{3}/
|
101
|
-
PGconn.unescape_bytea(value)
|
102
|
-
else
|
103
|
-
value
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
else
|
108
|
-
self.class.module_eval do
|
109
|
-
define_method(:binary_to_string) do |value|
|
110
|
-
if value =~ /\\\d{3}/
|
111
|
-
result = ''
|
112
|
-
i, max = 0, value.size
|
113
|
-
while i < max
|
114
|
-
char = value[i]
|
115
|
-
if char == ?\\
|
116
|
-
if value[i+1] == ?\\
|
117
|
-
char = ?\\
|
118
|
-
i += 1
|
119
|
-
else
|
120
|
-
char = value[i+1..i+3].oct
|
121
|
-
i += 3
|
122
|
-
end
|
123
|
-
end
|
124
|
-
result << char
|
125
|
-
i += 1
|
126
|
-
end
|
127
|
-
result
|
128
|
-
else
|
129
|
-
value
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
self.class.binary_to_string(value)
|
135
|
-
end
|
136
|
-
|
137
71
|
# Maps PostgreSQL-specific data types to logical Rails types.
|
138
72
|
def simplified_type(field_type)
|
139
73
|
case field_type
|
@@ -246,9 +180,26 @@ module ActiveRecord
|
|
246
180
|
# * <tt>:min_messages</tt> - An optional client min messages that is used in a <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
|
247
181
|
# * <tt>:allow_concurrency</tt> - If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods.
|
248
182
|
class PostgreSQLAdapter < AbstractAdapter
|
183
|
+
ADAPTER_NAME = 'PostgreSQL'.freeze
|
184
|
+
|
185
|
+
NATIVE_DATABASE_TYPES = {
|
186
|
+
:primary_key => "serial primary key".freeze,
|
187
|
+
:string => { :name => "character varying", :limit => 255 },
|
188
|
+
:text => { :name => "text" },
|
189
|
+
:integer => { :name => "integer" },
|
190
|
+
:float => { :name => "float" },
|
191
|
+
:decimal => { :name => "decimal" },
|
192
|
+
:datetime => { :name => "timestamp" },
|
193
|
+
:timestamp => { :name => "timestamp" },
|
194
|
+
:time => { :name => "time" },
|
195
|
+
:date => { :name => "date" },
|
196
|
+
:binary => { :name => "bytea" },
|
197
|
+
:boolean => { :name => "boolean" }
|
198
|
+
}
|
199
|
+
|
249
200
|
# Returns 'PostgreSQL' as adapter name for identification purposes.
|
250
201
|
def adapter_name
|
251
|
-
|
202
|
+
ADAPTER_NAME
|
252
203
|
end
|
253
204
|
|
254
205
|
# Initializes and connects a PostgreSQL adapter.
|
@@ -290,20 +241,7 @@ module ActiveRecord
|
|
290
241
|
end
|
291
242
|
|
292
243
|
def native_database_types #:nodoc:
|
293
|
-
|
294
|
-
:primary_key => "serial primary key",
|
295
|
-
:string => { :name => "character varying", :limit => 255 },
|
296
|
-
:text => { :name => "text" },
|
297
|
-
:integer => { :name => "integer" },
|
298
|
-
:float => { :name => "float" },
|
299
|
-
:decimal => { :name => "decimal" },
|
300
|
-
:datetime => { :name => "timestamp" },
|
301
|
-
:timestamp => { :name => "timestamp" },
|
302
|
-
:time => { :name => "time" },
|
303
|
-
:date => { :name => "date" },
|
304
|
-
:binary => { :name => "bytea" },
|
305
|
-
:boolean => { :name => "boolean" }
|
306
|
-
}
|
244
|
+
NATIVE_DATABASE_TYPES
|
307
245
|
end
|
308
246
|
|
309
247
|
# Does PostgreSQL support migrations?
|
@@ -331,6 +269,10 @@ module ActiveRecord
|
|
331
269
|
postgresql_version >= 80200
|
332
270
|
end
|
333
271
|
|
272
|
+
def supports_ddl_transactions?
|
273
|
+
true
|
274
|
+
end
|
275
|
+
|
334
276
|
# Returns the configured supported identifier length supported by PostgreSQL,
|
335
277
|
# or report the default of 63 on PostgreSQL 7.x.
|
336
278
|
def table_alias_length
|
@@ -339,10 +281,78 @@ module ActiveRecord
|
|
339
281
|
|
340
282
|
# QUOTING ==================================================
|
341
283
|
|
284
|
+
# Escapes binary strings for bytea input to the database.
|
285
|
+
def escape_bytea(value)
|
286
|
+
if PGconn.respond_to?(:escape_bytea)
|
287
|
+
self.class.instance_eval do
|
288
|
+
define_method(:escape_bytea) do |value|
|
289
|
+
PGconn.escape_bytea(value) if value
|
290
|
+
end
|
291
|
+
end
|
292
|
+
else
|
293
|
+
self.class.instance_eval do
|
294
|
+
define_method(:escape_bytea) do |value|
|
295
|
+
if value
|
296
|
+
result = ''
|
297
|
+
value.each_byte { |c| result << sprintf('\\\\%03o', c) }
|
298
|
+
result
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
escape_bytea(value)
|
304
|
+
end
|
305
|
+
|
306
|
+
# Unescapes bytea output from a database to the binary string it represents.
|
307
|
+
# NOTE: This is NOT an inverse of escape_bytea! This is only to be used
|
308
|
+
# on escaped binary output from database drive.
|
309
|
+
def unescape_bytea(value)
|
310
|
+
# In each case, check if the value actually is escaped PostgreSQL bytea output
|
311
|
+
# or an unescaped Active Record attribute that was just written.
|
312
|
+
if PGconn.respond_to?(:unescape_bytea)
|
313
|
+
self.class.instance_eval do
|
314
|
+
define_method(:unescape_bytea) do |value|
|
315
|
+
if value =~ /\\\d{3}/
|
316
|
+
PGconn.unescape_bytea(value)
|
317
|
+
else
|
318
|
+
value
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
else
|
323
|
+
self.class.instance_eval do
|
324
|
+
define_method(:unescape_bytea) do |value|
|
325
|
+
if value =~ /\\\d{3}/
|
326
|
+
result = ''
|
327
|
+
i, max = 0, value.size
|
328
|
+
while i < max
|
329
|
+
char = value[i]
|
330
|
+
if char == ?\\
|
331
|
+
if value[i+1] == ?\\
|
332
|
+
char = ?\\
|
333
|
+
i += 1
|
334
|
+
else
|
335
|
+
char = value[i+1..i+3].oct
|
336
|
+
i += 3
|
337
|
+
end
|
338
|
+
end
|
339
|
+
result << char
|
340
|
+
i += 1
|
341
|
+
end
|
342
|
+
result
|
343
|
+
else
|
344
|
+
value
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
unescape_bytea(value)
|
350
|
+
end
|
351
|
+
|
342
352
|
# Quotes PostgreSQL-specific data types for SQL input.
|
343
353
|
def quote(value, column = nil) #:nodoc:
|
344
354
|
if value.kind_of?(String) && column && column.type == :binary
|
345
|
-
"#{quoted_string_prefix}'#{
|
355
|
+
"#{quoted_string_prefix}'#{escape_bytea(value)}'"
|
346
356
|
elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/
|
347
357
|
"xml '#{quote_string(value)}'"
|
348
358
|
elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/
|
@@ -455,11 +465,20 @@ module ActiveRecord
|
|
455
465
|
|
456
466
|
# create a 2D array representing the result set
|
457
467
|
def result_as_array(res) #:nodoc:
|
468
|
+
# check if we have any binary column and if they need escaping
|
469
|
+
unescape_col = []
|
470
|
+
for j in 0...res.nfields do
|
471
|
+
# unescape string passed BYTEA field (OID == 17)
|
472
|
+
unescape_col << ( res.ftype(j)==17 )
|
473
|
+
end
|
474
|
+
|
458
475
|
ary = []
|
459
476
|
for i in 0...res.ntuples do
|
460
477
|
ary << []
|
461
478
|
for j in 0...res.nfields do
|
462
|
-
|
479
|
+
data = res.getvalue(i,j)
|
480
|
+
data = unescape_bytea(data) if unescape_col[j] and data.is_a?(String)
|
481
|
+
ary[i] << data
|
463
482
|
end
|
464
483
|
end
|
465
484
|
return ary
|
@@ -510,6 +529,45 @@ module ActiveRecord
|
|
510
529
|
execute "ROLLBACK"
|
511
530
|
end
|
512
531
|
|
532
|
+
# ruby-pg defines Ruby constants for transaction status,
|
533
|
+
# ruby-postgres does not.
|
534
|
+
PQTRANS_IDLE = defined?(PGconn::PQTRANS_IDLE) ? PGconn::PQTRANS_IDLE : 0
|
535
|
+
|
536
|
+
# Check whether a transaction is active.
|
537
|
+
def transaction_active?
|
538
|
+
@connection.transaction_status != PQTRANS_IDLE
|
539
|
+
end
|
540
|
+
|
541
|
+
# Wrap a block in a transaction. Returns result of block.
|
542
|
+
def transaction(start_db_transaction = true)
|
543
|
+
transaction_open = false
|
544
|
+
begin
|
545
|
+
if block_given?
|
546
|
+
if start_db_transaction
|
547
|
+
begin_db_transaction
|
548
|
+
transaction_open = true
|
549
|
+
end
|
550
|
+
yield
|
551
|
+
end
|
552
|
+
rescue Exception => database_transaction_rollback
|
553
|
+
if transaction_open && transaction_active?
|
554
|
+
transaction_open = false
|
555
|
+
rollback_db_transaction
|
556
|
+
end
|
557
|
+
raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
|
558
|
+
end
|
559
|
+
ensure
|
560
|
+
if transaction_open && transaction_active?
|
561
|
+
begin
|
562
|
+
commit_db_transaction
|
563
|
+
rescue Exception => database_transaction_rollback
|
564
|
+
rollback_db_transaction
|
565
|
+
raise
|
566
|
+
end
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
|
513
571
|
# SCHEMA STATEMENTS ========================================
|
514
572
|
|
515
573
|
def recreate_database(name) #:nodoc:
|
@@ -618,6 +676,19 @@ module ActiveRecord
|
|
618
676
|
end
|
619
677
|
end
|
620
678
|
|
679
|
+
# Returns the current database name.
|
680
|
+
def current_database
|
681
|
+
query('select current_database()')[0][0]
|
682
|
+
end
|
683
|
+
|
684
|
+
# Returns the current database encoding format.
|
685
|
+
def encoding
|
686
|
+
query(<<-end_sql)[0][0]
|
687
|
+
SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
|
688
|
+
WHERE pg_database.datname LIKE '#{current_database}'
|
689
|
+
end_sql
|
690
|
+
end
|
691
|
+
|
621
692
|
# Sets the schema search path to a string of comma-separated schema names.
|
622
693
|
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
623
694
|
# See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
|
@@ -851,7 +922,7 @@ module ActiveRecord
|
|
851
922
|
end
|
852
923
|
|
853
924
|
private
|
854
|
-
# The internal PostgreSQL
|
925
|
+
# The internal PostgreSQL identifier of the money data type.
|
855
926
|
MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
|
856
927
|
|
857
928
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|