From 24d557711c0143cf24bd3ca9c93efb9def913992 Mon Sep 17 00:00:00 2001 From: cfhowes Date: Wed, 10 Jan 2024 20:39:06 -0800 Subject: [PATCH] Some improvements to parsing when using normalize_names=True. --- CHANGELOG.txt | 5 + simple_ddl_parser/ddl_parser.py | 2 +- simple_ddl_parser/dialects/sql.py | 2 +- tests/test_references.py | 96 +++++++++++++++++ tests/test_simple_ddl_parser.py | 166 ++++++++++++++++++++++++++++++ 5 files changed, 269 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 9482586..5975269 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,8 @@ +**v1.0.1** +### Minor Fixes +1. When using `normalize_names=True` do not remove `[]` from types like `decimal(21)[]`. +2. When using `normalize_names=True` ensure that `"complex"."type"` style names convert to `complex.type`. + **v1.0.0** In output structure was done important changes that can in theory breaks code. diff --git a/simple_ddl_parser/ddl_parser.py b/simple_ddl_parser/ddl_parser.py index daa887e..eb2b491 100755 --- a/simple_ddl_parser/ddl_parser.py +++ b/simple_ddl_parser/ddl_parser.py @@ -238,7 +238,7 @@ def p_id(self, p): delimiters_to_end = ["`", '"', "]"] p[0] = p[1] - if self.normalize_names: + if self.normalize_names and len(p[0]) > 2: for num, symbol in enumerate(delimiters_to_start): if p[0].startswith(symbol) and p[0].endswith(delimiters_to_end[num]): p[0] = p[0][1:-1] diff --git a/simple_ddl_parser/dialects/sql.py b/simple_ddl_parser/dialects/sql.py index c4fce75..b0f97e0 100644 --- a/simple_ddl_parser/dialects/sql.py +++ b/simple_ddl_parser/dialects/sql.py @@ -221,7 +221,7 @@ def process_type(self, _type: Union[str, List], p_list: List, p: List) -> str: p[0] = {"property": {"distkey": True}} _type = _type.split("distkey")[0] - _type = _type.strip().replace('" . "', '"."') + _type = _type.strip().replace(" . ", ".") _type = self.process_array_types(_type, p_list) return _type diff --git a/tests/test_references.py b/tests/test_references.py index 14b864e..b90abd6 100644 --- a/tests/test_references.py +++ b/tests/test_references.py @@ -475,3 +475,99 @@ def test_foreigen_keys(): "types": [], } assert result == expected + + +def test_compound_foreigen_keys(): + """ + Tests that a compound foreign key will be properly output. + """ + result = DDLParser( + """ + CREATE TABLE "linked_to" ( + "id" int PRIMARY KEY, + "complexpk_complex_id" int, + "complexpk_date_part" int, + "comment" varchar, + CONSTRAINT "id_date_part_ibfk" FOREIGN KEY ("complexpk_complex_id", "complexpk_date_part") + REFERENCES "complexpk" ("complex_id", "date_part") + ) ENGINE=InnoDB CHARACTER SET utf8; + """, + normalize_names=True, + ).run(group_by_type=True) + expected = { + "ddl_properties": [], + "domains": [], + "schemas": [], + "sequences": [], + "tables": [ + { + "table_name": "linked_to", + "schema": None, + "primary_key": ["id"], + "columns": [ + { + "name": "id", + "type": "int", + "size": None, + "references": None, + "unique": False, + "nullable": False, + "default": None, + "check": None, + }, + { + "name": "complexpk_complex_id", + "type": "int", + "size": None, + "references": None, + "unique": False, + "nullable": True, + "default": None, + "check": None, + }, + { + "name": "complexpk_date_part", + "type": "int", + "size": None, + "references": None, + "unique": False, + "nullable": True, + "default": None, + "check": None, + }, + { + "name": "comment", + "type": "varchar", + "size": None, + "references": None, + "unique": False, + "nullable": True, + "default": None, + "check": None, + }, + ], + "alter": {}, + "checks": [], + "index": [], + "partitioned_by": [], + "constraints": { + "references": [ + { + "table": "complexpk", + "columns": ["complex_id", "date_part"], + "schema": None, + "on_delete": None, + "on_update": None, + "deferrable_initially": None, + "name": ["complexpk_complex_id", "complexpk_date_part"], + "constraint_name": "id_date_part_ibfk", + } + ] + }, + "tablespace": None, + "table_properties": {"engine=innodb": "CHARACTER", "set": "utf8"}, + } + ], + "types": [], + } + assert result == expected diff --git a/tests/test_simple_ddl_parser.py b/tests/test_simple_ddl_parser.py index 7a380f0..952662e 100644 --- a/tests/test_simple_ddl_parser.py +++ b/tests/test_simple_ddl_parser.py @@ -804,6 +804,119 @@ def test_arrays(): assert expected == parse_results +def test_arrays_with_normalized_names(): + parse_results = DDLParser( + """ + CREATE table arrays_2 ( + field_1 decimal(21)[] not null + ,field_2 integer(61) array not null + ,field_3 varchar array not null default '{"none"}' + ,squares integer[3][3] not null default '{1}' + ,schedule text[][] + ,pay_by_quarter integer[] + ,pay_by_quarter_2 integer ARRAY[4] + ,pay_by_quarter_3 integer ARRAY + ) ; + """, + normalize_names=True, + ).run() + expected = [ + { + "columns": [ + { + "name": "field_1", + "type": "decimal[]", + "size": 21, + "references": None, + "unique": False, + "nullable": False, + "default": None, + "check": None, + }, + { + "name": "field_2", + "type": "integer[]", + "size": 61, + "references": None, + "unique": False, + "nullable": False, + "default": None, + "check": None, + }, + { + "name": "field_3", + "type": "varchar[]", + "size": None, + "references": None, + "unique": False, + "nullable": False, + "default": "'{\"none\"}'", + "check": None, + }, + { + "name": "squares", + "type": "integer[3][3]", + "size": None, + "references": None, + "unique": False, + "nullable": False, + "default": "'{1}'", + "check": None, + }, + { + "name": "schedule", + "type": "text[][]", + "size": None, + "references": None, + "unique": False, + "nullable": True, + "default": None, + "check": None, + }, + { + "name": "pay_by_quarter", + "type": "integer[]", + "size": None, + "references": None, + "unique": False, + "nullable": True, + "default": None, + "check": None, + }, + { + "name": "pay_by_quarter_2", + "type": "integer[4]", + "size": None, + "references": None, + "unique": False, + "nullable": True, + "default": None, + "check": None, + }, + { + "name": "pay_by_quarter_3", + "type": "integer[]", + "size": None, + "references": None, + "unique": False, + "nullable": True, + "default": None, + "check": None, + }, + ], + "primary_key": [], + "index": [], + "alter": {}, + "checks": [], + "table_name": "arrays_2", + "tablespace": None, + "schema": None, + "partitioned_by": [], + } + ] + assert expected == parse_results + + def test_like_statement(): ddl = """ @@ -994,6 +1107,59 @@ def test_group_by_type_output(): assert result == expected +def test_enum_with_normalized_names(): + expected = { + "domains": [], + "ddl_properties": [], + "schemas": [], + "sequences": [], + "tables": [ + { + "alter": {}, + "checks": [], + "columns": [ + { + "check": None, + "default": None, + "name": "content_type", + "nullable": True, + "references": None, + "size": None, + "type": "schema--notification.ContentType", + "unique": False, + } + ], + "index": [], + "partitioned_by": [], + "primary_key": [], + "schema": "schema--notification", + "table_name": "notification", + "tablespace": None, + } + ], + "types": [ + { + "base_type": "ENUM", + "properties": {"values": ["'TEXT'", "'MARKDOWN'", "'HTML'"]}, + "schema": "schema--notification", + "type_name": "ContentType", + } + ], + } + + ddl = """ +CREATE TYPE "schema--notification"."ContentType" AS + ENUM ('TEXT','MARKDOWN','HTML'); + CREATE TABLE "schema--notification"."notification" ( + content_type "schema--notification"."ContentType" + ); +""" + + result = DDLParser(ddl, normalize_names=True).run(group_by_type=True) + + assert result == expected + + def test_do_not_fail_on_brackets_in_default(): ddl = """