From 68f3a348cf5ed4d98c44dcd516e6b0e7fb2ca3aa Mon Sep 17 00:00:00 2001 From: Catherine Quamme Date: Fri, 26 Aug 2022 08:52:29 -0400 Subject: [PATCH 01/21] 0005427: Creating a Parameter That, Upon Registration, Will Automatically Create A Group Link Between Nodes if None Exist --- .../symmetric/common/ParameterConstants.java | 1 + .../symmetric/service/impl/RegistrationService.java | 13 +++++++++++-- .../src/main/resources/symmetric-default.properties | 7 +++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java index 91ab169f7f..4ba4198f2c 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java @@ -106,6 +106,7 @@ private ParameterConstants() { public final static String REGISTRATION_REQUIRE_NODE_GROUP_LINK = "registration.require.node.group.link"; public final static String REGISTRATION_REQUIRE_INITIAL_LOAD = "registration.require.initial.load"; public final static String REGISTRATION_PUSH_CONFIG_ALLOWED = "registration.push.config.allowed"; + public final static String REGISTRATION_AUTO_CREATE_GROUP_LINK = "registration.auto.create.group.link"; public final static String REGISTRATION_URL = "registration.url"; public final static String SYNC_URL = "sync.url"; public final static String ENGINE_NAME = "engine.name"; diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java index 5d05a40a4d..f170eecf96 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java @@ -149,7 +149,7 @@ protected void extractConfiguration(OutputStream out, Node registeredNode) { protected Node processRegistration(Node nodePriorToRegistration, String remoteHost, String remoteAddress, String userId, String password, boolean isRequestedRegistration) throws IOException { - Node processedNode = new Node(); + Node processedNode = new Node(); processedNode.setSyncEnabled(false); if (!allowClientRegistration) { log.warn("Cannot register a client node until this node has synced triggers"); @@ -203,7 +203,7 @@ protected Node processRegistration(Node nodePriorToRegistration, String remoteHo identity.getNodeGroupId(), nodePriorToRegistration.getNodeGroupId(), false); if (link == null && parameterService.is(ParameterConstants.REGISTRATION_REQUIRE_NODE_GROUP_LINK, - true)) { + true) && !parameterService.is(ParameterConstants.REGISTRATION_AUTO_CREATE_GROUP_LINK)) { RegistrationRequest req = new RegistrationRequest(nodePriorToRegistration, RegistrationStatus.ER, remoteHost, remoteAddress); req.setErrorMessage(String.format( @@ -238,6 +238,15 @@ protected Node processRegistration(Node nodePriorToRegistration, String remoteHo RegistrationStatus.RQ, remoteHost, remoteAddress)); return processedNode; } + if (link == null && parameterService.is(ParameterConstants.REGISTRATION_AUTO_CREATE_GROUP_LINK)) { + link = new NodeGroupLink(identity.getNodeGroupId(), nodePriorToRegistration.getNodeGroupId()); + configurationService.saveNodeGroupLink(link); + link = configurationService.getNodeGroupLinkFor(nodePriorToRegistration.getNodeGroupId(), identity.getNodeGroupId(), false); + if (link == null) { + link = new NodeGroupLink(identity.getNodeGroupId(), nodePriorToRegistration.getNodeGroupId(), NodeGroupLinkAction.P); + configurationService.saveNodeGroupLink(link); + } + } // TODO: since we send sym_node in registration batch, save this record with source_node_id = node_id foundNode.setSyncEnabled(true); foundNode.setSyncUrl(nodePriorToRegistration.getSyncUrl()); diff --git a/symmetric-core/src/main/resources/symmetric-default.properties b/symmetric-core/src/main/resources/symmetric-default.properties index 30b19feb6f..c671f82f89 100644 --- a/symmetric-core/src/main/resources/symmetric-default.properties +++ b/symmetric-core/src/main/resources/symmetric-default.properties @@ -769,6 +769,13 @@ registration.require.initial.load=true # Type: boolean registration.push.config.allowed=true +# When this is set to true a group link will be created by default between two groups even if the user does not explicitly set one up. +# +# DatabaseOverridable: true +# Tags: registration +# Type: boolean +registration.auto.create.group.link=true + # Initial load and reload events should normally block other channels to ensure each table # is loaded first followed by changes captured during the initial load. Setting this to false # will allow all channels to load in priority order even when reload events or From 92612eed472a00d06ed2e9e6c507174317b92add Mon Sep 17 00:00:00 2001 From: Eric Long Date: Mon, 29 Aug 2022 16:13:04 -0400 Subject: [PATCH 02/21] 0005429: Node registering over push is not sent config for pro tables --- .../org/jumpmind/symmetric/web/SymmetricEngineHolder.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/symmetric-server/src/main/java/org/jumpmind/symmetric/web/SymmetricEngineHolder.java b/symmetric-server/src/main/java/org/jumpmind/symmetric/web/SymmetricEngineHolder.java index a3a0ec424d..3312e4d2d6 100644 --- a/symmetric-server/src/main/java/org/jumpmind/symmetric/web/SymmetricEngineHolder.java +++ b/symmetric-server/src/main/java/org/jumpmind/symmetric/web/SymmetricEngineHolder.java @@ -55,6 +55,7 @@ import org.jumpmind.symmetric.common.Constants; import org.jumpmind.symmetric.common.ParameterConstants; import org.jumpmind.symmetric.common.SystemConstants; +import org.jumpmind.symmetric.common.TableConstants; import org.jumpmind.symmetric.ext.IDatabaseInstallStatementListener; import org.jumpmind.symmetric.model.Node; import org.jumpmind.symmetric.model.NodeGroup; @@ -284,6 +285,9 @@ public ISymmetricEngine install(Properties passedInProperties, IDatabaseInstallS IRegistrationService registrationService = currentEngine.getRegistrationService(); if (!registrationService.isAutoRegistration() && !registrationService.isRegistrationOpen(clientNodeGroupId, externalId)) { Node node = new Node(properties); + if (TableConstants.getTables("").contains(TableConstants.SYM_CONSOLE_USER)) { + node.setDeploymentType(Constants.DEPLOYMENT_TYPE_PROFESSIONAL); + } registrationService.openRegistration(node); } } From 3f9b7ec591c44064e5464726ca6c50a29779db8f Mon Sep 17 00:00:00 2001 From: Eric Long Date: Tue, 30 Aug 2022 14:12:09 -0400 Subject: [PATCH 03/21] 0005430: Android not implemented exception when sym_monitor syncs --- .../android/AndroidMonitorService.java | 140 ++++++++++++++++++ .../android/AndroidSymmetricEngine.java | 10 +- .../symmetric/ClientSymmetricEngine.java | 8 - .../symmetric/AbstractSymmetricEngine.java | 12 ++ 4 files changed, 157 insertions(+), 13 deletions(-) create mode 100644 symmetric-android/src/main/java/org/jumpmind/symmetric/android/AndroidMonitorService.java diff --git a/symmetric-android/src/main/java/org/jumpmind/symmetric/android/AndroidMonitorService.java b/symmetric-android/src/main/java/org/jumpmind/symmetric/android/AndroidMonitorService.java new file mode 100644 index 0000000000..2226cddeaa --- /dev/null +++ b/symmetric-android/src/main/java/org/jumpmind/symmetric/android/AndroidMonitorService.java @@ -0,0 +1,140 @@ +/** + * Licensed to JumpMind Inc under one or more contributor + * license agreements. See the NOTICE file distributed + * with this work for additional information regarding + * copyright ownership. JumpMind Inc licenses this file + * to you under the GNU General Public License, version 3.0 (GPLv3) + * (the "License"); you may not use this file except in compliance + * with the License. + * + * You should have received a copy of the GNU General Public License, + * version 3.0 (GPLv3) along with this library; if not, see + * . + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jumpmind.symmetric.android; + +import java.util.List; + +import org.jumpmind.symmetric.ISymmetricEngine; +import org.jumpmind.symmetric.model.Monitor; +import org.jumpmind.symmetric.model.MonitorEvent; +import org.jumpmind.symmetric.model.Notification; +import org.jumpmind.symmetric.service.IMonitorService; + +public class AndroidMonitorService implements IMonitorService { + public AndroidMonitorService(ISymmetricEngine engine) { + } + + @Override + public void update() { + } + + @Override + public List getMonitors() { + return null; + } + + @Override + public List getActiveMonitorsForNode(String nodeGroupId, String externalId) { + return null; + } + + @Override + public List getActiveMonitorsForNodeFromDb(String nodeGroupId, String externalId) { + return null; + } + + @Override + public void deleteMonitor(String notificationId) { + } + + @Override + public void saveMonitor(Monitor monitor) { + } + + @Override + public void saveMonitorAsCopy(Monitor monitor) { + } + + @Override + public void renameMonitor(String oldId, Monitor monitor) { + } + + @Override + public List getMonitorEvents() { + return null; + } + + @Override + public List getMonitorEventsFiltered(int limit, String type, int severityLevel, String nodeId, Boolean isResolved) { + return null; + } + + @Override + public void saveMonitorEvent(MonitorEvent notificationEvent) { + } + + @Override + public void deleteMonitorEvent(MonitorEvent event) { + } + + @Override + public void updateMonitorEventAsResolved(MonitorEvent event) { + } + + @Override + public List getNotifications() { + return null; + } + + @Override + public List getActiveNotificationsForNode(String nodeGroupId, String externalId) { + return null; + } + + @Override + public List getActiveNotificationsForNodeFromDb(String nodeGroupId, String externalId) { + return null; + } + + @Override + public void saveNotification(Notification notification) { + } + + @Override + public void saveNotificationAsCopy(Notification notification) { + } + + @Override + public void renameNotification(String oldId, Notification notification) { + } + + @Override + public void deleteNotification(String notificationId) { + } + + @Override + public void flushMonitorCache() { + } + + @Override + public void flushNotificationCache() { + } + + @Override + public List getActiveMonitorsUnresolvedForNode(String nodeGroupId, String externalId) { + return null; + } + + @Override + public List getActiveMonitorsUnresolvedForNodeFromDb(String nodeGroupId, String externalId) { + return null; + } +} diff --git a/symmetric-android/src/main/java/org/jumpmind/symmetric/android/AndroidSymmetricEngine.java b/symmetric-android/src/main/java/org/jumpmind/symmetric/android/AndroidSymmetricEngine.java index 8fa262ae95..19cc82ee45 100644 --- a/symmetric-android/src/main/java/org/jumpmind/symmetric/android/AndroidSymmetricEngine.java +++ b/symmetric-android/src/main/java/org/jumpmind/symmetric/android/AndroidSymmetricEngine.java @@ -25,7 +25,6 @@ import java.util.List; import java.util.Properties; -import org.apache.commons.lang3.NotImplementedException; import org.jumpmind.db.platform.IDatabasePlatform; import org.jumpmind.extension.IProgressListener; import org.jumpmind.properties.TypedProperties; @@ -141,6 +140,11 @@ protected IFileSyncService buildFileSyncService() { return new AndroidFileSyncService(this); } + @Override + protected IMonitorService buildMonitorService(ISymmetricDialect symmetricDialect) { + return new AndroidMonitorService(this); + } + @Override protected IClusterService createClusterService() { return new AndroidClusterService(parameterService, symmetricDialect, nodeService); @@ -221,8 +225,4 @@ public File snapshot(IProgressListener listener) { public List listSnapshots() { return new ArrayList<>(0); } - - public IMonitorService getMonitorService() { - throw new NotImplementedException(); - } } diff --git a/symmetric-client/src/main/java/org/jumpmind/symmetric/ClientSymmetricEngine.java b/symmetric-client/src/main/java/org/jumpmind/symmetric/ClientSymmetricEngine.java index baa720a5ad..2599327cd3 100644 --- a/symmetric-client/src/main/java/org/jumpmind/symmetric/ClientSymmetricEngine.java +++ b/symmetric-client/src/main/java/org/jumpmind/symmetric/ClientSymmetricEngine.java @@ -67,9 +67,7 @@ import org.jumpmind.symmetric.job.JobManager; import org.jumpmind.symmetric.security.INodePasswordFilter; import org.jumpmind.symmetric.service.IExtensionService; -import org.jumpmind.symmetric.service.IMonitorService; import org.jumpmind.symmetric.service.impl.ClientExtensionService; -import org.jumpmind.symmetric.service.impl.MonitorService; import org.jumpmind.symmetric.service.impl.NodeService; import org.jumpmind.symmetric.statistic.IStatisticManager; import org.jumpmind.symmetric.statistic.StatisticManager; @@ -97,7 +95,6 @@ public class ClientSymmetricEngine extends AbstractSymmetricEngine { protected Properties properties; protected DataSource dataSource; protected ApplicationContext springContext; - protected IMonitorService monitorService; /** * @param dataSource @@ -197,7 +194,6 @@ protected void init() { SymmetricUtils.logNotices(); } super.init(); - this.monitorService = new MonitorService(this, symmetricDialect); this.dataSource = platform.getDataSource(); PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); configurer.setProperties(parameterService.getAllParameters()); @@ -515,8 +511,4 @@ public void clearCaches() { monitorService.flushMonitorCache(); monitorService.flushNotificationCache(); } - - public IMonitorService getMonitorService() { - return monitorService; - } } diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/AbstractSymmetricEngine.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/AbstractSymmetricEngine.java index f0b3088dbc..5ddc2ee13e 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/AbstractSymmetricEngine.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/AbstractSymmetricEngine.java @@ -85,6 +85,7 @@ import org.jumpmind.symmetric.service.IInitialLoadService; import org.jumpmind.symmetric.service.ILoadFilterService; import org.jumpmind.symmetric.service.IMailService; +import org.jumpmind.symmetric.service.IMonitorService; import org.jumpmind.symmetric.service.INodeCommunicationService; import org.jumpmind.symmetric.service.INodeService; import org.jumpmind.symmetric.service.IOfflinePullService; @@ -116,6 +117,7 @@ import org.jumpmind.symmetric.service.impl.InitialLoadService; import org.jumpmind.symmetric.service.impl.LoadFilterService; import org.jumpmind.symmetric.service.impl.MailService; +import org.jumpmind.symmetric.service.impl.MonitorService; import org.jumpmind.symmetric.service.impl.NodeCommunicationService; import org.jumpmind.symmetric.service.impl.NodeService; import org.jumpmind.symmetric.service.impl.OfflinePullService; @@ -196,6 +198,7 @@ abstract public class AbstractSymmetricEngine implements ISymmetricEngine { protected IMailService mailService; protected IContextService contextService; protected IUpdateService updateService; + protected IMonitorService monitorService; protected ICacheManager cacheManager; protected Date lastRestartTime = null; @@ -333,6 +336,7 @@ protected void init() { this.fileSyncService = buildFileSyncService(); this.fileSyncExtractorService = new FileSyncExtractorService(this); this.mailService = new MailService(parameterService, securityService, symmetricDialect); + this.monitorService = buildMonitorService(symmetricDialect); String updateServiceClassName = properties.get(ParameterConstants.UPDATE_SERVICE_CLASS); if (updateServiceClassName == null) { this.updateService = new UpdateService(this); @@ -386,6 +390,10 @@ protected INodeCommunicationService buildNodeCommunicationService(IClusterServic return new NodeCommunicationService(clusterService, nodeService, parameterService, configurationService, symmetricDialect); } + protected IMonitorService buildMonitorService(ISymmetricDialect symmetricDialect) { + return new MonitorService(this, symmetricDialect); + } + abstract protected IStagingManager createStagingManager(); abstract protected IStatisticManager createStatisticManager(); @@ -1217,6 +1225,10 @@ public IUpdateService getUpdateService() { return updateService; } + public IMonitorService getMonitorService() { + return monitorService; + } + @Override public String getNodeId() { return getNodeService().findIdentityNodeId(); From 038cfcc91708a20fce08036f79287f681efd2512 Mon Sep 17 00:00:00 2001 From: evan-miller-jumpmind <70151986+evan-miller-jumpmind@users.noreply.github.com> Date: Tue, 30 Aug 2022 16:09:42 -0400 Subject: [PATCH 04/21] 0000922: Prevented floats, doubles, and reals from being included in where clauses and joins, also ran spotlessApply --- .../symmetric/common/ParameterConstants.java | 3 +- .../db/AbstractSymmetricDialect.java | 2 +- .../db/platform/AbstractDatabasePlatform.java | 6 ++-- ...DefaultDatabaseWriterConflictResolver.java | 6 ++-- .../platform/JdbcDatabasePlatformFactory.java | 34 +++++++++---------- .../db/platform/ase/AseDatabasePlatform.java | 2 +- .../db/platform/db2/Db2DatabasePlatform.java | 2 +- .../platform/derby/DerbyDatabasePlatform.java | 3 +- .../mssql/MsSql2000DatabasePlatform.java | 2 +- .../mssql/MsSql2016DatabasePlatform.java | 4 +-- .../platform/mysql/MySqlDatabasePlatform.java | 2 +- .../oracle/OracleDatabasePlatform.java | 2 +- .../PostgreSqlDatabasePlatform.java | 2 +- .../tibero/TiberoDatabasePlatform.java | 2 +- 14 files changed, 36 insertions(+), 36 deletions(-) diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java index c69c5c6ec4..947df9a21d 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java @@ -451,7 +451,6 @@ private ParameterConstants() { public final static String CLOUD_BULK_FIELD_TERMINATOR = "cloud.bulk.field.terminator"; public final static String CLOUD_BULK_FIELD_QUOTE = "cloud.bulk.field.quote"; public final static String CLOUD_BULK_CODEPAGE = "cloud.bulk.codepage"; - public final static String SNAPSHOT_FILE_INCLUDE_HOSTNAME = "snapshot.file.include.hostname"; public final static String SNAPSHOT_MAX_FILES = "snapshot.max.files"; public final static String SNAPSHOT_MAX_BATCHES = "snapshot.max.batches"; @@ -484,7 +483,7 @@ private ParameterConstants() { public final static String S3_LOAD_AWS_SECRET_KEY = "s3.load.aws.secret.key"; public final static String SINGLESTORE_AUDIT_LOG_DIR = "single.store.audit.log.dir"; public final static String SPATIAL_TYPES_ENABLED = "spatial.data.types.enabled"; - + public static Map getParameterMetaData() { return parameterMetaData; } diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/db/AbstractSymmetricDialect.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/db/AbstractSymmetricDialect.java index 046cb92963..7e44667fdd 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/db/AbstractSymmetricDialect.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/db/AbstractSymmetricDialect.java @@ -92,7 +92,7 @@ abstract public class AbstractSymmetricDialect implements ISymmetricDialect { protected Map sqlReplacementTokens = new HashMap(); protected String tablePrefixLowerCase; protected boolean isSpatialTypesEnabled = true; - + public AbstractSymmetricDialect(IParameterService parameterService, IDatabasePlatform platform) { this.parameterService = parameterService; this.platform = platform; diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDatabasePlatform.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDatabasePlatform.java index 50599951f0..5c43de958a 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDatabasePlatform.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDatabasePlatform.java @@ -721,7 +721,7 @@ public Table makeAllColumnsPrimaryKeys(Table table) { } } else { for (Column column : result.getColumns()) { - if (!isLob(column.getMappedTypeCode())) { + if (!isLob(column.getMappedTypeCode()) && canColumnBeUsedInWhereClause(column)) { column.setPrimaryKey(true); } } @@ -935,7 +935,9 @@ public Database readDatabaseFromXml(InputStream is, boolean alterCaseToMatchData } public boolean canColumnBeUsedInWhereClause(Column column) { - return true; + return column.getJdbcTypeCode() != Types.FLOAT && + column.getJdbcTypeCode() != Types.DOUBLE && + column.getJdbcTypeCode() != Types.REAL; } public java.util.Date parseTimestamp(int type, String value) { diff --git a/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriterConflictResolver.java b/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriterConflictResolver.java index a43ce0d7cc..2e8ecf8dd5 100644 --- a/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriterConflictResolver.java +++ b/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriterConflictResolver.java @@ -205,9 +205,9 @@ protected void modifyTimestampsForPrecision(IDatabasePlatform platform, Table ta boolean checkDatetime = platform.getName().startsWith(DatabaseNamesConstants.MSSQL) || platform.getName().startsWith(DatabaseNamesConstants.ASE); Column[] pkColumns = table.getPrimaryKeyColumns(); for (int i = 0; i < pkColumns.length && i < pkData.length; i++) { - if (pkData[i] == null) { - continue; - } + if (pkData[i] == null) { + continue; + } int type = pkColumns[i].getMappedTypeCode(); if (type == Types.TIMESTAMP || type == Types.TIME || type == ColumnTypes.TIMESTAMPTZ || type == ColumnTypes.TIMESTAMPLTZ || type == ColumnTypes.TIMETZ) { diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/JdbcDatabasePlatformFactory.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/JdbcDatabasePlatformFactory.java index 91e4e57ba0..5c54ff5724 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/JdbcDatabasePlatformFactory.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/JdbcDatabasePlatformFactory.java @@ -311,11 +311,10 @@ protected void determineDatabaseNameVersionSubprotocol(DataSource dataSource, Co } } } - if (nameVersion.getProtocol().equalsIgnoreCase(MsSql2016DatabasePlatform.JDBC_SUBPROTOCOL)) { - if (isMSSQLAzureManagedInstance(connection)) { - nameVersion.setName(DatabaseNamesConstants.MSSQLAZURE); - } + if (isMSSQLAzureManagedInstance(connection)) { + nameVersion.setName(DatabaseNamesConstants.MSSQLAZURE); + } } } @@ -367,21 +366,20 @@ private boolean isFirebirdDialect1(Connection connection) { } return isDialect1; } - + private boolean isMSSQLAzureManagedInstance(Connection connection) { - boolean isManagedInstance = false; - - try (Statement s = connection.createStatement()) { - ResultSet rs = s.executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') AS INT)"); - if (rs.next()) { - if (rs.getInt(1) == 8) { - isManagedInstance = true; - } - } - } catch (Exception e) { - log.info("Azure Managed Instance of SQLServer not detected."); - } - return isManagedInstance; + boolean isManagedInstance = false; + try (Statement s = connection.createStatement()) { + ResultSet rs = s.executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') AS INT)"); + if (rs.next()) { + if (rs.getInt(1) == 8) { + isManagedInstance = true; + } + } + } catch (Exception e) { + log.info("Azure Managed Instance of SQLServer not detected."); + } + return isManagedInstance; } private boolean isOracle122Compatible(Connection connection) { diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseDatabasePlatform.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseDatabasePlatform.java index 5479e6adc4..6db4015c4d 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseDatabasePlatform.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseDatabasePlatform.java @@ -119,7 +119,7 @@ public Map getSqlScriptReplacementTokens() { @Override public boolean canColumnBeUsedInWhereClause(Column column) { - return !isLob(column.getJdbcTypeCode()); + return !isLob(column.getJdbcTypeCode()) && super.canColumnBeUsedInWhereClause(column); } @Override diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/db2/Db2DatabasePlatform.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/db2/Db2DatabasePlatform.java index b2dcf349a2..42f9c90adf 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/db2/Db2DatabasePlatform.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/db2/Db2DatabasePlatform.java @@ -107,7 +107,7 @@ public String getDefaultCatalog() { @Override public boolean canColumnBeUsedInWhereClause(Column column) { - return !column.isOfBinaryType(); + return !column.isOfBinaryType() && super.canColumnBeUsedInWhereClause(column); } @Override diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/derby/DerbyDatabasePlatform.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/derby/DerbyDatabasePlatform.java index fc1a36bf55..222d32ed41 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/derby/DerbyDatabasePlatform.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/derby/DerbyDatabasePlatform.java @@ -111,7 +111,8 @@ public boolean canColumnBeUsedInWhereClause(Column column) { return (!column.isOfBinaryType()) && column.getJdbcTypeCode() != Types.CLOB && column.getJdbcTypeCode() != Types.LONGVARCHAR && - column.getJdbcTypeCode() != Types.LONGNVARCHAR; + column.getJdbcTypeCode() != Types.LONGNVARCHAR && + super.canColumnBeUsedInWhereClause(column); } @Override diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mssql/MsSql2000DatabasePlatform.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mssql/MsSql2000DatabasePlatform.java index 7e4cd8227b..49ff8252c0 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mssql/MsSql2000DatabasePlatform.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mssql/MsSql2000DatabasePlatform.java @@ -98,7 +98,7 @@ public boolean canColumnBeUsedInWhereClause(Column column) { if (column.getMappedTypeCode() == Types.VARBINARY && column.getSizeAsInt() > 8000) { return false; } - return !isLob(column.getJdbcTypeCode()); + return !isLob(column.getJdbcTypeCode()) && super.canColumnBeUsedInWhereClause(column); } @Override diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mssql/MsSql2016DatabasePlatform.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mssql/MsSql2016DatabasePlatform.java index 35cc835fa7..5092bd39c9 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mssql/MsSql2016DatabasePlatform.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mssql/MsSql2016DatabasePlatform.java @@ -27,8 +27,8 @@ import org.jumpmind.db.sql.SqlTemplateSettings; public class MsSql2016DatabasePlatform extends MsSql2008DatabasePlatform { - public static final String JDBC_SUBPROTOCOL = "sqlserver"; - + public static final String JDBC_SUBPROTOCOL = "sqlserver"; + public MsSql2016DatabasePlatform(DataSource dataSource, SqlTemplateSettings settings) { super(dataSource, settings); if (settings.isAllowTriggerCreateOrReplace()) { diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mysql/MySqlDatabasePlatform.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mysql/MySqlDatabasePlatform.java index ea74b5b850..6bbb8c3912 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mysql/MySqlDatabasePlatform.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mysql/MySqlDatabasePlatform.java @@ -249,7 +249,7 @@ public boolean canColumnBeUsedInWhereClause(Column column) { || column.getMappedTypeCode() == Types.BINARY) { return true; } - return !column.isOfBinaryType(); + return !column.isOfBinaryType() && super.canColumnBeUsedInWhereClause(column); } @Override diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/oracle/OracleDatabasePlatform.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/oracle/OracleDatabasePlatform.java index 37683dcd50..ffce91e672 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/oracle/OracleDatabasePlatform.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/oracle/OracleDatabasePlatform.java @@ -120,7 +120,7 @@ public String getDefaultSchema() { @Override public boolean canColumnBeUsedInWhereClause(Column column) { - return !(isLob(column.getJdbcTypeCode()) || isGeometry(column)); + return !(isLob(column.getJdbcTypeCode()) || isGeometry(column)) && super.canColumnBeUsedInWhereClause(column); } private boolean isGeometry(Column column) { diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/postgresql/PostgreSqlDatabasePlatform.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/postgresql/PostgreSqlDatabasePlatform.java index bf3f586dac..f7a25e3c1d 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/postgresql/PostgreSqlDatabasePlatform.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/postgresql/PostgreSqlDatabasePlatform.java @@ -383,6 +383,6 @@ public boolean canColumnBeUsedInWhereClause(Column column) { if (column.getJdbcTypeName() != null && column.getJdbcTypeName().startsWith("json")) { return false; } - return true; + return super.canColumnBeUsedInWhereClause(column); } } diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/tibero/TiberoDatabasePlatform.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/tibero/TiberoDatabasePlatform.java index a04403489a..60f4ee23c7 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/tibero/TiberoDatabasePlatform.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/tibero/TiberoDatabasePlatform.java @@ -84,7 +84,7 @@ public String getDefaultSchema() { @Override public boolean canColumnBeUsedInWhereClause(Column column) { String jdbcTypeName = column.getJdbcTypeName(); - return !(isLob(column.getJdbcTypeCode()) || column.isOfBinaryType() || "RAW".equals(jdbcTypeName)); + return !(isLob(column.getJdbcTypeCode()) || column.isOfBinaryType() || "RAW".equals(jdbcTypeName)) && super.canColumnBeUsedInWhereClause(column); } @Override From 39ea7c511186be42cb2d54e739d161db6b91b87e Mon Sep 17 00:00:00 2001 From: Eric Long Date: Wed, 31 Aug 2022 08:04:52 -0400 Subject: [PATCH 05/21] 0005431: Install triggers on registration server before configuration is complete --- .../java/org/jumpmind/symmetric/common/Constants.java | 1 + .../jumpmind/symmetric/common/ParameterConstants.java | 1 + .../symmetric/service/impl/RouterService.java | 8 +++----- .../symmetric/service/impl/TriggerRouterService.java | 5 +++++ .../src/main/resources/symmetric-default.properties | 11 +++++++++++ 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/Constants.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/Constants.java index 5d2dd2f368..ae42b21bbe 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/Constants.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/Constants.java @@ -62,6 +62,7 @@ private Constants() { */ public static final String UNKNOWN_ROUTER_ID = "?"; public static final String UNKNOWN_STRING = "unknown"; + public static final String NO_GROUP = "nogroup"; public static final String ALWAYS_TRUE_CONDITION = "1=1"; public static final String UNROUTED_NODE_ID = "-1"; public static final long LONG_OPERATION_THRESHOLD = 30000; diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java index 947df9a21d..19a7f84fd9 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ParameterConstants.java @@ -379,6 +379,7 @@ private ParameterConstants() { public final static String LOG_SQL_PARAMETERS_INLINE = "log.sql.parameters.inline"; public final static String SYNC_TRIGGERS_THREAD_COUNT_PER_SERVER = "sync.triggers.thread.count.per.server"; public final static String SYNC_TRIGGERS_TIMEOUT_IN_SECONDS = "sync.triggers.timeout.in.seconds"; + public final static String SYNC_TRIGGERS_REG_SVR_INSTALL_WITHOUT_CONFIG = "sync.triggers.reg.svr.install.without.config"; public static final String SMTP_HOST = "smtp.host"; public static final String SMTP_TRANSPORT = "smtp.transport"; public static final String SMTP_PORT = "smtp.port"; diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RouterService.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RouterService.java index 55bfc9c22a..1dc321da48 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RouterService.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RouterService.java @@ -636,11 +636,9 @@ protected Set findAvailableNodes(TriggerRouter triggerRouter, ChannelRoute if (link != null) { nodes.addAll(engine.getNodeService().findEnabledNodesFromNodeGroup( router.getNodeGroupLink().getTargetNodeGroupId())); - } else { - log.error("The router {} has no node group link configured from {} to {}", - new Object[] { router.getRouterId(), - router.getNodeGroupLink().getSourceNodeGroupId(), - router.getNodeGroupLink().getTargetNodeGroupId() }); + } else if (!router.getRouterId().startsWith(parameterService.getTablePrefix().toLowerCase())) { + log.error("The router {} has no node group link configured from {} to {}", new Object[] { router.getRouterId(), + router.getNodeGroupLink().getSourceNodeGroupId(), router.getNodeGroupLink().getTargetNodeGroupId() }); } context.getAvailableNodes().put(triggerRouter, nodes); } diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java index 9f17727769..9887dcc3d1 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java @@ -686,6 +686,11 @@ protected List getConfigurationTablesTriggerRoutersForCurrentNode triggerRouters.addAll(buildTriggerRoutersForSymmetricTables(Version.version(), nodeGroupLink)); } + if (triggerRouters.size() == 0 && parameterService.is(ParameterConstants.SYNC_TRIGGERS_REG_SVR_INSTALL_WITHOUT_CONFIG, true) && + parameterService.isRegistrationServer()) { + NodeGroupLink link = new NodeGroupLink(sourceNodeGroupId, Constants.NO_GROUP); + triggerRouters.addAll(buildTriggerRoutersForSymmetricTables(Version.version(), link)); + } return triggerRouters; } diff --git a/symmetric-core/src/main/resources/symmetric-default.properties b/symmetric-core/src/main/resources/symmetric-default.properties index 2dd778bc97..7c0531be75 100644 --- a/symmetric-core/src/main/resources/symmetric-default.properties +++ b/symmetric-core/src/main/resources/symmetric-default.properties @@ -652,6 +652,17 @@ sync.triggers.thread.count.per.server=1 # Type: integer sync.triggers.timeout.in.seconds=3600 +# Whether or not sync triggers job will install triggers on a registration server before configuration is created. +# When true, triggers are installed as soon as possible, right after configuration tables are created. +# When false, triggers are installed when the configuration includes one or more group links where this node is the source. +# A default of true avoids race conditions associated with registering nodes and performing operations before configuration is complete +# and triggers are installed. +# +# DatabaseOverridable: false +# Tags: general +# Type: boolean +sync.triggers.reg.svr.install.without.config=true + # If this is true, when a configuration change is detected during routing, # symmetric will make sure all triggers in the database are up to date. # From d6c8d855bf2502d88c8e4e8b2fae260045fd8076 Mon Sep 17 00:00:00 2001 From: Catherine Quamme Date: Wed, 31 Aug 2022 08:13:04 -0400 Subject: [PATCH 06/21] 0005427: Creating a Parameter That, Upon Registration, Will Automatically Create A Group Link Between Nodes if None Exist --- .../service/impl/RegistrationService.java | 19 ++++++++++++++++--- .../service/impl/TriggerRouterService.java | 7 ++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java index f170eecf96..6371bc9e39 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java @@ -58,6 +58,7 @@ import org.jumpmind.symmetric.model.NodeSecurity; import org.jumpmind.symmetric.model.OutgoingBatch; import org.jumpmind.symmetric.model.RegistrationRequest; +import org.jumpmind.symmetric.model.Router; import org.jumpmind.symmetric.model.RegistrationRequest.RegistrationStatus; import org.jumpmind.symmetric.model.RemoteNodeStatus.Status; import org.jumpmind.symmetric.security.INodePasswordFilter; @@ -69,6 +70,7 @@ import org.jumpmind.symmetric.service.INodeService; import org.jumpmind.symmetric.service.IOutgoingBatchService; import org.jumpmind.symmetric.service.IRegistrationService; +import org.jumpmind.symmetric.service.ITriggerRouterService; import org.jumpmind.symmetric.service.RegistrationFailedException; import org.jumpmind.symmetric.service.RegistrationNotOpenException; import org.jumpmind.symmetric.service.RegistrationRedirectException; @@ -240,12 +242,23 @@ protected Node processRegistration(Node nodePriorToRegistration, String remoteHo } if (link == null && parameterService.is(ParameterConstants.REGISTRATION_AUTO_CREATE_GROUP_LINK)) { link = new NodeGroupLink(identity.getNodeGroupId(), nodePriorToRegistration.getNodeGroupId()); - configurationService.saveNodeGroupLink(link); + configurationService.saveNodeGroupLink(link); + ITriggerRouterService triggerRouterService = engine.getTriggerRouterService(); + Router router = new Router(); + router.setNodeGroupLink(link); + router.setRouterId(router.createDefaultName()); + triggerRouterService.saveRouter(router); link = configurationService.getNodeGroupLinkFor(nodePriorToRegistration.getNodeGroupId(), identity.getNodeGroupId(), false); if (link == null) { - link = new NodeGroupLink(identity.getNodeGroupId(), nodePriorToRegistration.getNodeGroupId(), NodeGroupLinkAction.P); - configurationService.saveNodeGroupLink(link); + link = new NodeGroupLink(nodePriorToRegistration.getNodeGroupId(), identity.getNodeGroupId(), NodeGroupLinkAction.P); + configurationService.saveNodeGroupLink(link); + router = new Router(); + router.setNodeGroupLink(link); + router.setRouterId(router.createDefaultName()); + triggerRouterService.saveRouter(router); } + configurationService.clearCache(); + triggerRouterService.clearCache(); } // TODO: since we send sym_node in registration batch, save this record with source_node_id = node_id foundNode.setSyncEnabled(true); diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java index 9f17727769..cb7e19c5fa 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java @@ -508,8 +508,13 @@ public List buildTriggersForSymmetricTables(String version, Set configTablesWithoutCapture = TableConstants.getConfigTablesWithoutCapture(symmetricDialect.getTablePrefix()); for (String tableName : tables) { Trigger trigger = buildTriggerForSymmetricTable(tableName, configTablesWithoutCapture); - triggers.add(trigger); + triggers.add(trigger); } + /* + if (parameterService.is(ParameterConstants.REGISTRATION_AUTO_CREATE_GROUP_LINK)) { + updateOrCreateDatabaseTriggers(triggers, new StringBuilder(), true, + true, getActiveTriggerHistories(), true); + }*/ return triggers; } From de11f15d7b5ef7e9f16569206eb44530f91fa4a7 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Wed, 31 Aug 2022 10:10:47 -0400 Subject: [PATCH 07/21] 0005392: Unable to write to sym_outgoing_batch when using Postgres --- .../service/impl/OutgoingBatchService.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/OutgoingBatchService.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/OutgoingBatchService.java index 924961c017..528c8211d2 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/OutgoingBatchService.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/OutgoingBatchService.java @@ -216,26 +216,26 @@ public void updateOutgoingBatch(ISqlTransaction transaction, OutgoingBatch outgo outgoingBatch.getConflictLoseCount(), outgoingBatch.getIgnoreRowCount(), outgoingBatch.getMissingDeleteCount(), outgoingBatch.getSkipCount(), outgoingBatch.getExtractRowCount(), outgoingBatch.getExtractInsertRowCount(), outgoingBatch.getExtractUpdateRowCount(), outgoingBatch.getExtractDeleteRowCount(), - outgoingBatch.getTransformExtractMillis(), outgoingBatch.getTransformLoadMillis(), outgoingBatch.isBulkLoadFlag(), + outgoingBatch.getTransformExtractMillis(), outgoingBatch.getTransformLoadMillis(), outgoingBatch.isBulkLoadFlag() ? 1 : 0, outgoingBatch.getBatchId(), outgoingBatch.getNodeId() }, - new int[] { Types.CHAR, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, + new int[] { Types.CHAR, symmetricDialect.getSqlTypeForIds(), Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, - Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.NUMERIC, Types.VARCHAR, Types.NUMERIC, Types.NUMERIC, - Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, - Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, + Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.NUMERIC, Types.VARCHAR, symmetricDialect.getSqlTypeForIds(), + Types.NUMERIC, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, + Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, symmetricDialect.getSqlTypeForIds(), Types.VARCHAR }); } public void updateOutgoingBatches(ISqlTransaction transaction, List batches, int flushSize) { - int[] types = new int[] { Types.CHAR, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, - Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, + int[] types = new int[] { Types.CHAR, symmetricDialect.getSqlTypeForIds(), Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, - Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.NUMERIC, Types.VARCHAR, Types.NUMERIC, Types.NUMERIC, - Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, - Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, + Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.NUMERIC, Types.VARCHAR, symmetricDialect.getSqlTypeForIds(), + Types.NUMERIC, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, + Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, + Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, symmetricDialect.getSqlTypeForIds(), Types.VARCHAR }; int count = 0; transaction.prepare(getSql("updateOutgoingBatchSql")); @@ -259,7 +259,7 @@ public void updateOutgoingBatches(ISqlTransaction transaction, List= flushSize) { transaction.flush(); From c3245e1f6fd94965231ffbfea59f2272d1e08069 Mon Sep 17 00:00:00 2001 From: Catherine Quamme Date: Wed, 31 Aug 2022 12:27:47 -0400 Subject: [PATCH 08/21] 0005427: Creating a Parameter That, Upon Registration, Will Automatically Create A Group Link Between Nodes if None Exist --- .../jumpmind/symmetric/service/impl/ConfigurationService.java | 1 + .../jumpmind/symmetric/service/impl/RegistrationService.java | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/ConfigurationService.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/ConfigurationService.java index cac8c14f7f..722b0c0bf0 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/ConfigurationService.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/ConfigurationService.java @@ -178,6 +178,7 @@ public void saveNodeGroupLink(NodeGroupLink link) { link.getLastUpdateTime(), link.getLastUpdateBy(), link.getCreateTime()); } + clearCache(); } public void renameNodeGroupLink(String oldSourceId, String oldTargetId, NodeGroupLink link) { diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java index 6371bc9e39..926f7f4cd2 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java @@ -257,8 +257,6 @@ protected Node processRegistration(Node nodePriorToRegistration, String remoteHo router.setRouterId(router.createDefaultName()); triggerRouterService.saveRouter(router); } - configurationService.clearCache(); - triggerRouterService.clearCache(); } // TODO: since we send sym_node in registration batch, save this record with source_node_id = node_id foundNode.setSyncEnabled(true); From ec312222ce871bd5ca4b86dd0ccb6b11dd94546e Mon Sep 17 00:00:00 2001 From: evan-miller-jumpmind <70151986+evan-miller-jumpmind@users.noreply.github.com> Date: Thu, 1 Sep 2022 11:47:57 -0400 Subject: [PATCH 09/21] 0001282: Made fix only apply to a default value of '0000-00-00' --- .../java/org/jumpmind/db/platform/mysql/MySqlDdlBuilder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/mysql/MySqlDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/mysql/MySqlDdlBuilder.java index 75fc3521a5..ae67fff2e0 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/mysql/MySqlDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/mysql/MySqlDdlBuilder.java @@ -174,7 +174,8 @@ protected void writeColumnDefaultValueStmt(Table table, Column column, StringBui super.writeColumnDefaultValueStmt(table, column, ddl); if (column.getParsedDefaultValue() == null && !(databaseInfo.isDefaultValueUsedForIdentitySpec() && column.isAutoIncrement()) - && StringUtils.isBlank(column.getDefaultValue()) && column.findPlatformColumn(databaseName) != null) { + && StringUtils.isBlank(column.getDefaultValue()) && column.findPlatformColumn(databaseName) != null + && "0000-00-00".equals(column.findPlatformColumn(databaseName).getDefaultValue())) { ddl.append(" DEFAULT "); writeColumnDefaultValue(table, column, ddl); } From cb6aabfe45126070375c899a391332dc3f8b5fba Mon Sep 17 00:00:00 2001 From: Eric Long Date: Thu, 1 Sep 2022 13:01:49 -0400 Subject: [PATCH 10/21] update keystore docs --- .../src/asciidoc/advanced-topics.ad | 312 +++++++----------- 1 file changed, 111 insertions(+), 201 deletions(-) diff --git a/symmetric-assemble/src/asciidoc/advanced-topics.ad b/symmetric-assemble/src/asciidoc/advanced-topics.ad index 01f5a47336..7e70cb311e 100644 --- a/symmetric-assemble/src/asciidoc/advanced-topics.ad +++ b/symmetric-assemble/src/asciidoc/advanced-topics.ad @@ -72,103 +72,36 @@ and registration when using registration redirect. include::advanced/offline.ad[] === Encrypted Passwords + +ifdef::pro[] +NOTE: This section describes command line usage. The Manage → Startup Parameters screen in the web console automatically encrypts database properties. +endif::pro[] -The `db.user` and `db.password` properties will accept encrypted text, which protects -against casual observation. This encryption can be done two different ways -using the command: encrypt-text +The `db.user` and `db.password` properties can be protected by encrypting them. +This encryption can be done two different ways. The first option is to pass the text as an argument: ==== [source, cli] ---- -symadmin -e {engine name} encrypt-text "text-to-encrypt" ----- - -[source, cli] ----- -symadmin -p {properties file} encrypt-text "text-to-encrypt" +symadmin encrypt-text "text-to-encrypt" ---- ==== -NOTE: The text-to-encrypt should be surrounded with double quotes if spaces or symbols are used. +NOTE: The text-to-encrypt on the command line should be surrounded with double quotes if spaces or symbols are used. -The second option is without an argument. In this case, it will prompt you to enter text: +The second option is without an argument, and it will prompt you to enter text: ==== [source, cli] ---- -symadmin -e {engine name} encrypt-text -Enter Text: ----- - -[source, cli] ----- -symadmin -p {properties file} encrypt-text +symadmin encrypt-text Enter Text: ---- ==== - -The text is encrypted using a secret key named "sym.secret" that is retrieved from a keystore file. -By default, the keystore is located in `security/keystore`. -The location and filename of the keystore can be overridden by setting the "sym.keystore.file" system property. -If the secret key is not found, the system will generate and install a secret key for use with Triple DES cipher. - -Generate a new secret key for encryption using the `keytool` -command that comes with the JRE. If there is an existing key in the keystore, first remove it: - -[source, cli] ----- -keytool -keystore keystore -storepass changeit -storetype jceks \ - -alias sym.secret -delete ----- - -Then generate a secret key, specifying a cipher algorithm and key size. -Commonly used algorithms that are supported include aes, blowfish, desede, and rc4. - -[source, cli] ----- -keytool -keystore keystore -storepass changeit -storetype jceks \ - -alias sym.secret -genseckey -keyalg aes -keysize 128 ----- - -If using an alternative provider, place the provider JAR file in the SymmetricDS `lib` folder. -The provider class name should be installed in the JRE security properties or specified on the command line. -To install in the JRE, edit the JRE `lib/security/java.security` file -and set a `security.provider.i` property for the provider class name. -Or, the provider can be specified on the command line instead. -Both `keytool` and `sym` accept command line arguments for the provider class name. -For example, using the Bouncy Castle provider, the command line options would look like: - -[source, cli] ----- -keytool -keystore keystore -storepass changeit -storetype jceks \ - -alias sym.secret -genseckey -keyalg idea -keysize 56 \ - -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider \ - -providerPath ..\lib\bcprov-ext.jar ----- - -[source, cli] ----- -symadmin -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider -e secret ----- - -To customize the encryption, write a Java class that implements the ISecurityService or extends the default SecurityService, and place -the class on the classpath in either `lib` or -`web/WEB-INF/lib` folders. -Then, in the `symmetric.properties` specify your class name for the security service. - -[source, cli] ----- -security.service.class.name=org.jumpmind.security.SecurityService ----- - -Remember to specify your properties file when encrypting passwords, so it will use your custom ISecurityService. - -[source, cli] ----- -symadmin -p symmetric.properties -e secret ----- +Encrypted text starts with "enc:" to differentiate it from plain text. +See the <> section for an explanation of the encryption key. === Secure Transport @@ -182,31 +115,19 @@ nodes to communicate over SSL with this node, you specify "https" in the URL. registration.url:: This is the URL where the node will connect for registration when it first starts up. To protect the registration with SSL, you specify "https" in the URL. - -For incoming HTTPS connections, SymmetricDS depends on the webserver where -it is deployed, so the webserver must be configured for HTTPS. -As a standalone deployment, the "sym" launcher command provides options for -enabling HTTPS support. -==== Sym Launcher +==== Standalone -The "sym" launch command uses Jetty as an embedded web server. -Using command line options, the web server can be told to listen for -HTTP, HTTPS, or both. +The SymmetricDS service and the "sym" launch command use Jetty as an embedded web server. +Edit the `conf/symmetric-server.properties` file to change port numbers +to listen on and whether or not to use HTTP and/or HTTPS. [source, cli] ---- -sym --port 8080 --server ----- - -[source, cli] ----- -sym --secure-port 8443 --secure-server ----- - -[source, cli] ----- -sym --port 8080 --secure-port 8443 --mixed-server +http.enable=true +http.port=31415 +https.enable=true +https.port=31417 ---- ==== Tomcat @@ -226,140 +147,129 @@ and changed to the following: ==== Keystores -When SymmetricDS connects to a URL with HTTPS, Java checks the validity of the -certificate using the built-in trusted keystore located at -`JRE_HOME/lib/security/cacerts`. -The "sym" launcher command overrides the trusted keystore to use its own -trusted keystore instead, which is located at -`security/cacerts`. -This keystore contains the certificate aliased as "sym" for use in testing -and easing deployments. -The trusted keystore can be overridden -by specifying the `javax.net.ssl.trustStore` system property. - -When SymmetricDS is run as a secure server with the "sym" launcher, -it accepts incoming requests using the key installed in the keystore -located at -`security/keystore`. -The default key is provided for convenience of testing, but should be -re-generated for security. +The `security` subdirectory contains a private keystore and a trusted keystore. System properties are used to specify the location +of each keystore file and a password to protect it. +The system properties are set in the `bin/setenv` (or `bin\setenv.bat` on Windows) and `conf/sym_service.conf` files. + +[cols="10,10,10,10,60", options="header"] +|=== +|Filename|Store Type|System Property for File|System Property for Password|Description +|keystore|PKCS12|sym.keystore.file|javax.net.ssl.keyStorePassword|Contains private encryption key and TLS certificate. +|cacerts|JKS|javax.net.ssl.trustStore|javax.net.ssl.trustStorePassword|Contains public certificates for trusted authorities who sign keys. +|=== + +The following entries in the `keystore` file are used. + +.Alias entries in keystore file +[cols="20,80"] +|=== +|sym|The TLS certificate used for handling incoming HTTPS communication. +|sym.secret|The encryption key used for protecting secrets like database password. +|=== + +If an entry is missing when encryption is requested, SymmetricDS will automatically generate a random key for use. +It tries to use the strongest encryption algorithm and the largest key size available on the system. +If the `keystore` file is missing, it will be created. Starting in SymmetricDS 3.14, it uses PKCS12 as the store type for new keystores, +but it is backwards compatible with the older JCEKS store type. + +==== Finding Keystore Password + +The keystores and each key entry is protected with a password. The default password is `changeit`. +ifdef::pro[] +During installation, the setup program chooses a random password and obfuscates it for the private `keystore` file. +endif::pro[] + +To obtain the current password, use the following steps: + +* Look in the `bin/setenv` (or `bin\setenv.bat` on Windows) or the `conf/sym_service.conf` files. (The password should be the same in both files.) + +* For the password to `keystore`, look for the `javax.net.ssl.keyStorePassword` system property. + +* For the password to `cacerts`, look for the `javax.net.ssl.trustStorePassword` system property. + +* If the password starts with "obf:" then it is obfuscated. To obtain the cleartext password, use the following command from the `bin` subdirectory: + +[source, cli] +---- +symadmin unobfuscate-text obf:cHVuYXRydmc= +---- + +* If the password does not start with "obf:" then it is the cleartext password. + +==== Changing Keystore Password + +To change the keystore password, use the following steps: -==== Generating Keys +* Open a command prompt and navigate to the SymmetricDS installation. + +* In the `security` subdirectory, use the following commands to enter the old and new password for the keystore and each key entry. + +[source, cli] +---- +keytool -keystore keystore -storepasswd +keytool -keystore keystore -alias sym -keypasswd +keytool -keystore keystore -alias sym.secret -keypasswd +---- + +* Edit `bin/setenv` (or `bin\setenv.bat` on Windows) and `conf/sym_service.conf` files to update the new password. + +[source, cli] +---- +-Djavax.net.ssl.keyStorePassword=changeit +---- + +* Optionally, obfuscate the new password in the previous step to prevent casual observation. + +[source, cli] +---- +syadmin obfuscate-text changeit +---- + + +==== Generating Certificates + +ifdef::pro[] +NOTE: This section describes command line usage. See <> for using the web console instead. +endif::pro[] To generate new keys and install a server certificate, use the following steps: -* Open a command prompt and navigate to the -`security` -subdirectory of your SymmetricDS installation on the server to which -communication will be secured (typically the "root" or "central office" server). +* Open a command prompt and navigate to the `security` subdirectory of SymmetricDS. * Delete the old key pair and certificate. [source, cli] ---- -keytool -keystore keystore -delete -alias sym -storetype jceks +keytool -keystore keystore -delete -alias sym +keytool -keystore cacerts -delete -alias sym ---- IMPORTANT: If you receive a message like, "Alias does not exist" - then the key entry does not exist and you can skip this step. -[source, cli] ----- -keytool -keystore cacerts -delete -alias sym -storetype jks ----- -IMPORTANT: See above for possible errors from this command. -[source] ----- -Enter keystore password: changeit ----- * Generate a new key pair. Note that the first name/last name (the "CN") must match the fully qualified hostname the client will be using to communcate to the server. [source, cli] ---- -keytool -keystore keystore -alias sym -genkey -keyalg RSA -validity 10950 -storetype jceks +keytool -keystore keystore -alias sym -genkey -keyalg RSA -validity 10950 ---- -[source] ----- -Enter keystore password: changeit -What is your first and last name? - [Unknown]: localhost -What is the name of your organizational unit? - [Unknown]: SymmetricDS -What is the name of your organization? - [Unknown]: JumpMind -What is the name of your City or Locality? - [Unknown]: -What is the name of your State or Province? - [Unknown]: -What is the two-letter country code for this unit? - [Unknown]: -Is CN=localhost, OU=SymmetricDS, O=JumpMind, L=Unknown, ST=Unknown, C=Unknown -correct? - [no]: yes - -Enter key password for - (RETURN if same as keystore password): ----- - * Export the certificate from the private keystore. [source, cli] ---- -keytool -keystore keystore -export -alias sym -rfc -file sym.cer -storetype jceks +keytool -keystore keystore -export -alias sym -rfc -file sym.cer ---- -* Install the certificate in the trusted keystore. +* Install the certificate into the trusted keystore. [source, cli] ---- -keytool -keystore cacerts -import -alias sym -file sym.cer -storetype jks ----- - -* Copy the cacerts file that is generated by this process to -the `security` directory of each client's SymmetricDS installation. - -==== Importing Signed Certificates from PKCS 12 files - -You would use the following command to import a p12 certificate into the SymmetricDS keystore: - -[source, cli] ----- -keytool -delete -alias sym -noprompt -keystore keystore -storetype jceks -storepass changeit - -keytool -importkeystore -deststorepass changeit -destkeypass changeit -destkeystore keystore -storetype jceks -srckeystore {yourcert.p12} -srcstoretype PKCS12 -srcstorepass {pkcs12 password} -srcalias {pkcs12 alias} -destalias sym +keytool -keystore cacerts -import -alias sym -file sym.cer ---- +* Copy the `cacerts` file that is generated by this process to the `security` directory of each client's SymmetricDS installation. -==== Changing Keystore Password - -The keystore and each key entry is protected with a password that defaults to "changeit". To change the password, use the following steps: - -* Open a command prompt and navigate to the `security` subdirectory of your SymmetricDS installation. - -* Use the keytool command to enter the old and new password for the keystore and each key entry. - -[source, cli] ----- -keytool -keystore keystore -storetype jceks -storepasswd -keytool -keystore keystore -storetype jceks -alias sym -keypasswd -keytool -keystore keystore -storetype jceks -alias sym.secret -keypasswd ----- - -* (Optional) Obfuscate the password to prevent casual observation from the configuration files. An obfuscated password starts with "obf:" -while a cleartext password does not. - -[source, cli] ----- -syadmin obfuscate-text changeit ----- - -* Edit `bin/setenv` (or `bin\setenv.bat` on Windows) and `conf/sym_service.conf` files to find a similar line as below to change the password. - -[source, cli] ----- --Djavax.net.ssl.keyStorePassword=changeit ----- === Java Management Extensions From a34b581558fa3fd2a2a73fcb44d97d6459eb1106 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Fri, 2 Sep 2022 10:35:00 -0400 Subject: [PATCH 11/21] 0005433: Sync triggers when parameters change that affect triggers --- .../common/ConfigurationChangedHelper.java | 21 ++++++- .../resources/symmetric-default.properties | 58 +++++++++++-------- .../properties/DefaultParameterParser.java | 1 + 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ConfigurationChangedHelper.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ConfigurationChangedHelper.java index a8e099314d..5eb49945af 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ConfigurationChangedHelper.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ConfigurationChangedHelper.java @@ -23,10 +23,12 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.jumpmind.db.model.Table; +import org.jumpmind.properties.DefaultParameterParser.ParameterMetaData; import org.jumpmind.symmetric.ISymmetricEngine; import org.jumpmind.symmetric.ext.IConfigurationChangedListener; import org.jumpmind.symmetric.io.data.CsvData; @@ -100,7 +102,8 @@ public void handleChange(Context context, Table table, CsvData data) { updateContext(TableConstants.SYM_TRANSFORM_TABLE, table, context, CTX_KEY_FLUSH_TRANSFORMS_NEEDED); updateContext(TableConstants.SYM_TRANSFORM_COLUMN, table, context, CTX_KEY_FLUSH_TRANSFORMS_NEEDED); updateContext(TableConstants.SYM_TRIGGER_ROUTER_GROUPLET, table, context, CTX_KEY_FLUSH_GROUPLETS_NEEDED, CTX_KEY_RESYNC_NEEDED); - if (matchesTable(table, TableConstants.SYM_PARAMETER)) { + if (matchesTable(table, TableConstants.SYM_PARAMETER) && matchesExternalId(table, data, "external_id") + && matchesNodeGroupId(table, data, "node_group_id")) { String jobName = JobDefinition.getJobNameFromData(data); if (jobName != null) { getHashSet(context, CTX_KEY_CHANGED_JOB_IDS).add(jobName); @@ -111,6 +114,10 @@ public void handleChange(Context context, Table table, CsvData data) { } else if (ParameterConstants.CLUSTER_LOCKING_ENABLED.equals(paramKey)) { context.put(CTX_KEY_CLUSTER_NEEDED, true); } + Map parameters = ParameterConstants.getParameterMetaData(); + if (parameters.get(paramKey).getTags().contains(ParameterMetaData.TAG_TRIGGER)) { + context.put(CTX_KEY_RESYNC_NEEDED, true); + } } if ((matchesTable(table, TableConstants.SYM_TRIGGER) || matchesTable(table, TableConstants.SYM_TRIGGER_ROUTER)) && isSyncTriggersAllowed(context) && context.get(CTX_KEY_RESYNC_NEEDED) == null) { @@ -294,6 +301,18 @@ private boolean matchesTable(Table table, String tableSuffix) { } } + private boolean matchesExternalId(Table table, CsvData data, String columnName) { + String externalId = engine.getParameterService().getExternalId(); + String columnValue = getColumnValue(table, data, columnName); + return columnValue == null || externalId.equals(columnValue) || columnValue.equals(ParameterConstants.ALL); + } + + private boolean matchesNodeGroupId(Table table, CsvData data, String columnName) { + String nodeGroupId = engine.getParameterService().getNodeGroupId(); + String columnValue = getColumnValue(table, data, columnName); + return columnValue == null || nodeGroupId.equals(columnValue) || columnValue.equals(ParameterConstants.ALL); + } + private void updateContext(String tableSuffix, Table table, Context context, String... constants) { if (matchesTable(table, tableSuffix)) { for (String constant : constants) { diff --git a/symmetric-core/src/main/resources/symmetric-default.properties b/symmetric-core/src/main/resources/symmetric-default.properties index 2354c3581f..54df6a8775 100644 --- a/symmetric-core/src/main/resources/symmetric-default.properties +++ b/symmetric-core/src/main/resources/symmetric-default.properties @@ -270,9 +270,9 @@ target.db.read.strings.as.bytes= # Whether target binary fields should be treated as lobs # -# DatabaseOverridable: false +# DatabaseOverridable: true # Type: boolean -# Tags: other +# Tags: trigger target.treat.binary.as.lob.enabled= # Defines the number of milliseconds before logging that a target query is slow. @@ -1686,7 +1686,7 @@ dataextractor.enable=true # convert($(columnName), 'AR8ISO8859P6', 'AR8MSWIN1256') # # DatabaseOverridable: true -# Tags: extract +# Tags: extract, trigger # Type: textbox dataextractor.text.column.expression= @@ -2018,7 +2018,7 @@ hsqldb.initialize.db=true # This is the precision that is used in the number template for oracle triggers # DatabaseOverridable: true -# Tags: other +# Tags: trigger oracle.template.precision=*,38 # Use the text minimum format model for capturing changes to number data types. @@ -2027,8 +2027,8 @@ oracle.template.precision=*,38 # When disabled, numbers are converted with cast to number(*,38), which # can capture up to 38 digits. # -# DatabaseOverridable: false -# Tags: other +# DatabaseOverridable: true +# Tags: trigger # Type: boolean oracle.template.precision.text.minimum=false @@ -2124,7 +2124,7 @@ oracle.load.query.hint.parallel.count=1 # when the database collation for char types isn't compatible with n char types. # # DatabaseOverridable: true -# Tags: other +# Tags: trigger # Type: boolean oracle.use.ntypes.for.sync=false @@ -2178,12 +2178,12 @@ db2.zseries.version=DSN08015 # Specify the database type to cast clob values to # DatabaseOverridable: true -# Tags: AS400 +# Tags: AS400, trigger as400.cast.clob.to=DBCLOB # Turn on the capture of transaction id for DB2 systems that support it. # DatabaseOverridable: false -# Tags: DB2 +# Tags: DB2, trigger db2.capture.transaction.id=false # Specify the type of line feed to use in JMX console methods. Possible values are: text or html. @@ -2241,7 +2241,7 @@ offline.node.detection.restart.minutes=5 # # This is currently supported by the following dialects: mysql, oracle, db2, postgres, sql server # DatabaseOverridable: true -# Tags: other +# Tags: trigger # Type: boolean trigger.update.capture.changed.data.only.enabled=false @@ -2258,18 +2258,19 @@ trigger.create.before.initial.load.enabled=true # to all nodes on configured group links. Supported on MS SQL-Server only. # # DatabaseOverridable: true -# Tags: other +# Tags: trigger # Type: boolean trigger.capture.ddl.changes=false # The delimiter to use when capturing changes from a DDL trigger. MS SQL-Server only. # See: trigger.capture.ddl.changes # DatabaseOverridable: true -# Tags: other +# Tags: trigger trigger.capture.ddl.delimiter=$ # Enable or disabled use of create or replace syntax on Oracle and MS-SQL 2016 and newer. # +# DatabaseOverridable: true # Tags: other # Type: boolean trigger.allow.create.or.replace=true @@ -2284,7 +2285,8 @@ trigger.allow.create.or.replace=true # an update. This parameter, when changed, requires a restart of the # SymmetricDS instance, followed by a rebuild of the triggers. # -# Tags: other +# DatabaseOverridable: true +# Tags: trigger # Type: boolean trigger.use.insert.delete.for.primary.key.changes=true @@ -2296,10 +2298,18 @@ trigger.use.insert.delete.for.primary.key.changes=true # be synchronized. # # DatabaseOverridable: true -# Tags: other +# Tags: trigger, load # Type: boolean db.treat.date.time.as.varchar.enabled=false +# Specify the timezone for the create_time value on sym_data when changes are captured. +# Only implemented for Oracle, Tibero, and PostgreSQL currently. +# +# DatabaseOverridable: true +# Tags: trigger, load +# Type: textbox +data.create_time.timezone= + # This is the expected increment value for the data_id in the data table. # This is useful if you use auto_increment_increment and auto_increment_offset in MySQL. # Note that these settings require innodb_autoinc_lock_mode=0, otherwise the increment @@ -2587,14 +2597,14 @@ mssql.allow.only.row.level.locks.on.runtime.tables=true # when the database collation for char types isn't compatible with n char types. # # DatabaseOverridable: true -# Tags: other, mssql +# Tags: trigger, mssql # Type: boolean mssql.use.ntypes.for.sync=false # Specify the user the SymmetricDS triggers should execute as. Possible values are # { CALLER | SELF | OWNER | 'user_name' } # DatabaseOverridable: true -# Tags: other, mssql +# Tags: trigger, mssql mssql.trigger.execute.as=caller # Set the order of triggers to 'First' using sp_settriggerorder after creating triggers. @@ -2603,7 +2613,7 @@ mssql.trigger.execute.as=caller # If the user has a trigger set as 'First', it will be changed to 'None'. # # DatabaseOverridable: true -# Tags: other, mssql +# Tags: trigger, mssql # Type: boolean mssql.trigger.order.first=false @@ -2617,7 +2627,7 @@ mssql.lock.escalation.disabled=true # Includes the catalog/database name within generated triggers (catalog.schema.table). May need turned off to support backup processes such as creating a bacpac file # # DatabaseOverridable: true -# Tags: other, mssql +# Tags: trigger, mssql # Type: boolean mssql.include.catalog.in.triggers=true @@ -2626,7 +2636,7 @@ mssql.include.catalog.in.triggers=true # collation of the varchar columns of a table # # DatabaseOverridable: true -# Tags: other, mssql +# Tags: trigger, mssql db.master.collation= # Automatically alter data, data_event and outgoing_batch tables to allow only @@ -2854,7 +2864,7 @@ node.offline.archive.dir= # When disabled, monitor events are still generated, just not synced to other nodes. # # DatabaseOverridable: true -# Tags: other +# Tags: trigger # Type: boolean monitor.events.capture.enabled=false @@ -2994,9 +3004,9 @@ log.conflict.resolution=false # Whether binary fields should be treated as lobs # -# DatabaseOverridable: false +# DatabaseOverridable: true # Type: boolean -# Tags: other +# Tags: trigger treat.binary.as.lob.enabled=true # Whether char fields should be right trimmed @@ -3178,8 +3188,8 @@ job.log.miner.period.time.ms=10000 # Postgres triggers default to "security invoker" with permissions based on caller. # Enable this parameter to use "security definer" with permissions based on owner. # -# DatabaseOverridable: false -# Tags: postgres +# DatabaseOverridable: true +# Tags: postgres, trigger # Type: boolean postgres.security.definer=false diff --git a/symmetric-util/src/main/java/org/jumpmind/properties/DefaultParameterParser.java b/symmetric-util/src/main/java/org/jumpmind/properties/DefaultParameterParser.java index 413bf931db..20c22bcb8a 100644 --- a/symmetric-util/src/main/java/org/jumpmind/properties/DefaultParameterParser.java +++ b/symmetric-util/src/main/java/org/jumpmind/properties/DefaultParameterParser.java @@ -193,6 +193,7 @@ public static class ParameterMetaData implements Serializable { public static final String TYPE_CODE = "code"; public static final String TYPE_XML = "xml"; public static final String TYPE_ENCRYPTED = "encrypted"; + public static final String TAG_TRIGGER = "trigger"; private static final long serialVersionUID = 1L; private String key; private String description; From 73ae78199ce6d3889b96707c666c064e7f600b7e Mon Sep 17 00:00:00 2001 From: evan-miller-jumpmind <70151986+evan-miller-jumpmind@users.noreply.github.com> Date: Fri, 2 Sep 2022 11:08:09 -0400 Subject: [PATCH 12/21] 0003251: Fixed inserts and updates for MySQL unsigned bigints larger than Long.MAX_VALUE --- .../platform/mysql/MySqlJdbcSqlTemplate.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mysql/MySqlJdbcSqlTemplate.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mysql/MySqlJdbcSqlTemplate.java index 7a91325d01..738e3d2c4d 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mysql/MySqlJdbcSqlTemplate.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/mysql/MySqlJdbcSqlTemplate.java @@ -20,12 +20,22 @@ */ package org.jumpmind.db.platform.mysql; +import java.math.BigInteger; +import java.nio.charset.Charset; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; + import javax.sql.DataSource; +import org.jumpmind.db.model.TypeMap; import org.jumpmind.db.platform.DatabaseInfo; import org.jumpmind.db.sql.JdbcSqlTemplate; import org.jumpmind.db.sql.SqlTemplateSettings; import org.jumpmind.db.sql.SymmetricLobHandler; +import org.springframework.jdbc.core.SqlTypeValue; +import org.springframework.jdbc.core.StatementCreatorUtils; +import org.springframework.jdbc.support.lob.LobHandler; public class MySqlJdbcSqlTemplate extends JdbcSqlTemplate { public MySqlJdbcSqlTemplate(DataSource dataSource, SqlTemplateSettings settings, @@ -42,4 +52,34 @@ public MySqlJdbcSqlTemplate(DataSource dataSource, SqlTemplateSettings settings, public String getSelectLastInsertIdSql(String sequenceName) { return "select last_insert_id()"; } + + @Override + public void setValues(PreparedStatement ps, Object[] args, int[] argTypes, LobHandler lobHandler) throws SQLException { + for (int i = 1; i <= args.length; i++) { + Object arg = args[i - 1]; + int argType = argTypes != null && argTypes.length >= i ? argTypes[i - 1] : SqlTypeValue.TYPE_UNKNOWN; + try { + if (argType == Types.BLOB && lobHandler != null && arg instanceof byte[]) { + lobHandler.getLobCreator().setBlobAsBytes(ps, i, (byte[]) arg); + } else if (argType == Types.BLOB && lobHandler != null && arg instanceof String) { + lobHandler.getLobCreator().setBlobAsBytes(ps, i, arg.toString().getBytes(Charset.defaultCharset())); + } else if (argType == Types.CLOB && lobHandler != null) { + lobHandler.getLobCreator().setClobAsString(ps, i, (String) arg); + } else if ((argType == Types.DECIMAL || argType == Types.NUMERIC) && arg != null) { + setDecimalValue(ps, i, arg, argType); + } else if (argType == Types.TINYINT) { + setTinyIntValue(ps, i, arg, argType); + } else if (argType == Types.BIGINT && arg instanceof BigInteger + && ((BigInteger) arg).compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { + ps.setString(i, arg.toString()); + } else { + StatementCreatorUtils.setParameterValue(ps, i, verifyArgType(arg, argType), arg); + } + } catch (SQLException ex) { + String msg = String.format("Parameter arg '%s' type: %s caused exception: %s", arg, + TypeMap.getJdbcTypeName(argType), ex.getMessage()); + throw new SQLException(msg, ex); + } + } + } } From d4c8ffd9a69a547f3c68f734b0ec86968929a6dc Mon Sep 17 00:00:00 2001 From: evan-miller-jumpmind <70151986+evan-miller-jumpmind@users.noreply.github.com> Date: Fri, 2 Sep 2022 11:08:38 -0400 Subject: [PATCH 13/21] Ran spotlessApply --- .../service/impl/RegistrationService.java | 26 +++++++++---------- .../service/impl/TriggerRouterService.java | 9 +++---- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java index 926f7f4cd2..973861b352 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/RegistrationService.java @@ -151,7 +151,7 @@ protected void extractConfiguration(OutputStream out, Node registeredNode) { protected Node processRegistration(Node nodePriorToRegistration, String remoteHost, String remoteAddress, String userId, String password, boolean isRequestedRegistration) throws IOException { - Node processedNode = new Node(); + Node processedNode = new Node(); processedNode.setSyncEnabled(false); if (!allowClientRegistration) { log.warn("Cannot register a client node until this node has synced triggers"); @@ -241,23 +241,23 @@ protected Node processRegistration(Node nodePriorToRegistration, String remoteHo return processedNode; } if (link == null && parameterService.is(ParameterConstants.REGISTRATION_AUTO_CREATE_GROUP_LINK)) { - link = new NodeGroupLink(identity.getNodeGroupId(), nodePriorToRegistration.getNodeGroupId()); - configurationService.saveNodeGroupLink(link); - ITriggerRouterService triggerRouterService = engine.getTriggerRouterService(); + link = new NodeGroupLink(identity.getNodeGroupId(), nodePriorToRegistration.getNodeGroupId()); + configurationService.saveNodeGroupLink(link); + ITriggerRouterService triggerRouterService = engine.getTriggerRouterService(); Router router = new Router(); router.setNodeGroupLink(link); router.setRouterId(router.createDefaultName()); - triggerRouterService.saveRouter(router); - link = configurationService.getNodeGroupLinkFor(nodePriorToRegistration.getNodeGroupId(), identity.getNodeGroupId(), false); - if (link == null) { - link = new NodeGroupLink(nodePriorToRegistration.getNodeGroupId(), identity.getNodeGroupId(), NodeGroupLinkAction.P); - configurationService.saveNodeGroupLink(link); - router = new Router(); + triggerRouterService.saveRouter(router); + link = configurationService.getNodeGroupLinkFor(nodePriorToRegistration.getNodeGroupId(), identity.getNodeGroupId(), false); + if (link == null) { + link = new NodeGroupLink(nodePriorToRegistration.getNodeGroupId(), identity.getNodeGroupId(), NodeGroupLinkAction.P); + configurationService.saveNodeGroupLink(link); + router = new Router(); router.setNodeGroupLink(link); router.setRouterId(router.createDefaultName()); - triggerRouterService.saveRouter(router); - } - } + triggerRouterService.saveRouter(router); + } + } // TODO: since we send sym_node in registration batch, save this record with source_node_id = node_id foundNode.setSyncEnabled(true); foundNode.setSyncUrl(nodePriorToRegistration.getSyncUrl()); diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java index 01b9d8a445..5300e66c34 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/service/impl/TriggerRouterService.java @@ -508,13 +508,12 @@ public List buildTriggersForSymmetricTables(String version, Set configTablesWithoutCapture = TableConstants.getConfigTablesWithoutCapture(symmetricDialect.getTablePrefix()); for (String tableName : tables) { Trigger trigger = buildTriggerForSymmetricTable(tableName, configTablesWithoutCapture); - triggers.add(trigger); + triggers.add(trigger); } /* - if (parameterService.is(ParameterConstants.REGISTRATION_AUTO_CREATE_GROUP_LINK)) { - updateOrCreateDatabaseTriggers(triggers, new StringBuilder(), true, - true, getActiveTriggerHistories(), true); - }*/ + * if (parameterService.is(ParameterConstants.REGISTRATION_AUTO_CREATE_GROUP_LINK)) { updateOrCreateDatabaseTriggers(triggers, new StringBuilder(), + * true, true, getActiveTriggerHistories(), true); } + */ return triggers; } From 7251c71f79e959312c6971a0f1696a08e775b013 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Fri, 2 Sep 2022 11:18:17 -0400 Subject: [PATCH 14/21] avoid keystore uninitialized exception --- .../src/main/java/org/jumpmind/security/SecurityService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/symmetric-util/src/main/java/org/jumpmind/security/SecurityService.java b/symmetric-util/src/main/java/org/jumpmind/security/SecurityService.java index 895cad53e9..c2610532e4 100644 --- a/symmetric-util/src/main/java/org/jumpmind/security/SecurityService.java +++ b/symmetric-util/src/main/java/org/jumpmind/security/SecurityService.java @@ -124,6 +124,7 @@ public KeyStore getKeyStore() { } } else { log.debug("Loading keystore from memory"); + ks.load(null); } return ks; } catch (RuntimeException e) { From 071f9db939afee3bf74949c20644165715d4c0cb Mon Sep 17 00:00:00 2001 From: evan-miller-jumpmind <70151986+evan-miller-jumpmind@users.noreply.github.com> Date: Fri, 2 Sep 2022 11:40:29 -0400 Subject: [PATCH 15/21] 0005435: Fixed NumberFormatException when querying for an integer greater than Long.MAX_VALUE in SQL Explorer --- .../jumpmind/vaadin/ui/sqlexplorer/TabularResultLayout.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/symmetric-sqlexplorer/src/main/java/org/jumpmind/vaadin/ui/sqlexplorer/TabularResultLayout.java b/symmetric-sqlexplorer/src/main/java/org/jumpmind/vaadin/ui/sqlexplorer/TabularResultLayout.java index d7aaa626f4..c32c36b8f7 100644 --- a/symmetric-sqlexplorer/src/main/java/org/jumpmind/vaadin/ui/sqlexplorer/TabularResultLayout.java +++ b/symmetric-sqlexplorer/src/main/java/org/jumpmind/vaadin/ui/sqlexplorer/TabularResultLayout.java @@ -26,6 +26,7 @@ import static org.jumpmind.vaadin.ui.sqlexplorer.Settings.SQL_EXPLORER_SHOW_ROW_NUMBERS; import java.math.BigDecimal; +import java.math.BigInteger; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -767,7 +768,7 @@ protected Grid> putResultsInGrid(int maxResultSize) throws SQLExcep case Types.SMALLINT: case Types.BIGINT: case Types.INTEGER: - if (o != null && !(o instanceof Long)) { + if (o != null && !(o instanceof Long) && !(o instanceof BigInteger)) { o = new Long(CommonUiUtils.castToNumber(o.toString())); } break; From 65388906b92a37beef372d30bfc6b86e46b4804a Mon Sep 17 00:00:00 2001 From: evan-miller-jumpmind <70151986+evan-miller-jumpmind@users.noreply.github.com> Date: Tue, 6 Sep 2022 13:47:59 -0400 Subject: [PATCH 16/21] 0004116: Fixed Postgres timestamp to time and time to timestamp conversion --- .../jumpmind/db/platform/AbstractDatabasePlatform.java | 10 +++++----- .../db/platform/AbstractDatabasePlatformTest.java | 6 ++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDatabasePlatform.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDatabasePlatform.java index 5c43de958a..37ca548b56 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDatabasePlatform.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDatabasePlatform.java @@ -652,13 +652,10 @@ public java.util.Date parseDate(int type, String value, boolean useVariableDates if (useTimestamp) { return parseTimestamp(type, value); } else if (type == Types.TIME) { - if (value.indexOf(".") == 8) { - /* - * Firebird (at least) captures fractional seconds in time fields which need to be parsed by Timestamp.valueOf - */ + if (value.indexOf(".") == 8 || value.length() <= 8) { return Timestamp.valueOf("1970-01-01 " + value); } else { - return FormatUtils.parseDate(value, FormatUtils.TIME_PATTERNS); + return Timestamp.valueOf(value); } } else { return FormatUtils.parseDate(value, FormatUtils.TIMESTAMP_PATTERNS); @@ -942,6 +939,9 @@ public boolean canColumnBeUsedInWhereClause(Column column) { public java.util.Date parseTimestamp(int type, String value) { try { + if (value.indexOf(".") == 8 || value.length() <= 8) { + value = "1970-01-01 " + value; + } return Timestamp.valueOf(value); } catch (IllegalArgumentException ex) { if (!getDatabaseInfo().isZeroDateAllowed() && value != null && value.startsWith(ZERO_DATE_STRING)) { diff --git a/symmetric-db/src/test/java/org/jumpmind/db/platform/AbstractDatabasePlatformTest.java b/symmetric-db/src/test/java/org/jumpmind/db/platform/AbstractDatabasePlatformTest.java index 0eb7094a54..d20965a993 100644 --- a/symmetric-db/src/test/java/org/jumpmind/db/platform/AbstractDatabasePlatformTest.java +++ b/symmetric-db/src/test/java/org/jumpmind/db/platform/AbstractDatabasePlatformTest.java @@ -45,6 +45,12 @@ public void testParseDate() { testDatabasePlatform.parseDate(Types.TIMESTAMP, "2015-11-03 01:35:03.714566 -05:00", false)); assertEquals(Timestamp.valueOf("2015-11-03 01:35:03.714566"), testDatabasePlatform.parseDate(Types.TIMESTAMP, "2015-11-03 01:35:03.714566", false)); + assertEquals(Timestamp.valueOf("1970-01-01 01:35:03.714566"), + testDatabasePlatform.parseDate(Types.TIMESTAMP, "01:35:03.714566", false)); + assertEquals(Timestamp.valueOf("2015-11-03 01:35:03.714566"), + testDatabasePlatform.parseDate(Types.TIME, "2015-11-03 01:35:03.714566", false)); + assertEquals(Timestamp.valueOf("1970-01-01 01:35:03.714566"), + testDatabasePlatform.parseDate(Types.TIME, "01:35:03.714566", false)); // TODO: build out more tests for date/time combinations. } } From e9a4283ca48f919400e91969a47c6dbbccf6aff5 Mon Sep 17 00:00:00 2001 From: jakobvanmeter Date: Tue, 6 Sep 2022 13:59:31 -0400 Subject: [PATCH 17/21] 0005439: Conflict resolver fails if there are no primary keys on the target and it is set to not use primary keys from source --- .../symmetric/io/data/writer/DefaultDatabaseWriter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriter.java b/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriter.java index 030ad9045f..5e2414345d 100644 --- a/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriter.java +++ b/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriter.java @@ -1036,6 +1036,9 @@ protected Table lookupTableAtTarget(Table sourceTable) { if (table != null) { table = table.copyAndFilterColumns(sourceTable.getColumnNames(), sourceTable.getPrimaryKeyColumnNames(), writerSettings.isUsePrimaryKeysFromSource()); + if (table.getPrimaryKeyColumnCount() == 0) { + table = getPlatform(table).makeAllColumnsPrimaryKeys(table); + } Column[] columns = table.getColumns(); for (Column column : columns) { if (column != null) { From 28c15e400b62d69ea288d9d8ba46544071cb4205 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Tue, 6 Sep 2022 15:28:20 -0400 Subject: [PATCH 18/21] 0005433: fix NPE --- .../jumpmind/symmetric/common/ConfigurationChangedHelper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ConfigurationChangedHelper.java b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ConfigurationChangedHelper.java index 5eb49945af..868e3aef85 100644 --- a/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ConfigurationChangedHelper.java +++ b/symmetric-core/src/main/java/org/jumpmind/symmetric/common/ConfigurationChangedHelper.java @@ -115,7 +115,8 @@ && matchesNodeGroupId(table, data, "node_group_id")) { context.put(CTX_KEY_CLUSTER_NEEDED, true); } Map parameters = ParameterConstants.getParameterMetaData(); - if (parameters.get(paramKey).getTags().contains(ParameterMetaData.TAG_TRIGGER)) { + ParameterMetaData pmd = parameters.get(paramKey); + if (pmd != null && pmd.getTags().contains(ParameterMetaData.TAG_TRIGGER)) { context.put(CTX_KEY_RESYNC_NEEDED, true); } } From 59dd9c3c30cc99688c531966d4b03ac27ad27df8 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Wed, 7 Sep 2022 08:46:15 -0400 Subject: [PATCH 19/21] 0005440: Change parameter treat.binary.as.lob.enabled to default to false --- .../src/main/resources/symmetric-default.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/symmetric-core/src/main/resources/symmetric-default.properties b/symmetric-core/src/main/resources/symmetric-default.properties index 54df6a8775..fb52e699ef 100644 --- a/symmetric-core/src/main/resources/symmetric-default.properties +++ b/symmetric-core/src/main/resources/symmetric-default.properties @@ -3004,10 +3004,10 @@ log.conflict.resolution=false # Whether binary fields should be treated as lobs # -# DatabaseOverridable: true +# DatabaseOverridable: false # Type: boolean # Tags: trigger -treat.binary.as.lob.enabled=true +treat.binary.as.lob.enabled=false # Whether char fields should be right trimmed # From e51f51d1ed43ac6c28b2eb25a3ea6f2c6edda35b Mon Sep 17 00:00:00 2001 From: Eric Long Date: Wed, 7 Sep 2022 09:16:06 -0400 Subject: [PATCH 20/21] 0005441: Windows service won't start if tmp dir has trailing slash --- .../java/org/jumpmind/symmetric/wrapper/WrapperService.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/symmetric-wrapper/src/main/java/org/jumpmind/symmetric/wrapper/WrapperService.java b/symmetric-wrapper/src/main/java/org/jumpmind/symmetric/wrapper/WrapperService.java index 20d420a753..557f7acdca 100644 --- a/symmetric-wrapper/src/main/java/org/jumpmind/symmetric/wrapper/WrapperService.java +++ b/symmetric-wrapper/src/main/java/org/jumpmind/symmetric/wrapper/WrapperService.java @@ -360,7 +360,11 @@ protected ArrayList getWrapperCommand(String arg, boolean isQuotedArgume ArrayList cmd = new ArrayList(); String quote = isQuotedArguments ? getWrapperCommandQuote() : ""; cmd.add(quote + config.getJavaCommand() + quote); - cmd.add("-Djava.io.tmpdir=" + quote + System.getProperty("java.io.tmpdir") + quote); + String tmpDir = System.getProperty("java.io.tmpdir"); + if (tmpDir != null && tmpDir.endsWith("\\")) { + tmpDir = tmpDir.substring(0, tmpDir.length() - 1); + } + cmd.add("-Djava.io.tmpdir=" + quote + tmpDir + quote); cmd.add("-jar"); cmd.add(quote + config.getWrapperJarPath() + quote); cmd.add(arg); From 40490c5956971a6f724929c8ba07d8a188b51a37 Mon Sep 17 00:00:00 2001 From: evan-miller-jumpmind <70151986+evan-miller-jumpmind@users.noreply.github.com> Date: Wed, 7 Sep 2022 12:32:52 -0400 Subject: [PATCH 21/21] Ran spotlessApply --- .../symmetric/io/data/writer/DefaultDatabaseWriter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriter.java b/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriter.java index 5e2414345d..fc0a90d9e2 100644 --- a/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriter.java +++ b/symmetric-io/src/main/java/org/jumpmind/symmetric/io/data/writer/DefaultDatabaseWriter.java @@ -1036,9 +1036,9 @@ protected Table lookupTableAtTarget(Table sourceTable) { if (table != null) { table = table.copyAndFilterColumns(sourceTable.getColumnNames(), sourceTable.getPrimaryKeyColumnNames(), writerSettings.isUsePrimaryKeysFromSource()); - if (table.getPrimaryKeyColumnCount() == 0) { - table = getPlatform(table).makeAllColumnsPrimaryKeys(table); - } + if (table.getPrimaryKeyColumnCount() == 0) { + table = getPlatform(table).makeAllColumnsPrimaryKeys(table); + } Column[] columns = table.getColumns(); for (Column column : columns) { if (column != null) {