Skip to content

Commit

Permalink
added support for create type as table
Browse files Browse the repository at this point in the history
  • Loading branch information
xnuinside committed Oct 6, 2021
1 parent 086bf15 commit cd452a5
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 34 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
2. ON [PRIMARY] after CREATE TABLE statement (sample in test files test_mssql_specific.py)
3. WITH statement for TABLE properties
4. TEXTIMAGE_ON statement
5. DEFAULT NEXT VALUE FOR in COLUMN DEFAULT

2. Added support for separating tables DDL by 'GO' statement as in output of MSSQL
3. Added support for CREATE TYPE as TABLE

**v0.20.0**
### New Features:
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ You also can provide a path where you want to have a dumps with schema with argu

- CREATE SEQUENCE with words: INCREMENT, START, MINVALUE, MAXVALUE, CACHE

- CREATE TYPE statement: AS ENUM, AS OBJECT, INTERNALLENGTH, INPUT, OUTPUT
- CREATE TYPE statement: AS TABLE, AS ENUM, AS OBJECT, INTERNALLENGTH, INPUT, OUTPUT

- LIKE statement (in this and only in this case to output will be added 'like' keyword with information about table from that we did like - 'like': {'schema': None, 'table_name': 'Old_Users'}).

Expand Down Expand Up @@ -318,6 +318,11 @@ You also can provide a path where you want to have a dumps with schema with argu

- CONSTRAINT [CLUSTERED]... PRIMARY KEY
- CONSTRAINT ... WITH statement
- PERIOD FOR SYSTEM_TIME in CREATE TABLE statement
- ON [PRIMARY] after CREATE TABLE statement (sample in test files test_mssql_specific.py)
- WITH statement for TABLE properties
- TEXTIMAGE_ON statement
- DEFAULT NEXT VALUE FOR in COLUMN DEFAULT

### MSSQL / MySQL/ Oracle

Expand Down Expand Up @@ -372,6 +377,21 @@ Big thanks for the involving & contribution with test cases with DDL samples & o


## Changelog
**v0.21.0**
### New Features:

## MSSQL:

1. Added support for statements:
1. PERIOD FOR SYSTEM_TIME in CREATE TABLE statement
2. ON [PRIMARY] after CREATE TABLE statement (sample in test files test_mssql_specific.py)
3. WITH statement for TABLE properties
4. TEXTIMAGE_ON statement
5. DEFAULT NEXT VALUE FOR in COLUMN DEFAULT

2. Added support for separating tables DDL by 'GO' statement as in output of MSSQL
3. Added support for CREATE TYPE as TABLE

**v0.20.0**
### New Features:

Expand Down
27 changes: 26 additions & 1 deletion docs/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ Supported Statements
CREATE SEQUENCE with words: INCREMENT, START, MINVALUE, MAXVALUE, CACHE

*
CREATE TYPE statement: AS ENUM, AS OBJECT, INTERNALLENGTH, INPUT, OUTPUT
CREATE TYPE statement: AS TABLE, AS ENUM, AS OBJECT, INTERNALLENGTH, INPUT, OUTPUT

*
LIKE statement (in this and only in this case to output will be added 'like' keyword with information about table from that we did like - 'like': {'schema': None, 'table_name': 'Old_Users'}).
Expand Down Expand Up @@ -356,6 +356,11 @@ MSSQL

* CONSTRAINT [CLUSTERED]... PRIMARY KEY
* CONSTRAINT ... WITH statement
* PERIOD FOR SYSTEM_TIME in CREATE TABLE statement
* ON [PRIMARY] after CREATE TABLE statement (sample in test files test_mssql_specific.py)
* WITH statement for TABLE properties
* TEXTIMAGE_ON statement
* DEFAULT NEXT VALUE FOR in COLUMN DEFAULT

MSSQL / MySQL/ Oracle
^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -426,6 +431,26 @@ Big thanks for the involving & contribution with test cases with DDL samples & o
Changelog
---------

**v0.21.0**

New Features:
^^^^^^^^^^^^^

.. code-block::
## MSSQL:
1. Added support for statements:
1. PERIOD FOR SYSTEM_TIME in CREATE TABLE statement
2. ON [PRIMARY] after CREATE TABLE statement (sample in test files test_mssql_specific.py)
3. WITH statement for TABLE properties
4. TEXTIMAGE_ON statement
5. DEFAULT NEXT VALUE FOR in COLUMN DEFAULT
2. Added support for separating tables DDL by 'GO' statement as in output of MSSQL
3. Added support for CREATE TYPE as TABLE
**v0.20.0**

New Features:
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[tool.poetry]
name = "simple-ddl-parser"
version = "0.20.0"
description = "Simple DDL Parser to parse SQL & dialects like HQL, TSQL, Oracle, AWS Redshift, Snowflake, etc ddl files to json/python dict with full information about columns: types, defaults, primary keys, etc.; sequences, alters, custom types & other entities from ddl."
version = "0.21.0"
description = "Simple DDL Parser to parse SQL & dialects like HQL, TSQL (MSSQL), Oracle, AWS Redshift, Snowflake, MySQL, PostgreSQL, etc ddl files to json/python dict with full information about columns: types, defaults, primary keys, etc.; sequences, alters, custom types & other entities from ddl."
authors = ["Iuliia Volkova <[email protected]>"]
license = "MIT"
readme = "docs/README.rst"
Expand Down
2 changes: 0 additions & 2 deletions simple_ddl_parser/ddl_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ def t_ID(self, t):
self.lexer.lp_open += 1
self.lexer.columns_def = True
self.lexer.last_token = "LP"
print(t.value, t.type)
return t

elif self.is_token_column_name(t):
Expand All @@ -129,7 +128,6 @@ def set_last_token(self, t):
self.lexer.is_table = False
elif t.type in ["TABLE", "INDEX"]:
self.lexer.is_table = True
print(t.value, t.type)
return t

def t_error(self, t):
Expand Down
2 changes: 0 additions & 2 deletions simple_ddl_parser/dialects/mssql.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ def p_with_args(self, p):
| with_args RP
"""
p_list = list(p)
print(p_list, "p_list, with_args")
if isinstance(p[1], dict):
p[0] = p[1]
else:
Expand All @@ -59,7 +58,6 @@ def p_with_args(self, p):

def p_period_for(self, p):
"""period_for : PERIOD FOR ID LP pid RP"""
print(list(p))
p[0] = {"period_for_system_time": p[5]}

def p_expression_on_primary(self, p):
Expand Down
75 changes: 52 additions & 23 deletions simple_ddl_parser/dialects/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,26 +409,44 @@ def p_multiple_column_names(self, p: List) -> None:
if p_list[-1] != ",":
p[0].append(p_list[-1])

def p_expression_type_as(self, p: List) -> None:
"""expr : type_name ID LP pid RP
def p_type_definition(self, p: List) -> None: # noqa: C901
"""type_definition : type_name ID LP pid RP
| type_name ID LP multiple_column_names RP
| type_name LP id_equals RP
| type_name TABLE LP defcolumn
| type_definition COMMA defcolumn
| type_definition RP
"""
p_list = list(p)
p_list = remove_par(list(p))
p[0] = p[1]
p[0]["base_type"] = p[2]
p[0]["properties"] = {}
base_type = p[0]["base_type"].upper()
if base_type == "ENUM":
p[0]["properties"]["values"] = p_list[4]
elif p[0]["base_type"] == "OBJECT":
if "type" in p_list[4][0]:
p[0]["properties"]["attributes"] = p_list[4]
if not p[0].get("properties"):
p[0]["properties"] = {}

if "TABLE" in p_list or isinstance(p_list[-1], dict) and p_list[-1].get("name"):
if not p[0]["properties"].get("columns"):
p[0]["properties"]["columns"] = []
p[0]["properties"]["columns"].append(p_list[-1])

if len(p_list) > 3:
p[0]["base_type"] = p_list[2]
else:
if isinstance(p_list[-2], list):
for item in p_list[-2]:
p[0]["base_type"] = None
if isinstance(p[0]["base_type"], str):
base_type = p[0]["base_type"].upper()
if base_type == "ENUM":
p[0]["properties"]["values"] = p_list[3]
elif p[0]["base_type"] == "OBJECT":
if "type" in p_list[3][0]:
p[0]["properties"]["attributes"] = p_list[3]
else:
if isinstance(p_list[-1], list):
for item in p_list[-1]:
p[0]["properties"].update(item)

def p_expression_type_as(self, p: List) -> None:
"""expr : type_definition"""
p[0] = p[1]

def p_type_name(self, p: List) -> None:
"""type_name : type_create ID AS
| type_create ID DOT ID AS
Expand Down Expand Up @@ -573,7 +591,6 @@ def p_expression_table(self, p: List) -> None:
"""
p[0] = p[1]
p_list = list(p)
print(p_list[-1])
if p_list[-1] != "," and p_list[-1] != ")":
if "type" in p_list[-1] and "name" in p_list[-1]:
p[0]["columns"].append(p_list[-1])
Expand Down Expand Up @@ -843,33 +860,45 @@ def p_funct_expr(self, p: List) -> None:
else:
p[0] = p[1]

def p_dot_id(self, p: List) -> None:
"""dot_id : ID DOT ID"""
p[0] = f"{p[1]}.{p[3]}"

def p_default(self, p: List) -> None:
"""default : DEFAULT ID
| DEFAULT STRING
| DEFAULT NULL
| default FOR dot_id
| DEFAULT funct_expr
| DEFAULT LP pid RP
| default ID
| default LP RP
"""
p_list = list(p)

if len(p_list) == 5 and isinstance(p[3], list):
default = p[3][0]
elif "DEFAULT" in p_list and len(p_list) == 4:
default = f"{p[2]} {p[3]}"
else:
default = p[2]

if default.isnumeric():
if not isinstance(default, dict) and default.isnumeric():
default = int(default)

if isinstance(p[1], dict):
p[0] = p[1]
for i in p[2:]:
if isinstance(p[2], str):
p[2] = p[2].replace("\\'", "'")
if i == ")" or i == "(":
p[0]["default"] = str(p[0]["default"]) + f"{i}"
else:
p[0]["default"] = str(p[0]["default"]) + f" {i}"
p[0]["default"] = p[0]["default"].replace("))", ")")
if "FOR" in default:
p[0]["default"] = {"next_value_for": p_list[-1]}
else:
for i in p[2:]:
if isinstance(p[2], str):
p[2] = p[2].replace("\\'", "'")
if i == ")" or i == "(":
p[0]["default"] = str(p[0]["default"]) + f"{i}"
else:
p[0]["default"] = str(p[0]["default"]) + f" {i}"
p[0]["default"] = p[0]["default"].replace("))", ")")
else:
p[0] = {"default": default}

Expand Down
2 changes: 0 additions & 2 deletions simple_ddl_parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ def parse_data(self):
skip_line_words = ["USE", "GO"]
set_line = None
for num, line in enumerate(lines):
print(repr(line))
skip = False
for word in skip_line_words:
if line.startswith(word):
Expand All @@ -164,7 +163,6 @@ def parse_data(self):

self.set_default_flags_in_lexer()
if not set_line and statement:
print("statement", statement)
self.parse_statement(tables, statement)

statement = None
Expand Down
53 changes: 52 additions & 1 deletion tests/test_custom_types_and_domains.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def test_create_type_with_input_properties():
"tables": [],
"types": [
{
"base_type": "(",
"base_type": None,
"properties": {
"INPUT": "my_box_in_function",
"INTERNALLENGTH": "16",
Expand All @@ -158,3 +158,54 @@ def test_create_type_with_input_properties():
}

assert expected == result


def test_as_table():
ddl = """
CREATE TYPE dbo.T_LCT_SLIPS AS TABLE (
hashKY varbinary(48),
numContratoGF bigint
);
"""
result = DDLParser(ddl).run(group_by_type=True)
expected = {
"ddl_properties": [],
"domains": [],
"schemas": [],
"sequences": [],
"tables": [],
"types": [
{
"base_type": None,
"properties": {
"columns": [
{
"check": None,
"default": None,
"name": "hashKY",
"nullable": True,
"primary_key": False,
"references": None,
"size": 48,
"type": "varbinary",
"unique": False,
},
{
"check": None,
"default": None,
"name": "numContratoGF",
"nullable": True,
"primary_key": False,
"references": None,
"size": None,
"type": "bigint",
"unique": False,
},
]
},
"schema": "dbo",
"type_name": "T_LCT_SLIPS",
}
],
}
assert result == expected
40 changes: 40 additions & 0 deletions tests/test_mssql_specific.py
Original file line number Diff line number Diff line change
Expand Up @@ -2972,3 +2972,43 @@ def test_output_separated_by_go_and_textimage():
}

assert expected == result


def test_next_value_for():

ddl = """CREATE TABLE [dbo].[SLIPEVENTO] (
[cdSLIP] [bigint] NOT NULL
DEFAULT NEXT VALUE FOR [dbo].[sqCdSLIPEvt] )"""
result = DDLParser(ddl).run(group_by_type=True)
expected = {
"ddl_properties": [],
"domains": [],
"schemas": [],
"sequences": [],
"tables": [
{
"alter": {},
"checks": [],
"columns": [
{
"check": None,
"default": {"next_value_for": "[dbo].[sqCdSLIPEvt]"},
"name": "[cdSLIP]",
"nullable": False,
"references": None,
"size": None,
"type": "[bigint]",
"unique": False,
}
],
"index": [],
"partitioned_by": [],
"primary_key": [],
"schema": "[dbo]",
"table_name": "[SLIPEVENTO]",
"tablespace": None,
}
],
"types": [],
}
assert expected == result

0 comments on commit cd452a5

Please sign in to comment.