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 d14bb2efc3 Add databricks driver download option, fixes #8058 (#8093)
d14bb2efc3 is described below

commit d14bb2efc380c38dea25889ec27eccb67fe2b97a
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Tue Aug 25 15:23:46 2026 +0200

    Add databricks driver download option, fixes #8058 (#8093)
---
 .../ROOT/pages/database/databases/databricks.adoc  |   3 +-
 .../java/org/apache/hop/driver/DriverCatalog.java  |  75 ++++++++-
 .../apache/hop/driver/DriverInstallCommand.java    |  69 +++++++-
 .../hop/driver/DriverInstallCommandTest.java       | 187 +++++++++++++++++++++
 .../databricks/DatabricksDatabaseMeta.java         |  15 ++
 .../databricks/DatabricksDatabaseMetaTest.java     |  17 ++
 6 files changed, 355 insertions(+), 11 deletions(-)

diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/database/databases/databricks.adoc 
b/docs/hop-user-manual/modules/ROOT/pages/database/databases/databricks.adoc
index 922b45c8c9..8687ec2cd0 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/database/databases/databricks.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/database/databases/databricks.adoc
@@ -28,7 +28,8 @@ WARNING: the database name field is not used in a Databricks 
connection. Use the
 |===
 | Option | Info
 |Type | Relational
-|Driver | https://docs.databricks.com/aws/en/integrations/jdbc/download[Driver 
Link]
+|Driver | Not bundled - install the open source driver on demand, or get it 
from https://docs.databricks.com/aws/en/integrations/jdbc/download[Databricks]
+|Install | `hop driver install databricks`
 |Hop Dependencies | None
 |Documentation | 
https://docs.databricks.com/aws/en/integrations/jdbc/[Documentation Link]
 |JDBC Url  | 
jdbc:databricks://<server-hostname>:443;httpPath=<http-path>[;<setting1>=<value1>;<setting2>=<value2>;<settingN>=<valueN>]
diff --git a/engine/src/main/java/org/apache/hop/driver/DriverCatalog.java 
b/engine/src/main/java/org/apache/hop/driver/DriverCatalog.java
index 34b5ce8b0a..b4b531bdff 100644
--- a/engine/src/main/java/org/apache/hop/driver/DriverCatalog.java
+++ b/engine/src/main/java/org/apache/hop/driver/DriverCatalog.java
@@ -23,6 +23,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.stream.Stream;
 import org.apache.hop.core.database.DatabasePluginType;
 import org.apache.hop.core.database.DriverDownload;
 import org.apache.hop.core.database.IDatabase;
@@ -38,29 +39,68 @@ import org.apache.hop.core.plugins.PluginRegistry;
 public class DriverCatalog {
 
   private final Map<String, DriverDefinition> driversById = new 
LinkedHashMap<>();
+  private final Map<String, KnownDatabase> databasesById = new 
LinkedHashMap<>();
 
   private DriverCatalog() {
     // use load()
   }
 
+  /**
+   * A database plugin as the driver commands see it. Every database type ends 
up here; only the
+   * ones declaring a {@link DriverDownload} also become a {@link 
DriverDefinition}. It is what lets
+   * "no download for this database" be told apart from "no such database 
type".
+   *
+   * @param id the command-line id: the database type lowercased, e.g. {@code 
databricks}
+   * @param name the plugin's display name, e.g. {@code Databricks}
+   * @param driverClass the JDBC driver class the plugin expects, null when it 
could not be asked
+   * @param classLoader the plugin's classloader, used to see whether that 
driver class is there
+   */
+  public record KnownDatabase(String id, String name, String driverClass, 
ClassLoader classLoader) {
+
+    /**
+     * @return true when the JDBC driver class is already loadable, i.e. the 
driver ships with this
+     *     Hop installation (bundled with the plugin, or installed earlier) 
and there is nothing
+     *     left to download.
+     */
+    public boolean isDriverAvailable() {
+      if (driverClass == null || driverClass.isBlank() || classLoader == null) 
{
+        return false;
+      }
+      try {
+        classLoader.loadClass(driverClass);
+        return true;
+      } catch (Exception | LinkageError e) {
+        return false;
+      }
+    }
+  }
+
   /** Build the catalog by scanning all registered database plugins for a 
driver download. */
   public static DriverCatalog load() {
     DriverCatalog catalog = new DriverCatalog();
     PluginRegistry registry = PluginRegistry.getInstance();
     for (IPlugin plugin : registry.getPlugins(DatabasePluginType.class)) {
+      String databaseType = plugin.getIds()[0];
+      String id = databaseType.toLowerCase(Locale.ROOT);
       try {
         Object loaded = registry.loadClass(plugin);
         if (loaded instanceof IDatabase database) {
+          String driverClass = driverClass(database);
+          catalog.databasesById.put(
+              id,
+              new KnownDatabase(
+                  id, plugin.getName(), driverClass, 
database.getClass().getClassLoader()));
           DriverDownload download = database.getDriverDownload();
           if (download != null) {
             DriverDefinition definition =
-                new DriverDefinition(
-                    plugin.getIds()[0], plugin.getName(), 
driverClass(database), download);
+                new DriverDefinition(databaseType, plugin.getName(), 
driverClass, download);
             catalog.driversById.put(definition.getId(), definition);
           }
         }
       } catch (Exception e) {
-        // Skip any plugin that fails to load; it simply has no downloadable 
driver here.
+        // The plugin failed to load, so it has no downloadable driver here - 
but the id is still a
+        // database type Hop knows, which is worth saying instead of "unknown 
driver id".
+        catalog.databasesById.putIfAbsent(id, new KnownDatabase(id, 
plugin.getName(), null, null));
       }
     }
     return catalog;
@@ -88,6 +128,35 @@ public class DriverCatalog {
     return id == null ? null : driversById.get(id.toLowerCase(Locale.ROOT));
   }
 
+  /**
+   * @return the database plugin known under this id/database type 
(case-insensitive), whether or
+   *     not it declares a driver download, or null when no such database type 
is installed.
+   */
+  public KnownDatabase getDatabaseType(String id) {
+    return id == null ? null : databasesById.get(id.toLowerCase(Locale.ROOT));
+  }
+
+  /**
+   * Ids close enough to an unknown one to be worth offering as a "did you 
mean", downloadable ones
+   * first - a typo or an id that only differs in its suffix ({@code postgres} 
for {@code
+   * postgresql}) is the common case.
+   *
+   * @param id the id the user typed
+   * @return at most 5 candidate ids, closest first, empty when nothing looks 
similar
+   */
+  public List<String> suggestIds(String id) {
+    if (id == null || id.isBlank()) {
+      return List.of();
+    }
+    String needle = id.toLowerCase(Locale.ROOT);
+    return Stream.concat(
+            databasesById.keySet().stream().filter(driversById::containsKey),
+            databasesById.keySet().stream().filter(known -> 
!driversById.containsKey(known)))
+        .filter(known -> known.contains(needle) || needle.contains(known))
+        .limit(5)
+        .toList();
+  }
+
   /**
    * Find a catalog entry by Hop database plugin type, e.g. {@code ORACLE} 
(matches {@code
    * DatabaseMeta.getPluginId()}). The id and the database type share the same 
key space.
diff --git 
a/engine/src/main/java/org/apache/hop/driver/DriverInstallCommand.java 
b/engine/src/main/java/org/apache/hop/driver/DriverInstallCommand.java
index b8295068ea..fb0128b0ad 100644
--- a/engine/src/main/java/org/apache/hop/driver/DriverInstallCommand.java
+++ b/engine/src/main/java/org/apache/hop/driver/DriverInstallCommand.java
@@ -32,6 +32,10 @@ import picocli.CommandLine.Parameters;
  * <p>For restricted (Category X) drivers the install is refused unless {@code 
--accept-license} is
  * given: Apache Hop neither ships nor hosts these drivers; you obtain them 
from the vendor / Maven
  * Central under the vendor's license, at your own request.
+ *
+ * <p>Exit codes: 0 the driver is installed (or was already there), 2 the 
vendor license still has
+ * to be accepted, 3 no database type goes by that id, 4 the database type 
exists but declares no
+ * downloadable driver.
  */
 @SuppressWarnings("java:S106")
 @Command(
@@ -43,6 +47,7 @@ public class DriverInstallCommand implements 
Callable<Integer> {
   private static final int EXIT_OK = 0;
   private static final int EXIT_LICENSE_REQUIRED = 2;
   private static final int EXIT_UNKNOWN_DRIVER = 3;
+  private static final int EXIT_NO_DOWNLOAD = 4;
 
   @Parameters(
       index = "0",
@@ -82,9 +87,7 @@ public class DriverInstallCommand implements 
Callable<Integer> {
     DriverCatalog catalog = DriverCatalog.load();
     DriverDefinition driver = catalog.get(driverId);
     if (driver == null) {
-      System.err.println("Unknown driver id: '" + driverId + "'.");
-      System.err.println("Run 'hop driver list' to see the available 
drivers.");
-      return EXIT_UNKNOWN_DRIVER;
+      return reportNothingToInstall(catalog);
     }
 
     if (driver.isRestricted() && !acceptLicense) {
@@ -92,10 +95,7 @@ public class DriverInstallCommand implements 
Callable<Integer> {
       return EXIT_LICENSE_REQUIRED;
     }
 
-    File target =
-        (targetFolder != null && !targetFolder.isBlank())
-            ? new File(targetFolder).getAbsoluteFile()
-            : DriverInstaller.defaultInstallFolder();
+    File target = resolveTarget();
 
     String installVersion =
         (version != null && !version.isBlank())
@@ -181,6 +181,61 @@ public class DriverInstallCommand implements 
Callable<Integer> {
     return EXIT_OK;
   }
 
+  private File resolveTarget() {
+    return (targetFolder != null && !targetFolder.isBlank())
+        ? new File(targetFolder).getAbsoluteFile()
+        : DriverInstaller.defaultInstallFolder();
+  }
+
+  /**
+   * Explain why there is nothing to install. "Unknown driver id" is only true 
when no database type
+   * goes by that id at all; when the type exists, what the user needs to hear 
is whether its driver
+   * is already there or has to come from the vendor by hand.
+   */
+  private int reportNothingToInstall(DriverCatalog catalog) {
+    DriverCatalog.KnownDatabase database = catalog.getDatabaseType(driverId);
+
+    if (database == null) {
+      System.err.println(
+          "Unknown driver id: '"
+              + driverId
+              + "'. No database type in this Hop installation uses that id.");
+      List<String> suggestions = catalog.suggestIds(driverId);
+      if (!suggestions.isEmpty()) {
+        System.err.println("Did you mean: " + String.join(", ", suggestions) + 
"?");
+      }
+      System.err.println("Run 'hop driver list' to see the drivers Hop can 
download.");
+      return EXIT_UNKNOWN_DRIVER;
+    }
+
+    if (database.isDriverAvailable()) {
+      System.out.println(
+          "Nothing to install: the "
+              + database.name()
+              + " JDBC driver ("
+              + database.driverClass()
+              + ") is already available in this Hop installation.");
+      return EXIT_OK;
+    }
+
+    System.err.println(
+        "No driver download available for '" + driverId + "' (" + 
database.name() + ").");
+    System.err.println(
+        "Hop knows this database type, but it declares no downloadable driver 
- typically because");
+    System.err.println("the vendor does not publish it to a public Maven 
repository.");
+    System.err.println();
+    System.err.println(
+        "Download the driver from the vendor and copy its jar(s) into "
+            + resolveTarget()
+            + ", then restart Hop.");
+    System.err.println(
+        "The documentation page for "
+            + database.name()
+            + " points at the vendor's download location.");
+    System.err.println("Run 'hop driver list' to see the drivers Hop can 
download.");
+    return EXIT_NO_DOWNLOAD;
+  }
+
   private void printLicenseNotice(DriverDefinition driver) {
     System.out.println();
     System.out.println(
diff --git 
a/engine/src/test/java/org/apache/hop/driver/DriverInstallCommandTest.java 
b/engine/src/test/java/org/apache/hop/driver/DriverInstallCommandTest.java
new file mode 100644
index 0000000000..5216a81484
--- /dev/null
+++ b/engine/src/test/java/org/apache/hop/driver/DriverInstallCommandTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.driver;
+
+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 static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.hop.core.HopClientEnvironment;
+import org.apache.hop.core.database.DatabasePluginType;
+import org.apache.hop.core.database.DriverDownload;
+import org.apache.hop.core.database.IDatabase;
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.plugins.IClassLoadingPlugin;
+import org.apache.hop.core.plugins.IPlugin;
+import org.apache.hop.core.plugins.PluginRegistry;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import picocli.CommandLine;
+
+/**
+ * What {@code hop driver install <id>} says when it has nothing to install. 
"Unknown driver id"
+ * only fits an id no database type goes by; a database type that simply 
declares no download needs
+ * to be told apart from one whose driver already ships with Hop.
+ */
+class DriverInstallCommandTest {
+
+  private static final int EXIT_OK = 0;
+  private static final int EXIT_UNKNOWN_DRIVER = 3;
+  private static final int EXIT_NO_DOWNLOAD = 4;
+
+  /** A database plugin the registry accepts and whose class it asks the 
plugin itself for. */
+  interface DatabasePluginMock extends IClassLoadingPlugin, IPlugin {}
+
+  private final List<DatabasePluginMock> registeredPlugins = new ArrayList<>();
+  private ByteArrayOutputStream out;
+  private ByteArrayOutputStream err;
+  private PrintStream originalOut;
+  private PrintStream originalErr;
+
+  @BeforeAll
+  static void setUpClass() throws HopException {
+    HopClientEnvironment.init();
+  }
+
+  @BeforeEach
+  void capture() {
+    originalOut = System.out;
+    originalErr = System.err;
+    out = new ByteArrayOutputStream();
+    err = new ByteArrayOutputStream();
+    System.setOut(new PrintStream(out, true, StandardCharsets.UTF_8));
+    System.setErr(new PrintStream(err, true, StandardCharsets.UTF_8));
+  }
+
+  @AfterEach
+  void restore() {
+    System.setOut(originalOut);
+    System.setErr(originalErr);
+    // The plugin registry is process-global, so a plugin registered here 
would otherwise be
+    // visible to every later test in this JVM.
+    registeredPlugins.forEach(
+        plugin -> 
PluginRegistry.getInstance().removePlugin(DatabasePluginType.class, plugin));
+    registeredPlugins.clear();
+  }
+
+  @Test
+  void anIdNoDatabaseTypeGoesByIsUnknown() throws HopException {
+    registerDatabase("TESTMANUAL", "Test Manual", 
noDownload("com.example.NoSuchDriver"));
+
+    assertEquals(EXIT_UNKNOWN_DRIVER, install("nosuchdatabase"));
+    assertTrue(stderr().contains("Unknown driver id: 'nosuchdatabase'"), 
stderr());
+    assertTrue(stderr().contains("hop driver list"), stderr());
+  }
+
+  @Test
+  void aNearMissGetsTheIdItAlmostTyped() throws HopException {
+    registerDatabase("TESTMANUAL", "Test Manual", 
noDownload("com.example.NoSuchDriver"));
+
+    assertEquals(EXIT_UNKNOWN_DRIVER, install("testmanu"));
+    assertTrue(stderr().contains("Did you mean: testmanual?"), stderr());
+  }
+
+  @Test
+  void aDatabaseTypeWithoutADownloadSaysSoAndPointsAtTheJdbcFolder() throws 
HopException {
+    registerDatabase("TESTMANUAL", "Test Manual", 
noDownload("com.example.NoSuchDriver"));
+
+    assertEquals(EXIT_NO_DOWNLOAD, install("testmanual"));
+    String message = stderr();
+    assertTrue(message.contains("No driver download available for 
'testmanual'"), message);
+    assertTrue(message.contains("Test Manual"), message);
+    // The whole point: tell the user where to put the jar they fetch from the 
vendor themselves.
+    
assertTrue(message.contains(DriverInstaller.defaultInstallFolder().getAbsolutePath()),
 message);
+    assertFalse(message.contains("Unknown driver id"), message);
+  }
+
+  @Test
+  void aDriverThatAlreadyShipsWithHopIsNothingToInstall() throws HopException {
+    // Any class on this classpath stands in for a bundled JDBC driver: what 
the command checks is
+    // whether the plugin's own classloader can load the driver class it names.
+    registerDatabase("TESTBUNDLED", "Test Bundled", 
noDownload(DriverCatalog.class.getName()));
+
+    assertEquals(EXIT_OK, install("testbundled"));
+    assertTrue(stdout().contains("Nothing to install"), stdout());
+    assertTrue(stdout().contains("Test Bundled"), stdout());
+  }
+
+  @Test
+  void aDatabaseTypeWithADownloadStillTakesTheInstallPath() throws 
HopException {
+    registerDatabase("TESTDOWNLOAD", "Test Download", withDownload());
+
+    // --target keeps the (restricted) driver from being fetched: the license 
notice comes first.
+    assertEquals(2, install("testdownload"));
+    assertTrue(stdout().contains("--accept-license"), stdout());
+  }
+
+  // ------------------------------------------------------------------ helpers
+
+  private int install(String... args) {
+    return new CommandLine(new DriverInstallCommand()).execute(args);
+  }
+
+  private String stdout() {
+    return out.toString(StandardCharsets.UTF_8);
+  }
+
+  private String stderr() {
+    return err.toString(StandardCharsets.UTF_8);
+  }
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  private void registerDatabase(String databaseType, String name, IDatabase 
database)
+      throws HopException {
+    DatabasePluginMock plugin = mock(DatabasePluginMock.class);
+    when(plugin.getIds()).thenReturn(new String[] {databaseType});
+    when(plugin.getName()).thenReturn(name);
+    when(plugin.getMainType()).thenReturn((Class) IDatabase.class);
+    when(plugin.loadClass(IDatabase.class)).thenReturn(database);
+    when(plugin.matches(databaseType)).thenReturn(true);
+    PluginRegistry.getInstance().registerPlugin(DatabasePluginType.class, 
plugin);
+    registeredPlugins.add(plugin);
+  }
+
+  private static IDatabase noDownload(String driverClass) {
+    IDatabase database = mock(IDatabase.class);
+    when(database.getDriverClass()).thenReturn(driverClass);
+    return database;
+  }
+
+  private static IDatabase withDownload() {
+    IDatabase database = mock(IDatabase.class);
+    when(database.getDriverClass()).thenReturn("com.example.NoSuchDriver");
+    when(database.getDriverDownload())
+        .thenReturn(
+            DriverDownload.builder()
+                .mavenCoordinate("com.example:example-jdbc")
+                .defaultVersion("1.0.0")
+                .licenseCategory("X")
+                .licenseName("Example License")
+                .build());
+    return database;
+  }
+}
diff --git 
a/plugins/tech/databricks/src/main/java/org/apache/hop/database/databricks/DatabricksDatabaseMeta.java
 
b/plugins/tech/databricks/src/main/java/org/apache/hop/database/databricks/DatabricksDatabaseMeta.java
index a0e8f31899..b26ede7f69 100644
--- 
a/plugins/tech/databricks/src/main/java/org/apache/hop/database/databricks/DatabricksDatabaseMeta.java
+++ 
b/plugins/tech/databricks/src/main/java/org/apache/hop/database/databricks/DatabricksDatabaseMeta.java
@@ -27,6 +27,7 @@ import org.apache.hop.core.Const;
 import org.apache.hop.core.database.BaseDatabaseMeta;
 import org.apache.hop.core.database.DatabaseMeta;
 import org.apache.hop.core.database.DatabaseMetaPlugin;
+import org.apache.hop.core.database.DriverDownload;
 import org.apache.hop.core.database.IDatabase;
 import org.apache.hop.core.database.types.ColumnContext;
 import org.apache.hop.core.database.types.DatabaseTypes;
@@ -213,6 +214,20 @@ public class DatabricksDatabaseMeta extends 
BaseDatabaseMeta implements IDatabas
     return "com.databricks.client.jdbc.Driver";
   }
 
+  @Override
+  public DriverDownload getDriverDownload() {
+    return DriverDownload.builder()
+        .mavenCoordinate("com.databricks:databricks-jdbc")
+        .defaultVersion("3.4.2")
+        .licenseCategory("A")
+        .licenseName("Apache-2.0")
+        
.licenseUrl("https://github.com/databricks/databricks-jdbc/blob/main/LICENSE";)
+        .vendor("Databricks")
+        .vendorUrl("https://github.com/databricks/databricks-jdbc";)
+        .notes("Open source Databricks JDBC driver (uber jar, ~39 MB)")
+        .build();
+  }
+
   @Override
   public String getURL(String hostname, String port, String databaseName)
       throws HopDatabaseException {
diff --git 
a/plugins/tech/databricks/src/test/java/org/apache/hop/database/databricks/DatabricksDatabaseMetaTest.java
 
b/plugins/tech/databricks/src/test/java/org/apache/hop/database/databricks/DatabricksDatabaseMetaTest.java
index 02b8e3b3b8..bbfa7aac97 100644
--- 
a/plugins/tech/databricks/src/test/java/org/apache/hop/database/databricks/DatabricksDatabaseMetaTest.java
+++ 
b/plugins/tech/databricks/src/test/java/org/apache/hop/database/databricks/DatabricksDatabaseMetaTest.java
@@ -17,9 +17,12 @@
 package org.apache.hop.database.databricks;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 import org.apache.hop.core.HopClientEnvironment;
 import org.apache.hop.core.database.DatabaseMeta;
+import org.apache.hop.core.database.DriverDownload;
 import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.row.value.ValueMetaBigNumber;
 import org.apache.hop.core.row.value.ValueMetaBinary;
@@ -140,6 +143,20 @@ class DatabricksDatabaseMetaTest {
         databaseMeta.getFieldDefinition(key, "ID", null, true, false, false));
   }
 
+  /**
+   * Databricks bundles no driver, so the connection dialog's "Download 
driver" button is the only
+   * way to get one - and that button only appears when the dialect declares a 
download.
+   */
+  @Test
+  void aDownloadableDriverIsDeclared() {
+    DriverDownload download = nativeMeta.getDriverDownload();
+    assertNotNull(download);
+    assertEquals("com.databricks:databricks-jdbc", 
download.getMavenCoordinate());
+    // The 2.x line is the proprietary Simba driver; only 3.x is Apache-2.0 
and freely downloadable.
+    assertEquals("3", download.getDefaultVersion().split("\\.")[0]);
+    assertFalse(download.isRestricted());
+  }
+
   @Test
   void alterStatementsCarryTheColumnAndItsType() {
     assertEquals(

Reply via email to