updated log message and changelog

This commit is contained in:
Ytbarek Hailu
2025-11-24 12:28:05 -08:00
parent e2737c8a11
commit 816e6f4a11
3 changed files with 27 additions and 4 deletions

View File

@@ -1,6 +1,10 @@
# DLSync Changelog # DLSync Changelog
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.6.0] - 2025-11-24
### Added
- Added support Masking Policy object type
- Fixed log message unsupported object types in create script
## [2.5.0] - 2025-11-06 ## [2.5.0] - 2025-11-06
### Added ### Added
- Added support for Dynamic Table object type - Added support for Dynamic Table object type

View File

@@ -29,7 +29,7 @@ public class SqlTokenizer {
private static final String IDENTIFIER_REGEX = "((?:\\\"[^\"]+\\\"\\.)|(?:[{}$a-zA-Z0-9_]+\\.))?((?:\\\"[^\"]+\\\"\\.)|(?:[{}$a-zA-Z0-9_]+\\.))?(?i)"; private static final String IDENTIFIER_REGEX = "((?:\\\"[^\"]+\\\"\\.)|(?:[{}$a-zA-Z0-9_]+\\.))?((?:\\\"[^\"]+\\\"\\.)|(?:[{}$a-zA-Z0-9_]+\\.))?(?i)";
private static final String MIGRATION_REGEX = VERSION_REGEX + AUTHOR_REGEX + CONTENT_REGEX + ROLL_BACK_REGEX + VERIFY_REGEX; private static final String MIGRATION_REGEX = VERSION_REGEX + AUTHOR_REGEX + CONTENT_REGEX + ROLL_BACK_REGEX + VERIFY_REGEX;
private static final String DDL_REGEX = ";\\n+(CREATE\\s+OR\\s+REPLACE\\s+(TRANSIENT\\s|HYBRID\\s|SECURE\\s)?(?<type>DYNAMIC TABLE|FILE FORMAT|VIEW|FUNCTION|PROCEDURE|TABLE|STREAM|SEQUENCE|STAGE|TASK|STREAMLIT|PIPE|ALERT|\\w+)\\s+(?<name>[\\\"\\w.]+)([\\s\\S]+?)(?=(;\\nCREATE\\s+)|(;$)))"; private static final String DDL_REGEX = ";\\n+(CREATE\\s+OR\\s+REPLACE\\s+(TRANSIENT\\s|HYBRID\\s|SECURE\\s)?(?<type>DYNAMIC TABLE|FILE FORMAT|MASKING POLICY|VIEW|FUNCTION|PROCEDURE|TABLE|STREAM|SEQUENCE|STAGE|TASK|STREAMLIT|PIPE|ALERT|\\w+)\\s+(?<name>[\\\"\\w.]+)([\\s\\S]+?)(?=(;\\nCREATE\\s+)|(;$)))";
private static final String STRING_LITERAL_REGEX = "(?<!as\\s{1,5})'([^'\\\\]*(?:\\\\.[^'\\\\]*)*(?:''[^'\\\\]*)*)'"; private static final String STRING_LITERAL_REGEX = "(?<!as\\s{1,5})'([^'\\\\]*(?:\\\\.[^'\\\\]*)*(?:''[^'\\\\]*)*)'";
@@ -254,9 +254,12 @@ public class SqlTokenizer {
log.error("Unable to parse object type from DDL: {}", content); log.error("Unable to parse object type from DDL: {}", content);
throw new RuntimeException("Unable to parse object type from DDL."); throw new RuntimeException("Unable to parse object type from DDL.");
} }
ScriptObjectType objectType = Arrays.stream(ScriptObjectType.values()) Optional<ScriptObjectType> optionalObjectType = Arrays.stream(ScriptObjectType.values()).filter( ot -> ot.getSingular().equalsIgnoreCase(type)).findFirst();
.filter(ot -> ot.getSingular().equalsIgnoreCase(type)) if(!optionalObjectType.isPresent()) {
.collect(Collectors.toList()).get(0); log.error("Unsupported object type: {} found in DDL!", type);
throw new RuntimeException("Unknown object type found in DDL: " + type);
}
ScriptObjectType objectType = optionalObjectType.get();
String fullObjectName = matcher.group("name"); String fullObjectName = matcher.group("name");
String scriptObjectName = fullObjectName.split("\\.")[2]; String scriptObjectName = fullObjectName.split("\\.")[2];

View File

@@ -202,6 +202,8 @@ class SqlTokenizerTest {
"create or replace transient table db1.schema1.table2 (col1 varchar, col2 number);\n" + "create or replace transient table db1.schema1.table2 (col1 varchar, col2 number);\n" +
"create or replace hybrid table db1.schema1.table3 (col1 varchar, col2 number);\n" + "create or replace hybrid table db1.schema1.table3 (col1 varchar, col2 number);\n" +
"create or replace table db1.schema1.\"table4\" (col1 varchar, col2 number);\n" + "create or replace table db1.schema1.\"table4\" (col1 varchar, col2 number);\n" +
"create or replace dynamic table db1.schema1.dynamic_table1 (col1 varchar, col2 number)\n as SELECT id, name, COUNT(*) as count FROM db1.schema1.source_table GROUP BY id, name;\n" +
"create or replace masking policy db1.schema1.masking_policy1 as (val string) returns string -> case when current_role() in ('ANALYST_ROLE', 'PUBLIC') then val else '****' end;\n" +
"create or replace function db1.schema1.function1(arg1 varchar)\n" + "create or replace function db1.schema1.function1(arg1 varchar)\n" +
"RETURNS VARCHAR(16777216)\n" + "RETURNS VARCHAR(16777216)\n" +
"LANGUAGE JAVASCRIPT\n" + "LANGUAGE JAVASCRIPT\n" +
@@ -215,6 +217,8 @@ class SqlTokenizerTest {
ScriptFactory.getMigrationScript("db1", "schema1", ScriptObjectType.TABLES, "table2","create or replace transient table db1.schema1.table2 (col1 varchar, col2 number);"), ScriptFactory.getMigrationScript("db1", "schema1", ScriptObjectType.TABLES, "table2","create or replace transient table db1.schema1.table2 (col1 varchar, col2 number);"),
ScriptFactory.getMigrationScript("db1", "schema1", ScriptObjectType.TABLES, "table3","create or replace hybrid table db1.schema1.table3 (col1 varchar, col2 number);"), ScriptFactory.getMigrationScript("db1", "schema1", ScriptObjectType.TABLES, "table3","create or replace hybrid table db1.schema1.table3 (col1 varchar, col2 number);"),
ScriptFactory.getMigrationScript("db1", "schema1", ScriptObjectType.TABLES, "\"table4\"","create or replace table db1.schema1.\"table4\" (col1 varchar, col2 number);"), ScriptFactory.getMigrationScript("db1", "schema1", ScriptObjectType.TABLES, "\"table4\"","create or replace table db1.schema1.\"table4\" (col1 varchar, col2 number);"),
ScriptFactory.getMigrationScript("db1", "schema1", ScriptObjectType.DYNAMIC_TABLES, "dynamic_table1","create or replace dynamic table db1.schema1.dynamic_table1 (col1 varchar, col2 number)\n as SELECT id, name, COUNT(*) as count FROM db1.schema1.source_table GROUP BY id, name;"),
ScriptFactory.getStateScript("db1", "schema1", ScriptObjectType.MASKING_POLICIES, "masking_policy1","create or replace masking policy db1.schema1.masking_policy1 as (val string) returns string -> case when current_role() in ('ANALYST_ROLE', 'PUBLIC') then val else '****' end;"),
ScriptFactory.getStateScript("db1", "schema1", ScriptObjectType.FUNCTIONS, "function1","create or replace function db1.schema1.function1(arg1 varchar)\n" + ScriptFactory.getStateScript("db1", "schema1", ScriptObjectType.FUNCTIONS, "function1","create or replace function db1.schema1.function1(arg1 varchar)\n" +
"RETURNS VARCHAR(16777216)\n" + "RETURNS VARCHAR(16777216)\n" +
"LANGUAGE JAVASCRIPT\n" + "LANGUAGE JAVASCRIPT\n" +
@@ -621,4 +625,16 @@ class SqlTokenizerTest {
"Exception message should indicate unknown script type"); "Exception message should indicate unknown script type");
} }
@Test
void parseDdlScriptUnsupportedObjectType() {
String ddl = "create or replace schema schema1;\n\nCREATE OR REPLACE UNKNOWN db1.schema1.OBJECT1;";
RuntimeException exception = assertThrows(RuntimeException.class, () -> {
SqlTokenizer.parseDdlScripts(ddl, "db1", "schema1");
}, "Should throw RuntimeException for unsupported object type");
assertEquals("Unknown object type found in DDL: UNKNOWN", exception.getMessage(),
"Exception message should indicate unsupported DDL statement");
}
} }