For one reason or another the pre-existing Postgres schema I'm using with my Rails app doesn't have a default sequence set for a table's primary key, so I am required to query for it every time I want to create a new row.
I have set_sequence_name "seq_people_id" in my model, but whenever I call Person.new Postgres complains to me because Rails is executing the insert query without the ID (which is marked as NOT NULL in the schema).
How do I tell Rails to always use the sequence when creating new records?
Postgres 8.1.4
ActiveRecord 3.0.3
Rails 2.3.10
Here's what I get when I run psql and \d foo:
Table "public.foo"
Column | Type | Modifiers
--------+---------------+------------------------------------------------------
id | integer | not null default nextval('foo_id_seq'::regclass)
(etc.)
I'd check the following:
Verify the actual sequence name is the same as what you reference (people_id_seq vs. seq_people_id)
Verify the table's default is similar to what I have above
(just checking) is the primary key's field named "id" ?
Did you create the table using a migration or by hand? If the latter, try creating a table with a migration, specifying the same fields as in your people table. Does it work properly? Compare the tables.
Related
I am working on a Ruby on Rails app. We are using a PostgreSQL database.
There is a table named scores with the following columns:
Column | Type
--------------+-----------------------
id | integer
value | double precision
ran_at | timestamp
active | boolean
build_id | bigint
metric_id | integer
platform_id | integer
mode_id | integer
machine_id | integer
higher_better | boolean
job_id | integer
variation_id | integer
step | character varying(255)
I need to add a sequence to job_id (note: there is no model for job).
How do I create this sequence?
Use CREATE SEQUENCE:
CREATE SEQUENCE scores_job_id_seq; -- = default name for plain a serial
Then add a column default to scores.job_id:
ALTER TABLE scores ALTER COLUMN job_id SET DEFAULT nextval('scores_job_id_seq');
If you want to bind the sequence to the column (so it is deleted when the column is deleted), also run:
ALTER SEQUENCE scores_job_id_seq OWNED BY scores.job_id;
All of this can be replaced with using the pseudo data type serial for the column job_id to begin with:
Safely and cleanly rename tables that use serial primary key columns in Postgres?
If your table already has rows, you may want to set the SEQUENCE to the next highest value and fill in missing serial values in the table:
SELECT setval('scores_job_id_seq', COALESCE(max(job_id), 1)) FROM scores;
Optionally:
UPDATE scores
SET job_id = nextval('scores_job_id_seq')
WHERE job_id IS NULL;
How to check a sequence efficiently for used and unused values in PostgreSQL
Postgres manually alter sequence
How to reset postgres' primary key sequence when it falls out of sync?
The only remaining difference, a serial column is also set to NOT NULL. You may or may not want that, too:
ALTER TABLE scores ALTER COLUMN job_id SET NOT NULL;
But you cannot just alter the type of an existing integer:
ALTER TABLE scores ALTER job_id TYPE serial;
serial is not an actual data type. It's just a notational convenience feature for CREATE TABLE.
In Postgres 10 or later consider an IDENTITY column:
Auto increment table column
So I figured out how to do this using ActiveRecord migrations on Ruby on Rails. I basically used Erwin's commands and help from this page and put them in the migration files. These are the steps:
1.
In the terminal, type:
rails g migration CreateJobIdSequence
rails g migration AddJobIdSequenceToScores
2.
Edit the migration files as follows:
20140709181616_create_job_id_sequence.rb :
class CreateJobIdSequence < ActiveRecord::Migration
def up
execute <<-SQL
CREATE SEQUENCE job_id_seq;
SQL
end
def down
execute <<-SQL
DROP SEQUENCE job_id_seq;
SQL
end
end
20140709182313_add_job_id_sequence_to_scores.rb :
class AddJobIdSequenceToScores < ActiveRecord::Migration
def up
execute <<-SQL
ALTER SEQUENCE job_id_seq OWNED BY scores.job_id;
ALTER TABLE scores ALTER COLUMN job_id SET DEFAULT nextval('job_id_seq');
SQL
end
def down
execute <<-SQL
ALTER SEQUENCE job_id_seq OWNED BY NONE;
ALTER TABLE scores ALTER COLUMN job_id SET NOT NULL;
SQL
end
end
3.
Migrate the database. In the terminal type:
rake db:migrate
I have a Rails app that uses postgreSQL.
I recently did a backup of production and restored it to development.
When I try to add a Payment record in development, I get:
ERROR: duplicate key value violates unique constraint "payments_pkey"
DETAIL: Key (id)=(1) already exists.
Yet, there is only one record in the table with id=1 and the payments_id_seq has Current value = 1.
So, whey isn't Rails trying to add id=2 ??
Thanks for the help!
PS - is there a script or command in pgadmin to force the id_seq to be correct?
If you receive a PostgreSQL unique key violation error message ("duplicate key value violates unique constraint..."), probably your primary key index is out of sync, e.g. after populating the database.
Use
ActiveRecord::Base.connection.reset_pk_sequence!('[table_name]')
to fix the sequence for the users table.
Presumably whatever method you used to copy your database didn't update your sequences along the way, a standard dump/restore should have take care of that but if you copied things row-by-row by hand then you'll have to fix things using setval.
If you only need to fix the sequence for a table T, then you could do this from the console:
ActiveRecord::Base.connection.execute(%q{
select setval('T_id_seq', m)
from (
select max(id) from T
) as dt(m)
})
or you could feed that SQL to pgadmin. You'd repeat that for each table T.
Im using sqlite db in a sample rails application. From my users table, i have cleared all records using User.delete_all. But now whenever i insert a new record in the table using User.create, the id is starting at a value which is one more than the id of the last record which was there in the table. For example, if my table had 5 records and i cleared all, then when i do User.create, its starting at id 6.
Is there any way i can make the id start from 1 again ?
Thank You
Similar question : How to reset a single table in rails? . We can run the following at rails console to reset id column to 1 for a sqlite table
ActiveRecord::Base.connection.execute("DELETE from sqlite_sequence where name = '<table_name>'")
You seem to have autoincrement turned on for the id column.
Sqlite handles these values in an internal table called sqlite_sequence. You could reset the id for a particular autoincrement-enabled table by querying:
UPDATE "sqlite_sequence" SET "seq" = 0 WHERE "name" = $YOURTABLENAME
However, this is not a good idea because the autoincrement functionality is intended to be used in a way that the user does not influence its algorithm. Ideally, you should not care about the actual value of your id but consider it only as a unique identifier for a record.
In PostgreSQL, I created a new table and assigned a new sequence to the id column. If I insert a record from the PostgreSQL console it works but when I try to import a record from from Rails, it raises an exception that it is unable to find the associated sequence.
Here is the table:
\d+ user_messages;
Table "public.user_messages"
Column | Type | Modifiers | Storage | Description
-------------+-----------------------------+------------------------------------------------------------+----------+-------------
id | integer | not null default nextval('new_user_messages_id'::regclass) | plain |
But when I try to get the sequence with the SQL query which Rails uses, it returns NULL:
select pg_catalog.pg_get_serial_sequence('user_messages', 'id');
pg_get_serial_sequence
------------------------
(1 row)
The error being raised by Rails is:
UserMessage.import [UserMessage.new]
NoMethodError: undefined method `split' for nil:NilClass
from /app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:910:in `default_sequence_name'
This problem only occurs when I use the ActiveRecord extension for importing bulk records, single records get saved through ActiveRecord.
How do I fix it?
I think your problem is that you set all this up by hand rather than by using a serial column. When you use a serial column, PostgreSQL will create the sequence, set up the appropriate default value, and ensure that the sequence is owned by the table and column in question. From the fine manual:
pg_get_serial_sequence(table_name, column_name)
get name of the sequence that a serial or bigserial column uses
But you're not using serial or bigserial so pg_get_serial_sequence won't help.
You can remedy this by doing:
alter sequence new_user_messages_id owned by user_messages.id
I'm not sure if this is a complete solution and someone (hi Erwin) will probably fill in the missing bits.
You can save yourself some trouble here by using serial as the data type of your id column. That will create and hook up the sequence for you.
For example:
=> create sequence seq_test_id;
=> create table seq_test (id integer not null default nextval('seq_test_id'::regclass));
=> select pg_catalog.pg_get_serial_sequence('seq_test','id');
pg_get_serial_sequence
------------------------
(1 row)
=> alter sequence seq_test_id owned by seq_test.id;
=> select pg_catalog.pg_get_serial_sequence('seq_test','id');
pg_get_serial_sequence
------------------------
public.seq_test_id
(1 row)
I'm writing a migration to convert a non-rails app into the right format for rails - one of the tables for some reason does not have auto increment set on the id column. Is there a quick way to turn it on while in a migration, maybe with change_column or something?
You need to execute an SQL statement.
statement = "ALTER TABLE `users` CHANGE `id` `id` SMALLINT( 5 ) UNSIGNED NOT NULL AUTO_INCREMENT"
ActiveRecord::Base.connection.execute(statement)
Note this is just an example. The final SQL statement syntax depends on the database.
If you're on postgesql, a single request won't make it. You'll need to create a new sequence in the database.
create sequence users_id_seq;
Then add the id column to your table
alter table users
add id INT UNIQUE;
Then set the default value for the id
alter table users
alter column id
set default nextval('users_id_seq');
Then populate the id column. This may be quite long if the table has many rows
update users
set id = nextval('users_id_seq');
Hope this helps postgresql users...
The Postgres answer by #jlfenaux misses out on the serial type, which does all of it for you automatically:
ALTER TABLE tbl add tbl_id serial;
More details in this related answer.