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

Database inspection capability - fixes #13 #90

Merged
merged 13 commits into from
Feb 14, 2022

Conversation

MattFisher
Copy link
Contributor

@MattFisher MattFisher commented Dec 20, 2021

Subject: Get inspectdb working

Feature or Bugfix

  • Bugfix

Purpose

  • Implement database inspection methods necessary for inspectdb management command to work.

Redshift is based on PostgreSQL 8.x, but Django supports PostgreSQL >= 9.4, so some of the more modern features of PostgreSQL such as unnest and array_agg are not available in Redshift. Django uses some of these in the postgres backend introspection methods, so different implementations were needed to retrieve the same information from a Redshift database.

  • Allow use with django-sql-explorer, which uses get_table_description to provide its schema view.

This has been broken since Django 3.2 introduced support for collations in the postgres backend. Relevant commit

Detail

Implementation of the changes needed for get_table_description was straightforward, and involved removing the join to the pg_collation table from the SQL.

The changes required to get_constraints, used by inspectdb, were more complex. The postgres version uses unnest, but there is no good substitute for this function in Redshift.
Rather than attempting to build an overly complex query to achieve the same output as the postgres backend, a second query is made to the pg_attribute table and the join made in python. This is done twice within the get_constraints method, once for constraints and again for indexes.

These changes are difficult to write tests for, as the current test suite does not assume a connection to a Redshift cluster, and using a database cursor in a test requires a working database (pytest will enforce use of a db fixture).
The tests as written mock the cursor, but this has the disadvantage of having to hard code expected SQL and return values, which leaves the tests very brittle.

I have not attempted to implement the orders, type, definition, or options fields within get_constraints. I don't know where these are used or if they're necessary.

Relates

@shimizukawa
Copy link
Member

Thank you for your PR!
I'll take a look in a week (or around new year holiday)

@codecov
Copy link

codecov bot commented Dec 20, 2021

Codecov Report

Merging #90 (a6e1015) into master (4fd73cd) will increase coverage by 2.08%.
The diff coverage is 92.30%.

❗ Current head a6e1015 differs from pull request most recent head a85bd90. Consider uploading reports for the commit a85bd90 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##           master      #90      +/-   ##
==========================================
+ Coverage   47.01%   49.09%   +2.08%     
==========================================
  Files           3        3              
  Lines         251      277      +26     
  Branches       62       79      +17     
==========================================
+ Hits          118      136      +18     
- Misses        123      130       +7     
- Partials       10       11       +1     
Impacted Files Coverage Δ
django_redshift_backend/base.py 48.27% <92.30%> (+4.87%) ⬆️
django_redshift_backend/__init__.py 50.00% <0.00%> (-50.00%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4fd73cd...a85bd90. Read the comment docs.

@MattFisher
Copy link
Contributor Author

It might also be worth adding supports_collation_on_charfield = False and supports_collation_on_textfield = False to the DatabaseFeatures class, but I haven't looked into this enough to discover what it affects or how to test it.

From https://docs.djangoproject.com/en/4.0/releases/3.2/#database-backend-api

Third-party database backends must implement support for column database collations on CharFields and TextFields or set DatabaseFeatures.supports_collation_on_charfield and DatabaseFeatures.supports_collation_on_textfield to False. If non-deterministic collations are not supported, set supports_non_deterministic_collations to False.

Copy link
Member

@shimizukawa shimizukawa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, it worked on my Redshift environment!

@@ -553,6 +555,162 @@ class DatabaseCreation(BasePGDatabaseCreation):
pass


class DatabaseIntrospection(BasePGDatabaseIntrospection):
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits: I'll remove this pass line after merging

# contain details of materialized views.

# This function is based on the version from the Django postgres backend
# from before support for collations were introduced in Django 3.2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits: I'll add a URL to original django source after merging:

Suggested change
# from before support for collations were introduced in Django 3.2
# from before support for collations were introduced in Django 3.2
# https://github.com/django/django/blob/3.1.14/django/db/backends/
# postgresql/introspection.py#L66-L94

one or more columns. Also retrieve the definition of expression-based
indexes.
"""
# Based on code from Django 3.2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits: I'll add a URL to original django source after merging:

Suggested change
# Based on code from Django 3.2
# Based on code from Django 3.2
# https://github.com/django/django/blob/3.2.12/django/db/backends/
# postgresql/introspection.py#L148-L182

}

# Now get indexes
# Based on code from Django 1.7
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits: I'll add a URL to original django source after merging:

Suggested change
# Based on code from Django 1.7
# Based on code from Django 1.7
# https://github.com/django/django/blob/1.7.11/django/db/backends/
# postgresql_psycopg2/introspection.py#L182-L207

@shimizukawa
Copy link
Member

These changes are difficult to write tests for, as the current test suite does not assume a connection to a Redshift cluster, and using a database cursor in a test requires a working database (pytest will enforce use of a db fixture).
The tests as written mock the cursor, but this has the disadvantage of having to hard code expected SQL and return values, which leaves the tests very brittle.

Yes, it is currently difficult to provide a connection to Redshift for automated testing.
As an alternative, I have set up postgres on GitHub Actions so that we can do a simple test on #93.
I don't think it works for this case yet, but I hope to make it work with postgres in the future.

Anyway, thanks to this PR, we can now use inspectdb with django-redshift-backend, and I've prepared a project showcase in the examples directory, so I'll provide django-sql-explorer case later.

Thanks for your awesome contribution!

@shimizukawa shimizukawa mentioned this pull request Feb 16, 2022
@shimizukawa
Copy link
Member

As an alternative, I have set up postgres on GitHub Actions so that we can do a simple test on #93.
I don't think it works for this case yet, but I hope to make it work with postgres in the future.

done by #95 ;)

@shimizukawa
Copy link
Member

It has been released https://pypi.org/project/django-redshift-backend/3.0.0/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants