This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new ba427ab25a Issue #9050 : Keep database SQL editors in the database 
perspective (#9051)
ba427ab25a is described below

commit ba427ab25a0f990437e610db77ab74cc55d95a95
Author: Matt Casters <[email protected]>
AuthorDate: Wed Oct 21 13:34:12 2026 +0200

    Issue #9050 : Keep database SQL editors in the database perspective (#9051)
    
    * Issue #9050 : Keep database SQL editors in the database perspective
    
    Saved SQL editors were restored in the file explorer as well, because
    "Database SQL" is not a registered file type and the .sql extension
    fell through to the explorer. Opening a .sql file while the database
    perspective is active now stays there. Other file types still open in
    the explorer, and a .sql file opened from the explorer stays there.
    
    * Issue #9050 : Skip explorer copies of database SQL files only once
    
    Startup no longer drops a File Explorer tab just because the same file
    is open in a database SQL editor. An explorer copy is skipped only while
    a legacy "Database SQL" entry is still in the database-perspective
    last-open list. writeLastOpenFiles no longer writes those entries, so
    the filter stops after the first save.
---
 .../ui/hopgui/delegates/HopGuiAuditDelegate.java   | 86 ++++++++++++++++++++
 .../ui/hopgui/delegates/HopGuiFileDelegate.java    | 28 +++++++
 .../perspective/database/DatabasePerspective.java  | 28 ++++++-
 .../perspective/database/DatabaseSqlFileType.java  | 15 +++-
 .../perspective/database/DatabaseWorkbench.java    | 92 +++++++++++++++++++++-
 .../database/messages/messages_en_US.properties    |  4 +
 .../HopGuiAuditDelegateStatePropertiesTest.java    | 58 ++++++++++++++
 .../delegates/HopGuiFileDelegateSqlOpenTest.java   | 33 ++++++++
 .../database/DatabaseSqlFileOpenTest.java          | 58 ++++++++++++++
 9 files changed, 398 insertions(+), 4 deletions(-)

diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiAuditDelegate.java 
b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiAuditDelegate.java
index bb69ba6488..aaa54a92d3 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiAuditDelegate.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiAuditDelegate.java
@@ -19,8 +19,10 @@ package org.apache.hop.ui.hopgui.delegates;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.regex.Pattern;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hop.core.exception.HopException;
@@ -43,6 +45,9 @@ import org.apache.hop.ui.hopgui.file.IHopFileType;
 import org.apache.hop.ui.hopgui.file.IHopFileTypeHandler;
 import org.apache.hop.ui.hopgui.perspective.IHopPerspective;
 import org.apache.hop.ui.hopgui.perspective.TabItemHandler;
+import org.apache.hop.ui.hopgui.perspective.database.DatabasePerspective;
+import org.apache.hop.ui.hopgui.perspective.database.DatabaseSqlEditorTab;
+import org.apache.hop.ui.hopgui.perspective.database.DatabaseSqlFileType;
 import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
 import org.apache.hop.ui.hopgui.perspective.metadata.MetadataPerspective;
 import org.apache.hop.ui.util.SwtErrorHandler;
@@ -106,6 +111,14 @@ public class HopGuiAuditDelegate {
     // Collect files that fail to open
     List<String> failedFiles = new ArrayList<>();
 
+    // Saved database SQL editors are reopened by DatabaseSqlTabMemory. A 
legacy "Database SQL"
+    // entry in the database-perspective list is not a registered file type, 
so the .sql extension
+    // used to open it again in the file explorer (issue #9050). Skip that 
entry, and an explorer
+    // copy of the same path, only while the legacy entry is still in the 
list. writeLastOpenFiles
+    // no longer writes it, so the filter stops after the first save.
+    //
+    Set<String> databasePerspectiveLastOpen = 
databasePerspectiveLastOpenFiles();
+
     // Open the last files for each perspective...
     //
     List<IHopPerspective> perspectives = 
hopGui.getPerspectiveManager().getPerspectives();
@@ -173,6 +186,9 @@ public class HopGuiAuditDelegate {
                         
HopFileTypeRegistry.getInstance().getFileTypeByName(fileTypeName);
                   }
                 }
+                if (!openInLastFiles(filename, databasePerspectiveLastOpen)) {
+                  continue;
+                }
                 if (hopFileToUse == null) {
                   hopFileToUse =
                       
HopFileTypeRegistry.getInstance().findHopFileType(resolvedFilename);
@@ -353,6 +369,9 @@ public class HopGuiAuditDelegate {
         List<String> files = new ArrayList<>();
         for (TabItemHandler tabItem : tabItemsToSave) {
           IHopFileTypeHandler typeHandler = tabItem.getTypeHandler();
+          if (!rememberInLastOpenFiles(typeHandler)) {
+            continue;
+          }
           String filename = typeHandler.getFilename();
           String name = typeHandler.getName();
           if (StringUtils.isNotEmpty(filename)) {
@@ -413,6 +432,73 @@ public class HopGuiAuditDelegate {
     }
   }
 
+  /**
+   * Database SQL editors are restored by {@code DatabaseSqlTabMemory}. 
Keeping them in this list
+   * makes the next startup open the same file in the file explorer.
+   */
+  static boolean rememberInLastOpenFiles(IHopFileTypeHandler typeHandler) {
+    return !(typeHandler instanceof DatabaseSqlEditorTab);
+  }
+
+  /**
+   * Whether a last-open-files entry should be opened by the generic restore.
+   *
+   * <p>A legacy {@code Database SQL} entry is never opened here. An explorer 
copy of the same path
+   * is skipped only while {@code databasePerspectiveLastOpen} still contains 
that legacy entry.
+   * {@link #writeLastOpenFiles()} no longer writes those entries, so the 
filter stops after the
+   * first save.
+   */
+  static boolean openInLastFiles(String auditName, Set<String> 
databasePerspectiveLastOpen) {
+    if (StringUtils.isEmpty(auditName)) {
+      return false;
+    }
+    String fileTypeName = null;
+    String filename = auditName;
+    if (auditName.contains(TAB_KEY_DELIMITER)) {
+      String[] parts = auditName.split(Pattern.quote(TAB_KEY_DELIMITER), 2);
+      if (parts.length >= 2) {
+        fileTypeName = parts[0];
+        filename = parts[1];
+      }
+    }
+    if (DatabaseSqlFileType.NAME.equals(fileTypeName)) {
+      return false;
+    }
+    if (databasePerspectiveLastOpen == null || StringUtils.isEmpty(filename)) {
+      return true;
+    }
+    return !databasePerspectiveLastOpen.contains(
+        DatabaseSqlFileType.NAME + TAB_KEY_DELIMITER + filename);
+  }
+
+  /**
+   * Names currently stored in the database-perspective last-open list. Used 
to find legacy {@code
+   * Database SQL} entries left from before those editors were kept out of 
this list.
+   */
+  private Set<String> databasePerspectiveLastOpenFiles() {
+    try {
+      AuditList auditList =
+          AuditManager.getActive()
+              .retrieveList(HopNamespace.getNamespace(), 
DatabasePerspective.PERSPECTIVE_ID);
+      if (auditList == null || auditList.getNames() == null) {
+        return Set.of();
+      }
+      Set<String> names = new HashSet<>();
+      for (String name : auditList.getNames()) {
+        if (StringUtils.isNotEmpty(name)) {
+          names.add(name);
+        }
+      }
+      return names;
+    } catch (Exception e) {
+      hopGui
+          .getLog()
+          .logError(
+              "Error reading audit list of perspective " + 
DatabasePerspective.PERSPECTIVE_ID, e);
+      return Set.of();
+    }
+  }
+
   /**
    * Copy handler state into a mutable map. Some handlers return {@link
    * java.util.Collections#emptyMap()}; the audit list still needs to record 
active/fileType/pane.
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java 
b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java
index 8cbd1b7025..14a9da8a3f 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java
@@ -58,7 +58,9 @@ import 
org.apache.hop.ui.hopgui.file.pipeline.HopGuiPipelineGraph;
 import org.apache.hop.ui.hopgui.file.workflow.HopGuiWorkflowGraph;
 import org.apache.hop.ui.hopgui.perspective.IHopPerspective;
 import org.apache.hop.ui.hopgui.perspective.TabItemHandler;
+import org.apache.hop.ui.hopgui.perspective.database.DatabasePerspective;
 import org.apache.hop.ui.hopgui.perspective.database.DatabaseSqlEditorTab;
+import org.apache.hop.ui.hopgui.perspective.database.DatabaseSqlFileType;
 import org.apache.hop.ui.hopgui.perspective.execution.ExecutionPerspective;
 import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
 import org.apache.hop.ui.util.EnvironmentUtils;
@@ -141,6 +143,22 @@ public class HopGuiFileDelegate {
 
   public IHopFileTypeHandler fileOpen(String filename, boolean 
activatePerspective)
       throws Exception {
+    // A .sql file opened while the database perspective is active belongs in 
a SQL editor there.
+    // The file registry only knows the explorer handler, which would switch 
perspectives.
+    //
+    if (openSqlInDatabasePerspective(isDatabasePerspectiveActive(), filename)) 
{
+      IHopPerspective active = hopGui.getActivePerspective();
+      if (active instanceof DatabasePerspective database && 
database.canOpenSqlFiles()) {
+        IHopFileTypeHandler handler = database.openLocalSqlFile(filename);
+        if (handler != null) {
+          hopGui.handleFileCapabilities(
+              handler.getFileType(), handler, handler.hasChanged(), false, 
false);
+          hopGui.auditDelegate.writeLastOpenFiles();
+        }
+        return handler;
+      }
+    }
+
     HopFileTypeRegistry fileRegistry = HopFileTypeRegistry.getInstance();
     IHopFileType hopFile = fileRegistry.findHopFileType(filename);
     if (hopFile == null) {
@@ -154,6 +172,16 @@ public class HopGuiFileDelegate {
     return fileOpenWithType(filename, hopFile, activatePerspective);
   }
 
+  /** True when {@code filename} is a SQL file and the database perspective is 
the active one. */
+  static boolean openSqlInDatabasePerspective(boolean 
databasePerspectiveActive, String filename) {
+    return databasePerspectiveActive && 
DatabaseSqlFileType.isSqlFilename(filename);
+  }
+
+  private boolean isDatabasePerspectiveActive() {
+    IHopPerspective active = hopGui.getActivePerspective();
+    return active instanceof DatabasePerspective && 
hopGui.isActivePerspective(active);
+  }
+
   /**
    * Open a file with a specific file type. Used when restoring tabs so the 
same file can be
    * reopened in different modes (e.g. pipeline and text).
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabasePerspective.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabasePerspective.java
index 22087e618b..b3001c1ca8 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabasePerspective.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabasePerspective.java
@@ -24,6 +24,7 @@ import org.apache.hop.core.gui.plugin.GuiPlugin;
 import org.apache.hop.core.gui.plugin.key.GuiKeyboardShortcut;
 import org.apache.hop.core.gui.plugin.key.GuiOsxKeyboardShortcut;
 import org.apache.hop.core.logging.ILoggingObject;
+import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.ui.core.FormDataBuilder;
@@ -62,6 +63,9 @@ public class DatabasePerspective implements IHopPerspective, 
TabClosable, IDatab
 
   public static final Class<?> PKG = DatabasePerspective.class;
 
+  /** Id of this perspective. Also the audit group of its last-open files. */
+  public static final String PERSPECTIVE_ID = "database-perspective";
+
   private static DatabasePerspective instance;
 
   private HopGui hopGui;
@@ -89,7 +93,7 @@ public class DatabasePerspective implements IHopPerspective, 
TabClosable, IDatab
 
   @Override
   public String getId() {
-    return "database-perspective";
+    return PERSPECTIVE_ID;
   }
 
   @GuiKeyboardShortcut(control = true, shift = true, key = 'd', global = true)
@@ -203,6 +207,28 @@ public class DatabasePerspective implements 
IHopPerspective, TabClosable, IDatab
     workbench.openSqlFile(filename, connection, buffer, dirty);
   }
 
+  /** True when this perspective can open a SQL file into an editor tab. */
+  public boolean canOpenSqlFiles() {
+    return isInitialized();
+  }
+
+  /**
+   * Open {@code filename} in this perspective. Uses the selected connection, 
the connection of the
+   * active editor, or the only connection. Otherwise asks. Returns null when 
there is no connection
+   * or the user cancels.
+   */
+  public IHopFileTypeHandler openLocalSqlFile(String filename) {
+    if (!isInitialized() || Utils.isEmpty(filename)) {
+      return null;
+    }
+    DatabaseMeta meta = workbench.chooseConnectionForSqlFile();
+    if (meta == null) {
+      return null;
+    }
+    activate();
+    return workbench.openSqlFile(filename, meta, null, false);
+  }
+
   /**
    * Bring this perspective to the front, select {@code meta} in the tree, 
connect in the
    * background, and open a SQL tab with {@code sql} (may be empty).
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseSqlFileType.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseSqlFileType.java
index b64b726991..ae8423391d 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseSqlFileType.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseSqlFileType.java
@@ -36,6 +36,12 @@ import 
org.apache.hop.ui.hopgui.perspective.explorer.file.capabilities.FileTypeC
  */
 public class DatabaseSqlFileType extends HopFileTypeBase {
 
+  /**
+   * Stored in the open-files audit key. Not a {@code @HopFileTypePlugin} 
name, so startup must not
+   * resolve it by the {@code .sql} extension.
+   */
+  public static final String NAME = "Database SQL";
+
   private static final Properties CAPABILITIES =
       FileTypeCapabilities.getCapabilities(
           IHopFileType.CAPABILITY_SAVE,
@@ -50,7 +56,7 @@ public class DatabaseSqlFileType extends HopFileTypeBase {
 
   @Override
   public String getName() {
-    return "Database SQL";
+    return NAME;
   }
 
   @Override
@@ -90,8 +96,15 @@ public class DatabaseSqlFileType extends HopFileTypeBase {
     return new EmptyHopFileTypeHandler();
   }
 
+  /** {@code .sql} path, using the same extension rules as the other file 
types. */
+  public static boolean isSqlFilename(String filename) {
+    return "sql".equals(HopFileTypeBase.extractExtension(filename));
+  }
+
   @Override
   public boolean isHandledBy(String filename, boolean checkContent) {
+    // False so the file registry keeps the explorer handler for .sql. The 
database perspective
+    // claims these files only while it is the active perspective.
     return false;
   }
 
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseWorkbench.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseWorkbench.java
index 17663d4f2e..c6b7a8cf76 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseWorkbench.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseWorkbench.java
@@ -47,7 +47,9 @@ import org.apache.hop.ui.core.PropsUi;
 import org.apache.hop.ui.core.bus.HopGuiEvents;
 import org.apache.hop.ui.core.database.DatabaseTreeNode;
 import org.apache.hop.ui.core.database.DatabaseTreeUtil;
+import org.apache.hop.ui.core.dialog.EnterSelectionDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
+import org.apache.hop.ui.core.dialog.MessageBox;
 import org.apache.hop.ui.core.dialog.MessageDialogWithToggle;
 import org.apache.hop.ui.core.gui.GuiMenuWidgets;
 import org.apache.hop.ui.core.gui.GuiResource;
@@ -1284,8 +1286,94 @@ public class DatabaseWorkbench extends Composite 
implements TabClosable {
     tab.loadDetails();
   }
 
-  public void openSqlFile(String filename, DatabaseMeta meta, String buffer, 
boolean dirty) {
-    openSqlTab(meta, null, filename, buffer, dirty);
+  public DatabaseSqlEditorTab openSqlFile(
+      String filename, DatabaseMeta meta, String buffer, boolean dirty) {
+    if (meta != null) {
+      selectConnection(meta.getName());
+    }
+    return openSqlTab(meta, null, filename, buffer, dirty);
+  }
+
+  /**
+   * Connection for a SQL file opened while this workbench is showing. The 
selected tree connection
+   * wins, then the active editor, then the only connection in the project. 
Returns null when the
+   * user must pick, or when there is no connection.
+   */
+  DatabaseMeta chooseConnectionForSqlFile() {
+    String chosenName =
+        connectionNameForOpenedSqlFile(
+            nameOf(selectedDatabaseMeta()), 
nameOf(activeEditorDatabaseMeta()), connectionNames());
+    if (!Utils.isEmpty(chosenName)) {
+      DatabaseMeta chosen = findConnection(chosenName);
+      if (chosen != null) {
+        return chosen;
+      }
+    }
+    List<String> names = connectionNames();
+    if (names.isEmpty()) {
+      MessageBox box = new MessageBox(host.getShell(), SWT.OK | 
SWT.ICON_INFORMATION);
+      box.setText(
+          BaseMessages.getString(PKG, 
"DatabasePerspective.OpenSqlFile.NoConnection.Title"));
+      box.setMessage(
+          BaseMessages.getString(PKG, 
"DatabasePerspective.OpenSqlFile.NoConnection.Message"));
+      box.open();
+      return null;
+    }
+    EnterSelectionDialog dialog =
+        new EnterSelectionDialog(
+            host.getShell(),
+            names.toArray(new String[0]),
+            BaseMessages.getString(PKG, 
"DatabasePerspective.OpenSqlFile.SelectConnection.Title"),
+            BaseMessages.getString(
+                PKG, 
"DatabasePerspective.OpenSqlFile.SelectConnection.Message"));
+    String selected = dialog.open();
+    if (selected == null) {
+      return null;
+    }
+    return findConnection(selected);
+  }
+
+  static String connectionNameForOpenedSqlFile(
+      String selectedName, String activeEditorName, List<String> 
connectionNames) {
+    if (!Utils.isEmpty(selectedName)) {
+      return selectedName;
+    }
+    if (!Utils.isEmpty(activeEditorName)) {
+      return activeEditorName;
+    }
+    String only = null;
+    if (connectionNames != null) {
+      for (String name : connectionNames) {
+        if (Utils.isEmpty(name)) {
+          continue;
+        }
+        if (only != null) {
+          return null;
+        }
+        only = name;
+      }
+    }
+    return only;
+  }
+
+  private static String nameOf(DatabaseMeta meta) {
+    return meta == null ? null : meta.getName();
+  }
+
+  private DatabaseMeta selectedDatabaseMeta() {
+    DatabaseConnectionState state = selectedState();
+    return state == null ? null : state.getDatabaseMeta();
+  }
+
+  private DatabaseMeta activeEditorDatabaseMeta() {
+    IHopFileTypeHandler handler = getActiveFileTypeHandler();
+    if (handler instanceof DatabaseSqlEditorTab tab) {
+      return tab.getDatabaseMeta();
+    }
+    if (handler instanceof DatabaseTableInfoTab tab) {
+      return tab.getDatabaseMeta();
+    }
+    return null;
   }
 
   /**
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/database/messages/messages_en_US.properties
 
b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/database/messages/messages_en_US.properties
index 80688d969c..bd3583dda7 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/database/messages/messages_en_US.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/database/messages/messages_en_US.properties
@@ -105,6 +105,10 @@ DatabasePerspective.Save.Error.Message=Error saving file 
''{0}''
 DatabasePerspective.OpenInDatabase.Title=Open in Database perspective
 DatabasePerspective.OpenInDatabase.Message=Select the database connection to 
run this SQL against
 DatabasePerspective.OpenInDatabase.Tooltip=Open this SQL file in the Database 
perspective
+DatabasePerspective.OpenSqlFile.NoConnection.Title=No database connection
+DatabasePerspective.OpenSqlFile.NoConnection.Message=Create a relational 
database connection before opening a SQL file in the Database perspective.
+DatabasePerspective.OpenSqlFile.SelectConnection.Title=Open SQL file
+DatabasePerspective.OpenSqlFile.SelectConnection.Message=Select the database 
connection to run this SQL against
 DatabasePerspective.Dialog.Title=Database
 DatabasePerspective.Menu.Tools.Window=Database window
 DatabasePerspective.Error.ConnectionNotFound=Database connection ''{0}'' no 
longer exists.
diff --git 
a/ui/src/test/java/org/apache/hop/ui/hopgui/delegates/HopGuiAuditDelegateStatePropertiesTest.java
 
b/ui/src/test/java/org/apache/hop/ui/hopgui/delegates/HopGuiAuditDelegateStatePropertiesTest.java
index 0112d30d81..8202c0f35a 100644
--- 
a/ui/src/test/java/org/apache/hop/ui/hopgui/delegates/HopGuiAuditDelegateStatePropertiesTest.java
+++ 
b/ui/src/test/java/org/apache/hop/ui/hopgui/delegates/HopGuiAuditDelegateStatePropertiesTest.java
@@ -19,12 +19,17 @@ package org.apache.hop.ui.hopgui.delegates;
 
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
 
 import java.util.Collections;
 import java.util.Map;
+import java.util.Set;
 import org.apache.hop.ui.hopgui.file.IHopFileTypeHandler;
 import org.apache.hop.ui.hopgui.file.empty.EmptyHopFileTypeHandler;
+import org.apache.hop.ui.hopgui.perspective.database.DatabaseSqlEditorTab;
+import org.apache.hop.ui.hopgui.perspective.database.DatabaseSqlFileType;
 import org.junit.jupiter.api.Test;
 
 class HopGuiAuditDelegateStatePropertiesTest {
@@ -51,4 +56,57 @@ class HopGuiAuditDelegateStatePropertiesTest {
     copy.put("active", true);
     assertTrue((Boolean) copy.get("active"));
   }
+
+  @Test
+  void databaseSqlEditorsAreNotReopenedInTheFileExplorer() {
+    String delimiter = "\u0001";
+    String savedSql = "/tmp/load.sql";
+    // A bare filename is not a legacy "Database SQL" entry, so it does not 
hide an explorer tab.
+    Set<String> databasePerspectiveLastOpen = Set.of(savedSql);
+
+    assertFalse(
+        HopGuiAuditDelegate.openInLastFiles(
+            DatabaseSqlFileType.NAME + delimiter + savedSql, Set.of()));
+    assertTrue(
+        HopGuiAuditDelegate.openInLastFiles(
+            "SQL File" + delimiter + savedSql, databasePerspectiveLastOpen));
+    assertTrue(
+        HopGuiAuditDelegate.openInLastFiles(
+            "SQL File" + delimiter + "/tmp/notes.sql", 
databasePerspectiveLastOpen));
+    assertTrue(
+        HopGuiAuditDelegate.openInLastFiles("/tmp/pipeline.hpl", 
databasePerspectiveLastOpen));
+    assertFalse(HopGuiAuditDelegate.openInLastFiles("", 
databasePerspectiveLastOpen));
+
+    
assertFalse(HopGuiAuditDelegate.rememberInLastOpenFiles(mock(DatabaseSqlEditorTab.class)));
+    assertTrue(HopGuiAuditDelegate.rememberInLastOpenFiles(new 
EmptyHopFileTypeHandler()));
+  }
+
+  @Test
+  void fileOpenInBothPerspectivesKeepsItsExplorerTab() {
+    String delimiter = "\u0001";
+    String savedSql = "/tmp/load.sql";
+
+    assertTrue(HopGuiAuditDelegate.openInLastFiles("SQL File" + delimiter + 
savedSql, Set.of()));
+    assertTrue(HopGuiAuditDelegate.openInLastFiles("SQL File" + delimiter + 
savedSql, null));
+  }
+
+  @Test
+  void legacyDatabaseSqlEntrySkipsExplorerCopyOnce() {
+    String delimiter = "\u0001";
+    String savedSql = "/tmp/load.sql";
+    Set<String> databasePerspectiveLastOpen =
+        Set.of(DatabaseSqlFileType.NAME + delimiter + savedSql);
+
+    assertFalse(
+        HopGuiAuditDelegate.openInLastFiles(
+            "SQL File" + delimiter + savedSql, databasePerspectiveLastOpen));
+    assertFalse(
+        HopGuiAuditDelegate.openInLastFiles(
+            DatabaseSqlFileType.NAME + delimiter + savedSql, 
databasePerspectiveLastOpen));
+    assertTrue(
+        HopGuiAuditDelegate.openInLastFiles(
+            "SQL File" + delimiter + "/tmp/notes.sql", 
databasePerspectiveLastOpen));
+    assertTrue(
+        HopGuiAuditDelegate.openInLastFiles("/tmp/pipeline.hpl", 
databasePerspectiveLastOpen));
+  }
 }
diff --git 
a/ui/src/test/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegateSqlOpenTest.java
 
b/ui/src/test/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegateSqlOpenTest.java
new file mode 100644
index 0000000000..18383fd23a
--- /dev/null
+++ 
b/ui/src/test/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegateSqlOpenTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.apache.hop.ui.hopgui.delegates;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class HopGuiFileDelegateSqlOpenTest {
+
+  @Test
+  void sqlFilesStayInTheDatabasePerspectiveOnlyWhileItIsActive() {
+    assertTrue(HopGuiFileDelegate.openSqlInDatabasePerspective(true, 
"/data/load.sql"));
+    assertFalse(HopGuiFileDelegate.openSqlInDatabasePerspective(false, 
"/data/load.sql"));
+    assertFalse(HopGuiFileDelegate.openSqlInDatabasePerspective(true, 
"/data/load.hpl"));
+  }
+}
diff --git 
a/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseSqlFileOpenTest.java
 
b/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseSqlFileOpenTest.java
new file mode 100644
index 0000000000..e51d381ca8
--- /dev/null
+++ 
b/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseSqlFileOpenTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.apache.hop.ui.hopgui.perspective.database;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class DatabaseSqlFileOpenTest {
+
+  @Test
+  void sqlFilenameRecognizesTheSqlExtensionOnly() {
+    assertTrue(DatabaseSqlFileType.isSqlFilename("/data/load.SQL"));
+    
assertTrue(DatabaseSqlFileType.isSqlFilename("file:///tmp/query.sql?unused"));
+    assertFalse(DatabaseSqlFileType.isSqlFilename("/data/load.hpl"));
+    assertFalse(DatabaseSqlFileType.isSqlFilename("/data/notes.sql.txt"));
+    assertFalse(DatabaseSqlFileType.isSqlFilename(null));
+    assertFalse(DatabaseSqlFileType.isSqlFilename(".sql"));
+  }
+
+  @Test
+  void 
openedSqlFileUsesTheSelectedConnectionThenTheActiveEditorThenTheOnlyConnection()
 {
+    assertEquals(
+        "selected",
+        DatabaseWorkbench.connectionNameForOpenedSqlFile(
+            "selected", "editor", List.of("only", "other")));
+    assertEquals(
+        "editor",
+        DatabaseWorkbench.connectionNameForOpenedSqlFile(null, "editor", 
List.of("only", "other")));
+    assertEquals(
+        "only", DatabaseWorkbench.connectionNameForOpenedSqlFile(null, null, 
List.of("only")));
+    assertNull(
+        DatabaseWorkbench.connectionNameForOpenedSqlFile(null, null, 
List.of("only", "other")));
+    assertNull(DatabaseWorkbench.connectionNameForOpenedSqlFile(null, null, 
List.of()));
+    assertNull(DatabaseWorkbench.connectionNameForOpenedSqlFile(null, null, 
List.of("")));
+    assertEquals(
+        "editor", DatabaseWorkbench.connectionNameForOpenedSqlFile("", 
"editor", List.of("only")));
+  }
+}

Reply via email to