How to reset the id sequence of a Postgresql table on Heroku - ruby-on-rails

I have a table 'entreprises' in my database which I filled by manually loading data using a thirds party application. As a result the id sequence is a all messed up and I get id=null error message when trying to create new records.
Heroku support recommended me to use: heroku pg:psql -a app_name, and go from there.
but I am not sure how to proceed from there actually. I tried : pg_set_serial_sequence('entreprises','id') but get an error as well: No Function Matches the given name and argument types....
I don t think it is a difficult task, not sure why Heroku support did not provide the full solution to the problem.

Connect to heroku console
heroku run rails c -app app_name
Run the following code
ActiveRecord::Base.connection.reset_pk_sequence!('entreprises')

Just log in to Postgres server or connect your database with pgadmin or any other Postgres GUI tool and run the following command in the query window.
truncate table entreprises restart identity
This will delete all the records from your table.

None of the solutions worked for me, especially not the one that could make me delete the entire table. I chose a different approach, which is passing all the table into UUID and save the old_id into a different table:
class ResetIdOfEntreprise < ActiveRecord::Migration[5.1]
def change
add_column :entreprises, :uuid, :uuid, default: "uuid_generate_v4()", null: false
add_column :entreprises, :old_id, :integer
Entreprise.update_all("old_id=id")
change_table :entreprises do |t|
t.remove :id
t.rename :uuid, :id
end
execute "ALTER TABLE entreprises ADD PRIMARY KEY (id);"
end
end

Related

Trying to db:migrate inside of heroku, migration is failing it

So I have this migration inside of my Rails project:
class CreateSettings < ActiveRecord::Migration[5.2]
def change
create_table :settings do |t|
t.string :frequency
t.text :emails, array: true, default: [].to_yaml
t.integer :reorder
t.timestamps
end
end
end
The default value was originally failing it so I created another migration in order to remove that default value:
class ChangeDefaultColumnForSetting < ActiveRecord::Migration[5.2]
def change
change_column_default(:settings, :emails, nil)
end
end
The schema now looks good and that default value is gone but when I push it up to Heroku and run heroku run rails db:migrate, it fails at the original CreateSettings migration since it still includes the default value. Even if I remove that default value from the first migration manually, I get a "Expected 1 argument but got 0" error inside of Heroku.
Any ideas on how I can go about this? The migration works in development so it must just be a Postgres problem (as I'm using SQLite in dev).
A quick fix is to delete the ChangeDefaultColumnForSetting migration and edit CreateSettings to say:
t.text :emails, array: true, default: []
or
t.text :emails, array: true, default: nil
Then commit the changes and push to Heroku.
After that you really want to stop using SQLite if you're deploying on PostgreSQL. Developing on top of SQLite and deploying on PostgreSQL is going to cause all kinds of problems. You really need to develop, test, and deploy with the same database. Do yourself a big favor and install PostgreSQL locally so that you can test and develop with the same database that you're deploying with.
Alternatively, you could look at Rails.env in your migrations and run different code in different environments. But really, this is a bad idea.

Active Record adding plain sql index fails with case sensitivity [duplicate]

I need to create a case-insensitive index on a column in rails. I did this via SQL:
execute(
"CREATE UNIQUE INDEX index_users_on_lower_email_index
ON users (lower(email))"
)
This works great, but in my schema.rb file I have:
add_index "users", [nil],
:name => "index_users_on_lower_email_index",
:unique => true
Notice the "nil". So when I try to clone the database to run a test, I get an obvious error. Am I doing something wrong here? Is there some other convention that I should be using inside rails?
Thanks for the help.
Since MySQL indexes are already case-insensitive, I'm guessing you're dealing with PostgreSQL, which creates case-sensitive indexes by default. I'm answering here based on Rails 3.2.3 and PostgreSQL 8.4.
It seems functional indexes are one more example of things that ActiveRecord can't generate. Foreign keys and UUID columns are two more that come to mind. So there is no choice (other than monkey-patching ActiveRecord) but to use execute statements.
This means for an accurate dump of your database, you'll need to abandon the DB-agnostic schema.rb in favor of DB-specific structure.sql. See the Rails Guide on Migrations, section 6.2 Types of Schema Dumps. This is set as follows:
config/application.rb
config.active_record.schema_format = :sql
db/structure.sql should be updated automatically when you run a migration. You can generate it manually with this command:
rake db:structure:dump
The file is pure Postgres SQL. Although not documented when you use rake -T to list rake tasks, it seems that you can use this command to load the database from the structure.sql dump:
rake db:structure:load
There's nothing magic here: the source code (shown here from Rails 3.2.16) just calls psql on structure.sql.
Finally, here is my migration to drop an old, case-sensitive email constraint and add the case-sensitive functional index:
class FixEmailUniqueIndexOnUsers < ActiveRecord::Migration
def up
remove_index :users, :email
execute "CREATE UNIQUE INDEX index_users_on_lowercase_email
ON users USING btree (lower(email));"
end
def down
execute "DROP INDEX index_users_on_lowercase_email;"
add_index :users, :email, :unique => true
end
end
If you are using PostgreSQL you can change your column type to citext - case-insensitive string. It also makes search independent from the register.
def change
enable_extension :citext
change_column :users, :email, :citext
add_index :users, :email, unique: true
end
I would simplify this...
In your model:
before_validation :downcase_email
def downcase_email
self.email = email.downcase
end
That way, the index is database agnostic, and your emails are all lowercase in the database.
Have you considered using schema_plus (https://github.com/lomba/schema_plus)? Among other things (support for enforcing foreign keys in the database and for views), it supports setting case-insensitive indexes for PostgreSQL databases and handles dumping them in the schema. From the Readme, "If you’re using Postgresql, SchemaPlus provides support for conditions, expressions, index methods, and case-insensitive indexes."
The documentation is unclear on how to do this but the source looks like this:
def add_index(table_name, column_name, options = {})
index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})"
end
So, if your database's quote_column_name is the default implementation (which does nothing at all), then this might work:
add_index "users", ['lower(email)'], :name => "index_users_on_lower_email_index", :unique => true
You note that you tried that one but it didn't work (adding that to your question might be a good idea). Looks like ActiveRecord simply doesn't understand indexes on a computed value. I can think of an ugly hack that will get it done but it is ugly:
Add an email_lc column.
Add a before_validation or before_save hook to put a lower case version of email into email_lc.
Put your unique index on email_lc.
That's pretty ugly and you might feel dirty for doing it but that's the best I can think of right now.
I would suggest (just as an opportunity to consider among others) to use two separate fields:
email
email_original
The first one is always down cased and thus can be uniquely indexed in a database agnostic manner, while the second field keeps verbatim to what user enetered and can have upper characters.
Obviously in User model one then needs to set email based on email_original on each save and prohibit direct mangling with email field.
For Rails 4.2, create case-insensitive unique index in users table on name column.
Create new migration file with empty change method:
$ rails generate migration add_index_in_users_on_name
Add call of add_index method to empty change method:
add_index :users, 'lower(name)', name: 'index_users_on_lower_name', unique: true
Run Rake db:migrate task:
$ rake db:migrate
In result, index will be added correctly and file db/schema.rb contains correct add_index:
add_index "users", ["LOWER(\"NAME\")"], name: "index_users_on_lower_name", unique: true
This tested only with RDB Oracle.
I think you need column name(s) as below
add_index "users", [email], {:name => "index_users_on_lower_email_index", :unique => true }
And you have to make email field in database with proper Case Insensitive collation, that way your index will be also case insensitive.
depending of db engine which you are using it syntax may be different but
alter table [users] alter column [email] varchar(250) collate utf8_general_ci ...
and when you add index to this column it will be case insensitive.

how i can i check the existence of the table

hy
A lot of time when i run rake db:migrate i get an error because the table user or foor or bar exist.
I try to check the existence of table, but that didn't work and i dont know why .
I use rails 3.2.2
class AddDeviseToUsers < ActiveRecord::Migration
#def self.table_exists?(users)
# ActiveRecord::Base.connection.tables.include?(users)
#end
if !table_exists?("users")
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, :null => false, :default => ""
t.string :encrypted_password, :null => false, :default => ""
end
end
end
You can check the existence of a table with the following command. This question has already been posted and answered here.
ActiveRecord::Base.connection.table_exists? 'users'
However, your code contains errors. You cannot define a method inside an if block with the syntax you're using. Put the block inside your method, instead.
def change
if !table_exists?("users")
# ...
end
end
Also, you should not be getting this kind of errors often, as you state. Migrations are applied sequentially. Creating your users table in a separate migration before adding Devise to it would take care of that problem. If you run into this problem while migrating an existing project from scratch, consider using rake db:schema:load instead.
In your command terminal type in
sqlite3 db/development.sqlite3
then you can search through your database by SQL command. Use .tables to list all tables in the database and you can see if yours is missing or not.
You can also just look at your schema file to see if the migration worked. The table along with all of it's attributes will be listed there.
You should never have multiple migrations that both try to create the same table. You also should not experience errors like this if you're working with rails migrations correctly.
The way rails migrations work is that every time you run rake db:migrate, for every migration file that runs successfully, it stores the number part of the migration file 123456789_create_users_table.rb (123456789 in this case) in a table in your db called schema_migrations. This way if a migration has already been run, it will never get run again.
It doesn't sound like you're working with rails migrations correctly. I suggest reading this thoroughly: http://guides.rubyonrails.org/migrations.html

Altered my database structure in development, so tried to reset Heroku deployed database. Erased it but now I can't migrate my db over

so my app was working fine. I created a new model and some associaitons, rendering all the old seed data in my heroku app useless. so I tried to reset it and populate it again. but I can't even migrate my db to heroku with the heroku rake db:migrate command. I'm using SQLite, but it seems my error is related to Postgres. I don't know what that means, if anything
Here is the error:
rake aborted!
An error has occurred, this and all later migrations canceled:
PGError: ERROR: column "to" cannot be cast to type "pg_catalog.int4"
: ALTER TABLE "emails" ALTER COLUMN "to" TYPE integer
Tasks: TOP => db:migrate
My migration:
class ChangeDataTypeForEmailUsers < ActiveRecord::Migration
def self.up
change_column :emails, :to, :integer
change_column :emails, :from, :integer
end
def self.down
change_column :to, :string
change_column :from, :string
end
end
What is the problem? My deployed app was working fine. I added a new model and figured I should reset the deployed database. So I ran heroku pg:reset then pushed my code to heroku. then tried to migrate the db, but it doesn't work! What have I done? I have been trying to figure this out for the last 4 hours. I can't think straight anymore. Any help would be greatly appreciated
Further to Hishalv's answer. If you can't use "change_column" then you can go the roundabout way:
def self.up
rename_column :emails, :to, :old_to
add_column :emails, :to, :integer
Email.reset_column_information
Email.all.each {|e| e.update_attribute(:to, e.old_to.to_i) }
remove_column :emails, :old_to
end
It's roundabout, and the Email.all.each might be slow - but it'll work and correctly cast for you.
If you're only going to use it on prod, then you might be able to replace the ruby-updates with a SQL UPDATE command that does the same thing in-database using a POSTGRES-specific cast method.
As per heroku docs here
Cause: PostgreSQL doesn’t know how to cast all the rows in that table to the specified type. Most likely it means you have an integer or a string in that column.
Solution: Inspect your records and make sure they can be converted to the new type. Sometimes it’s easier to just avoid using change_column, renaming/creating a new column instead.
I also was in this position before and i did not find a solution so i had to create a new column, postgresql seems to be sensitive when i comes to this issues. Hope it helps

SQLite 3 Busy Exception in Rails 2.3.8

SQLite3::BusyException: database is locked: CREATE UNIQUE INDEX "index_users_on_email" ON "users" ("email")
I get the above error when I try to migrate a migration (written below)
class AddIndexEmailUniquenessToUsers < ActiveRecord::Migration
def self.up
add_index :users,:email,:unique => true
end
def self.down
remove_index :users,:email
end
end
what went wrong. I didnt do any lock kind of thing in previous transactions.
You have a running process (rails console, ./script/server, etc) which is holding open connections to the database preventing the migration from modifying a table that's in use.
Kill off those.
If you can't find an obvious culprit, try:
ps aux | grep ruby
... to see a list of ruby processes that might be holding that db session.

Resources