I'm converting my Rails project to multi-tenant and I'm using Apartment.
I followed the directions on their README and the video they link to
My apartment.rb looks like this:
require 'apartment/elevators/subdomain'
Apartment.configure do |config|
config.excluded_models = ['Project', 'User']
config.tenant_names = lambda { Project.pluck :subdomain }
config.use_schemas = true
config.persistent_schemas = %w{ public }
end
Rails.application.config.middleware.use Apartment::Elevators::Subdomain
My Project model looks like this:
class Project < ApplicationRecord
after_create :create_tenant
private
def create_tenant
Apartment::Tenant.create(subdomain)
end
end
I can run db:migrate and db:setup no problem. It creates all my tables in the public schema then.
But, when I try to create a new Project, I get this (from rails c)
C:\code\vulnerability-history>rails c
Loading development environment (Rails 5.0.1)
irb(main):001:0> Project.create(name: 'foo', subdomain: 'bar')
(0.0ms) BEGIN
SQL (1.0ms) INSERT INTO "public"."projects" ("name", "subdomain") VALUES ($1, $2) RETURNING "id" [["name", "foo"], ["subdomain", "bar"]]
(1.0ms) CREATE SCHEMA "bar"
SQL (1.0ms) CREATE EXTENSION IF NOT EXISTS "plpgsql"
(2.0ms) DROP TABLE "commit_filepaths" CASCADE
(14.0ms) CREATE TABLE "commit_filepaths" ("id" serial primary key, "commit_id" integer NOT NULL, "filepath_id" integer NOT NULL, "total_churn" integer DEFAULT 0 NOT NULL)
(1.0ms) DROP TABLE "commits" CASCADE
(11.0ms) CREATE TABLE "commits" ("id" serial primary key, "commit_hash" character varying NOT NULL, "author_id" integer NOT NULL, "message" character varying NOT NULL, "date_created" timestamp NOT NULL, "notes" jsonb DEFAULT '{}' NOT NULL)
(1.0ms) DROP TABLE "developers" CASCADE
(11.0ms) CREATE TABLE "developers" ("id" serial primary key, "email" character varying NOT NULL)
(1.5ms) DROP TABLE "events" CASCADE
(9.4ms) CREATE TABLE "events" ("id" serial primary key, "detail_type" character varying NOT NULL, "detail_id" integer NOT NULL, "title_template" character varying DEFAULT ':title:' NOT NULL, "description_template" character varying DEFAULT ':description:' NOT NULL, "type_template" character varying DEFAULT ':type:' NOT NULL, "date_template" character varying DEFAULT ':date:' NOT NULL, "style_id" integer NOT NULL)
(1.0ms) DROP TABLE "filepaths" CASCADE
(11.0ms) CREATE TABLE "filepaths" ("id" serial primary key, "filepath" character varying NOT NULL, "notes" jsonb DEFAULT '{}' NOT NULL)
(1.0ms) DROP TABLE "fixes" CASCADE
(11.0ms) CREATE TABLE "fixes" ("id" serial primary key, "commit_id" integer NOT NULL, "vulnerability_id" integer NOT NULL, "notes" jsonb DEFAULT '{}' NOT NULL)
(1.0ms) DROP TABLE "projects" CASCADE
(13.0ms) CREATE TABLE "projects" ("id" serial primary key, "name" character varying NOT NULL, "subdomain" character varying NOT NULL)
(1.0ms) DROP TABLE "releases" CASCADE
(10.6ms) CREATE TABLE "releases" ("id" serial primary key, "number" integer NOT NULL, "date_released" timestamp NOT NULL, "project" character varying NOT NULL, "notes" jsonb NOT NULL)
(1.0ms) DROP TABLE "styles" CASCADE
(11.3ms) CREATE TABLE "styles" ("id" serial primary key, "name" character varying NOT NULL, "color" character varying DEFAULT '#ffffff' NOT NULL, "icon" character varying DEFAULT 'stars' NOT NULL)
(1.0ms) DROP TABLE "users" CASCADE
(11.4ms) CREATE TABLE "users" ("id" serial primary key, "username" character varying NOT NULL)
(1.0ms) DROP TABLE "vccs" CASCADE
(10.0ms) CREATE TABLE "vccs" ("id" serial primary key, "commit_id" integer NOT NULL, "vulnerability_id" integer NOT NULL, "notes" jsonb DEFAULT '{}' NOT NULL)
(1.0ms) DROP TABLE "vulnerabilities" CASCADE
(12.3ms) CREATE TABLE "vulnerabilities" ("id" serial primary key, "cve" character varying NOT NULL, "announced" timestamp NOT NULL, "description" character varying DEFAULT '' NOT NULL, "notes" jsonb DEFAULT '{}' NOT NULL)
(1.0ms) SELECT version FROM "schema_migrations"
ActiveRecord::InternalMetadata Load (1.0ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", :environment], ["LIMIT", 1]]
(0.0ms) ROLLBACK
ActiveRecord::StatementInvalid: PG::InFailedSqlTransaction: ERROR: current transaction is aborted, commands ignored until end of transaction block
: SET search_path TO "public", "public"
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:98:in `async_exec'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:98:in `block in execute'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/notifications/instrumenter.rb:21:in `instrument'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/connection_adapters/postgresql/database_statements.rb:97:in `execute'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/connection_adapters/postgresql/schema_statements.rb:315:in `schema_search_path='
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/apartment-1.2.0/lib/apartment/adapters/postgresql_adapter.rb:40:in `reset'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/apartment-1.2.0/lib/apartment/adapters/abstract_adapter.rb:109:in `rescue in ensure in switch'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/apartment-1.2.0/lib/apartment/adapters/abstract_adapter.rb:109:in `ensure in switch'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/apartment-1.2.0/lib/apartment/adapters/abstract_adapter.rb:109:in `switch'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/apartment-1.2.0/lib/apartment/adapters/abstract_adapter.rb:26:in `block in create'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:97:in `__run_callbacks__'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:750:in `_run_create_callbacks'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activesupport-5.0.1/lib/active_support/callbacks.rb:90:in `run_callbacks'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/apartment-1.2.0/lib/apartment/adapters/abstract_adapter.rb:23:in `create'
... 22 levels...
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/transactions.rb:211:in `transaction'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/transactions.rb:392:in `with_transaction_returning_status'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/transactions.rb:319:in `block in save'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/transactions.rb:334:in `rollback_active_record_state!'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/transactions.rb:318:in `save'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/suppressor.rb:41:in `save'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-5.0.1/lib/active_record/persistence.rb:34:in `create'
from (irb):1
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/railties-5.0.1/lib/rails/commands/console.rb:65:in `start'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/railties-5.0.1/lib/rails/commands/console_helper.rb:9:in `start'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/railties-5.0.1/lib/rails/commands/commands_tasks.rb:78:in `console'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/railties-5.0.1/lib/rails/commands/commands_tasks.rb:49:in `run_command!'
from C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/railties-5.0.1/lib/rails/commands.rb:18:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'
I did notice this issue with a similar stacktrace, but the workaround doesn't make sense to me.
I'm using:
Ruby 2.3.1 (2016-04-26 patchlevel 112) [x64-mingw32]
apartment 1.2.0
Rails 5.0.1
If the log is to be trusted, it appears to be failing on:
SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", :environment], ["LIMIT", 1]]
Does your db/schema.rb contain this table? It's a new system table in Rails 5.
Related
I have a simple migration which fails: The database is not affected, it still contains the old column name after migrating.
class RenameTypeToLicenseTypeInLicenses < ActiveRecord::Migration[5.1]
def change
rename_column :licenses, :type, :license_type
end
end
Renaming type to license_type is necessary because of this error:
The single-table inheritance mechanism failed to locate the subclass: 'creative commons'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite License.inheritance_column to use another column for that information.
This is the output of rails db:migrate
igrating to RenameTypeToLicenseTypeInLicenses (20210422110849)
(0.2ms) BEGIN
== 20210422110849 RenameTypeToLicenseTypeInLicenses: migrating ================
== 20210422110849 RenameTypeToLicenseTypeInLicenses: migrated (0.0000s) =======
SQL (0.9ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20210422110849"]]
(1.4ms) COMMIT
ActiveRecord::InternalMetadata Load (0.5ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
(0.3ms) BEGIN
(0.3ms) COMMIT
(0.4ms) SELECT pg_advisory_unlock(2195010657070977375)
(0.6ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
How can I rename the type column?
edit:
As there's only little data in the table, I tried another approach:
remove_column :licenses, :type, :string
add_column :licenses, :license_type, :string
It doesn't work either. Output is
[NAME COLLISION] `type` is a reserved key in LicenseResource.
(0.7ms) SELECT pg_try_advisory_lock(2195010657070977375);
(1.3ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Migrating to ChangeTypeToLicenseTypeFromLicenses (20210422122638)
(0.5ms) BEGIN
== 20210422122638 ChangeTypeToLicenseTypeFromLicenses: migrating ==============
== 20210422122638 ChangeTypeToLicenseTypeFromLicenses: migrated (0.0000s) =====
SQL (1.0ms) INSERT INTO "schema_migrations" ("version") VALUES ($1) RETURNING "version" [["version", "20210422122638"]]
(4.6ms) COMMIT
ActiveRecord::InternalMetadata Load (0.4ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
(0.4ms) BEGIN
(0.4ms) COMMIT
(0.6ms) SELECT pg_advisory_unlock(2195010657070977375)
(0.9ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
Yeah, I wonder if ActiveRecord's migration code might be trying its best to keep type intact, on the assumption that it's currently being used for single table inheritance; it's oblivious to the fact you want to change it because it currently isn't 😊
There may be an idiomatic Rails way of telling the migration that what you're doing is fine, but to be honest I'd be tempted to drop down to the SQL level, thus bypassing Rails' erroneous protection:
class RenameTypeToLicenseTypeInLicenses < ActiveRecord::Migration[5.1]
def up
execute <<~SQL
ALTER TABLE licenses RENAME COLUMN type TO license_type;
SQL
end
def down
execute <<~SQL
ALTER TABLE licenses RENAME COLUMN license_type TO type;
SQL
end
end
As you're not using migration commands, you have to provide separate up/down code for both directions of the migration.
I believe this answer should help you: issue with column name 'type' in rails 3
type is one of those reserved words we shouldn't use. Check other reserved words here: https://riptutorial.com/ruby-on-rails/example/32446/reserved-word-list
We use Heroku review apps for QAing our PRs on Github. At some point a couple months ago we did some housekeeping and deleted a lot of old migration files and I think that is causing issues with the deploy on Heroku.
Here is my app.json file -
{
"name": "myapp",
"scripts": {
"postdeploy": "rake db:migrate db:seed db:fixtures:repopulate"
},
"env": {
"WEB_CONCURRENCY": "1",
"HEROKU_APP_NAME": {
"required": true
}
},
"formation": {
},
"addons": [
"heroku-redis",
{
"plan": "heroku-postgresql",
"options": {
"version": "9.6"
}
}
],
"buildpacks": [
]
}
Here is the results of running heroku releases:output -
Migrating to PopulateOldPasswordTable (20180305160642)
(1.3ms) BEGIN
== 20180305160642 PopulateOldPasswordTable: migrating =========================
User Load (2.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1000]]
(1.2ms) ROLLBACK
(1.4ms) SELECT pg_advisory_unlock(5078732648953702200)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::UndefinedTable: ERROR: relation "users" does not exist
LINE 1: SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIM...
^
: SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `async_exec'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `block in exec_no_cache'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log'
/app/vendor/bundle/ruby/2.3.0/gems/activesupport-5.0.2/lib/active_support/notifications/instrumenter.rb:21:in `instrument'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `exec_no_cache'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/connection_adapters/postgresql_adapter.rb:587:in `execute_and_clear'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/connection_adapters/postgresql/database_statements.rb:103:in `exec_query'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/querying.rb:39:in `find_by_sql'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/relation.rb:702:in `exec_queries'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/relation.rb:583:in `load'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/relation.rb:260:in `records'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/relation/batches.rb:200:in `block in in_batches'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/relation/batches.rb:198:in `loop'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/relation/batches.rb:198:in `in_batches'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/relation/batches.rb:120:in `find_in_batches'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/relation/batches.rb:58:in `find_each'
/app/vendor/bundle/ruby/2.3.0/gems/activerecord-5.0.2/lib/active_record/querying.rb:9:in `find_each'
/app/db/migrate/20180305160642_populate_old_password_table.rb:3:in `change'
It sounds like you deleted a migration that hadn't been run in all environments. You really don't want to do that.
The simplest way to recover, if the database doesn't contain anything of value, would be to destroy it and recreate it from scratch -- that'll use your schema.rb file, which should better match your other working environments.
If that isn't an option, consider bringing the old migration(s) back [via your git history], at least until you've finished running them everywhere.
Model Layout
Article (has_many :attachments, :comments, :tags)
- id: string
Comment (has_many :attachments)
- id: integer
- article_id: string
Attachment
- id: integer
- reference_type: string
- reference_id: string
Tag
- id: integer
- article_id: string
Erroneous Query
In the rails console I run:
Article.includes(:tags, :comments => :attachments).references(:tags)
And the resulting error is:
Article.includes(:tags, :comments => :attachments).references(:tags)
SQL (0.7ms) SELECT "articles"."id" AS t0_r0, "articles"."created_at" AS t0_r1, "articles"."updated_at" AS t0_r2, "tags"."id" AS t1_r0, "tags"."article_id" AS t1_r1, "tags"."created_at" AS t1_r2, "tags"."updated_at" AS t1_r3, "comments"."id" AS t2_r0, "comments"."article_id" AS t2_r1, "comments"."created_at" AS t2_r2, "comments"."updated_at" AS t2_r3, "attachments"."id" AS t3_r0, "attachments"."reference_type" AS t3_r1, "attachments"."reference_id" AS t3_r2, "attachments"."created_at" AS t3_r3, "attachments"."updated_at" AS t3_r4 FROM "articles" LEFT OUTER JOIN "tags" ON "tags"."article_id" = "articles"."id" LEFT OUTER JOIN "comments" ON "comments"."article_id" = "articles"."id" LEFT OUTER JOIN "attachments" ON "attachments"."reference_id" = "comments"."id" AND "attachments"."reference_type" = $1 [["reference_type", "Comment"]]
ActiveRecord::StatementInvalid: PG::UndefinedFunction: ERROR: operator does not exist: character varying = integer
LINE 1: ...OIN "attachments" ON "attachments"."reference_id" = "comment_...
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
: SELECT "articles"."id" AS t0_r0, "articles"."created_at" AS t0_r1, "articles"."updated_at" AS t0_r2, "tags"."id" AS t1_r0, "tags"."article_id" AS t1_r1, "tags"."created_at" AS t1_r2, "tags"."updated_at" AS t1_r3, "comments"."id" AS t2_r0, "comments"."article_id" AS t2_r1, "comments"."created_at" AS t2_r2, "comments"."updated_at" AS t2_r3, "attachments"."id" AS t3_r0, "attachments"."reference_type" AS t3_r1, "attachments"."reference_id" AS t3_r2, "attachments"."created_at" AS t3_r3, "attachments"."updated_at" AS t3_r4 FROM "articles" LEFT OUTER JOIN "tags" ON "tags"."article_id" = "articles"."id" LEFT OUTER JOIN "comments" ON "comments"."article_id" = "articles"."id" LEFT OUTER JOIN "attachments" ON "attachments"."reference_id" = "comments"."id" AND "attachments"."reference_type" = $1
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `async_exec'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `block in exec_no_cache'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/connection_adapters/abstract_adapter.rb:566:in `block in log'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activesupport-5.0.0/lib/active_support/notifications/instrumenter.rb:21:in `instrument'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/connection_adapters/abstract_adapter.rb:560:in `log'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `exec_no_cache'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/connection_adapters/postgresql_adapter.rb:587:in `execute_and_clear'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/connection_adapters/postgresql/database_statements.rb:103:in `exec_query'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/connection_adapters/abstract/query_cache.rb:70:in `select_all'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/relation/finder_methods.rb:389:in `find_with_associations'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/relation.rb:699:in `exec_queries'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/relation.rb:580:in `load'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/relation.rb:260:in `records'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activerecord-5.0.0/lib/active_record/relation.rb:683:in `inspect'
... 1 levels...
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/railties-5.0.0/lib/rails/commands/console_helper.rb:9:in `start'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/railties-5.0.0/lib/rails/commands/commands_tasks.rb:78:in `console'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/railties-5.0.0/lib/rails/commands/commands_tasks.rb:49:in `run_command!'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/railties-5.0.0/lib/rails/commands.rb:18:in `<top (required)>'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activesupport-5.0.0/lib/active_support/dependencies.rb:293:in `require'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activesupport-5.0.0/lib/active_support/dependencies.rb:293:in `block in require'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activesupport-5.0.0/lib/active_support/dependencies.rb:259:in `load_dependency'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activesupport-5.0.0/lib/active_support/dependencies.rb:293:in `require'
from /Users/jeremy/Documents/Synack/Code/experiments/joins-test/bin/rails:9:in `<top (required)>'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activesupport-5.0.0/lib/active_support/dependencies.rb:287:in `load'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activesupport-5.0.0/lib/active_support/dependencies.rb:287:in `block in load'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activesupport-5.0.0/lib/active_support/dependencies.rb:259:in `load_dependency'
from /Users/jeremy/.rvm/gems/ruby-2.2.2#rails5/gems/activesupport-5.0.0/lib/active_support/dependencies.rb:287:in `load'
from /Users/jeremy/.rvm/rubies/ruby-2.2.2/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
from /Users/jeremy/.rvm/rubies/ruby-2.2.2/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
Successful Query
Without a .references() clause, the query works properly, and fetches all models separately, creating four separate requests.
In the rails console I run:
Article.includes(:tags, :comments => :attachments)
And I see:
Article.includes(:tags, :comments => :attachments)
Article Load (5.1ms) SELECT "articles".* FROM "articles"
Tag Load (0.3ms) SELECT "tags".* FROM "tags" WHERE "tags"."article_id" = 'seedacorn-123'
Comment Load (0.2ms) SELECT "comments".* FROM "comments" WHERE "comments"."article_id" = 'seedacorn-123'
Attachment Load (0.3ms) SELECT "attachments".* FROM "attachments" WHERE "attachments"."reference_type" = $1 AND "attachments"."reference_id" IN ('1', '2') [["reference_type", "Comment"]]
=> #<ActiveRecord::Relation [#<article id: "seedacorn-123", created_at: "2016-07-15 22:41:27", updated_at: "2016-07-15 22:41:27">]>
The Question
So I guess, what I'm really wondering, is why ActiveRecord adding all of the included models to the JOIN rather than just those that I added to the .references()? Is this intended as a performance benefit? Or is this needed for some other reason?
I did not add :comments or :comments => :attachments to the .references(), but they were still added to the JOIN. This is causing a typecasting error, but I'm wondering why this is even happening in the first place?
Ask
If you know how to resolve this with some kind of creative solution, I'm trying desperately to figure it out. The core problems is that my polymorphic Attachment model can reference other models with either String or Integer primary keys, and so its reference_type is a string. Then this is not getting properly typecast when querying other models, in the case where JOIN's are introduced.
Found an answer to this question after posting an issue on github.com/rail/rails.
As it turns out, this is known (and unfortunately expected) behavior in Rails. If you require that an association be loaded with a separate query and not as part of a JOIN, you can use preload instead of includes, which will always load the association as in a separate query.
Likewise, if you want to force an association to load as part of a JOIN, and you're not using includes, you can replace references with eager_load.
So
Article.includes(:tags, :comments => :attachments).references(:tags)
Would become
Article.preload(:tags, :comments => :attachments).eager_load(:tags)
I have the problem that inside my e-commerce rails 3.0.x application I have products, line_items and carts.
a line_item belongs to a cart and a product.
a product can have many line_items.
if I delete a product, the line_items should be deleted as well (:dependent => :destroy)
Now, the strange thing is, when I delete a line_item from the cart this will delete my complete product record as well!
Here is the necessary code:
#cart_view.erb
<% for line_item in cart.line_items %>
<!-- HTML omitted -->
<!-- This link actually removes not only the line_item from cart, but also deletes the product...? . -->
<%= link_to "remove", line_item, :confirm => 'Remove from cart?', :method => :delete %>
<% end %>
#product.rb
class Product < ActiveRecord::Base
# for cart
has_many :line_items, :dependent => :destroy
has_many :stock_items, :dependent => :destroy
end
#line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :product
end
# stock_item.rb
class StockItem < ActiveRecord::Base
belongs_to :product, :dependent => :destroy
has_many :line_items
end
#line_items_controller.rb
class LineItemsController < ApplicationController
def destroy
#line_item = LineItem.find(params[:id])
#cart = #line_item.cart
#line_item.destroy
flash[:notice] = "Removed from cart."
redirect_to #cart
end
end
This behaviour was not always like this, at least I don't remember. Also I see nothing wrong here at first glance, so any ideas are highly appreciated.
edit
db table definitions (i use mysql2 db adapter):
| line_items | CREATE TABLE `line_items` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`unit_price` int(11) NOT NULL DEFAULT '0',
`product_id` int(11) DEFAULT NULL,
`cart_id` int(11) DEFAULT NULL,
`quantity` int(11) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`stock_item_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `line_items_cart_id_fk` (`cart_id`),
CONSTRAINT `line_items_cart_id_fk` FOREIGN KEY (`cart_id`) REFERENCES `carts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 |
| products | CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`description` text,
`price` int(11) NOT NULL DEFAULT '0',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`title` varchar(255) DEFAULT NULL,
`subtitle` varchar(255) DEFAULT NULL,
`currency` varchar(255) DEFAULT 'GBP',
`legacy` tinyint(1) DEFAULT '0',
`product_code` varchar(255) DEFAULT NULL,
`category_id` int(11) DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 |
** edit 2 **
the rails s log when I hit the rmove line_item link:
Started POST "/line_items/16?locale=en" for 127.0.0.1 at 2012-11-27 16:51:06 +0100
SQL (1.6ms) describe `roles_users`
Processing by LineItemsController#destroy as HTML
Parameters: {"authenticity_token"=>"sN78OIdiVW5WgSoLA3JR7RFOWIy5B+j1XLqe47vZf3I=", "locale"=>"en", "id"=>"16"}
LineItem Load (0.4ms) SELECT `line_items`.* FROM `line_items` WHERE `line_items`.`id` = 16 LIMIT 1
Cart Load (0.3ms) SELECT `carts`.* FROM `carts` WHERE `carts`.`id` = 16 LIMIT 1
SQL (0.1ms) BEGIN
AREL (0.2ms) DELETE FROM `line_items` WHERE `line_items`.`id` = 16
StockItem Load (0.5ms) SELECT `stock_items`.* FROM `stock_items` WHERE `stock_items`.`id` = 7 LIMIT 1
AREL (0.3ms) DELETE FROM `stock_items` WHERE `stock_items`.`id` = 7
Product Load (0.7ms) SELECT `products`.* FROM `products` WHERE `products`.`id` = 7 LIMIT 1
Product::Translation Load (0.7ms) SELECT `product_translations`.* FROM `product_translations` WHERE (`product_translations`.product_id = 7)
AREL (0.3ms) DELETE FROM `product_translations` WHERE `product_translations`.`id` = 11
AREL (0.2ms) DELETE FROM `product_translations` WHERE `product_translations`.`id` = 12
ProductLike Load (0.4ms) SELECT `product_likes`.* FROM `product_likes` WHERE (`product_likes`.product_id = 7)
Asset Load (0.4ms) SELECT `assets`.* FROM `assets` WHERE (`assets`.product_id = 7)
AREL (0.3ms) DELETE FROM `assets` WHERE `assets`.`id` = 6
SetItem Load (0.4ms) SELECT `set_items`.* FROM `set_items` WHERE (`set_items`.product_id = 7)
StockItem Load (0.3ms) SELECT `stock_items`.* FROM `stock_items` WHERE (`stock_items`.product_id = 7)
AREL (0.2ms) DELETE FROM `products` WHERE `products`.`id` = 7
SQL (0.6ms) COMMIT
Redirected to http://localhost:3000/carts/16?locale=en
Completed 302 Found in 3093ms
/Users/admin/.rvm/gems/ruby-1.9.2-p136#rails3/gems/activesupport-3.0.17/lib/active_support/core_ext/string/output_safety.rb:23: warning: regexp match /.../n against to UTF-8 string
I have a table in a legacy database that has a column named field.
It is impossible to link the table directly to an activeRecord model, since the latter will try to create the method field_changed? which is already defined.
So I created a view to rename the field and created the following rules to allow inserts, updates and deletes.
create or replace rule new_linked_fields_upd as on update to new_linked_fields
do instead
update linked_fields set race_id = new.race_id , master_id = new.master_id, field = new.field_name
where id = new.id;
create or replace rule new_linked_fields_ins as on insert to new_linked_fields
do instead
insert into linked_fields select nextval('linked_fields_id_seq'), new.race_id, new.field_name, new.master_id;
create or replace rule new_linked_fields_del as on delete to new_linked_fields
do instead
delete from linked_fields where id = old.id;
I linked the activeRecord model to the view.
class LinkedField < ActiveRecord::Base
self.table_name ="new_linked_fields"
self.primary_key = "id"
end
I had to add self.primary_key = "id", otherwise, methods such as LinkedField.last would fail (generating bad SQL).
In SQL everything works great.
In ruby, I can update the attributes of a LinkedField, or destroy an instance, but I can't create one.
LinkedField.create(field_name:'toto',race_id:-3,master_id:4)
Returns the following error.
(0.3ms) BEGIN
SQL (1.0ms) INSERT INTO "new_linked_fields" ("field_name", "master_id", "race_id") VALUES ($1, $2, $3) RETURNING "id" [["field_name", "toto"], ["master_id", 4], ["race_id", -3]]
(0.3ms) ROLLBACK
ActiveRecord::StatementInvalid: PG::Error: ERROR: syntax error at end of input
LINE 1: DEALLOCATE
^
: INSERT INTO "new_linked_fields" ("field_name", "master_id", "race_id") VALUES ($1, $2, $3) RETURNING "id"
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/postgresql_adapter.rb:297:in `exec'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/postgresql_adapter.rb:297:in `dealloc'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/postgresql_adapter.rb:287:in `delete'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/postgresql_adapter.rb:1172:in `rescue in exec_cache'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/postgresql_adapter.rb:1155:in `exec_cache'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/postgresql_adapter.rb:665:in `block in exec_query'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/abstract_adapter.rb:280:in `block in log'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activesupport-3.2.6/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/abstract_adapter.rb:275:in `log'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/postgresql_adapter.rb:663:in `exec_query'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/abstract/database_statements.rb:63:in `exec_insert'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/abstract/database_statements.rb:90:in `insert'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/connection_adapters/abstract/query_cache.rb:14:in `insert'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/relation.rb:66:in `insert'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/persistence.rb:363:in `create'
from /Users/macbook/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.6/lib/active_record/timestamp.rb:57:in `create'
Any cue? I'm using postgres 9.0 and rails 3.2.6.
Thanks
First, if you're able to update to PostgreSQL 9.1, view triggers are way easier to get right than rules.
Second, where is DEALLOCATE coming from? Is it trying to free a prepared statement?
Try enabling log_statement = 'all' in postgresql.conf, reloading PostgreSQL (pg_ctl reload, SIGHUP the postmaster, or restart it), re-run your test and examine Pg's logs. See what SQL your Rails program is actually running.