diff --git a/repository/src/main/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java b/repository/src/main/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java
index fc0cab680d..f2106e814a 100644
--- a/repository/src/main/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java
+++ b/repository/src/main/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -40,7 +40,8 @@ public class SchemaUpgradeScriptPatch extends AbstractPatch
{
private static final String MSG_NOT_EXECUTED = "patch.schemaUpgradeScript.err.not_executed";
- private String scriptUrl;
+ private String scriptUrl;
+ private String problemsPatternFileUrl;
public SchemaUpgradeScriptPatch()
{
@@ -52,8 +53,13 @@ public class SchemaUpgradeScriptPatch extends AbstractPatch
public String getScriptUrl()
{
return scriptUrl;
- }
+ }
+ public String getProblemPatternsFileUrl()
+ {
+ return problemsPatternFileUrl;
+ }
+
/**
* Set the URL of the upgrade scriptUrl to execute. This is the full URL of the
* file, e.g. classpath:alfresco/patch/scripts/upgrade-1.4/${hibernate.dialect.class}/patchAlfrescoSchemaUpdate-1.4-2.sql
@@ -65,12 +71,25 @@ public class SchemaUpgradeScriptPatch extends AbstractPatch
public void setScriptUrl(String script)
{
this.scriptUrl = script;
- }
+ }
+
+ /**
+ * Set the URL of the problems pattern file to accompany the upgrade script. This is the full URL of the
+ * file, e.g. classpath:alfresco/patch/scripts/upgrade-1.4/${hibernate.dialect.class}/patchAlfrescoSchemaUpdate-1.4-2-problems.txt
+ * where the ${hibernate.dialect.class} placeholder will be substituted with the Hibernate
+ * Dialect
as configured for the system.
+ *
+ * @param problemsFile the problems file
+ */
+ public void setProblemsPatternFileUrl(String problemsFile)
+ {
+ this.problemsPatternFileUrl = problemsFile;
+ }
protected void checkProperties()
{
super.checkProperties();
- checkPropertyNotNull(scriptUrl, "scriptUrl");
+ checkPropertyNotNull(scriptUrl, "scriptUrl");
}
/**
@@ -79,6 +98,6 @@ public class SchemaUpgradeScriptPatch extends AbstractPatch
@Override
protected String applyInternal() throws Exception
{
- throw new PatchException(MSG_NOT_EXECUTED, scriptUrl);
- }
+ throw new PatchException(MSG_NOT_EXECUTED, scriptUrl);
+ }
}
diff --git a/repository/src/main/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/repository/src/main/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java
index a18212cc53..873fd3a753 100644
--- a/repository/src/main/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java
+++ b/repository/src/main/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -85,12 +85,14 @@ import org.alfresco.util.DialectUtil;
import org.alfresco.util.LogUtil;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.TempFileProvider;
+import org.alfresco.util.schemacomp.Difference;
import org.alfresco.util.schemacomp.ExportDb;
import org.alfresco.util.schemacomp.MultiFileDumper;
import org.alfresco.util.schemacomp.MultiFileDumper.DbToXMLFactory;
import org.alfresco.util.schemacomp.Result;
import org.alfresco.util.schemacomp.Results;
import org.alfresco.util.schemacomp.SchemaComparator;
+import org.alfresco.util.schemacomp.SchemaDifferenceHelper;
import org.alfresco.util.schemacomp.XMLToSchema;
import org.alfresco.util.schemacomp.model.Schema;
import org.apache.commons.logging.Log;
@@ -124,6 +126,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean
private static final String MSG_EXECUTING_COPIED_SCRIPT = "schema.update.msg.executing_copied_script";
private static final String MSG_EXECUTING_STATEMENT = "schema.update.msg.executing_statement";
private static final String MSG_OPTIONAL_STATEMENT_FAILED = "schema.update.msg.optional_statement_failed";
+ private static final String MSG_OPTIONAL_PATCH_RUN_SUGGESTION = "system.schema_comp.patch_run_suggestion";
private static final String ERR_FORCED_STOP = "schema.update.err.forced_stop";
private static final String ERR_MULTIPLE_SCHEMAS = "schema.update.err.found_multiple";
private static final String ERR_PREVIOUS_FAILED_BOOTSTRAP = "schema.update.err.previous_failed";
@@ -153,7 +156,8 @@ public class SchemaBootstrap extends AbstractLifecycleBean
private static volatile int maxStringLength = DEFAULT_MAX_STRING_LENGTH;
private Dialect dialect;
-
+ private SchemaDifferenceHelper differenceHelper;
+
private ResourcePatternResolver rpr = new PathMatchingResourcePatternResolver(this.getClass().getClassLoader());
/**
@@ -233,6 +237,11 @@ public class SchemaBootstrap extends AbstractLifecycleBean
this.dialect = dialect;
}
+ public void setDifferenceHelper(SchemaDifferenceHelper differenceHelper)
+ {
+ this.differenceHelper = differenceHelper;
+ }
+
private static Log logger = LogFactory.getLog(SchemaBootstrap.class);
private DescriptorService descriptorService;
@@ -1815,7 +1824,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean
// Return number of problems found across all reference files.
return totalProblems;
}
-
+
private int validateSchema(Resource referenceResource, String outputFileNameTemplate, PrintWriter out)
{
try
@@ -1916,11 +1925,42 @@ public class SchemaBootstrap extends AbstractLifecycleBean
pw = out;
}
+ Map> optionalPatchMessages = new HashMap<>();
// Populate the file with details of the comparison's results.
for (Result result : results)
{
- pw.print(result.describe());
+ String optionalPatchId = findPatchCausingDifference(result, target);
+ String differenceMessage = result.describe();
+ if (optionalPatchId == null)
+ {
+ pw.print(differenceMessage);
+ pw.print(SchemaComparator.LINE_SEPARATOR);
+ }
+ else
+ {
+ if (optionalPatchMessages.containsKey(optionalPatchId))
+ {
+ optionalPatchMessages.get(optionalPatchId).add(differenceMessage);
+ }
+ else
+ {
+ List newResults = new ArrayList<>();
+ newResults.add(differenceMessage);
+ optionalPatchMessages.put(optionalPatchId, newResults);
+ }
+ }
+ }
+
+ for (String optionalPatchId: optionalPatchMessages.keySet())
+ {
pw.print(SchemaComparator.LINE_SEPARATOR);
+ pw.print(I18NUtil.getMessage(MSG_OPTIONAL_PATCH_RUN_SUGGESTION, optionalPatchId));
+ pw.print(SchemaComparator.LINE_SEPARATOR);
+ for (String optionalPatchMessage: optionalPatchMessages.get(optionalPatchId))
+ {
+ pw.print(optionalPatchMessage);
+ pw.print(SchemaComparator.LINE_SEPARATOR);
+ }
}
}
finally
@@ -1946,7 +1986,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean
}
else
{
- LogUtil.warn(logger, WARN_SCHEMA_COMP_PROBLEMS_FOUND, numProblems, outputFile);
+ LogUtil.warn(logger, WARN_SCHEMA_COMP_PROBLEMS_FOUND, numProblems, outputFile);
}
}
Date endTime = new Date();
@@ -1956,6 +1996,17 @@ public class SchemaBootstrap extends AbstractLifecycleBean
return results.size();
}
+ private String findPatchCausingDifference(Result result, Schema currentDb)
+ {
+ // In new installations of the system the schema validation is run twice. Since none of the alf_ tables is present there is no need to seek for unapplied patches.
+ if (!currentDb.containsByName("alf_applied_patch"))
+ {
+ return null;
+ }
+
+ return differenceHelper.findPatchCausingDifference((Difference)result);
+ }
+
/**
* Produces schema dump in XML format: this is performed pre- and post-upgrade (i.e. if
* changes are made to the schema) and can made upon demand via JMX.
diff --git a/repository/src/main/java/org/alfresco/repo/domain/schema/SchemaBootstrapRegistration.java b/repository/src/main/java/org/alfresco/repo/domain/schema/SchemaBootstrapRegistration.java
index 619d7590ef..686859f2e4 100644
--- a/repository/src/main/java/org/alfresco/repo/domain/schema/SchemaBootstrapRegistration.java
+++ b/repository/src/main/java/org/alfresco/repo/domain/schema/SchemaBootstrapRegistration.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -29,7 +29,8 @@ import java.util.Collections;
import java.util.List;
import org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch;
-import org.alfresco.util.PropertyCheck;
+import org.alfresco.util.PropertyCheck;
+import org.alfresco.util.schemacomp.SchemaDifferenceHelper;
/**
* Registers a list of create scripts.
@@ -44,7 +45,8 @@ public class SchemaBootstrapRegistration
private List postCreateScriptUrls;
private List preUpdateScriptPatches;
private List postUpdateScriptPatches;
- private List updateActivitiScriptPatches;
+ private List updateActivitiScriptPatches;
+ private SchemaDifferenceHelper differenceHelper;
public SchemaBootstrapRegistration()
{
@@ -61,6 +63,14 @@ public class SchemaBootstrapRegistration
public void setSchemaBootstrap(SchemaBootstrap schemaBootstrap)
{
this.schemaBootstrap = schemaBootstrap;
+ }
+
+ /**
+ * @param differenceHelper the component with which to register upgrade script pacthes
+ */
+ public void setDifferenceHelper(SchemaDifferenceHelper differenceHelper)
+ {
+ this.differenceHelper = differenceHelper;
}
/**
@@ -139,7 +149,8 @@ public class SchemaBootstrapRegistration
}
for (SchemaUpgradeScriptPatch postUpdateScriptPatch : postUpdateScriptPatches)
{
- schemaBootstrap.addPostUpdateScriptPatch(postUpdateScriptPatch);
+ schemaBootstrap.addPostUpdateScriptPatch(postUpdateScriptPatch);
+ differenceHelper.addUpgradeScriptPatch(postUpdateScriptPatch);
}
for (SchemaUpgradeScriptPatch updateActivitiScriptPatch : updateActivitiScriptPatches)
{
diff --git a/repository/src/main/java/org/alfresco/util/schemacomp/SchemaDifferenceHelper.java b/repository/src/main/java/org/alfresco/util/schemacomp/SchemaDifferenceHelper.java
new file mode 100644
index 0000000000..9b12bcafd1
--- /dev/null
+++ b/repository/src/main/java/org/alfresco/util/schemacomp/SchemaDifferenceHelper.java
@@ -0,0 +1,170 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.util.schemacomp;
+
+import static java.util.Locale.ENGLISH;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.alfresco.repo.admin.patch.PatchService;
+import org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch;
+import org.alfresco.repo.domain.dialect.Dialect;
+import org.alfresco.util.DialectUtil;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.extensions.surf.util.I18NUtil;
+
+public class SchemaDifferenceHelper
+{
+ private static Log logger = LogFactory.getLog(SchemaDifferenceHelper.class);
+
+ private Dialect dialect;
+ private PatchService patchService;
+ private List optionalUpgradePatches;
+ private ResourcePatternResolver rpr = new PathMatchingResourcePatternResolver(this.getClass().getClassLoader());
+
+ public SchemaDifferenceHelper(Dialect dialect, PatchService patchService)
+ {
+ this.dialect = dialect;
+ this.patchService = patchService;
+ this.optionalUpgradePatches = new ArrayList(4);
+ }
+
+ public SchemaDifferenceHelper(Dialect dialect, PatchService patchService,
+ List upgradePatches)
+ {
+ this.dialect = dialect;
+ this.patchService = patchService;
+ this.optionalUpgradePatches = upgradePatches;
+ }
+
+ public void addUpgradeScriptPatch(SchemaUpgradeScriptPatch patch)
+ {
+ if (patch.isIgnored())
+ {
+ this.optionalUpgradePatches.add(patch);
+ }
+ }
+
+ public String findPatchCausingDifference(Difference difference)
+ {
+ for (SchemaUpgradeScriptPatch patch: optionalUpgradePatches)
+ {
+ if (!isPatchApplied(patch))
+ {
+ List problemPatterns = getProblemsPatterns(patch);
+ for (String problemPattern: problemPatterns)
+ {
+ if (describe(difference).matches(problemPattern))
+ {
+ return patch.getId();
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private boolean isPatchApplied(SchemaUpgradeScriptPatch patch)
+ {
+ return patchService.getPatch(patch.getId()) != null;
+ }
+
+ protected Resource getDialectResource(String resourceUrl)
+ {
+ if(resourceUrl == null)
+ {
+ return null;
+ }
+
+ return DialectUtil.getDialectResource(rpr, dialect.getClass(), resourceUrl);
+ }
+
+ private List getProblemsPatterns(SchemaUpgradeScriptPatch patch)
+ {
+ List optionalProblems = new ArrayList<>();
+ String problemFileUrl = patch.getProblemPatternsFileUrl();
+ Resource problemFile = getDialectResource(problemFileUrl);
+
+ if (problemFile != null)
+ {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(problemFile.getInputStream(), StandardCharsets.UTF_8)))
+ {
+ String line = reader.readLine();
+ while (line != null)
+ {
+ optionalProblems.add(line);
+ line = reader.readLine();
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.error("Error while parsing problems patterns for patch " + patch.getId() + ex);
+ }
+ }
+
+ return optionalProblems;
+ }
+
+ protected String describe(Difference difference)
+ {
+ if (difference.getLeft() == null)
+ {
+ return I18NUtil.getMessage(
+ "system.schema_comp.diff.target_only",
+ ENGLISH,
+ difference.getRight().getDbObject().getTypeName(),
+ difference.getRight().getPath(),
+ difference.getRight().getPropertyValue());
+ }
+ if (difference.getRight() == null)
+ {
+ return I18NUtil.getMessage(
+ "system.schema_comp.diff.ref_only",
+ ENGLISH,
+ difference.getLeft().getDbObject().getTypeName(),
+ difference.getLeft().getPath(),
+ difference.getLeft().getPropertyValue());
+ }
+
+ return I18NUtil.getMessage(
+ "system.schema_comp.diff",
+ ENGLISH,
+ difference.getLeft().getDbObject().getTypeName(),
+ difference.getLeft().getPath(),
+ difference.getLeft().getPropertyValue(),
+ difference.getRight().getPath(),
+ difference.getRight().getPropertyValue());
+ }
+}
diff --git a/repository/src/main/resources/alfresco/bootstrap-context.xml b/repository/src/main/resources/alfresco/bootstrap-context.xml
index d47197fa22..31d9da6957 100644
--- a/repository/src/main/resources/alfresco/bootstrap-context.xml
+++ b/repository/src/main/resources/alfresco/bootstrap-context.xml
@@ -96,6 +96,14 @@
classpath:alfresco/dbscripts/create/${db.script.dialect}/Schema-Reference-ACT.xml
+
+
+
+
+
+
+
+
diff --git a/repository/src/main/resources/alfresco/dbscripts/db-schema-context.xml b/repository/src/main/resources/alfresco/dbscripts/db-schema-context.xml
index f646454b35..9f1c79fb9d 100644
--- a/repository/src/main/resources/alfresco/dbscripts/db-schema-context.xml
+++ b/repository/src/main/resources/alfresco/dbscripts/db-schema-context.xml
@@ -29,6 +29,7 @@
+
diff --git a/repository/src/main/resources/alfresco/messages/system-messages.properties b/repository/src/main/resources/alfresco/messages/system-messages.properties
index 8ca6e11ff6..a563e83277 100644
--- a/repository/src/main/resources/alfresco/messages/system-messages.properties
+++ b/repository/src/main/resources/alfresco/messages/system-messages.properties
@@ -37,6 +37,8 @@ system.schema_comp.name_validator=name must match pattern ''{0}''
system.schema_comp.index_columns_validator=Number of columns in index doesn''t match. Was {0}, but expected {1}
system.schema_comp.column_names_validator=Column types do not match. Was {0}, but expected {1}
system.schema_comp.schema_version_validator=version must be at least ''{0}''
+# Optional long running patch messages...
+system.schema_comp.patch_run_suggestion=The following problems will be resolved once the long running patch {0} has been run
# Clustering
system.cluster.license.not_enabled=License does not permit clustering: clustering is disabled.
diff --git a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java
index d2cdb9d177..2ce7585989 100644
--- a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java
+++ b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2020 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -233,7 +233,9 @@ import org.junit.runners.Suite;
org.alfresco.repo.rendition2.TransformationOptionsConverterTest.class,
org.alfresco.transform.client.registry.TransformServiceRegistryConfigTest.class,
- org.alfresco.repo.event2.RepoEvent2UnitSuite.class
+ org.alfresco.repo.event2.RepoEvent2UnitSuite.class,
+
+ org.alfresco.util.schemacomp.SchemaDifferenceHelperUnitTest.class
})
public class AllUnitTestsSuite
{
diff --git a/repository/src/test/java/org/alfresco/AppContext04TestSuite.java b/repository/src/test/java/org/alfresco/AppContext04TestSuite.java
index 52c936b170..45676ea4b1 100644
--- a/repository/src/test/java/org/alfresco/AppContext04TestSuite.java
+++ b/repository/src/test/java/org/alfresco/AppContext04TestSuite.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -85,6 +85,7 @@ import org.junit.runners.Suite;
org.alfresco.util.schemacomp.DbToXMLTest.class,
org.alfresco.util.schemacomp.ExportDbTest.class,
org.alfresco.util.schemacomp.SchemaReferenceFileTest.class,
+ org.alfresco.util.schemacomp.SchemaBootstrapTest.class,
org.alfresco.repo.module.ModuleComponentHelperTest.class,
org.alfresco.repo.node.getchildren.GetChildrenCannedQueryTest.class,
diff --git a/repository/src/test/java/org/alfresco/util/schemacomp/SchemaBootstrapTest.java b/repository/src/test/java/org/alfresco/util/schemacomp/SchemaBootstrapTest.java
new file mode 100644
index 0000000000..f1bfe14f5f
--- /dev/null
+++ b/repository/src/test/java/org/alfresco/util/schemacomp/SchemaBootstrapTest.java
@@ -0,0 +1,86 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.util.schemacomp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.List;
+
+import org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch;
+import org.alfresco.repo.domain.schema.SchemaBootstrap;
+import org.alfresco.test_category.OwnJVMTestsCategory;
+import org.alfresco.util.test.junitrules.ApplicationContextInit;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.RuleChain;
+
+@Category({OwnJVMTestsCategory.class})
+public class SchemaBootstrapTest
+{
+ private static final String BOOTSTRAP_TEST_CONTEXT = "classpath*:alfresco/dbscripts/test-bootstrap-context.xml";
+ private static final List TEST_SCHEMA_REFERENCE_URLS = Arrays.asList(
+ "classpath:alfresco/dbscripts/create/${db.script.dialect}/Test-Schema-Reference-ALF.xml",
+ "classpath:alfresco/dbscripts/create/${db.script.dialect}/Schema-Reference-ACT.xml");
+
+ private static ApplicationContextInit APP_CONTEXT_INIT = ApplicationContextInit.createStandardContextWithOverrides(BOOTSTRAP_TEST_CONTEXT);
+
+ @ClassRule
+ public static RuleChain staticRuleChain = RuleChain.outerRule(APP_CONTEXT_INIT);
+
+ private SchemaBootstrap schemaBootstrap;
+ private SchemaUpgradeScriptPatch optionalPatch;
+
+ @Before
+ public void setUp() throws Exception
+ {
+ schemaBootstrap = (SchemaBootstrap) APP_CONTEXT_INIT.getApplicationContext().getBean("schemaBootstrap");
+ schemaBootstrap.setSchemaReferenceUrls(TEST_SCHEMA_REFERENCE_URLS);
+ optionalPatch = (SchemaUpgradeScriptPatch) APP_CONTEXT_INIT.getApplicationContext().getBean("patchDbVOAddIndexTest");
+ }
+
+ @Test
+ public void shouldSchemaValidationReportProblemsCausedByUnappliedOptionalPatch()
+ {
+ ByteArrayOutputStream buff = new ByteArrayOutputStream();
+
+ PrintWriter out = new PrintWriter(buff);
+ int numProblems = schemaBootstrap.validateSchema(null, out);
+ out.flush();
+
+ assertEquals(1, numProblems);
+ String problems = buff.toString();
+ assertTrue("Missing optional patch-specific problems report: \n" + problems,
+ problems.contains("The following problems will be resolved once the long running patch "
+ + optionalPatch.getId() + " has been run"));
+ }
+
+}
diff --git a/repository/src/test/java/org/alfresco/util/schemacomp/SchemaDifferenceHelperUnitTest.java b/repository/src/test/java/org/alfresco/util/schemacomp/SchemaDifferenceHelperUnitTest.java
new file mode 100644
index 0000000000..2592e45600
--- /dev/null
+++ b/repository/src/test/java/org/alfresco/util/schemacomp/SchemaDifferenceHelperUnitTest.java
@@ -0,0 +1,207 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2021 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.util.schemacomp;
+
+import static org.alfresco.util.schemacomp.Difference.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+
+import org.alfresco.repo.admin.patch.AppliedPatch;
+import org.alfresco.repo.admin.patch.PatchService;
+import org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch;
+import org.alfresco.repo.domain.dialect.Dialect;
+import org.alfresco.util.schemacomp.model.Index;
+import org.alfresco.util.schemacomp.model.Schema;
+import org.alfresco.util.schemacomp.model.Table;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.core.io.Resource;
+
+public class SchemaDifferenceHelperUnitTest
+{
+ private static final String TEST_PATCH_ID = "patch.db-V1.0-test";
+ private static final String BASE_PROBLEM_PATTERN = ".*missing %s.*%s";
+
+ private SchemaDifferenceHelper differenceHelper;
+ private PatchService patchService;
+ private Dialect dialect;
+
+ @Rule
+ public TemporaryFolder testFolder = new TemporaryFolder();
+
+ @Before
+ public void setup()
+ {
+ dialect = mock(Dialect.class);
+ patchService = mock(PatchService.class);
+ }
+
+ @Test
+ public void shouldNotFindPatchWhenThereAreNoUpgradePatches()
+ {
+ Difference diff = createDifference();
+ differenceHelper = createHelper(Arrays.asList());
+
+ assertNull(differenceHelper.findPatchCausingDifference(diff));
+ }
+
+ @Test
+ public void shouldNotFindPatchWhenUpgradePatchHasBeenApplied() throws IOException
+ {
+ Difference diff = createDifference();
+ SchemaUpgradeScriptPatch upgradeScript = createUpgradeScript(TEST_PATCH_ID);
+ when(patchService.getPatch(TEST_PATCH_ID)).thenReturn(new AppliedPatch());
+
+ differenceHelper = createHelper(Arrays.asList(upgradeScript));
+ String result = differenceHelper.findPatchCausingDifference(diff);
+
+ assertNull(result);
+ }
+
+ @Test
+ public void shouldNotFindPatchWhenUpgradePatchDoesNotProvideAnyProblemPatternsFile() throws IOException
+ {
+ Difference diff = createDifference();
+ SchemaUpgradeScriptPatch upgradeScript = createUpgradeScript(TEST_PATCH_ID);
+ upgradeScript.setProblemsPatternFileUrl(null);
+ when(patchService.getPatch(TEST_PATCH_ID)).thenReturn(null);
+
+ differenceHelper = createHelper(Arrays.asList(upgradeScript));
+ String result = differenceHelper.findPatchCausingDifference(diff);
+
+ assertNull(result);
+ }
+
+ @Test
+ public void shouldFindPatchWhenDifferenceCausedByUpgradePatchIsDetected() throws IOException
+ {
+ Index index = createTableIndex("alf_node");
+ Difference diff = new Difference(Where.ONLY_IN_REFERENCE, new DbProperty(index), null);
+ SchemaUpgradeScriptPatch upgradeScript = createUpgradeScript(TEST_PATCH_ID,
+ String.format(BASE_PROBLEM_PATTERN, index.getTypeName(), "idx_alf_node_test"));
+ when(patchService.getPatch(TEST_PATCH_ID)).thenReturn(null);
+
+ differenceHelper = createHelper(Arrays.asList(upgradeScript));
+ String result = differenceHelper.findPatchCausingDifference(diff);
+
+ assertEquals(TEST_PATCH_ID, result);
+ }
+
+ private Difference createDifference()
+ {
+ Difference difference = new Difference(Where.IN_BOTH_BUT_DIFFERENCE, mock(DbProperty.class), mock(DbProperty.class));
+ return difference;
+ }
+
+ private Index createTableIndex(String tableName)
+ {
+ Table table = new Table(tableName);
+ table.setParent(new Schema(""));
+ return new Index(table, "idx_alf_node_test", Arrays.asList("col_a", "col_b"));
+ }
+
+ private SchemaUpgradeScriptPatch createUpgradeScript(String id, String problemPattern) throws IOException
+ {
+ SchemaUpgradeScriptPatch upgradeScript = new SchemaUpgradeScriptPatch();
+ upgradeScript.setId(id);
+ Path file = createTempFile(problemPattern);
+ upgradeScript.setProblemsPatternFileUrl(file.toAbsolutePath().toString());
+ return upgradeScript;
+ }
+
+ private SchemaUpgradeScriptPatch createUpgradeScript(String id) throws IOException
+ {
+ return createUpgradeScript(id, "");
+ }
+
+ private Path createTempFile() throws IOException
+ {
+ return Files.createTempFile(testFolder.getRoot().toPath(), null, "txt");
+ }
+
+ private Path createTempFile(String content) throws IOException
+ {
+ Path tempFile = createTempFile();
+ Files.write(tempFile, content.getBytes(StandardCharsets.UTF_8));
+ return tempFile;
+ }
+
+ private SchemaDifferenceHelper createHelper(List upgradePatches)
+ {
+ return new SchemaDifferenceHelper(dialect, patchService, upgradePatches) {
+ @Override
+ protected String describe(Difference difference)
+ {
+ if (difference.getLeft() == null)
+ {
+ return String.format("Difference: unexpected %s found in database with path: %s",
+
+ difference.getRight().getDbObject().getTypeName(),
+ difference.getRight().getPath());
+ }
+ if(difference.getRight() == null)
+ {
+ return String.format("Difference: missing %s from database, expected at path: %s",
+ difference.getLeft().getDbObject().getTypeName(),
+ difference.getLeft().getPath());
+ }
+
+ return String.format("Difference: expected %s %s=\"%s\", but was %s=\"%s\"",
+ difference.getLeft().getDbObject().getTypeName(),
+ difference.getLeft().getPath(),
+ difference.getLeft().getPropertyValue(),
+ difference.getRight().getPath(),
+ difference.getRight().getPropertyValue());
+ }
+
+ @Override
+ protected Resource getDialectResource(String resourceUrl)
+ {
+ try
+ {
+ return new InputStreamResource(new FileInputStream(resourceUrl));
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+ };
+ }
+}
diff --git a/repository/src/test/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.PostgreSQLDialect/Test-Schema-Reference-ALF.xml b/repository/src/test/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.PostgreSQLDialect/Test-Schema-Reference-ALF.xml
new file mode 100644
index 0000000000..927a2fc2a2
--- /dev/null
+++ b/repository/src/test/resources/alfresco/dbscripts/create/org.alfresco.repo.domain.dialect.PostgreSQLDialect/Test-Schema-Reference-ALF.xml
@@ -0,0 +1,2829 @@
+
+
+
+
+
+ .*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ bool
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+ context_id
+ alf_ace_context
+ id
+
+
+ authority_id
+ alf_authority
+ id
+
+
+ permission_id
+ alf_permission
+ id
+
+
+
+
+
+ permission_id
+ authority_id
+ allowed
+ applies
+
+
+
+
+ authority_id
+
+
+
+
+ context_id
+
+
+
+
+ permission_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(36)
+ false
+ false
+
+
+ bool
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ bool
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+ int4
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+ bool
+ false
+ false
+
+
+ bool
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+ acl_change_set
+ alf_acl_change_set
+ id
+
+
+
+
+
+ acl_id
+ latest
+ acl_version
+
+
+
+
+ acl_change_set
+
+
+
+
+ inherits
+ inherits_from
+
+
+
+
+ acl_change_set
+ id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(1024)
+ true
+ false
+
+
+ varchar(1024)
+ true
+ false
+
+
+ varchar(1024)
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ commit_time_ms
+ id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+ ace_id
+ alf_access_control_entry
+ id
+
+
+ acl_id
+ alf_access_control_list
+ id
+
+
+
+
+
+ acl_id
+ ace_id
+ pos
+
+
+
+
+ ace_id
+
+
+
+
+ acl_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+ timestamp
+ false
+ false
+
+
+ varchar(1024)
+ true
+ false
+
+
+ varchar(255)
+ true
+ false
+
+
+ varchar(255)
+ false
+ false
+
+
+ varchar(255)
+ true
+ false
+
+
+ varchar(36)
+ true
+ false
+
+
+ varchar(255)
+ false
+ false
+
+
+ timestamp
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ feed_user_id
+
+
+
+
+ post_date
+
+
+
+
+ post_user_id
+
+
+
+
+ site_network
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ varchar(255)
+ false
+ false
+
+
+ varchar(255)
+ true
+ false
+
+
+ varchar(36)
+ true
+ false
+
+
+ timestamp
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ feed_user_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ timestamp
+ false
+ false
+
+
+ varchar(10)
+ false
+ false
+
+
+ varchar(1024)
+ false
+ false
+
+
+ varchar(255)
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+ varchar(255)
+ true
+ false
+
+
+ varchar(36)
+ true
+ false
+
+
+ varchar(255)
+ false
+ false
+
+
+ timestamp
+ false
+ false
+
+
+
+
+ sequence_id
+
+
+
+
+
+
+ job_task_node
+
+
+
+
+ status
+
+
+
+
+
+
+
+ varchar(64)
+ false
+ false
+
+
+ varchar(1024)
+ true
+ false
+
+
+ int4
+ true
+ false
+
+
+ int4
+ true
+ false
+
+
+ int4
+ true
+ false
+
+
+ int4
+ true
+ false
+
+
+ timestamp
+ true
+ false
+
+
+ varchar(64)
+ true
+ false
+
+
+ bool
+ true
+ false
+
+
+ bool
+ true
+ false
+
+
+ varchar(1024)
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+ audit_model_id
+ alf_audit_model
+ id
+
+
+ disabled_paths_id
+ alf_prop_root
+ id
+
+
+ app_name_id
+ alf_prop_value
+ id
+
+
+
+
+
+ app_name_id
+
+
+
+
+ disabled_paths_id
+
+
+
+
+ audit_model_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+ int8
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+ audit_app_id
+ alf_audit_app
+ id
+
+
+ audit_values_id
+ alf_prop_root
+ id
+
+
+ audit_user_id
+ alf_prop_value
+ id
+
+
+
+
+
+ audit_app_id
+
+
+
+
+ audit_values_id
+
+
+
+
+ audit_user_id
+
+
+
+
+ audit_time
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+ content_data_id
+ alf_content_data
+ id
+
+
+
+
+
+ content_crc
+
+
+
+
+ content_data_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(100)
+ true
+ false
+
+
+ int8
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ authority
+ crc
+
+
+
+
+ authority
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+ alias_id
+ alf_authority
+ id
+
+
+ auth_id
+ alf_authority
+ id
+
+
+
+
+
+ auth_id
+ alias_id
+
+
+
+
+ alias_id
+
+
+
+
+ auth_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ varchar(100)
+ false
+ false
+
+
+ bool
+ false
+ false
+
+
+ bool
+ false
+ false
+
+
+ bytea
+ false
+ false
+
+
+ varchar(10)
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+ username
+ authorized
+
+
+
+
+ deleted
+
+
+
+
+ authaction
+
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(50)
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(255)
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ bool
+ true
+ false
+
+
+ int4
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+ qname_ns_id
+ alf_namespace
+ id
+
+
+ parent_node_id
+ alf_node
+ id
+
+
+ child_node_id
+ alf_node
+ id
+
+
+ type_qname_id
+ alf_qname
+ id
+
+
+
+
+
+ parent_node_id
+ type_qname_id
+ child_node_name_crc
+ child_node_name
+
+
+
+
+ child_node_id
+
+
+
+
+ parent_node_id
+ assoc_index
+ id
+
+
+
+
+ qname_ns_id
+
+
+
+
+ type_qname_id
+
+
+
+
+ parent_node_id
+ is_primary
+ child_node_id
+
+
+
+
+ qname_crc
+ type_qname_id
+ parent_node_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+ int8
+ true
+ false
+
+
+ int8
+ true
+ false
+
+
+ int8
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+ content_url_id
+ alf_content_url
+ id
+
+
+ content_encoding_id
+ alf_encoding
+ id
+
+
+ content_locale_id
+ alf_locale
+ id
+
+
+ content_mimetype_id
+ alf_mimetype
+ id
+
+
+
+
+
+ content_encoding_id
+
+
+
+
+ content_locale_id
+
+
+
+
+ content_mimetype_id
+
+
+
+
+ content_url_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ varchar(255)
+ false
+ false
+
+
+ varchar(12)
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ content_url_short
+ content_url_crc
+
+
+
+
+ orphan_time
+
+
+
+
+ content_size
+ id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(10)
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+ bytea
+ false
+ false
+
+
+ varchar(20)
+ false
+ false
+
+
+ varchar(15)
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+ content_url_id
+ alf_content_url
+ id
+
+
+
+
+
+ content_url_id
+
+
+
+
+ master_key_alias
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(100)
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ encoding_str
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(20)
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ locale_str
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(36)
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+ excl_resource_id
+ alf_lock_resource
+ id
+
+
+ shared_resource_id
+ alf_lock_resource
+ id
+
+
+
+
+
+ shared_resource_id
+ excl_resource_id
+
+
+
+
+ excl_resource_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(255)
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+ qname_ns_id
+ alf_namespace
+ id
+
+
+
+
+
+ qname_ns_id
+ qname_localname
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(100)
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ mimetype_str
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(100)
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ uri
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(36)
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+ varchar(255)
+ true
+ false
+
+
+ varchar(30)
+ true
+ false
+
+
+ varchar(255)
+ true
+ false
+
+
+ varchar(30)
+ true
+ false
+
+
+ varchar(30)
+ true
+ false
+
+
+
+
+
+
+ alf_node_pkey1?
+
+
+
+
+ id
+
+
+
+
+ acl_id
+ alf_access_control_list
+ id
+
+
+ locale_id
+ alf_locale
+ id
+
+
+ type_qname_id
+ alf_qname
+ id
+
+
+ store_id
+ alf_store
+ id
+
+
+ transaction_id
+ alf_transaction
+ id
+
+
+
+
+
+ store_id
+ uuid
+
+
+
+
+ acl_id
+
+
+
+
+ locale_id
+
+
+
+
+ store_id
+
+
+
+
+ type_qname_id
+ store_id
+ id
+
+
+
+
+ transaction_id
+ type_qname_id
+
+
+
+
+ store_id
+ type_qname_id
+ id
+
+
+
+
+ audit_creator
+ store_id
+ type_qname_id
+ id
+
+
+
+
+ audit_created
+ store_id
+ type_qname_id
+ id
+
+
+
+
+ audit_modifier
+ store_id
+ type_qname_id
+ id
+
+
+
+
+ audit_modified
+ store_id
+ type_qname_id
+ id
+
+
+
+
+ version
+
+
+
+
+ transaction_id
+
+
+
+
+ acl_id
+ audit_creator
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ node_id
+ qname_id
+
+
+
+
+ node_id
+ alf_node
+ id
+
+
+ qname_id
+ alf_qname
+ id
+
+
+
+
+
+ node_id
+
+
+
+
+ qname_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+
+
+ alf_node_assoc_pkey1?
+
+
+
+
+ id
+
+
+
+
+ target_node_id
+ alf_node
+ id
+
+
+ source_node_id
+ alf_node
+ id
+
+
+ type_qname_id
+ alf_qname
+ id
+
+
+
+
+
+ source_node_id
+ target_node_id
+ type_qname_id
+
+
+
+
+ source_node_id
+ type_qname_id
+ assoc_index
+
+
+
+
+ target_node_id
+ type_qname_id
+
+
+
+
+ type_qname_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+ bool
+ true
+ false
+
+
+ int8
+ true
+ false
+
+
+ float4
+ true
+ false
+
+
+ float8
+ true
+ false
+
+
+ varchar(1024)
+ true
+ false
+
+
+ bytea
+ true
+ false
+
+
+ int8
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ node_id
+ qname_id
+ list_index
+ locale_id
+
+
+
+
+ locale_id
+ alf_locale
+ id
+
+
+ node_id
+ alf_node
+ id
+
+
+ qname_id
+ alf_qname
+ id
+
+
+
+
+
+ locale_id
+
+
+
+
+ node_id
+
+
+
+
+ qname_id
+
+
+
+
+ qname_id
+ string_value
+ node_id
+
+
+
+
+ qname_id
+ long_value
+ node_id
+
+
+
+
+
+ qname_id
+ boolean_value
+ node_id
+
+
+
+
+ qname_id
+ double_value
+ node_id
+
+
+
+
+ qname_id
+ float_value
+ node_id
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(100)
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+ type_qname_id
+ alf_qname
+ id
+
+
+
+
+
+ type_qname_id
+ name
+
+
+
+
+ type_qname_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ varchar(255)
+ false
+ false
+
+
+ varchar(32)
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ java_class_name_crc
+ java_class_name_short
+
+
+
+
+ java_class_name
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+ int2
+ false
+ false
+
+
+ int2
+ false
+ false
+
+
+ int2
+ false
+ false
+
+
+ int2
+ false
+ false
+
+
+ int2
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+ int2
+ false
+ false
+
+
+ int2
+ false
+ false
+
+
+
+
+ date_value
+
+
+
+
+
+
+ full_year
+ month_of_year
+ day_of_month
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ float8
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ double_value
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ root_prop_id
+ contained_in
+ prop_index
+
+
+
+
+ root_prop_id
+ alf_prop_root
+ id
+
+
+ value_prop_id
+ alf_prop_value
+ id
+
+
+ key_prop_id
+ alf_prop_value
+ id
+
+
+
+
+
+ key_prop_id
+
+
+
+
+ value_prop_id
+
+
+
+
+ root_prop_id
+ key_prop_id
+ value_prop_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ bytea
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ varchar(1024)
+ false
+ false
+
+
+ varchar(16)
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ string_end_lower
+ string_crc
+
+
+
+
+ string_value
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int4
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+ prop1_id
+ alf_prop_root
+ id
+
+
+ value3_prop_id
+ alf_prop_value
+ id
+
+
+ value2_prop_id
+ alf_prop_value
+ id
+
+
+ value1_prop_id
+ alf_prop_value
+ id
+
+
+
+
+
+ value1_prop_id
+ value2_prop_id
+ value3_prop_id
+
+
+
+
+ prop1_id
+
+
+
+
+ value2_prop_id
+
+
+
+
+ value3_prop_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int2
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ actual_type_id
+ long_value
+
+
+
+
+ persisted_type
+ long_value
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(200)
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+ ns_id
+ alf_namespace
+ id
+
+
+
+
+
+ ns_id
+ local_name
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(39)
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+
+
+ ip_address
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ varchar(50)
+ false
+ false
+
+
+ varchar(100)
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+ root_node_id
+ alf_node
+ id
+
+
+
+
+
+ protocol
+ identifier
+
+
+
+
+ root_node_id
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ user_node_id
+ node_id
+
+
+
+
+ node_id
+ alf_node
+ id
+
+
+ user_node_id
+ alf_node
+ id
+
+
+
+
+
+ node_id
+
+
+
+
+
+
+
+ varchar(75)
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ bool
+ false
+ false
+
+
+ varchar(75)
+ true
+ false
+
+
+ varchar(255)
+ true
+ false
+
+
+ varchar(255)
+ true
+ false
+
+
+
+
+ tenant_domain
+
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+ varchar(56)
+ false
+ false
+
+
+ int8
+ true
+ false
+
+
+
+
+ id
+
+
+
+
+ server_id
+ alf_server
+ id
+
+
+
+
+
+ server_id
+
+
+
+
+ commit_time_ms
+ id
+
+
+
+
+ commit_time_ms
+
+
+
+
+ id
+ commit_time_ms
+
+
+
+
+
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+ int8
+ false
+ false
+
+
+
+
+ id
+
+
+
+
+ node_id
+ alf_node
+ id
+
+
+
+
+
+ node_id
+
+
+
+
+
+
diff --git a/repository/src/test/resources/alfresco/dbscripts/test-bootstrap-context.xml b/repository/src/test/resources/alfresco/dbscripts/test-bootstrap-context.xml
new file mode 100644
index 0000000000..e8bdc861c6
--- /dev/null
+++ b/repository/src/test/resources/alfresco/dbscripts/test-bootstrap-context.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ patch.db-V0-add-index-test
+ patch.db-V0-add-index-test.description
+ 0
+ 15000
+ 15001
+ ${system.new-node-transaction-indexes.ignored}
+
+ classpath:alfresco/dbscripts/upgrade/0/${db.script.dialect}/add-index-test.sql
+
+
+ classpath:alfresco/dbscripts/upgrade/0/${db.script.dialect}/add-index-test-problem-patterns.txt
+
+
+
+
\ No newline at end of file
diff --git a/repository/src/test/resources/alfresco/dbscripts/upgrade/0/org.alfresco.repo.domain.dialect.PostgreSQLDialect/add-index-test-problem-patterns.txt b/repository/src/test/resources/alfresco/dbscripts/upgrade/0/org.alfresco.repo.domain.dialect.PostgreSQLDialect/add-index-test-problem-patterns.txt
new file mode 100644
index 0000000000..9fcd0f3e8c
--- /dev/null
+++ b/repository/src/test/resources/alfresco/dbscripts/upgrade/0/org.alfresco.repo.domain.dialect.PostgreSQLDialect/add-index-test-problem-patterns.txt
@@ -0,0 +1 @@
+.*missing index.*.alf_node.idx_alf_node_test
\ No newline at end of file
diff --git a/repository/src/test/resources/alfresco/dbscripts/upgrade/0/org.alfresco.repo.domain.dialect.PostgreSQLDialect/add-index-test.sql b/repository/src/test/resources/alfresco/dbscripts/upgrade/0/org.alfresco.repo.domain.dialect.PostgreSQLDialect/add-index-test.sql
new file mode 100644
index 0000000000..0370e43385
--- /dev/null
+++ b/repository/src/test/resources/alfresco/dbscripts/upgrade/0/org.alfresco.repo.domain.dialect.PostgreSQLDialect/add-index-test.sql
@@ -0,0 +1,15 @@
+
+CREATE INDEX idx_alf_node_test ON alf_node (acl_id, audit_creator); --(optional)
+
+
+--
+-- Record script finish
+--
+DELETE FROM alf_applied_patch WHERE id = 'patch.db-V0-add-index-test';
+INSERT INTO alf_applied_patch
+ (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report)
+ VALUES
+ (
+ 'patch.db-V0-add-index-test', 'Manually executed script upgrade V0: Added new index test',
+ 0, 15000, -1, 15001, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed'
+ );
\ No newline at end of file