-
Notifications
You must be signed in to change notification settings - Fork 879
Schema Diff: complete the SERIAL/integer column conversion script #10318
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
Open
dpage
wants to merge
2
commits into
pgadmin-org:master
Choose a base branch
from
dpage:fix/10292-integer-serial-conversion
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+563
−11
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
60 changes: 60 additions & 0 deletions
60
...groups/servers/databases/schemas/tables/columns/tests/test_parse_nextval_sequence_unit.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| ########################################################################## | ||
| # | ||
| # pgAdmin 4 - PostgreSQL Tools | ||
| # | ||
| # Copyright (C) 2013 - 2026, The pgAdmin Development Team | ||
| # This software is released under the PostgreSQL Licence | ||
| # | ||
| ########################################################################## | ||
|
|
||
| """Unit tests for parse_nextval_sequence(), covering the schema-qualified | ||
| identifier it extracts out of a column's ``nextval(...)`` default, and in | ||
| particular the SQL string-literal quote-doubling PostgreSQL applies when | ||
| the sequence name itself contains a single quote (#10318). | ||
| """ | ||
|
|
||
| from pgadmin.browser.server_groups.servers.databases.schemas.tables.\ | ||
| columns.utils import parse_nextval_sequence | ||
| from pgadmin.utils.route import BaseTestGenerator | ||
|
|
||
|
|
||
| class TestParseNextvalSequence(BaseTestGenerator): | ||
| """Unit tests for parse_nextval_sequence().""" | ||
|
|
||
| scenarios = [ | ||
| ('No default value returns None', | ||
| dict(test_method='test_none_defval')), | ||
| ('A non-nextval default returns None', | ||
| dict(test_method='test_non_nextval_defval')), | ||
| ('A plain schema-qualified sequence name is extracted verbatim', | ||
| dict(test_method='test_plain_sequence_name')), | ||
| ('A sequence name containing a single quote has the doubled ' | ||
| 'quote decoded back to one', | ||
| dict(test_method='test_quoted_sequence_name_with_embedded_quote')), | ||
| ] | ||
|
|
||
| def runTest(self): | ||
| getattr(self, self.test_method)() | ||
|
|
||
| def test_none_defval(self): | ||
| self.assertIsNone(parse_nextval_sequence(None)) | ||
|
|
||
| def test_non_nextval_defval(self): | ||
| self.assertIsNone(parse_nextval_sequence('1')) | ||
|
|
||
| def test_plain_sequence_name(self): | ||
| seq_name = parse_nextval_sequence( | ||
| "nextval('public.t_id_seq'::regclass)") | ||
| self.assertEqual(seq_name, 'public.t_id_seq') | ||
|
|
||
| def test_quoted_sequence_name_with_embedded_quote(self): | ||
| # PostgreSQL renders the sequence "id'seq" as the double-quoted | ||
| # identifier "id'seq", and then - because the whole thing is the | ||
| # argument of a string literal - doubles the embedded single | ||
| # quote: nextval('public."id''seq"'::regclass). The extracted | ||
| # identifier must have that doubling undone, since it is spliced | ||
| # verbatim into CREATE SEQUENCE / ALTER SEQUENCE DDL rather than | ||
| # back into a string literal. | ||
| seq_name = parse_nextval_sequence( | ||
| 'nextval(\'public."id\'\'seq"\'::regclass)') | ||
| self.assertEqual(seq_name, 'public."id\'seq"') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
...server_groups/servers/databases/schemas/tables/tests/test_normalise_serial_column_unit.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| ########################################################################## | ||
| # | ||
| # pgAdmin 4 - PostgreSQL Tools | ||
| # | ||
| # Copyright (C) 2013 - 2026, The pgAdmin Development Team | ||
| # This software is released under the PostgreSQL Licence | ||
| # | ||
| ########################################################################## | ||
|
|
||
| """Unit tests for BaseTableView._normalise_serial_column(), covering both | ||
| directions of converting a column between a plain integer type and | ||
| SERIAL/BIGSERIAL/SMALLSERIAL (#10292), and guarding against the ordinary | ||
| (non Schema Diff) column PUT being mistaken for one. | ||
| """ | ||
|
|
||
| from pgadmin.browser.server_groups.servers.databases.schemas.tables.utils \ | ||
| import BaseTableView | ||
| from pgadmin.utils.route import BaseTestGenerator | ||
|
|
||
|
|
||
| class TestNormaliseSerialColumn(BaseTestGenerator): | ||
| """Unit tests for BaseTableView._normalise_serial_column().""" | ||
|
|
||
| scenarios = [ | ||
| ('Converting a plain column to SERIAL creates the sequence and ' | ||
| 'restores the default', | ||
| dict(test_method='test_becoming_serial')), | ||
| ('Converting a SERIAL column to plain queues the sequence for ' | ||
| 'dropping', | ||
| dict(test_method='test_leaving_serial')), | ||
| ('A genuine difference on a column that is SERIAL on both sides ' | ||
| 'is unaffected', | ||
| dict(test_method='test_both_sides_already_serial')), | ||
| ('A partial update that never mentions cltype leaves an ' | ||
| 'already-SERIAL column alone', | ||
| dict(test_method='test_partial_update_without_cltype_is_ignored')), | ||
| ] | ||
|
|
||
| def runTest(self): | ||
| getattr(self, self.test_method)() | ||
|
|
||
| def test_becoming_serial(self): | ||
| # Schema Diff's source column, reprojected as BIGSERIAL, with the | ||
| # real nextval() default preserved under 'serial_defval'. | ||
| data = { | ||
| 'cltype': 'bigserial', 'typname': 'bigserial', | ||
| 'serial_defval': "nextval('public.t_id_seq'::regclass)", | ||
| 'seqincrement': 1, 'seqstart': 1, 'seqmin': 1, | ||
| 'seqmax': 9223372036854775807, 'seqcache': 1, 'seqcycle': False, | ||
| } | ||
| # The target's current (plain, unreprojected) column. | ||
| old_col_data = { | ||
| 'cltype': 'integer', 'typname': 'integer', 'defval': None, | ||
| 'seqrelid': None, 'defseqrelid': None, 'attidentity': '', | ||
| } | ||
|
|
||
| BaseTableView._normalise_serial_column(data, old_col_data) | ||
|
|
||
| self.assertEqual(data['cltype'], 'bigint') | ||
| self.assertEqual(data['typname'], 'bigint') | ||
| self.assertEqual(data['defval'], | ||
| "nextval('public.t_id_seq'::regclass)") | ||
| self.assertEqual(data['serial_seq_create']['name'], 'public.t_id_seq') | ||
| self.assertEqual(data['serial_seq_create']['increment'], 1) | ||
| self.assertNotIn('serial_defval', data) | ||
| self.assertNotIn('seqincrement', data) | ||
|
|
||
| def test_leaving_serial(self): | ||
| # Schema Diff's source column: a plain integer, never reprojected. | ||
| data = {'cltype': 'integer', 'typname': 'integer', 'defval': None} | ||
| # The target's current column genuinely is SERIAL. | ||
| old_col_data = { | ||
| 'cltype': 'integer', 'typname': 'integer', | ||
| 'defval': "nextval('public.t_id_seq'::regclass)", | ||
| 'seqrelid': 100, 'defseqrelid': 100, 'attidentity': '', | ||
| } | ||
|
|
||
| BaseTableView._normalise_serial_column(data, old_col_data) | ||
|
|
||
| self.assertEqual(data['serial_seq_drop'], 'public.t_id_seq') | ||
| # The type didn't really change; the default is still queued to | ||
| # be dropped by the generic template logic (data['defval'] stays | ||
| # None/empty and differs from o_data['defval']). | ||
| self.assertEqual(data['cltype'], 'integer') | ||
|
|
||
| def test_both_sides_already_serial(self): | ||
| # Both sides are BIGSERIAL; only some other property (a comment, | ||
| # say) differs. The reprojection emptied 'defval' on the source | ||
| # side; that must not be read as a request to drop the real one, | ||
| # and no sequence should be created or dropped. | ||
| data = { | ||
| 'cltype': 'bigserial', 'typname': 'bigserial', | ||
| 'serial_defval': "nextval('public.t_id_seq'::regclass)", | ||
| 'seqincrement': 1, | ||
| } | ||
| old_col_data = { | ||
| 'cltype': 'integer', 'typname': 'integer', | ||
| 'defval': "nextval('public.t_id_seq'::regclass)", | ||
| 'seqrelid': 100, 'defseqrelid': 100, 'attidentity': '', | ||
| } | ||
|
|
||
| BaseTableView._normalise_serial_column(data, old_col_data) | ||
|
|
||
| self.assertNotIn('defval', data) | ||
| self.assertNotIn('serial_seq_create', data) | ||
| self.assertNotIn('serial_seq_drop', data) | ||
| self.assertNotIn('seqincrement', data) | ||
|
|
||
| def test_partial_update_without_cltype_is_ignored(self): | ||
| # The ordinary column PUT (not Schema Diff) submits only the | ||
| # fields the user actually changed - e.g. a privilege - and omits | ||
| # 'cltype' entirely when the type itself wasn't touched, even if | ||
| # the column already is SERIAL. This must be a complete no-op. | ||
| data = {'attacl': {'added': []}} | ||
| old_col_data = { | ||
| 'cltype': 'integer', 'typname': 'integer', | ||
| 'defval': "nextval('public.t_id_seq'::regclass)", | ||
| 'seqrelid': 100, 'defseqrelid': 100, 'attidentity': '', | ||
| } | ||
|
|
||
| BaseTableView._normalise_serial_column(data, old_col_data) | ||
|
|
||
| self.assertEqual(data, {'attacl': {'added': []}}) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.