| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
| |
|
| |
|
| |
|
|\
| |
| | |
Build a multi-statement query when inserting fixtures
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
- The `insert_fixtures` method can be optimized by making a single multi statement query for all fixtures having the same connection instead of doing a single query per table
- The previous code was bulk inserting fixtures for a single table, making X query for X fixture files
- This patch builds a single **multi statement query** for every tables. Given a set of 3 fixtures (authors, dogs, computers):
```ruby
# before
%w(authors dogs computers).each do |table|
sql = build_sql(table)
connection.query(sql)
end
# after
sql = build_sql(authors, dogs, computers)
connection.query(sql)
```
- `insert_fixtures` is now deprecated, `insert_fixtures_set` is the new way to go with performance improvement
- My tests were done with an app having more than 700 fixtures, the time it takes to insert all of them was around 15s. Using a single multi statement query, it took on average of 8 seconds
- In order for a multi statement to be executed, mysql needs to be connected with the `MULTI_STATEMENTS` [flag](https://dev.mysql.com/doc/refman/5.7/en/c-api-multiple-queries.html), which is done before inserting the fixtures by reconnecting to da the database with the flag declared. Reconnecting to the database creates some caveats:
1. We loose all open transactions; Inside the original code, when inserting fixtures, a transaction is open. Multple delete statements are [executed](https://github.com/rails/rails/blob/a681eaf22955734c142609961a6d71746cfa0583/activerecord/lib/active_record/fixtures.rb#L566) and finally the fixtures are inserted. The problem with this patch is that we need to open the transaction only after we reconnect to the DB otherwise reconnecting drops the open transaction which doesn't commit all delete statements and inserting fixtures doesn't work since we duplicated them (Primary key duplicate exception)...
- In order to fix this problem, the transaction is now open directly inside the `insert_fixtures` method, right after we reconnect to the db
- As an effect, since the transaction is open inside the `insert_fixtures` method, the DELETE statements need to be executed here since the transaction is open later
2. The same problem happens for the `disable_referential_integrity` since we reconnect, the `FOREIGN_KEY_CHECKS` is reset to the original value
- Same solution as 1. , the disable_referential_integrity can be called after we reconnect to the transaction
3. When the multi statement query is executed, no other queries can be performed until we paginate over the set of results, otherwise mysql throws a "Commands out of sync" [Ref](https://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html)
- Iterating over the set of results until `mysql_client.next_result` is false. [Ref](https://github.com/brianmario/mysql2#multiple-result-sets)
- Removed the `active_record.sql "Fixture delete"` notification, the delete statements are now inside the INSERT's one
- On mysql the `max_allowed_packet` is looked up:
1. Before executing the multi-statements query, we check the packet length of each statements, if the packet is bigger than the max_allowed_packet config, an `ActiveRecordError` is raised
2. Otherwise we concatenate the current sql statement into the previous and so on until the packet is `< max_allowed_packet`
|
| | |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Rails has some support for multiple databases but it can be hard to
handle migrations with those. The easiest way to implement multiple
databases is to contain migrations into their own folder ("db/migrate"
for the primary db and "db/seconddb_migrate" for the second db). Without
this you would need to write code that allowed you to switch connections
in migrations. I can tell you from experience that is not a fun way to
implement multiple databases.
This refactoring is a pre-requisite for implementing other features
related to parallel testing and improved handling for multiple
databases.
The refactoring here moves the class methods from the `Migrator` class
into it's own new class `MigrationContext`. The goal was to move the
`migrations_paths` method off of the `Migrator` class and onto the
connection. This allows users to do the following in their
`database.yml`:
```
development:
adapter: mysql2
username: root
password:
development_seconddb:
adapter: mysql2
username: root
password:
migrations_paths: "db/second_db_migrate"
```
Migrations for the `seconddb` can now be store in the
`db/second_db_migrate` directory. Migrations for the primary database
are stored in `db/migrate`".
The refactoring here drastically reduces the internal API for migrations
since we don't need to pass `migrations_paths` around to every single
method. Additionally this change does not require any Rails applications
to make changes unless they want to use the new public API. All of the
class methods from the `Migrator` class were `nodoc`'d except for the
`migrations_paths` and `migrations_path` getter/setters respectively.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
If `collation` is given without `charset`, it may generate invalid SQL.
For example `create_database(:matt_aimonetti, collation: "utf8mb4_bin")`:
```
> CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `utf8` COLLATE `utf8mb4_bin`;
ERROR 1253 (42000): COLLATION 'utf8mb4_bin' is not valid for CHARACTER SET 'utf8'
```
In MySQL, charset is used to find the default collation. If `collation`
is given explicitly, it is not necessary to give extra charset.
|
| |
| |
| |
| |
| | |
This method which is used only in the internal was introduced in
ac384820 and was renamed in #17579. It does not need to be exposed.
|
| |
| |
| |
| |
| |
| |
| |
| | |
An empty string is an invalid value in Ruby's range class.
So need to handle `Float::INFINITY` as it is and cast it in
`encode_range`.
Fixes #31612
|
| | |
|
| | |
|
| |
| |
| |
| | |
Follow up of #31177.
|
|\ \
| | |
| | |
| | |
| | | |
albertoalmagro/remove-default-mysql-engine-from-ar-5-2
Remove default ENGINE=InnoDB for Mysql2 adapter
|
| |/
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Before this commit ENGINE=InnoDB was added by default to Mysql2 adapter
+create_table+ if no +options+ option was provided. This default ENGINE
was lost as soon as something was passed in at +options+ option, making
its goal and propagation inconsistent, as the programmer needed to
remember including ENGINE=InnoDB when something was passed in.
This commit removes default ENGINE as its use isn't needed anymore for
current MySQL and MariaDB versions. It adds compatibility support and
tests to ensure that default ENGINE is still present for migrations
with version 5.1 and before. It also ensures we still dump the ENGINE
option to +schema.rb+ in order to avoid inconsistencies.
|
| |
| |
| |
| |
| | |
These are using `remove_column` directly, not used `t` in
`change_table`.
|
| |
| |
| |
| | |
Follow up #31428 to address similar exceptions with mysql2 adapter
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
`BigDecimal.new` has been deprecated in BigDecimal 1.3.3
which will be a default for Ruby 2.5.
Refer https://github.com/ruby/bigdecimal/commit/533737338db915b00dc7168c3602e4b462b23503
```
$ cd rails/activerecord/
$ git grep -l BigDecimal.new | grep \.rb | xargs sed -i -e "s/BigDecimal.new/BigDecimal/g"
```
- Changes made only to Active Record. Will apply the same change to
other module once this commit is merged.
- The following deprecation has not been addressed because it has been
reported at `ActiveRecord::Result.new`. `ActiveRecord::Result.ancestors`
did not show `BigDecimal`.
* Not addressed
```ruby
/path/to/rails/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb:34:
warning: BigDecimal.new is deprecated
```
* database_statements.rb:34
```ruby
ActiveRecord::Result.new(result.fields, result.to_a) if result
```
* ActiveRecord::Result.ancestors
```ruby
[ActiveRecord::Result,
Enumerable,
ActiveSupport::ToJsonWithActiveSupportEncoder,
Object,
Metaclass::ObjectMethods,
Mocha::ObjectMethods,
PP::ObjectMixin,
ActiveSupport::Dependencies::Loadable,
ActiveSupport::Tryable,
JSON::Ext::Generator::GeneratorMethods::Object,
Kernel,
BasicObject]
```
This commit has been tested with these Ruby and BigDecimal versions
- ruby 2.5 and bigdecimal 1.3.3
```
$ ruby -v
ruby 2.5.0dev (2017-12-14 trunk 61217) [x86_64-linux]
$ gem list |grep bigdecimal
bigdecimal (default: 1.3.3, default: 1.3.2)
```
- ruby 2.4 and bigdecimal 1.3.0
```
$ ruby -v
ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-linux-gnu]
$ gem list |grep bigdecimal
bigdecimal (default: 1.3.0)
```
- ruby 2.3 and bigdecimal 1.2.8
```
$ ruby -v
ruby 2.3.5p376 (2017-09-14 revision 59905) [x86_64-linux]
$ gem list |grep -i bigdecimal
bigdecimal (1.2.8)
```
- ruby 2.2 and bigdecimal 1.2.6
```
$ ruby -v
ruby 2.2.8p477 (2017-09-14 revision 59906) [x86_64-linux]
$ gem list |grep bigdecimal
bigdecimal (1.2.6)
```
|
|/
|
|
|
|
|
|
|
|
|
|
|
| |
* Ruby 2.4 introduces `report_on_exception` to control if it reports exceptions in thread,
this default value has been `false` in Ruby 2.4.
Refer https://www.ruby-lang.org/en/news/2016/11/09/ruby-2-4-0-preview3-released/
* Ruby 2.5 changes `report_on_exception` default value to `true`
since this commit https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?revision=61183&view=revision
This pull request suppresses expected exceptions by setting `report_on_exception` = `false`
it also supports Ruby 2.3 which does not have`report_on_exception`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`connection.primary_key` also return composite primary keys, so
`from_primary_key_column` may not be found even if `from_primary_key` is
presented.
```
% ARCONN=sqlite3 be ruby -w -Itest
test/cases/adapters/sqlite3/sqlite3_adapter_test.rb -n
test_copy_table_with_composite_primary_keys
Using sqlite3
Run options: -n test_copy_table_with_composite_primary_keys --seed 19041
# Running:
E
Error:
ActiveRecord::ConnectionAdapters::SQLite3AdapterTest#test_copy_table_with_composite_primary_keys:
NoMethodError: undefined method `type' for nil:NilClass
/path/to/rails/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb:411:in
`block in copy_table'
```
This change fixes `copy_table` to do not lose composite primary keys.
|
|\
| |
| | |
Fix sqlite migrations with custom primary keys
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Previously, if a record was created with a custom primary key, that
table could not be migrated using sqlite. While attempting to copy the
table, the type of the primary key was ignored.
Once that was corrected, copying the indexes would fail because custom
primary keys are autoindexed by sqlite by default.
To correct that, this skips copying the index if the index name begins
with "sqlite_". This is a reserved word that indicates that the
index is an internal schema object. SQLite prohibits applications from
creating objects whose names begin with "sqlite_", so this string should
be safe to use as a check.
ref https://www.sqlite.org/fileformat2.html#intschema
|
| |
| |
| |
| |
| | |
Actually SQLite3 doesn't have JSON storage class (so it is stored as a
TEXT like Date and Time). But emulating JSON types is convinient for
making database agnostic migrations.
|
| | |
|
|\ \
| |/
|/| |
Extract sql fragment generators from PostgreSQL adapter
|
| | |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Add support for specifying non-default operator classes in PostgreSQL
indexes. An example CREATE INDEX query that becomes possible is:
CREATE INDEX users_name ON users USING gist (name gist_trgm_ops);
Previously it was possible to specify the `gist` index but not the
custom operator class. The `add_index` call for the above query is:
add_index :users, :name, using: :gist, opclasses: {name: :gist_trgm_ops}
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
statement due to user request (#31235)
This changes `StatementTimeout` to `QueryCanceled` for PostgreSQL.
In MySQL, errno 1317 (`ER_QUERY_INTERRUPTED`) is only used when the
query is manually cancelled.
But in PostgreSQL, `QUERY_CANCELED` error code (57014) which is used
`StatementTimeout` is also used when the both case. And, we can not tell
which reason happened.
So I decided to introduce new error class `QueryCanceled` closer to the
error code name.
|
|/
|
|
|
|
| |
Since #31129, new error class `StatementTimeout` has been added.
`TransactionTimeout` is caused by the timeout shorter than
`StatementTimeout`, but its name is too generic. I think that it should
be a name that understands the difference with `StatementTimeout`.
|
|\
| |
| | |
Do not use `Arel.star` when `ignored_columns`
|
| | |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
timeout exceeded (#31129)
We are sometimes using The MAX_EXECUTION_TIME hint for MySQL depending
on the situation. It will prevent catastrophic performance down by wrong
performing queries.
The new error class `StatementTimeout` will make to be easier to handle
that case.
https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html#optimizer-hints-execution-time
|
| |
| |
| |
| | |
Follow up of #30360.
|
| | |
|
| |
| |
| |
| | |
Follow up of #31004.
|
|/ |
|
|
|
|
|
|
|
|
| |
since the minimum version of PostgreSQL currently Rails supports is 9.1,
there is no need to handle if `supports_extensions?`
Refer https://www.postgresql.org/docs/9.1/static/sql-createextension.html
"CREATE EXTENSION"
|
| |
|
| |
|
|
|
|
|
|
|
|
| |
We already found the longer sequence name, but we could not consider
whether it was the sequence name created by serial type due to missed a
max identifier length limitation. I've addressed the sequence name
consideration to respect the max identifier length.
Fixes #28332.
|
|
|
|
|
|
|
|
|
|
| |
Currently `AUTO_INCREMENT` is implicitly used in the default primary key
definition. But `AUTO_INCREMENT` is not only used for single column
primary key, but also for composite primary key. In that case,
`auto_increment: true` should be dumped explicitly in the
`db/schema.rb`.
Fixes #30894.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
PostgreSQL 9.1+ introduced range types, and Rails added support for
using this datatype in ActiveRecord. However, the serialization of
`PostgreSQL::OID::Range` was incomplete, because it did not properly
quote the bounds that make up the range. A clear example of this is a
`tsrange`.
Normally, ActiveRecord quotes Date/Time objects to include the
milliseconds. However, the way `PostgreSQL::OID::Range` serialized its
bounds, the milliseconds were dropped. This meant that the value was
incomplete and not equal to the submitted value.
An example of normal timestamps vs. a `tsrange`. Note how the bounds
for the range do not include their milliseconds (they were present in
the ruby Range):
UPDATE "iterations" SET "updated_at" = $1, "range" = $2 WHERE
"iterations"."id" = $3
[["updated_at", "2017-09-23 17:07:01.304864"],
["range", "[2017-09-23 00:00:00 UTC,2017-09-23 23:59:59 UTC]"],
["id", 1234]]
`PostgreSQL::OID::Range` serialized the range by interpolating a
string for the range, which works for most cases, but does not work
for timestamps:
def serialize(value)
if value.is_a?(::Range)
from = type_cast_single_for_database(value.begin)
to = type_cast_single_for_database(value.end)
"[#{from},#{to}#{value.exclude_end? ? ')' : ']'}"
else
super
end
end
(byebug) from = type_cast_single_for_database(value.begin)
2010-01-01 13:30:00 UTC
(byebug) to = type_cast_single_for_database(value.end)
2011-02-02 19:30:00 UTC
(byebug) "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}"
"[2010-01-01 13:30:00 UTC,2011-02-02 19:30:00 UTC)"
@sgrif (the original implementer for Postgres Range support) provided
some feedback about where the quoting should occur:
Yeah, quoting at all is definitely wrong here. I'm not sure what I
was thinking in 02579b5, but what this is doing is definitely in the
wrong place. It should probably just be returning a range of
subtype.serialize(value.begin) and subtype.serialize(value.end), and
letting the adapter handle the rest.
`Postgres::OID::Range` now returns a `Range` object, and
`ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting` can now encode
and quote a `Range`:
def encode_range(range)
"[#{type_cast(range.first)},#{type_cast(range.last)}#{range.exclude_end? ? ')' : ']'}"
end
...
encode_range(range)
#=> "['2010-01-01 13:30:00.670277','2011-02-02 19:30:00.745125')"
This commit includes tests to make sure the milliseconds are
preserved in `tsrange` and `tstzrange` columns
|
|
|
|
|
|
| |
If collided named sequence already exists, newly created serial column
will generate alternative sequence name. Fix sequence name detection to
allow the alternative names.
|
| |
|
|
|
|
| |
Fixes #30539.
|
| |
|
|
|
|
|
|
| |
I was added a table options after `force: :cascade` in #17569 for not
touching existing tests (reducing diff). But `force: :cascade` is not an
important information. So I prefer to place a table options before
`force: :cascade`.
|
|\
| |
| | |
Add TransactionTimeout for MySQL error code 1205
|