Create HSTORE with multiple schemas - ruby-on-rails

I have been trying to migrate my database to have HSTORE but the extension only works for public SCHEMA when I want to add an HSTORE column in other schemas it does not work
def up
# My hstore looks like this
execute "CREATE EXTENSION hstore SCHEMA public"
# I have also tried
# execute "CREATE EXTENSION hstore"
end
but when I run my next migration it just doesn't work and if I go to psql console and alter tables I get this:
set search_path to public;
alter table accounts add column extras hstore; -- Works fine
set search_path to schema2;
alter table accounts add column extras hstore; -- Raises an error
I'm using rails 4
Thanks.

You need to refer to your objects in a way that is consistent with your schema naming and search path. For example:
CREATE EXTENSION hstore SCHEMA public;
SET search_path TO schema2;
ALTER TABLE accounts ADD COLUMN extras public.hstore;
or
SET search_path TO public;
ALTER TABLE schema2.accounts ADD COLUMN extras hstore;

If you're using multiple different schemas, I suggest putting hstore in its own and ensuring it is always on the search_path. You might not want public on your search_path at all times, and it's nice to keep things compartmentalized.
CREATE SCHEMA hstore;
CREATE EXTENSION hstore WITH SCHEMA hstore;
... then either amend your search_path consistently, or just always schema-qualify everything, using hstore.hstore as the type name, OPERATOR(hstore.->). E.g.
SELECT hstore.hstore('"x" => "42"') OPERATOR(hstore.->) "x"
Alternately, it's safe to install hstore into pg_catalog:
CREATE EXTENSION hstore WITH SCHEMA pg_catalog;
pg_catalog is always implicitly on the search path.

the alternative to Peter Eisentraut's answer is to amend your search path so that the public schema is always on the search path.
This is particularly useful if you rely on schemas for a multitenant app (which is my case).
In your database.yml file you would put the following instruction:
schema_search_path: "schema2, public"
note: put your main schema first.
if you want to change the search path in a more dynamic way in your code, you can play with connection.schema_search_path

Related

Duplicate postgres schema

I'm working on multi-tenant system in which I need to work on different tenats. in some cases I need to create new schema which contains some tables and default data. for that I just want to duplicate or copy public schema with diff. name Is there any way to duplicate or copy it.
I did work around on this problem but I want a solution to copy schema not to create schema and regenrate table and data
Take a back up of the schema you want along with the data In a plain text SQL file using pgadmin. And then create new schema and execute the SQL file under the new schema.

Rails add_foreign_key limited to a single reference field

I am working with a database that uses a lot of constraints defined within the schema. This is necessary, to ensure that other services and clients that use the database do not break the data model (please don't reply that this level of DB definition is inappropriate for a Rails application). Unfortunately this seems to take Rails beyond its ability to define, dump and subsequently recreate schemas, unless somebody knows something that I have missed.
The specific issue that I have encountered is with add_foreign_key statements in schema.rb, and I am looking to see if anybody knows a workaround that will save me embedding SQL directly into the schema.rb definition.
The Postgres DDL that I need to represent is:
ALTER TABLE ONLY trackers
ADD CONSTRAINT valid_protocol_sub_process
FOREIGN KEY (protocol_id, sub_process_id)
REFERENCES sub_processes(protocol_id, id) MATCH FULL;
Unfortunately, when I rake db:schema:dump the existing database to schema.rb this results in the following:
add_foreign_key "trackers", "sub_processes",
column: "protocol_id",
primary_key: "protocol_id",
name: "valid_protocol_sub_process"
This results in an invalid specification, when recreating the database, that only includes a single field and (fortunately) fails to run, since the resulting schema constraints would be incorrect.
I have attempted to change the primary_key and column option strings to include both fields to match the required SQL, but ActiveRecord puts quotes around the whole lot, making the SQL statement invalid. I also attempted to use an array of columns too, but it appears to just #to_s the array.
Is this just beyond the ability of add_foreign_key, or is there a way to use multiple fields in a foreign key specification?
It appears that there is no checking that schema.rb can validly represent the full database if you use database specific DDL. Although I understand that schema.rb may not be able to represent every possibility, it is unfortunately that there is no error produced to indicate that the schema.rb generated by rake is invalid.
In order to get a full SQL dump of the database, performed by the database's own schema dumping tool, I added:
config.active_record.schema_format = :sql
to application.rb. This ensures that in future I get a valid, usable database schema to rebuild an environment.

Hstore and multitenancy

I'm bulding an app where users can add custom fields and submit data. I'm using Postgresql so I thought that Hstore would be the perfect solution for this. Also because it allows to query the custom data that the users may introduce. My problem is that Hstore can only be installed into one schema and I'm using apartment gem for multitenancy. So each User has one schema in the database.
The data into the Hstore column is private, so I don't want other users to get access to it. How can I acomplish this? I prefer to store this data into the same user's schema. Is there another solution?
Craig is telling you to install hstore in its own schema. That means doing something along these lines.
create schema hs;
create extension hstore with schema hs;
This has nothing to do with how and where you store hstore data.
If I had a personal schema named "mike", I would build tables in the "mike" schema with hstore data types like this.
create schema mike;
create table mike.test (
some_column_name hs.hstore
);
You can avoid having to use the "hs" schema name (as in "hs".hstore) by putting that schema in the search path. If you were doing a "normal" client/server application, you'd probably want to set it at the database level.
alter database your_database_name set search_path TO mike, hs, public;
But in your multi-tenant architecture, which has one schema per tenant, you'd probably want one database role per tenant, and you'd probably want to set the search path for each role. (I'm not familiar with the "apartment" gem; I presume it creates one role per tenant and one schema per tenant. Verify that by checking the database schema.)
alter role one_role_name set search_path to one_role_name, hs, public;
I'd also want to verify sensible privileges on the schemas. For example, in a multi-tenant architecture like yours, I'd want to verify that only "mike" has privileges in the "mike" schema.
The first schema in the search path becomes the default schema for new objects. On the other hand, database objects in that schema can hide database objects of the same name in other schemas. Keep that in the back of your mind.
Finally, thousands of rows is a tiny database. Performance problems will probably have nothing to do with your database search path.

How do define the schema that a rails model is set to?

For instance, when I generate an Event model, the table automatically sets to the public schema. How do I specify it to get set to a different schema?
Furthermore, how do you alter the schema of an existing table? Perhaps move it to a different schema?
Thank you!
Disclaimer: I don't know rails, so I'm going to give very postgresql-oriented answers here. For the first part of your question, there is quite possibly a much better way to do this, by making rails specify the schema when creating tables.
In PostgreSQL, tables are searched for in schemas according to the search_path setting. This is set by default to "$user",public. Tables are created in the first schema found in the search path that exists. So if you connect as "my_user", it will try to create tables in "my_user", and fall back to creating them in "public" if "my_user" doesn't exist.
So one approach is to update the "search_path" setting used for the user you connect to the database to make schema changes. For example you can say ALTER USER my_user SET search_path = my_app, public. If you then create a "my_app" schema then subsequent CREATE TABLE foo(...) commands executed by "my_user" will put the new table into "my_app".
You can change the schema of a table using ALTER TABLE foo SET SCHEMA my_app.
Create a migration to generate your new schema. ActiveRecord can't update you schema to you it's the pattern system. You can try sequel or DataMapper if you want update you schema from your code.

Use Rails Migrations to delete an index without knowing its name

I have a table with a compound index that wasn't created through a rails migration. Now, I need to create a rails migration that will delete this index and create a new one, but I don't necessarily know what the name of the index will be.
I know that it is possible to get a list of table names and column names within a migration step. Is it possible to get a list of index names on a particular table? Or, looking at it another way, is it possible to delete all indexes on a table? Or is the only option to write my own database-specific SQL queries to get this info?
You can get details of all the indexes on a table with:
ActiveRecord::Base.connection.indexes('tablename')
This returns an array of ActiveRecord::ConnectionAdapters::IndexDefinition objects, each of which has a #name and #columns method.
To expand on #showaltb's great answer, here is a complete migration to remove all indexes on a table, without knowing their names:
ActiveRecord::Base.connection.indexes('tablename').each do |index|
remove_index 'tablename', name: index.name
end
You could get the info directly from the database. If you're using MySQL:
>> select TABLE_NAME, INDEX_NAME from information_schema.statistics WHERE TABLE_SCHEMA = 'your_database_name';
You only need to replace the your_database_name bit. You'll need priviledges for the information_schema database (or be logging in as root).

Resources