Sequence "setval" operation not working the same as "nextval" for Postgres in Rails 3.2.16

StackOverflow https://stackoverflow.com/questions/21146760

  •  28-09-2022
  •  | 
  •  

Вопрос

I have a model that loads data directly to the database with the pg gem. It does this by creating a CSV file on the fly, and assigning the values to the id field by calling:

self.class.select("nextval('apps_id_seq'::regclass)").first['nextval']

in a to_csv instance method.

This works great in production. However, I'm in the process of filling in some tests, and if this test is run first, or by itself, it fails because the DB has just been reset by the Database Cleaner gem and the sequences are reset. But they aren't given a starting value.

Postgres documentation says you can call setval on the sequence in question to set it up. So let's say I want to set it up to start at 1, so that the next time nextval is called, it will return a 1:

select setval('apps_id_seq'::regclass, 1, false);

But I can't call this the same way as I call nextval above, because it just plain doesn't work. It returns nil, and does not set up the sequence.

No iteration of anything I try:

self.class.select("setval('apps_id_seq'::regclass)").first
App.connection.execute("select setval('apps_id_seq'::regclass,1,false)")
ActiveRecord::Base.connection.execute("select setval('apps_id_seq'::regclass,1)")
ActiveRecord::Base.connection.execute("select setval('apps_id_seq'::regclass,1,false)")
ActiveRecord::Base.connection.execute("select setval('apps_id_seq'::regclass,1,false)")

nor any combination thereof works, it just refuses to work. I don't know if it's a pg gem problem.

UPDATE

These statements work in development mode:

App.select("setval('apps_id_seq'::regclass,1)").first

and:

App.select("nextval('apps_id_seq'::regclass)").first

Yet, neither of them work in test.

BUT, these statements DO work in test mode:

ActiveRecord::Base.connection.execute("select setval('apps_id_seq'::regclass,1,false)").first

and:

ActiveRecord::Base.connection.execute("select nextval('apps_id_seq'::regclass)").first

The ONLY DIFFERENCE in the two environments, as far as I can see, is that one has data, while the other one does not. And my test db is created with rake db:test:prepare from a structure.sql file.

Это было полезно?

Решение

setval and nextval will work regardless of whether there is data in the table that the sequence is attached to. A sequence can be created and incremented without even being associated to a column of an existing table. When you use Model.select to execute the raw SQL, ActiveRecord generates the SQL with a from clause for the table of the model you are using:

> App.select("setval('apps_id_seq'::regclass,1)").first

  SELECT nextval('apps_id_seq'::regclass)
  FROM "apps" ORDER BY "apps"."id" ASC LIMIT 1'

=> nil

Because there are no records in the apps table, the setval function is never executed. This is why the same select works when using the connection directly because the select doesn't have the extraneous from clause:

> ActiveRecord::Base.connection.execute("SELECT setval('apps_id_seq'::regclass, 1)").first

  SELECT setval('apps_id_seq'::regclass, 1)

=> {"setval"=>"1"}

Use this form to execute SQL so that you can ensure the setval function is always evaluated.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top