Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PostgreSQL adapter doesn't fetch column defaults when using multiple schemas and domains #7914

Closed
arturopie opened this issue Oct 12, 2012 · 1 comment

Comments

@arturopie
Copy link
Contributor

Context

In our app, we have partitioned our db into multiple schemas, and each schema may use different collations for its Text columns. To accomplish this, we overrode the Text data type by creating a domain over text type for each schema, so each schema will use its own collation for every text value stored.

However, we noticed that ActiveRecord was setting to nil the attributes of new records, even though those attributes have default values in db.

Unit Test Case

Here is a unit test that show how to reproduce the bug and what the expected behaviour should be:

class DefaultDomainPartitionTest < ActiveSupport::TestCase
  class TestTable < ActiveRecord::Base ;end

  def exec sql
    ActiveRecord::Base.connection.execute sql
  end

  def setup
    ActiveRecord::Base.connection.create_table "test_tables" do |t|
      t.text "col1", :default => "some value"
    end
  end

  def test_default_in_new_schema_when_overriding_domain
    exec "CREATE SCHEMA schema_1"
    exec "CREATE DOMAIN schema_1.text AS text"

    ActiveRecord::Base.connection.schema_search_path = "schema_1, pg_catalog"

    ActiveRecord::Base.connection.create_table "test_tables" do |t|
      t.text "col1", :default => "some value"
    end

    assert_equal "some value" , TestTable.column_defaults["col1"] # it actualy returns nil
  end

  def teardown
    exec "DROP SCHEMA schema_1 CASCADE"
    exec "DROP TABLE public.test_tables"
  end
end

I already know where the problem is and how to fix it. I'd like to know what branches I should apply the patch to.

Thanks

@steveklabnik
Copy link
Member

I'd like to know what branches I should apply the patch to.

Please do all work against master, and we'll backport as appropriate.

arturopie pushed a commit to arturopie/rails that referenced this issue Oct 12, 2012
PostgreSQL adapter properly parses default values when using multiple
schemas and domains.

When using domains across schemas, PostgresSQL prefixes the type of the
default value with the name of the schema where that type (or domain) is.

For example, this query:
```
SELECT a.attname, d.adsrc
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = "defaults"'::regclass
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum;
```

could return something like "'<default_value>'::pg_catalog.text" or
"(''<default_value>'::pg_catalog.text)::text" for the text columns with
defaults.

I modified the regexp used to parse this value so that it ignores
anything between ':: and \b(?:character varying|bpchar|text), and it
allows to have optional parens like in the above second example.
arturopie pushed a commit to arturopie/rails that referenced this issue Oct 12, 2012
arturopie added a commit to arturopie/rails that referenced this issue Oct 13, 2012
According to postgreSQL documentation:
(http://www.postgresql.org/docs/8.2/static/catalog-pg-attrdef.html)
we should not be using 'adsrc' field because this field is unaware of
outside changes that could affect the way that default values are
represented. Thus, I changed the queries to use
"pg_get_expr(adbin, adrelid)" instead of the historical "adsrc" field.
arturopie added a commit to arturopie/rails that referenced this issue Oct 13, 2012
Remove parsing of character type default values for 8.1 formatting since
Rails doesn't support postgreSQL 8.1 anymore.

Remove misleading comment unrelated to code.
arturopie pushed a commit to arturopie/rails that referenced this issue Oct 14, 2012
PostgreSQL adapter properly parses default values when using multiple
schemas and domains.

When using domains across schemas, PostgresSQL prefixes the type of the
default value with the name of the schema where that type (or domain) is.

For example, this query:
```
SELECT a.attname, d.adsrc
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = "defaults"'::regclass
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum;
```

could return something like "'<default_value>'::pg_catalog.text" or
"(''<default_value>'::pg_catalog.text)::text" for the text columns with
defaults.

I modified the regexp used to parse this value so that it ignores
anything between ':: and \b(?:character varying|bpchar|text), and it
allows to have optional parens like in the above second example.
arturopie pushed a commit to arturopie/rails that referenced this issue Oct 14, 2012
arturopie added a commit to arturopie/rails that referenced this issue Oct 14, 2012
According to postgreSQL documentation:
(http://www.postgresql.org/docs/8.2/static/catalog-pg-attrdef.html)
we should not be using 'adsrc' field because this field is unaware of
outside changes that could affect the way that default values are
represented. Thus, I changed the queries to use
"pg_get_expr(adbin, adrelid)" instead of the historical "adsrc" field.
arturopie added a commit to arturopie/rails that referenced this issue Oct 14, 2012
Remove parsing of character type default values for 8.1 formatting since
Rails doesn't support postgreSQL 8.1 anymore.

Remove misleading comment unrelated to code.
arturopie added a commit to arturopie/rails that referenced this issue Oct 14, 2012
PostgreSQL adapter properly parses default values when using multiple
schemas and domains.

When using domains across schemas, PostgresSQL prefixes the type of the
default value with the name of the schema where that type (or domain) is.

For example, this query:
```
SELECT a.attname, d.adsrc
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = "defaults"'::regclass
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum;
```

could return something like "'<default_value>'::pg_catalog.text" or
"(''<default_value>'::pg_catalog.text)::text" for the text columns with
defaults.

I modified the regexp used to parse this value so that it ignores
anything between ':: and \b(?:character varying|bpchar|text), and it
allows to have optional parens like in the above second example.
arturopie added a commit to arturopie/rails that referenced this issue Oct 14, 2012
arturopie added a commit to arturopie/rails that referenced this issue Oct 14, 2012
According to postgreSQL documentation:
(http://www.postgresql.org/docs/8.2/static/catalog-pg-attrdef.html)
we should not be using 'adsrc' field because this field is unaware of
outside changes that could affect the way that default values are
represented. Thus, I changed the queries to use
"pg_get_expr(adbin, adrelid)" instead of the historical "adsrc" field.
arturopie added a commit to arturopie/rails that referenced this issue Oct 14, 2012
Remove parsing of character type default values for 8.1 formatting since
Rails doesn't support postgreSQL 8.1 anymore.

Remove misleading comment unrelated to code.
arturopie added a commit to arturopie/rails that referenced this issue Oct 14, 2012
PostgreSQL adapter properly parses default values when using multiple
schemas and domains.

When using domains across schemas, PostgresSQL prefixes the type of the
default value with the name of the schema where that type (or domain) is.

For example, this query:
```
SELECT a.attname, d.adsrc
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = "defaults"'::regclass
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum;
```

could return something like "'<default_value>'::pg_catalog.text" or
"(''<default_value>'::pg_catalog.text)::text" for the text columns with
defaults.

I modified the regexp used to parse this value so that it ignores
anything between ':: and \b(?:character varying|bpchar|text), and it
allows to have optional parens like in the above second example.
rafaelfranca added a commit that referenced this issue Oct 14, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants