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

lihaosky pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/master by this push:
     new 42290314753 [FLINK-38262][table] show create connection operation 
(#28791)
42290314753 is described below

commit 42290314753281773b13ce504038d6d8493b37a0
Author: Shekhar Rajak <[email protected]>
AuthorDate: Wed Sep 16 02:22:30 2026 +0530

    [FLINK-38262][table] show create connection operation (#28791)
    
    * [FLINK-38262][table] Add SHOW CREATE CONNECTION operation
    
    Generated-by: Codex (GPT-5)
    
    * Update 
flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/CatalogManager.java
    
    Co-authored-by: Ramin Gharib <[email protected]>
    
    * Update 
flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/CatalogManager.java
    
    Co-authored-by: Ramin Gharib <[email protected]>
    
    * [FLINK-38262][table] Add SHOW CREATE CONNECTION util tests
    
    * [FLINK-38262][table] Hide connection secret metadata
    
    * [FLINK-38262][table] Strengthen connection show create test
    
    * [FLINK-38262][table] Fix secret-only connection show create
    
    * [FLINK-38262][table] Reuse connection secret reference key
    
    * [FLINK-38262][table] Reuse connection lookup
    
    * [FLINK-38262][table] Reuse comment extraction
    
    * [FLINK-38262][table] Test permanent connection show create
    
    ---------
    
    Co-authored-by: Ramin Gharib <[email protected]>
---
 .../flink/table/api/internal/ShowCreateUtil.java   | 64 ++++++++++++---
 .../apache/flink/table/catalog/CatalogManager.java | 12 +++
 .../table/catalog/ContextResolvedConnection.java   | 54 +++++++++++++
 .../operations/ShowCreateConnectionOperation.java  | 74 +++++++++++++++++
 .../table/api/internal/ShowCreateUtilTest.java     | 83 +++++++++++++++++++
 .../flink/table/catalog/CatalogManagerTest.java    | 42 ++++++++++
 .../table/factories/DefaultConnectionFactory.java  |  2 +-
 .../operations/converters/SqlNodeConverters.java   |  1 +
 .../SqlShowCreateConnectionConverter.java          | 55 +++++++++++++
 .../SqlConnectionOperationConverterTest.java       | 48 +++++++++++
 .../runtime/batch/sql/CreateConnectionITCase.java  | 93 ++++++++++++++++++++++
 11 files changed, 515 insertions(+), 13 deletions(-)

diff --git 
a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/api/internal/ShowCreateUtil.java
 
b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/api/internal/ShowCreateUtil.java
index 0e690c4d0cb..d8c0fa9306d 100644
--- 
a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/api/internal/ShowCreateUtil.java
+++ 
b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/api/internal/ShowCreateUtil.java
@@ -23,6 +23,7 @@ import org.apache.flink.configuration.GlobalConfiguration;
 import org.apache.flink.table.api.TableException;
 import org.apache.flink.table.catalog.CatalogBaseTable;
 import org.apache.flink.table.catalog.CatalogBaseTable.TableKind;
+import org.apache.flink.table.catalog.CatalogConnection;
 import org.apache.flink.table.catalog.CatalogDescriptor;
 import org.apache.flink.table.catalog.CatalogView;
 import org.apache.flink.table.catalog.Column;
@@ -39,6 +40,8 @@ import org.apache.flink.table.catalog.StartMode;
 import org.apache.flink.table.catalog.TableDistribution;
 import org.apache.flink.table.catalog.UniqueConstraint;
 import org.apache.flink.table.expressions.SqlFactory;
+import org.apache.flink.table.factories.DefaultConnectionFactory;
+import org.apache.flink.table.factories.FactoryUtil;
 import org.apache.flink.table.utils.EncodingUtils;
 
 import org.apache.commons.lang3.StringUtils;
@@ -84,7 +87,7 @@ public class ShowCreateUtil {
         extractFormattedColumns(model.getResolvedOutputSchema())
                 .ifPresent(
                         c -> sb.append(String.format("OUTPUT (%s)%s", c, 
System.lineSeparator())));
-        extractComment(model)
+        extractComment(model.getComment())
                 .ifPresent(c -> 
sb.append(formatComment(c)).append(System.lineSeparator()));
         extractFormattedOptions(model.getOptions(), PRINT_INDENT, 
additionalSensitiveKeys)
                 .ifPresent(
@@ -99,6 +102,41 @@ public class ShowCreateUtil {
         return sb.toString();
     }
 
+    public static String buildShowCreateConnectionRow(
+            CatalogConnection connection,
+            ObjectIdentifier connectionIdentifier,
+            boolean isTemporary,
+            List<String> additionalSensitiveKeys) {
+        StringBuilder sb =
+                new StringBuilder()
+                        .append(
+                                buildCreateFormattedPrefix(
+                                        "CONNECTION",
+                                        isTemporary,
+                                        connectionIdentifier,
+                                        false,
+                                        false));
+        extractComment(connection.getComment())
+                .ifPresent(c -> sb.append(formatComment(c)).append("\n"));
+        final Map<String, String> connectionOptions =
+                withoutConnectionInternalOptions(connection.getOptions());
+        extractFormattedOptions(
+                        connectionOptions.isEmpty()
+                                        && connection
+                                                .getOptions()
+                                                .containsKey(
+                                                        
DefaultConnectionFactory
+                                                                
.SECRET_REFERENCE_KEY)
+                                ? Map.of(
+                                        FactoryUtil.CONNECTION_TYPE.key(),
+                                        
FactoryUtil.CONNECTION_TYPE.defaultValue())
+                                : connectionOptions,
+                        PRINT_INDENT,
+                        additionalSensitiveKeys)
+                .ifPresent(v -> sb.append("WITH 
(\n").append(v).append("\n)\n"));
+        return sb.toString();
+    }
+
     public static String buildShowCreateTableRow(
             ResolvedCatalogBaseTable<?> table,
             ObjectIdentifier tableIdentifier,
@@ -117,7 +155,7 @@ public class ShowCreateUtil {
         extractFormattedPrimaryKey(table, PRINT_INDENT)
                 .ifPresent(pk -> sb.append(",\n").append(pk));
         sb.append("\n)\n");
-        extractComment(table).ifPresent(c -> 
sb.append(formatComment(c)).append("\n"));
+        extractComment(table.getComment()).ifPresent(c -> 
sb.append(formatComment(c)).append("\n"));
         extractFormattedDistributedInfo((ResolvedCatalogTable) table)
                 .ifPresent(d -> sb.append(d).append("\n"));
         extractFormattedPartitionedInfo((ResolvedCatalogTable) table)
@@ -233,7 +271,7 @@ public class ShowCreateUtil {
         extractFormattedPrimaryKey(table, PRINT_INDENT)
                 .ifPresent(pk -> sb.append(",\n").append(pk));
         sb.append("\n)\n");
-        extractComment(table).ifPresent(c -> 
sb.append(formatComment(c)).append("\n"));
+        extractComment(table.getComment()).ifPresent(c -> 
sb.append(formatComment(c)).append("\n"));
         table.getDistribution()
                 .map(TableDistribution::toString)
                 .ifPresent(d -> sb.append(d).append("\n"));
@@ -270,7 +308,7 @@ public class ShowCreateUtil {
                                 buildCreateFormattedPrefix(
                                         "VIEW", isTemporary, viewIdentifier, 
false, true));
         sb.append(extractFormattedColumnNames(view, 
PRINT_INDENT)).append("\n)\n");
-        extractComment(view).ifPresent(c -> 
sb.append(formatComment(c)).append("\n"));
+        extractComment(view.getComment()).ifPresent(c -> 
sb.append(formatComment(c)).append("\n"));
         sb.append("AS ").append(((CatalogView) 
origin).getExpandedQuery()).append("\n");
 
         return sb.toString();
@@ -394,16 +432,18 @@ public class ShowCreateUtil {
         return String.format("PARTITIONED BY (%s)\n", partitionedByColumns);
     }
 
-    static Optional<String> extractComment(ResolvedCatalogBaseTable<?> table) {
-        return StringUtils.isEmpty(table.getComment())
-                ? Optional.empty()
-                : Optional.of(table.getComment());
+    private static Optional<String> extractComment(String comment) {
+        return StringUtils.isEmpty(comment) ? Optional.empty() : 
Optional.of(comment);
     }
 
-    static Optional<String> extractComment(ResolvedCatalogModel model) {
-        return StringUtils.isEmpty(model.getComment())
-                ? Optional.empty()
-                : Optional.of(model.getComment());
+    private static Map<String, String> withoutConnectionInternalOptions(
+            Map<String, String> options) {
+        return options.entrySet().stream()
+                .filter(
+                        entry ->
+                                
!DefaultConnectionFactory.SECRET_REFERENCE_KEY.equals(
+                                        entry.getKey()))
+                .collect(Collectors.toMap(Map.Entry::getKey, 
Map.Entry::getValue));
     }
 
     static Optional<String> 
extractFormattedDistributedInfo(ResolvedCatalogTable catalogTable) {
diff --git 
a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/CatalogManager.java
 
b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/CatalogManager.java
index fa9db8cb50d..9a306d4e0bc 100644
--- 
a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/CatalogManager.java
+++ 
b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/CatalogManager.java
@@ -1924,6 +1924,18 @@ public final class CatalogManager implements 
CatalogRegistry, AutoCloseable {
         }
     }
 
+    /** Get a connection from the catalog with contextual metadata. */
+    public Optional<ContextResolvedConnection> getResolvedConnection(
+            ObjectIdentifier objectIdentifier) {
+        return getConnection(objectIdentifier)
+                .map(
+                        connection ->
+                                ContextResolvedConnection.of(
+                                        objectIdentifier,
+                                        connection,
+                                        
temporaryConnections.containsKey(objectIdentifier)));
+    }
+
     /**
      * List all connections in the given catalog and database.
      *
diff --git 
a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/ContextResolvedConnection.java
 
b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/ContextResolvedConnection.java
new file mode 100644
index 00000000000..5df73f53d35
--- /dev/null
+++ 
b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/catalog/ContextResolvedConnection.java
@@ -0,0 +1,54 @@
+/*
+ * 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.flink.table.catalog;
+
+import org.apache.flink.annotation.Internal;
+
+/** Contextual metadata for a resolved connection. */
+@Internal
+public final class ContextResolvedConnection {
+
+    private final ObjectIdentifier identifier;
+    private final CatalogConnection connection;
+    private final boolean temporary;
+
+    private ContextResolvedConnection(
+            ObjectIdentifier identifier, CatalogConnection connection, boolean 
temporary) {
+        this.identifier = identifier;
+        this.connection = connection;
+        this.temporary = temporary;
+    }
+
+    public static ContextResolvedConnection of(
+            ObjectIdentifier identifier, CatalogConnection connection, boolean 
temporary) {
+        return new ContextResolvedConnection(identifier, connection, 
temporary);
+    }
+
+    public ObjectIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    public CatalogConnection getConnection() {
+        return connection;
+    }
+
+    public boolean isTemporary() {
+        return temporary;
+    }
+}
diff --git 
a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/ShowCreateConnectionOperation.java
 
b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/ShowCreateConnectionOperation.java
new file mode 100644
index 00000000000..f3f25cd0b46
--- /dev/null
+++ 
b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/ShowCreateConnectionOperation.java
@@ -0,0 +1,74 @@
+/*
+ * 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.flink.table.operations;
+
+import org.apache.flink.annotation.Internal;
+import org.apache.flink.configuration.SecurityOptions;
+import org.apache.flink.table.api.internal.ShowCreateUtil;
+import org.apache.flink.table.api.internal.TableResultInternal;
+import org.apache.flink.table.catalog.CatalogConnection;
+import org.apache.flink.table.catalog.ObjectIdentifier;
+
+import static 
org.apache.flink.table.api.internal.TableResultUtils.buildStringArrayResult;
+
+/** Operation to describe a SHOW CREATE CONNECTION statement. */
+@Internal
+public class ShowCreateConnectionOperation implements ShowOperation {
+
+    private final ObjectIdentifier connectionIdentifier;
+    private final CatalogConnection connection;
+    private final boolean isTemporary;
+
+    public ShowCreateConnectionOperation(
+            ObjectIdentifier connectionIdentifier,
+            CatalogConnection connection,
+            boolean isTemporary) {
+        this.connectionIdentifier = connectionIdentifier;
+        this.connection = connection;
+        this.isTemporary = isTemporary;
+    }
+
+    public ObjectIdentifier getConnectionIdentifier() {
+        return connectionIdentifier;
+    }
+
+    public CatalogConnection getConnection() {
+        return connection;
+    }
+
+    public boolean isTemporary() {
+        return isTemporary;
+    }
+
+    @Override
+    public String asSummaryString() {
+        return String.format("SHOW CREATE CONNECTION %s", 
connectionIdentifier.asSummaryString());
+    }
+
+    @Override
+    public TableResultInternal execute(Context ctx) {
+        String resultRow =
+                ShowCreateUtil.buildShowCreateConnectionRow(
+                        connection,
+                        connectionIdentifier,
+                        isTemporary,
+                        
ctx.getTableConfig().get(SecurityOptions.ADDITIONAL_SENSITIVE_KEYS));
+        return buildStringArrayResult("result", new String[] {resultRow});
+    }
+}
diff --git 
a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/api/internal/ShowCreateUtilTest.java
 
b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/api/internal/ShowCreateUtilTest.java
index 9a4391973c8..9b0ce1d7b4c 100644
--- 
a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/api/internal/ShowCreateUtilTest.java
+++ 
b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/api/internal/ShowCreateUtilTest.java
@@ -22,6 +22,7 @@ import org.apache.flink.configuration.Configuration;
 import org.apache.flink.table.api.DataTypes;
 import org.apache.flink.table.api.Schema;
 import org.apache.flink.table.catalog.CatalogBaseTable;
+import org.apache.flink.table.catalog.CatalogConnection;
 import org.apache.flink.table.catalog.CatalogDescriptor;
 import org.apache.flink.table.catalog.CatalogMaterializedTable;
 import 
org.apache.flink.table.catalog.CatalogMaterializedTable.LogicalRefreshMode;
@@ -44,6 +45,7 @@ import org.apache.flink.table.catalog.StartMode.StartModeKind;
 import org.apache.flink.table.catalog.TableDistribution;
 import org.apache.flink.table.catalog.UniqueConstraint;
 import org.apache.flink.table.expressions.DefaultSqlFactory;
+import org.apache.flink.table.factories.DefaultConnectionFactory;
 
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -69,6 +71,8 @@ class ShowCreateUtilTest {
             ObjectIdentifier.of("catalogName", "dbName", "tableName");
     private static final ObjectIdentifier VIEW_IDENTIFIER =
             ObjectIdentifier.of("catalogName", "dbName", "viewName");
+    private static final ObjectIdentifier CONNECTION_IDENTIFIER =
+            ObjectIdentifier.of("catalogName", "dbName", "connectionName");
     private static final ObjectIdentifier MATERIALIZED_TABLE_IDENTIFIER =
             ObjectIdentifier.of("catalogName", "dbName", 
"materializedTableName");
 
@@ -227,6 +231,69 @@ class ShowCreateUtilTest {
         assertThat(createCatalogString).isEqualTo(expected);
     }
 
+    @ParameterizedTest(name = "{index}: {3}")
+    @MethodSource("argsForShowCreateConnection")
+    void showCreateConnection(
+            CatalogConnection catalogConnection,
+            boolean isTemporary,
+            List<String> additionalSensitiveKeys,
+            String expected) {
+        final String createConnectionString =
+                ShowCreateUtil.buildShowCreateConnectionRow(
+                        catalogConnection,
+                        CONNECTION_IDENTIFIER,
+                        isTemporary,
+                        additionalSensitiveKeys);
+        assertThat(createConnectionString).isEqualTo(expected);
+    }
+
+    private static Collection<Arguments> argsForShowCreateConnection() {
+        final Collection<Arguments> argList = new ArrayList<>();
+
+        final Map<String, String> options = new HashMap<>();
+        options.put("url", "jdbc:mysql://localhost:3306/db");
+        options.put("type", "default");
+        addTemporaryAndPermanent(
+                argList,
+                CatalogConnection.of(options, null),
+                List.of(),
+                "CREATE %sCONNECTION `catalogName`.`dbName`.`connectionName`\n"
+                        + "WITH (\n"
+                        + "  'type' = 'default',\n"
+                        + "  'url' = 'jdbc:mysql://localhost:3306/db'\n"
+                        + ")\n");
+
+        final Map<String, String> commentedOptions = new HashMap<>();
+        commentedOptions.put("connector", "jdbc");
+        addTemporaryAndPermanent(
+                argList,
+                CatalogConnection.of(commentedOptions, "Connection's comment"),
+                List.of(),
+                "CREATE %sCONNECTION `catalogName`.`dbName`.`connectionName`\n"
+                        + "COMMENT 'Connection''s comment'\n"
+                        + "WITH (\n"
+                        + "  'connector' = 'jdbc'\n"
+                        + ")\n");
+
+        final Map<String, String> sensitiveOptions = new HashMap<>();
+        sensitiveOptions.put(DefaultConnectionFactory.SECRET_REFERENCE_KEY, 
"secret-id");
+        sensitiveOptions.put("endpoint", "service");
+        sensitiveOptions.put("my.custom.cred", "supersecret");
+        sensitiveOptions.put("password", "topsecret");
+        addTemporaryAndPermanent(
+                argList,
+                CatalogConnection.of(sensitiveOptions, null),
+                List.of("custom.cred"),
+                "CREATE %sCONNECTION `catalogName`.`dbName`.`connectionName`\n"
+                        + "WITH (\n"
+                        + "  'endpoint' = 'service',\n"
+                        + "  'my.custom.cred' = '******',\n"
+                        + "  'password' = '******'\n"
+                        + ")\n");
+
+        return argList;
+    }
+
     private static Collection<Arguments> argsForShowCreateCatalog() {
         Collection<Arguments> argList = new ArrayList<>();
         Map<String, String> options = new HashMap<>();
@@ -583,6 +650,22 @@ class ShowCreateUtilTest {
         argList.add(Arguments.of(catalogBaseTable, true, String.format(sql, 
"TEMPORARY ")));
     }
 
+    private static void addTemporaryAndPermanent(
+            Collection<Arguments> argList,
+            CatalogConnection catalogConnection,
+            List<String> additionalSensitiveKeys,
+            String sql) {
+        argList.add(
+                Arguments.of(
+                        catalogConnection, false, additionalSensitiveKeys, 
String.format(sql, "")));
+        argList.add(
+                Arguments.of(
+                        catalogConnection,
+                        true,
+                        additionalSensitiveKeys,
+                        String.format(sql, "TEMPORARY ")));
+    }
+
     private static void addCreateAndCreateOrAlter(
             Collection<Arguments> argList, CatalogBaseTable catalogBaseTable, 
String sql) {
         argList.add(Arguments.of(catalogBaseTable, false, String.format(sql, 
"CREATE ")));
diff --git 
a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/catalog/CatalogManagerTest.java
 
b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/catalog/CatalogManagerTest.java
index 51774ffb6f6..544cb3f8fa1 100644
--- 
a/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/catalog/CatalogManagerTest.java
+++ 
b/flink-table/flink-table-api-java/src/test/java/org/apache/flink/table/catalog/CatalogManagerTest.java
@@ -527,6 +527,48 @@ class CatalogManagerTest {
         
assertThat(dropTemporaryEvent.identifier().getObjectName()).isEqualTo("conn2");
     }
 
+    @Test
+    void testGetResolvedConnection() throws Exception {
+        CatalogManager catalogManager = createCatalogManager(null);
+        ObjectIdentifier identifier =
+                ObjectIdentifier.of(
+                        catalogManager.getCurrentCatalog(),
+                        catalogManager.getCurrentDatabase(),
+                        "connection");
+        catalogManager
+                .getCatalog(catalogManager.getCurrentCatalog())
+                .orElseThrow()
+                .createConnection(
+                        identifier.toObjectPath(),
+                        CatalogConnection.of(Map.of("endpoint", "permanent"), 
null),
+                        false);
+        catalogManager.createTemporaryConnection(
+                SensitiveConnection.of(Map.of("type", "default", "endpoint", 
"temporary"), null),
+                identifier,
+                false);
+
+        ContextResolvedConnection temporaryConnection =
+                catalogManager.getResolvedConnection(identifier).orElseThrow();
+        assertThat(temporaryConnection.isTemporary()).isTrue();
+        assertThat(temporaryConnection.getConnection().getOptions())
+                .containsEntry("endpoint", "temporary");
+
+        catalogManager.dropTemporaryConnection(identifier, false);
+
+        ContextResolvedConnection permanentConnection =
+                catalogManager.getResolvedConnection(identifier).orElseThrow();
+        assertThat(permanentConnection.isTemporary()).isFalse();
+        assertThat(permanentConnection.getConnection().getOptions())
+                .containsEntry("endpoint", "permanent");
+        assertThat(
+                        catalogManager.getResolvedConnection(
+                                ObjectIdentifier.of(
+                                        catalogManager.getCurrentCatalog(),
+                                        catalogManager.getCurrentDatabase(),
+                                        "missing")))
+                .isEmpty();
+    }
+
     @Test
     public void testCreateConnectionWithoutTypeFallsBackToDefaultFactory() 
throws Exception {
         CompletableFuture<CreateConnectionEvent> createFuture = new 
CompletableFuture<>();
diff --git 
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/factories/DefaultConnectionFactory.java
 
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/factories/DefaultConnectionFactory.java
index 236e8c6a5f4..c098bc6cc4f 100644
--- 
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/factories/DefaultConnectionFactory.java
+++ 
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/factories/DefaultConnectionFactory.java
@@ -64,7 +64,7 @@ public class DefaultConnectionFactory implements 
ConnectionFactory {
      * surrounding double underscores make collision with user-supplied option 
names unlikely; user
      * options containing this key will be rejected at create-time.
      */
-    static final String SECRET_REFERENCE_KEY = 
"__flink.encrypted-secret-key__";
+    public static final String SECRET_REFERENCE_KEY = 
"__flink.encrypted-secret-key__";
 
     /**
      * Default whitelist of option keys treated as sensitive. Seeded from 
{@link
diff --git 
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java
 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java
index ce8f41df992..9faecf21871 100644
--- 
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java
+++ 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java
@@ -141,6 +141,7 @@ public class SqlNodeConverters {
 
     private static void registerConnectionConverters() {
         register(new SqlCreateConnectionConverter());
+        register(new SqlShowCreateConnectionConverter());
     }
 
     private static void registerMaterializedTableConverters() {
diff --git 
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlShowCreateConnectionConverter.java
 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlShowCreateConnectionConverter.java
new file mode 100644
index 00000000000..35fcaa32f3d
--- /dev/null
+++ 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlShowCreateConnectionConverter.java
@@ -0,0 +1,55 @@
+/*
+ * 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.flink.table.planner.operations.converters;
+
+import org.apache.flink.sql.parser.dql.SqlShowCreateConnection;
+import org.apache.flink.table.api.ValidationException;
+import org.apache.flink.table.catalog.ContextResolvedConnection;
+import org.apache.flink.table.catalog.ObjectIdentifier;
+import org.apache.flink.table.catalog.UnresolvedIdentifier;
+import org.apache.flink.table.operations.Operation;
+import org.apache.flink.table.operations.ShowCreateConnectionOperation;
+
+import java.util.Optional;
+
+/** A converter for {@link SqlShowCreateConnection}. */
+public class SqlShowCreateConnectionConverter implements 
SqlNodeConverter<SqlShowCreateConnection> {
+
+    @Override
+    public Operation convertSqlNode(
+            SqlShowCreateConnection showCreateConnection, ConvertContext 
context) {
+        UnresolvedIdentifier unresolvedIdentifier =
+                
UnresolvedIdentifier.of(showCreateConnection.getConnectionName().names);
+        ObjectIdentifier identifier =
+                
context.getCatalogManager().qualifyIdentifier(unresolvedIdentifier);
+        Optional<ContextResolvedConnection> connection =
+                context.getCatalogManager().getResolvedConnection(identifier);
+
+        if (connection.isEmpty()) {
+            throw new ValidationException(
+                    String.format(
+                            "Could not execute SHOW CREATE CONNECTION. 
Connection with identifier %s does not exist.",
+                            identifier.asSerializableString()));
+        }
+
+        ContextResolvedConnection resolvedConnection = connection.get();
+        return new ShowCreateConnectionOperation(
+                identifier, resolvedConnection.getConnection(), 
resolvedConnection.isTemporary());
+    }
+}
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlConnectionOperationConverterTest.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlConnectionOperationConverterTest.java
index a410c95ec0b..fbed59026ae 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlConnectionOperationConverterTest.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlConnectionOperationConverterTest.java
@@ -20,9 +20,11 @@ package org.apache.flink.table.planner.operations;
 
 import org.apache.flink.sql.parser.error.SqlValidateException;
 import org.apache.flink.table.api.SqlParserException;
+import org.apache.flink.table.api.ValidationException;
 import org.apache.flink.table.catalog.ObjectIdentifier;
 import org.apache.flink.table.catalog.SensitiveConnection;
 import org.apache.flink.table.operations.Operation;
+import org.apache.flink.table.operations.ShowCreateConnectionOperation;
 import org.apache.flink.table.operations.ddl.CreateConnectionOperation;
 
 import org.junit.jupiter.api.Test;
@@ -121,4 +123,50 @@ class SqlConnectionOperationConverterTest extends 
SqlNodeToOperationConversionTe
                 .isInstanceOf(SqlValidateException.class)
                 .hasMessageContaining("Connection property list can not be 
empty.");
     }
+
+    @Test
+    void testShowCreateConnection() {
+        createTemporaryConnection("my_conn");
+
+        Operation operation = parse("SHOW CREATE CONNECTION my_conn");
+        
assertThat(operation).isInstanceOf(ShowCreateConnectionOperation.class);
+        ShowCreateConnectionOperation op = (ShowCreateConnectionOperation) 
operation;
+
+        assertThat(op.getConnectionIdentifier())
+                .isEqualTo(ObjectIdentifier.of("builtin", "default", 
"my_conn"));
+        assertThat(op.getConnection().getOptions()).containsEntry("k", "v");
+        
assertThat(op.getConnection().getOptions()).doesNotContainKey("password");
+        assertThat(op.isTemporary()).isTrue();
+    }
+
+    @Test
+    void testShowCreateConnectionWithFullyQualifiedName() {
+        createTemporaryConnection("my_conn");
+
+        Operation operation = parse("SHOW CREATE CONNECTION 
`builtin`.`default`.`my_conn`");
+        ShowCreateConnectionOperation op = (ShowCreateConnectionOperation) 
operation;
+
+        assertThat(op.getConnectionIdentifier())
+                .isEqualTo(ObjectIdentifier.of("builtin", "default", 
"my_conn"));
+    }
+
+    @Test
+    void testShowCreateConnectionMissingConnectionRejected() {
+        assertThatThrownBy(() -> parse("SHOW CREATE CONNECTION missing_conn"))
+                .isInstanceOf(ValidationException.class)
+                .hasMessageContaining(
+                        "Could not execute SHOW CREATE CONNECTION. Connection 
with identifier");
+    }
+
+    private void createTemporaryConnection(String connectionName) {
+        catalogManager.createTemporaryConnection(
+                SensitiveConnection.of(
+                        Map.of("type", "default", "k", "v", "password", 
"super-secret"),
+                        "hi there"),
+                ObjectIdentifier.of(
+                        catalogManager.getCurrentCatalog(),
+                        catalogManager.getCurrentDatabase(),
+                        connectionName),
+                false);
+    }
 }
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/CreateConnectionITCase.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/CreateConnectionITCase.java
index a9d99cbfe5d..170768b656d 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/CreateConnectionITCase.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/CreateConnectionITCase.java
@@ -18,14 +18,22 @@
 
 package org.apache.flink.table.planner.runtime.batch.sql;
 
+import org.apache.flink.table.api.TableResult;
 import org.apache.flink.table.api.ValidationException;
 import org.apache.flink.table.api.internal.TableEnvironmentInternal;
+import org.apache.flink.table.catalog.CatalogConnection;
 import org.apache.flink.table.catalog.CatalogManager;
 import org.apache.flink.table.catalog.ObjectIdentifier;
+import org.apache.flink.table.factories.DefaultConnectionFactory;
 import org.apache.flink.table.planner.runtime.utils.BatchTestBase;
+import org.apache.flink.types.Row;
+import org.apache.flink.util.CollectionUtil;
 
 import org.junit.jupiter.api.Test;
 
+import java.util.List;
+import java.util.Map;
+
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.api.Assertions.entry;
@@ -73,6 +81,91 @@ class CreateConnectionITCase extends BatchTestBase {
                 .hasMessageContaining("WritableSecretStore must be 
configured");
     }
 
+    @Test
+    void testShowCreatePermanentConnection() throws Exception {
+        ObjectIdentifier identifier = connectionIdentifier("my_conn");
+        catalogManager()
+                .getCatalog(catalogManager().getCurrentCatalog())
+                .orElseThrow()
+                .createConnection(
+                        identifier.toObjectPath(),
+                        CatalogConnection.of(Map.of("k", "v", "type", 
"default"), "hi there"),
+                        false);
+
+        List<Row> rows = collectRows("SHOW CREATE CONNECTION my_conn");
+
+        assertThat(rows).hasSize(1);
+        String showCreate = (String) rows.get(0).getField(0);
+        assertThat(showCreate)
+                .contains("CREATE CONNECTION")
+                .doesNotContain("CREATE TEMPORARY CONNECTION")
+                .contains("`my_conn`")
+                .contains("COMMENT 'hi there'")
+                .contains("'k' = 'v'")
+                .contains("'type' = 'default'");
+    }
+
+    @Test
+    void testShowCreateTemporaryConnection() {
+        tEnv().executeSql(
+                        "CREATE TEMPORARY CONNECTION my_conn COMMENT 'hi 
there' "
+                                + "WITH ('type' = 'default', 'k' = 'v', 
'password' = 'super-secret')");
+
+        
assertThat(catalogManager().getConnection(connectionIdentifier("my_conn")))
+                .hasValueSatisfying(
+                        connection ->
+                                assertThat(connection.getOptions())
+                                        .containsKeys(
+                                                "k",
+                                                "type",
+                                                
DefaultConnectionFactory.SECRET_REFERENCE_KEY)
+                                        .doesNotContainKey("password"));
+
+        List<Row> rows = collectRows("SHOW CREATE CONNECTION my_conn");
+
+        assertThat(rows).hasSize(1);
+        String showCreate = (String) rows.get(0).getField(0);
+        assertThat(showCreate)
+                .contains("CREATE TEMPORARY CONNECTION")
+                .contains("`my_conn`")
+                .contains("COMMENT 'hi there'")
+                .contains("'k' = 'v'")
+                .contains("'type' = 'default'")
+                .doesNotContain("super-secret")
+                .doesNotContain("password")
+                .doesNotContain(DefaultConnectionFactory.SECRET_REFERENCE_KEY);
+    }
+
+    @Test
+    void testShowCreateSecretOnlyTemporaryConnection() {
+        tEnv().executeSql("CREATE TEMPORARY CONNECTION my_conn WITH 
('password' = 'super-secret')");
+
+        List<Row> rows = collectRows("SHOW CREATE CONNECTION my_conn");
+
+        assertThat(rows).hasSize(1);
+        String showCreate = (String) rows.get(0).getField(0);
+        assertThat(showCreate)
+                .contains("CREATE TEMPORARY CONNECTION")
+                .contains("WITH (\n  'type' = 'default'\n)\n")
+                .doesNotContain("password")
+                .doesNotContain("super-secret")
+                .doesNotContain(DefaultConnectionFactory.SECRET_REFERENCE_KEY);
+
+        
catalogManager().dropTemporaryConnection(connectionIdentifier("my_conn"), 
false);
+        tEnv().executeSql(showCreate);
+
+        
assertThat(catalogManager().getConnection(connectionIdentifier("my_conn")))
+                .hasValueSatisfying(
+                        connection ->
+                                assertThat(connection.getOptions())
+                                        .containsOnly(entry("type", 
"default")));
+    }
+
+    private List<Row> collectRows(String sql) {
+        TableResult result = tEnv().executeSql(sql);
+        return CollectionUtil.iteratorToList(result.collect());
+    }
+
     private CatalogManager catalogManager() {
         return ((TableEnvironmentInternal) tEnv()).getCatalogManager();
     }

Reply via email to