From 4730be8c899accfe05e0eaaea924d59532b21918 Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Tue, 4 Jan 2022 13:20:18 -0600 Subject: [PATCH 1/4] WIP DAT-4793 --- .../FormattedSqlChangeLogParser.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java b/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java index cc688fce693..4c332ff1d5c 100644 --- a/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java +++ b/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java @@ -1,5 +1,6 @@ package liquibase.parser.core.formattedsql; +import liquibase.ContextExpression; import liquibase.Labels; import liquibase.Scope; import liquibase.change.core.EmptyChange; @@ -20,6 +21,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -81,6 +83,7 @@ public DatabaseChangeLog parse(String physicalChangeLogLocation, ChangeLogParame ChangeSet changeSet = null; RawSQLChange change = null; Pattern changeLogPattern = Pattern.compile("\\-\\-\\s*liquibase formatted.*", Pattern.CASE_INSENSITIVE); + Pattern propertyPattern = Pattern.compile("\\s*\\-\\-[\\s]*property\\s+(.*:.*)\\s+(.*:.*).*", Pattern.CASE_INSENSITIVE); Pattern changeSetPattern = Pattern.compile("\\s*\\-\\-[\\s]*changeset\\s+(\"[^\"]+\"|[^:]+):\\s*(\"[^\"]+\"|\\S+).*", Pattern.CASE_INSENSITIVE); Pattern rollbackPattern = Pattern.compile("\\s*\\-\\-[\\s]*rollback (.*)", Pattern.CASE_INSENSITIVE); Pattern preconditionsPattern = Pattern.compile("\\s*\\-\\-[\\s]*preconditions(.*)", Pattern.CASE_INSENSITIVE); @@ -114,7 +117,31 @@ public DatabaseChangeLog parse(String physicalChangeLogLocation, ChangeLogParame String line; while ((line = reader.readLine()) != null) { + /* + if (((Map) obj).containsKey("property")) { + Map property = (Map) ((Map) obj).get("property"); + ContextExpression context = new ContextExpression((String) property.get("context")); + Labels labels = new Labels((String) property.get("labels")); + + Boolean global = getGlobalParam(property); + + if (property.containsKey("name")) { + Object value = property.get("value"); + if (value != null) { + value = value.toString(); // TODO: not nice... + } + changeLogParameters.set((String) property.get("name"), (String) value, context, labels, (String) property.get("dbms"), global, changeLog); + } else if (property.containsKey("file")) { + loadChangeLogParametersFromFile(changeLogParameters, resourceAccessor, changeLog, property, + context, labels, global); + } + */ + Matcher propertyPatternMatcher = propertyPattern.matcher(line); + if (propertyPatternMatcher.matches()) { + changeLogParameters.set("test", "value"); + continue; + } Matcher changeLogPatterMatcher = changeLogPattern.matcher (line); if (changeLogPatterMatcher.matches ()) { Matcher logicalFilePathMatcher = logicalFilePathPattern.matcher (line); From 55cbf3fdd93ed8c8aa9c89db7d7216b23ce5cb0e Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Wed, 5 Jan 2022 14:55:55 -0600 Subject: [PATCH 2/4] Enabled property parsing in the FormattedSqlChangeParser DAT-4793 --- .../FormattedSqlChangeLogParser.java | 87 ++++++++++++------- .../FormattedSqlChangeLogParserTest.groovy | 20 ++++- 2 files changed, 75 insertions(+), 32 deletions(-) diff --git a/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java b/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java index 4c332ff1d5c..1d276a6ac53 100644 --- a/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java +++ b/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java @@ -1,6 +1,5 @@ package liquibase.parser.core.formattedsql; -import liquibase.ContextExpression; import liquibase.Labels; import liquibase.Scope; import liquibase.change.core.EmptyChange; @@ -21,7 +20,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -96,7 +94,7 @@ public DatabaseChangeLog parse(String physicalChangeLogLocation, ChangeLogParame Pattern commentPattern = Pattern.compile("\\-\\-[\\s]*comment:? (.*)", Pattern.CASE_INSENSITIVE); Pattern validCheckSumPattern = Pattern.compile("\\-\\-[\\s]*validCheckSum:? (.*)", Pattern.CASE_INSENSITIVE); Pattern ignoreLinesPattern = Pattern.compile("\\-\\-[\\s]*ignoreLines:(\\w+)", Pattern.CASE_INSENSITIVE); - Pattern runWithPattern = Pattern.compile(".*runWith:(\\w+).*", Pattern.CASE_INSENSITIVE); + Pattern runWithPattern = Pattern.compile(".*runWith:([\\w\\$\\{\\}]+).*", Pattern.CASE_INSENSITIVE); Pattern runOnChangePattern = Pattern.compile(".*runOnChange:(\\w+).*", Pattern.CASE_INSENSITIVE); Pattern runAlwaysPattern = Pattern.compile(".*runAlways:(\\w+).*", Pattern.CASE_INSENSITIVE); @@ -117,29 +115,9 @@ public DatabaseChangeLog parse(String physicalChangeLogLocation, ChangeLogParame String line; while ((line = reader.readLine()) != null) { - /* - if (((Map) obj).containsKey("property")) { - Map property = (Map) ((Map) obj).get("property"); - ContextExpression context = new ContextExpression((String) property.get("context")); - Labels labels = new Labels((String) property.get("labels")); - - Boolean global = getGlobalParam(property); - - if (property.containsKey("name")) { - Object value = property.get("value"); - if (value != null) { - value = value.toString(); // TODO: not nice... - } - - changeLogParameters.set((String) property.get("name"), (String) value, context, labels, (String) property.get("dbms"), global, changeLog); - } else if (property.containsKey("file")) { - loadChangeLogParametersFromFile(changeLogParameters, resourceAccessor, changeLog, property, - context, labels, global); - } - */ Matcher propertyPatternMatcher = propertyPattern.matcher(line); if (propertyPatternMatcher.matches()) { - changeLogParameters.set("test", "value"); + handleProperty(changeLogParameters, changeLog, propertyPatternMatcher); continue; } Matcher changeLogPatterMatcher = changeLogPattern.matcher (line); @@ -229,21 +207,39 @@ public DatabaseChangeLog parse(String physicalChangeLogLocation, ChangeLogParame boolean failOnError = parseBoolean(failOnErrorPatternMatcher, changeSet, true); String runWith = parseString(runWithMatcher); + if (runWith != null) { + runWith = changeLogParameters.expandExpressions(runWith, changeLog); + } String endDelimiter = parseString(endDelimiterPatternMatcher); rollbackEndDelimiter = parseString(rollbackEndDelimiterPatternMatcher); String context = StringUtil.trimToNull( - StringUtil.trimToEmpty(parseString(contextPatternMatcher)).replaceFirst("^\"", "").replaceFirst("\"$", "") //remove surrounding quotes if they're in there + StringUtil.trimToEmpty(parseString(contextPatternMatcher)).replaceFirst("^\"", "").replaceFirst("\"$", "") //remove surrounding quotes if they're in there ); + if (context != null) { + context = changeLogParameters.expandExpressions(context, changeLog); + } String labels = parseString(labelsPatternMatcher); + if (labels != null) { + labels = changeLogParameters.expandExpressions(labels, changeLog); + } String logicalFilePath = parseString(logicalFilePathMatcher); if ((logicalFilePath == null) || "".equals(logicalFilePath)) { - logicalFilePath = changeLog.getLogicalFilePath (); + logicalFilePath = changeLog.getLogicalFilePath(); + } + if (logicalFilePath != null) { + logicalFilePath = changeLogParameters.expandExpressions(logicalFilePath, changeLog); } String dbms = parseString(dbmsPatternMatcher); + if (dbms != null) { + dbms = changeLogParameters.expandExpressions(dbms, changeLog); + } - + String changeSetId = + changeLogParameters.expandExpressions(StringUtil.stripEnclosingQuotes(changeSetPatternMatcher.group(2)), changeLog); + String changeSetAuthor = + changeLogParameters.expandExpressions(StringUtil.stripEnclosingQuotes(changeSetPatternMatcher.group(1)), changeLog); changeSet = - new ChangeSet(StringUtil.stripEnclosingQuotes(changeSetPatternMatcher.group(2)), StringUtil.stripEnclosingQuotes(changeSetPatternMatcher.group(1)), runAlways, runOnChange, logicalFilePath, context, dbms, runWith, runInTransaction, changeLog.getObjectQuotingStrategy(), changeLog); + new ChangeSet(changeSetId, changeSetAuthor, runAlways, runOnChange, logicalFilePath, context, dbms, runWith, runInTransaction, changeLog.getObjectQuotingStrategy(), changeLog); changeSet.setLabels(new Labels(labels)); changeSet.setFailOnError(failOnError); changeLog.addChangeSet(changeSet); @@ -346,7 +342,40 @@ public DatabaseChangeLog parse(String physicalChangeLogLocation, ChangeLogParame return changeLog; } - + private void handleProperty(ChangeLogParameters changeLogParameters, DatabaseChangeLog changeLog, Matcher propertyPatternMatcher) { + String name = null; + String value = null; + String context = null; + String labels = null; + String dbms = null; + boolean global = false; + for (int i = 1; i <= propertyPatternMatcher.groupCount(); i++) { + String temp = propertyPatternMatcher.group(i); + String[] parts = temp.split(":"); + String key = parts[0].trim(); + switch (key) { + case "name": + name = parts[1].trim(); + break; + case "value": + value = parts[1].trim(); + break; + case "context": + context = parts[1].trim(); + break; + case "labels": + labels = parts[1].trim(); + break; + case "dbms": + dbms = parts[1].trim(); + break; + case "global": + global = Boolean.parseBoolean(parts[1].trim()); + break; + } + } + changeLogParameters.set(name, value, context, labels, dbms, global, changeLog); + } private SqlPrecondition parseSqlCheckCondition(String body) throws ChangeLogParseException{ Pattern[] patterns = new Pattern[] { diff --git a/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy b/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy index 9853850f488..9fdc74dfe9f 100644 --- a/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy +++ b/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy @@ -23,8 +23,14 @@ public class FormattedSqlChangeLogParserTest extends Specification { private static final String VALID_CHANGELOG = """ --liquibase formatted sql - --changeset nvoxland:1 -select * from table1; +--property name:idProp value:1 +--property name:authorProp value:nvoxland +--property name:tableNameProp value:table1 +--property name:runWith value: sqlplus + + +--changeset \${authorProp}:\${idProp} +select * from \${tableNameProp}; --changeset "n voxland":"change 2" (stripComments:false splitStatements:false endDelimiter:X runOnChange:true runAlways:true context:y dbms:mysql runInTransaction:false failOnError:false) create table table1 ( @@ -78,6 +84,12 @@ create table my_table ( --changeset complexContext:1 context:"a or b" select 1 + +-- changeset wesley:wesley-1 runWith:\${runWith} +create table table2 ( + id int primary key +); + """.trim() @@ -109,7 +121,7 @@ select 1 changeLog.getLogicalFilePath() == "asdf.sql" - changeLog.getChangeSets().size() == 10 + changeLog.getChangeSets().size() == 11 changeLog.getChangeSets().get(0).getAuthor() == "nvoxland" changeLog.getChangeSets().get(0).getId() == "1" @@ -195,6 +207,8 @@ select 1 assert changeLog.getChangeSets().get(7).getContexts().toString().contains("second") assert changeLog.getChangeSets().get(7).getContexts().toString().contains("third") + changeLog.getChangeSets().get(10).getRunWith() == "sqlplus" + ChangeSet cs = changeLog.getChangeSets().get(8) cs.getAuthor() == "bboisvert" From f9c75bcc3cb4171d95651a5facad4ec55911b61d Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Thu, 6 Jan 2022 08:40:08 -0600 Subject: [PATCH 3/4] Make test property be lowercase to test case-insensitivity DAT-4793 --- .../core/formattedsql/FormattedSqlChangeLogParserTest.groovy | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy b/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy index 9fdc74dfe9f..66108faf92f 100644 --- a/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy +++ b/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy @@ -26,7 +26,7 @@ public class FormattedSqlChangeLogParserTest extends Specification { --property name:idProp value:1 --property name:authorProp value:nvoxland --property name:tableNameProp value:table1 ---property name:runWith value: sqlplus +--property name:runwith value: sqlplus --changeset \${authorProp}:\${idProp} @@ -209,7 +209,6 @@ create table table2 ( changeLog.getChangeSets().get(10).getRunWith() == "sqlplus" - ChangeSet cs = changeLog.getChangeSets().get(8) cs.getAuthor() == "bboisvert" cs.getId() == "with_preconditions" From 857c52b6d91ffe1edc5595e64df9d5409f3749a9 Mon Sep 17 00:00:00 2001 From: Wesley Willard Date: Thu, 6 Jan 2022 09:11:07 -0600 Subject: [PATCH 4/4] Make property 'name' key comparison case insensitive DAT-4793 --- .../parser/core/formattedsql/FormattedSqlChangeLogParser.java | 2 +- .../core/formattedsql/FormattedSqlChangeLogParserTest.groovy | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java b/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java index 1d276a6ac53..f1128501e1b 100644 --- a/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java +++ b/liquibase-core/src/main/java/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParser.java @@ -352,7 +352,7 @@ private void handleProperty(ChangeLogParameters changeLogParameters, DatabaseCha for (int i = 1; i <= propertyPatternMatcher.groupCount(); i++) { String temp = propertyPatternMatcher.group(i); String[] parts = temp.split(":"); - String key = parts[0].trim(); + String key = parts[0].trim().toLowerCase(); switch (key) { case "name": name = parts[1].trim(); diff --git a/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy b/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy index 66108faf92f..bec892f6fd1 100644 --- a/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy +++ b/liquibase-core/src/test/groovy/liquibase/parser/core/formattedsql/FormattedSqlChangeLogParserTest.groovy @@ -25,7 +25,7 @@ public class FormattedSqlChangeLogParserTest extends Specification { --property name:idProp value:1 --property name:authorProp value:nvoxland ---property name:tableNameProp value:table1 +--property nAmE:tableNameProp value:table1 --property name:runwith value: sqlplus