lihaosky commented on code in PR #28792:
URL: https://github.com/apache/flink/pull/28792#discussion_r4075207630


##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/CreateConnectionITCase.java:
##########
@@ -161,6 +161,38 @@ void testShowCreateSecretOnlyTemporaryConnection() {
                                         .containsOnly(entry("type", 
"default")));
     }
 
+    @Test
+    void testDescribeTemporaryConnection() {
+        tEnv().executeSql(
+                        "CREATE TEMPORARY CONNECTION my_conn COMMENT 'hi 
there' "
+                                + "WITH ('type' = 'default', 'k' = 'v', 
'password' = 'super-secret')");
+
+        List<Row> rows = collectRows("DESCRIBE CONNECTION my_conn");
+
+        assertThat(rows)
+                .contains(

Review Comment:
   nit: the rows are sorted via `TreeMap`, so the output is deterministic. 
`containsExactly` would pin the full output and also catch leaked or duplicated 
rows, which makes the `noneMatch` checks below mostly redundant.



##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/CreateConnectionITCase.java:
##########
@@ -161,6 +161,38 @@ void testShowCreateSecretOnlyTemporaryConnection() {
                                         .containsOnly(entry("type", 
"default")));
     }
 
+    @Test
+    void testDescribeTemporaryConnection() {
+        tEnv().executeSql(
+                        "CREATE TEMPORARY CONNECTION my_conn COMMENT 'hi 
there' "
+                                + "WITH ('type' = 'default', 'k' = 'v', 
'password' = 'super-secret')");
+
+        List<Row> rows = collectRows("DESCRIBE CONNECTION my_conn");
+
+        assertThat(rows)
+                .contains(
+                        Row.of("k", "v"), Row.of("type", "default"), 
Row.of("comment", "hi there"));
+        assertThat(rows.stream().map(Row::toString))
+                .noneMatch(row -> row.contains("super-secret"))
+                .noneMatch(row -> row.contains("password"))
+                .noneMatch(row -> 
row.contains("__flink.encrypted-secret-key__"));
+    }
+
+    @Test
+    void testDescribeTemporaryConnectionExtended() {
+        tEnv().executeSql("CREATE TEMPORARY CONNECTION my_conn WITH ('k' = 
'v')");
+
+        assertThat(collectRows("DESCRIBE CONNECTION EXTENDED my_conn"))
+                .contains(Row.of("temporary", "true"));

Review Comment:
   nit: nothing asserts `temporary` = `false` for a permanent connection. 
`CREATE CONNECTION` fails in this test base because no secret store is 
configured, but the connection can be registered directly through the catalog 
like `testShowCreatePermanentConnection` does.



##########
flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/DescribeConnectionOperation.java:
##########
@@ -0,0 +1,118 @@
+/*
+ * 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.GlobalConfiguration;
+import org.apache.flink.configuration.SecurityOptions;
+import org.apache.flink.table.api.DataTypes;
+import org.apache.flink.table.api.ValidationException;
+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.ContextResolvedConnection;
+import org.apache.flink.table.catalog.ObjectIdentifier;
+import org.apache.flink.table.types.DataType;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import static 
org.apache.flink.table.api.internal.TableResultUtils.buildTableResult;
+
+/** Operation to describe a DESCRIBE CONNECTION statement. */
+@Internal
+public class DescribeConnectionOperation implements Operation, 
ExecutableOperation {
+
+    private final ObjectIdentifier connectionIdentifier;
+    private final boolean isExtended;
+
+    public DescribeConnectionOperation(ObjectIdentifier connectionIdentifier, 
boolean isExtended) {
+        this.connectionIdentifier = connectionIdentifier;
+        this.isExtended = isExtended;
+    }
+
+    public ObjectIdentifier getConnectionIdentifier() {
+        return connectionIdentifier;
+    }
+
+    public boolean isExtended() {
+        return isExtended;
+    }
+
+    @Override
+    public String asSummaryString() {
+        Map<String, Object> params = new LinkedHashMap<>();
+        params.put("identifier", connectionIdentifier);
+        params.put("isExtended", isExtended);
+        return OperationUtils.formatWithChildren(
+                "DESCRIBE CONNECTION", params, Collections.emptyList(), 
Operation::asSummaryString);
+    }
+
+    @Override
+    public TableResultInternal execute(Context ctx) {
+        ContextResolvedConnection resolvedConnection =
+                ctx.getCatalogManager()
+                        .getResolvedConnection(connectionIdentifier)
+                        .orElseThrow(
+                                () ->
+                                        new ValidationException(
+                                                String.format(
+                                                        "Connection with 
identifier '%s' does not exist.",
+                                                        
connectionIdentifier.asSummaryString())));
+        return buildTableResult(
+                new String[] {"name", "value"},
+                new DataType[] {DataTypes.STRING(), DataTypes.STRING()},
+                buildRows(
+                        resolvedConnection.getConnection(),
+                        resolvedConnection.isTemporary(),
+                        
ctx.getTableConfig().get(SecurityOptions.ADDITIONAL_SENSITIVE_KEYS)));
+    }
+
+    private Object[][] buildRows(
+            CatalogConnection connection,
+            boolean isTemporary,
+            List<String> additionalSensitiveKeys) {
+        List<Object[]> rows = new ArrayList<>();
+        new 
TreeMap<>(ShowCreateUtil.withoutConnectionInternalOptions(connection.getOptions()))

Review Comment:
   When every option is sensitive, e.g. `CREATE TEMPORARY CONNECTION c WITH 
('password' = 'x')`, `withoutConnectionInternalOptions` leaves an empty map and 
`DESCRIBE CONNECTION c` returns zero rows for a connection that exists. `SHOW 
CREATE CONNECTION` handles the same case in 
`ShowCreateUtil.buildShowCreateConnectionRow` by falling back to `'type' = 
'default'`.
   
   I think DESCRIBE should always emit a `type` row (from 
`FactoryUtil.CONNECTION_TYPE`, defaulting to `default`), similar to how 
`DescribeCatalogOperation` always emits `type`. That also keeps DESCRIBE and 
SHOW CREATE consistent for secret-only connections.



##########
flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/DescribeConnectionOperation.java:
##########
@@ -0,0 +1,118 @@
+/*
+ * 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.GlobalConfiguration;
+import org.apache.flink.configuration.SecurityOptions;
+import org.apache.flink.table.api.DataTypes;
+import org.apache.flink.table.api.ValidationException;
+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.ContextResolvedConnection;
+import org.apache.flink.table.catalog.ObjectIdentifier;
+import org.apache.flink.table.types.DataType;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import static 
org.apache.flink.table.api.internal.TableResultUtils.buildTableResult;
+
+/** Operation to describe a DESCRIBE CONNECTION statement. */
+@Internal
+public class DescribeConnectionOperation implements Operation, 
ExecutableOperation {
+
+    private final ObjectIdentifier connectionIdentifier;
+    private final boolean isExtended;
+
+    public DescribeConnectionOperation(ObjectIdentifier connectionIdentifier, 
boolean isExtended) {
+        this.connectionIdentifier = connectionIdentifier;
+        this.isExtended = isExtended;
+    }
+
+    public ObjectIdentifier getConnectionIdentifier() {
+        return connectionIdentifier;
+    }
+
+    public boolean isExtended() {
+        return isExtended;
+    }
+
+    @Override
+    public String asSummaryString() {
+        Map<String, Object> params = new LinkedHashMap<>();
+        params.put("identifier", connectionIdentifier);
+        params.put("isExtended", isExtended);
+        return OperationUtils.formatWithChildren(
+                "DESCRIBE CONNECTION", params, Collections.emptyList(), 
Operation::asSummaryString);
+    }
+
+    @Override
+    public TableResultInternal execute(Context ctx) {
+        ContextResolvedConnection resolvedConnection =
+                ctx.getCatalogManager()
+                        .getResolvedConnection(connectionIdentifier)
+                        .orElseThrow(
+                                () ->
+                                        new ValidationException(
+                                                String.format(
+                                                        "Connection with 
identifier '%s' does not exist.",
+                                                        
connectionIdentifier.asSummaryString())));
+        return buildTableResult(
+                new String[] {"name", "value"},
+                new DataType[] {DataTypes.STRING(), DataTypes.STRING()},
+                buildRows(
+                        resolvedConnection.getConnection(),
+                        resolvedConnection.isTemporary(),
+                        
ctx.getTableConfig().get(SecurityOptions.ADDITIONAL_SENSITIVE_KEYS)));
+    }
+
+    private Object[][] buildRows(
+            CatalogConnection connection,
+            boolean isTemporary,
+            List<String> additionalSensitiveKeys) {
+        List<Object[]> rows = new ArrayList<>();
+        new 
TreeMap<>(ShowCreateUtil.withoutConnectionInternalOptions(connection.getOptions()))
+                .forEach(
+                        (key, value) -> {
+                            rows.add(
+                                    new Object[] {
+                                        key, maskValue(key, value, 
additionalSensitiveKeys)
+                                    });
+                        });
+        if (connection.getComment() != null && 
!connection.getComment().isEmpty()) {

Review Comment:
   nit: `StringUtils.isNotEmpty(connection.getComment())` would be simpler 
here, and matches what `ShowCreateUtil.extractComment` does for SHOW CREATE.



##########
flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlRichDescribeConnectionConverter.java:
##########
@@ -0,0 +1,40 @@
+/*
+ * 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.SqlRichDescribeConnection;
+import org.apache.flink.table.catalog.ObjectIdentifier;
+import org.apache.flink.table.catalog.UnresolvedIdentifier;
+import org.apache.flink.table.operations.DescribeConnectionOperation;
+import org.apache.flink.table.operations.Operation;
+
+/** A converter for {@link SqlRichDescribeConnection}. */
+public class SqlRichDescribeConnectionConverter

Review Comment:
   nit: the sibling converters for `SqlRichDescribeModel` and 
`SqlRichDescribeFunction` are named `SqlDescribeModelConverter` and 
`SqlDescribeFunctionConverter`. Consider `SqlDescribeConnectionConverter` for 
consistency.



##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/batch/sql/CreateConnectionITCase.java:
##########
@@ -161,6 +161,38 @@ void testShowCreateSecretOnlyTemporaryConnection() {
                                         .containsOnly(entry("type", 
"default")));
     }
 
+    @Test
+    void testDescribeTemporaryConnection() {
+        tEnv().executeSql(
+                        "CREATE TEMPORARY CONNECTION my_conn COMMENT 'hi 
there' "
+                                + "WITH ('type' = 'default', 'k' = 'v', 
'password' = 'super-secret')");
+
+        List<Row> rows = collectRows("DESCRIBE CONNECTION my_conn");
+
+        assertThat(rows)
+                .contains(
+                        Row.of("k", "v"), Row.of("type", "default"), 
Row.of("comment", "hi there"));
+        assertThat(rows.stream().map(Row::toString))
+                .noneMatch(row -> row.contains("super-secret"))
+                .noneMatch(row -> row.contains("password"))

Review Comment:
   This passes because `DefaultConnectionFactory.createConnection` already 
removed the `password` key at CREATE time, not because of `maskValue`. As far 
as I can tell no test exercises the masking branch at all.
   
   If masking stays (see my comment on `maskValue` in 
`DescribeConnectionOperation`), please add a case that actually hits it. For 
example `'db.password' = 'x'`: the factory whitelist is exact-match so that key 
stays in the catalog options, while `GlobalConfiguration.isSensitive` matches 
by substring and redacts it. The test should assert the value is `******`, not 
just that the key is absent.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to