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
Add support for PostgreSQL index storage options, and reflection of index storage options and index access methods #179
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -401,6 +401,15 @@ | |
underlying CREATE INDEX command, so it *must* be a valid index type for your | ||
version of PostgreSQL. | ||
|
||
Index Storage Parameters | ||
^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
PostgreSQL allows storage parameters to be set on indexes. The storage | ||
parameters available depend on the index method used by the index. Storage | ||
parameters can be specified on :class:`.Index` using the ``postgresql_with`` | ||
keyword argument:: | ||
|
||
Index('my_index', my_table.c.data, postgresql_with={"fillfactor": 50}) | ||
|
||
.. _postgresql_index_concurrently: | ||
|
||
|
@@ -1592,6 +1601,13 @@ def visit_create_index(self, create): | |
]) | ||
) | ||
|
||
withclause = index.dialect_options['postgresql']['with'] | ||
|
||
if withclause: | ||
text += " WITH (%s)" % (', '.join( | ||
['%s = %s' % storage_parameter | ||
for storage_parameter in withclause.items()])) | ||
|
||
whereclause = index.dialect_options["postgresql"]["where"] | ||
|
||
if whereclause is not None: | ||
|
@@ -1919,6 +1935,7 @@ class PGDialect(default.DefaultDialect): | |
"where": None, | ||
"ops": {}, | ||
"concurrently": False, | ||
"with": {} | ||
}), | ||
(schema.Table, { | ||
"ignore_search_path": False, | ||
|
@@ -2607,14 +2624,18 @@ def get_indexes(self, connection, table_name, schema, **kw): | |
SELECT | ||
i.relname as relname, | ||
ix.indisunique, ix.indexprs, ix.indpred, | ||
a.attname, a.attnum, NULL, ix.indkey%s | ||
a.attname, a.attnum, NULL, ix.indkey%s, | ||
i.reloptions, am.amname | ||
FROM | ||
pg_class t | ||
join pg_index ix on t.oid = ix.indrelid | ||
join pg_class i on i.oid = ix.indexrelid | ||
left outer join | ||
pg_attribute a | ||
on t.oid = a.attrelid and %s | ||
left outer join | ||
pg_am am | ||
on i.relam = am.oid | ||
WHERE | ||
t.relkind IN ('r', 'v', 'f', 'm') | ||
and t.oid = :table_oid | ||
|
@@ -2634,7 +2655,8 @@ def get_indexes(self, connection, table_name, schema, **kw): | |
SELECT | ||
i.relname as relname, | ||
ix.indisunique, ix.indexprs, ix.indpred, | ||
a.attname, a.attnum, c.conrelid, ix.indkey::varchar | ||
a.attname, a.attnum, c.conrelid, ix.indkey::varchar, | ||
i.reloptions, am.amname | ||
FROM | ||
pg_class t | ||
join pg_index ix on t.oid = ix.indrelid | ||
|
@@ -2647,6 +2669,9 @@ def get_indexes(self, connection, table_name, schema, **kw): | |
on (ix.indrelid = c.conrelid and | ||
ix.indexrelid = c.conindid and | ||
c.contype in ('p', 'u', 'x')) | ||
left outer join | ||
pg_am am | ||
on i.relam = am.oid | ||
WHERE | ||
t.relkind IN ('r', 'v', 'f', 'm') | ||
and t.oid = :table_oid | ||
|
@@ -2663,7 +2688,7 @@ def get_indexes(self, connection, table_name, schema, **kw): | |
|
||
sv_idx_name = None | ||
for row in c.fetchall(): | ||
idx_name, unique, expr, prd, col, col_num, conrelid, idx_key = row | ||
idx_name, unique, expr, prd, col, col_num, conrelid, idx_key, options, amname = row | ||
|
||
if expr: | ||
if idx_name != sv_idx_name: | ||
|
@@ -2689,6 +2714,10 @@ def get_indexes(self, connection, table_name, schema, **kw): | |
index['unique'] = unique | ||
if conrelid is not None: | ||
index['duplicates_constraint'] = idx_name | ||
if options: | ||
index['options'] = dict([option.split("=") for option in options]) | ||
if amname and amname != 'btree': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HMmmm. Maybe we want to include that its "btree". I know we don't want it in the CREATE INDEX on the other end all the time though. I'll leave it like this for now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It may well be fine to have it in the CREATE INDEX; that is what pg_dump does: > create table test (a integer);
CREATE TABLE
> create index on test(a);
CREATE INDEX
It avoids any ambiguity. |
||
index['amname'] = amname | ||
|
||
result = [] | ||
for name, idx in indexes.items(): | ||
|
@@ -2699,6 +2728,10 @@ def get_indexes(self, connection, table_name, schema, **kw): | |
} | ||
if 'duplicates_constraint' in idx: | ||
entry['duplicates_constraint'] = idx['duplicates_constraint'] | ||
if 'options' in idx: | ||
entry.setdefault('dialect_options', {})["postgresql_with"] = idx['options'] | ||
if 'amname' in idx: | ||
entry.setdefault('dialect_options', {})["postgresql_using"] = idx['amname'] | ||
result.append(entry) | ||
return result | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eek, < 8.5, does this query work perfectly on PG back to the earliest 8.x versions under all circumstances? the reason the SQL for < 8.5 is here is so the many issues we've had with old versions are no longer impacted by new features in our reflection system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK it works back to 8.3.23 at least. good enough
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I did check, should be fine. All the 8.x versions are now EOL so not sure it's worth keeping the special cases around that much longer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately Amazon Redshift implements an old 8.X version of Postgresql, and the PG dialect is the basis for the redshift dialect so this is still a thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed this breaks code with redshift. Also the reloptions column doesn't exist in database PostgreSQL 8.1 but exists only in database PostgreSQL 8.2 and up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I understand it, Redshift is a fork of PosgreSQL 8.2. That seems fair enough to fix, I'll look at that.
I don't see a reason why SQLAlchemy should be supporting PostgreSQL 8.1 though, given that it's been EOLed and unsupported since November 2010 (unless another of the forks is based off that version).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I now find it's 8.0.2 (http://docs.aws.amazon.com/redshift/latest/dg/c_redshift-and-postgres-sql.html). I'll amend with that in mind.
It would be good to have a policy on this, perhaps to support 8.0.2 and the currently active versions of PostgreSQL (which are 9.0-9.4 at the moment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK I've created a pull request which should fix this. Tested on PostgreSQL 8.0.2 as I don't have access to a Redshift instance.