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

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


The following commit(s) were added to refs/heads/master by this push:
     new 22327842e8f [test](regression) Add external correctness P0 coverage 
(#66002)
22327842e8f is described below

commit 22327842e8f0760f2cfcca00104121318fde621c
Author: Gabriel <[email protected]>
AuthorDate: Fri Jul 24 18:54:06 2026 +0800

    [test](regression) Add external correctness P0 coverage (#66002)
    
    ### What problem does this PR solve?
    
    Problem Summary:
    
    This PR fills P0 regression gaps found while auditing the external-table
    correctness issues under JIRA:
    
    - bind Iceberg predicates and runtime filters correctly across
    rename/drop/type evolution;
    - scan, filter, and aggregate files written with multiple Iceberg
    partition specs;
    - keep an Iceberg write atomic after a pipeline error and make a
    corrected retry visible exactly once;
    - mask S3 and OAuth credentials in persisted audit statements.
    
    The audit also found this, including a nested required-field
    reproduction. This test-only PR does not change production code or
    attempt to fix that issue.
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test
        - [x] Regression test
        - [ ] Unit Test
        - [x] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason
    
      Local verification:
    
    `./run-regression-test.sh --run -s
    
test_iceberg_schema_evolution_filter_binding,test_iceberg_multi_spec_filter_aggregate,test_iceberg_failed_write_atomicity_retry,test_external_catalog_credential_masking`
    
      Result: 4 suites passed, 0 failed.
    
    - Behavior changed:
        - [x] No.
        - [ ] Yes.
    
    - Does this need documentation?
        - [x] No.
        - [ ] Yes.
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label
---
 .../test_iceberg_multi_spec_filter_aggregate.out   |  35 ++++++
 ...est_iceberg_schema_evolution_filter_binding.out |  21 ++++
 .../test_iceberg_failed_write_atomicity_retry.out  |   9 ++
 ...test_external_catalog_credential_masking.groovy |  99 +++++++++++++++++
 ...test_iceberg_multi_spec_filter_aggregate.groovy | 120 +++++++++++++++++++++
 ..._iceberg_schema_evolution_filter_binding.groovy | 119 ++++++++++++++++++++
 ...est_iceberg_failed_write_atomicity_retry.groovy | 107 ++++++++++++++++++
 7 files changed, 510 insertions(+)

diff --git 
a/regression-test/data/external_table_p0/iceberg/test_iceberg_multi_spec_filter_aggregate.out
 
b/regression-test/data/external_table_p0/iceberg/test_iceberg_multi_spec_filter_aggregate.out
new file mode 100644
index 00000000000..1875865c3bc
--- /dev/null
+++ 
b/regression-test/data/external_table_p0/iceberg/test_iceberg_multi_spec_filter_aggregate.out
@@ -0,0 +1,35 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !multi_spec_rows --
+1      aa-01   10
+11     bb-11   110
+12     bb-12   120
+13     bb-13   130
+14     bb-14   140
+2      aa-02   20
+3      aa-03   30
+4      aa-04   40
+
+-- !multi_spec_static_filter --
+1      aa-01
+12     bb-12
+14     bb-14
+3      aa-03
+
+-- !multi_spec_runtime_filter --
+12     bb-12   120
+14     bb-14   140
+2      aa-02   20
+3      aa-03   30
+
+-- !multi_spec_grouped --
+aa     4       4       100
+bb     4       4       500
+
+-- !multi_spec_distinct --
+8      8       8
+
+-- !multi_spec_metadata --
+0      2
+1      2
+2      2
+3      2
diff --git 
a/regression-test/data/external_table_p0/iceberg/test_iceberg_schema_evolution_filter_binding.out
 
b/regression-test/data/external_table_p0/iceberg/test_iceberg_schema_evolution_filter_binding.out
new file mode 100644
index 00000000000..6f263263055
--- /dev/null
+++ 
b/regression-test/data/external_table_p0/iceberg/test_iceberg_schema_evolution_filter_binding.out
@@ -0,0 +1,21 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !schema_filter_warm --
+1      old-a   10
+2      old-b   20
+3      old-c   30
+
+-- !schema_filter_static --
+2      old-b   20
+4      new-d   4000000000
+
+-- !schema_filter_runtime --
+2      old-b   20
+4      new-d   4000000000
+
+-- !schema_filter_aggregate --
+2      5000000020
+
+-- !schema_filter_null_semantics --
+2      old-b
+3      old-c
+4      new-d
diff --git 
a/regression-test/data/external_table_p0/iceberg/write/test_iceberg_failed_write_atomicity_retry.out
 
b/regression-test/data/external_table_p0/iceberg/write/test_iceberg_failed_write_atomicity_retry.out
new file mode 100644
index 00000000000..b6c590e22b7
--- /dev/null
+++ 
b/regression-test/data/external_table_p0/iceberg/write/test_iceberg_failed_write_atomicity_retry.out
@@ -0,0 +1,9 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !failed_write_state --
+1      committed
+
+-- !failed_write_retry --
+1      committed       1
+2      candidate-0     1
+3      candidate-1     1
+4      candidate-2     1
diff --git 
a/regression-test/suites/audit/test_external_catalog_credential_masking.groovy 
b/regression-test/suites/audit/test_external_catalog_credential_masking.groovy
new file mode 100644
index 00000000000..b7b1bd4aebc
--- /dev/null
+++ 
b/regression-test/suites/audit/test_external_catalog_credential_masking.groovy
@@ -0,0 +1,99 @@
+// 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.
+
+suite("test_external_catalog_credential_masking", "p0,nonConcurrent") {
+    String credentialCatalog = "audit_iceberg_credential_masking"
+    String tokenCatalog = "audit_iceberg_token_masking"
+    // Low-entropy placeholders keep this negative masking test distinct from 
real credentials.
+    String accessKey = "aaaaaaaaaaaaaaaaaaaa"
+    String secretKey = "bbbbbbbbbbbbbbbbbbbb"
+    String oauthCredential = "cccccccccccccccccccc"
+    String oauthToken = "dddddddddddddddddddd"
+
+    def waitForAuditStatement = { String catalogName ->
+        String query = """
+            select stmt
+            from __internal_schema.audit_log
+            where stmt_type = 'OTHER'
+              and lower(stmt) like '%create catalog ${catalogName}%'
+              and instr(stmt, '*XXX') > 0
+            order by time asc
+            limit 1
+        """
+        int retry = 60
+        def rows = sql query
+        while (rows.isEmpty()) {
+            if (retry-- < 0) {
+                throw new RuntimeException("audit statement for ${catalogName} 
was not found")
+            }
+            sleep(1000)
+            sql """call flush_audit_log()"""
+            rows = sql query
+        }
+        return rows[0][0].toString()
+    }
+
+    setGlobalVarTemporary([enable_audit_plugin: true], {
+        try {
+            sql """drop catalog if exists ${credentialCatalog}"""
+            sql """drop catalog if exists ${tokenCatalog}"""
+            sql """truncate table __internal_schema.audit_log"""
+
+            sql """
+                create catalog ${credentialCatalog} properties (
+                    'type' = 'iceberg',
+                    'iceberg.catalog.type' = 'rest',
+                    'uri' = 'http://127.0.0.1:1',
+                    's3.access_key' = '${accessKey}',
+                    's3.secret_key' = '${secretKey}',
+                    's3.endpoint' = 'http://127.0.0.1:1',
+                    's3.region' = 'us-east-1',
+                    'iceberg.rest.security.type' = 'oauth2',
+                    'iceberg.rest.oauth2.credential' = '${oauthCredential}',
+                    'iceberg.rest.oauth2.server-uri' = 
'http://127.0.0.1:1/oauth/tokens'
+                )
+            """
+            sql """
+                create catalog ${tokenCatalog} properties (
+                    'type' = 'iceberg',
+                    'iceberg.catalog.type' = 'rest',
+                    'uri' = 'http://127.0.0.1:1',
+                    'iceberg.rest.security.type' = 'oauth2',
+                    'iceberg.rest.oauth2.token' = '${oauthToken}'
+                )
+            """
+            sql """call flush_audit_log()"""
+
+            // Audit statements are independently persisted after parsing, so 
they must use the
+            // same sensitive-key masking contract as SHOW CREATE and 
printable catalog metadata.
+            String credentialStmt = waitForAuditStatement(credentialCatalog)
+            assertFalse(credentialStmt.contains(accessKey))
+            assertFalse(credentialStmt.contains(secretKey))
+            assertFalse(credentialStmt.contains(oauthCredential))
+            assertTrue(credentialStmt.contains('"s3.access_key" = "*XXX"'))
+            assertTrue(credentialStmt.contains('"s3.secret_key" = "*XXX"'))
+            
assertTrue(credentialStmt.contains('"iceberg.rest.oauth2.credential" = "*XXX"'))
+
+            String tokenStmt = waitForAuditStatement(tokenCatalog)
+            assertFalse(tokenStmt.contains(oauthToken))
+            assertTrue(tokenStmt.contains('"iceberg.rest.oauth2.token" = 
"*XXX"'))
+        } finally {
+            sql """drop catalog if exists ${credentialCatalog}"""
+            sql """drop catalog if exists ${tokenCatalog}"""
+        }
+    })
+}
diff --git 
a/regression-test/suites/external_table_p0/iceberg/test_iceberg_multi_spec_filter_aggregate.groovy
 
b/regression-test/suites/external_table_p0/iceberg/test_iceberg_multi_spec_filter_aggregate.groovy
new file mode 100644
index 00000000000..dd09b7be944
--- /dev/null
+++ 
b/regression-test/suites/external_table_p0/iceberg/test_iceberg_multi_spec_filter_aggregate.groovy
@@ -0,0 +1,120 @@
+// 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.
+
+suite("test_iceberg_multi_spec_filter_aggregate", "p0,external") {
+    String enabled = context.config.otherConfigs.get("enableIcebergTest")
+    if (enabled == null || !enabled.equalsIgnoreCase("true")) {
+        logger.info("disable iceberg test.")
+        return
+    }
+
+    String catalogName = "test_iceberg_multi_spec_filter_aggregate"
+    String dbName = "multi_spec_filter_aggregate_db"
+    String tableName = "multi_spec_filter_aggregate"
+    String restPort = context.config.otherConfigs.get("iceberg_rest_uri_port")
+    String minioPort = context.config.otherConfigs.get("iceberg_minio_port")
+    String externalEnvIp = context.config.otherConfigs.get("externalEnvIp")
+
+    sql """drop catalog if exists ${catalogName}"""
+    sql """
+        create catalog ${catalogName} properties (
+            'type' = 'iceberg',
+            'iceberg.catalog.type' = 'rest',
+            'uri' = 'http://${externalEnvIp}:${restPort}',
+            's3.access_key' = 'admin',
+            's3.secret_key' = 'password',
+            's3.endpoint' = 'http://${externalEnvIp}:${minioPort}',
+            's3.region' = 'us-east-1'
+        )
+    """
+
+    try {
+        sql """switch ${catalogName}"""
+        sql """drop database if exists ${dbName} force"""
+        sql """create database ${dbName}"""
+        sql """use ${dbName}"""
+        sql """set enable_fallback_to_original_planner = false"""
+        sql """set runtime_filter_mode = 'GLOBAL'"""
+
+        sql """
+            create table ${tableName} (
+                id int,
+                code string,
+                metric int
+            )
+        """
+        sql """insert into ${tableName} values (1, 'aa-01', 10), (11, 'bb-11', 
110)"""
+
+        sql """alter table ${tableName} add partition key bucket(4, id)"""
+        sql """insert into ${tableName} values (2, 'aa-02', 20), (12, 'bb-12', 
120)"""
+
+        sql """alter table ${tableName} add partition key truncate(2, code)"""
+        sql """insert into ${tableName} values (3, 'aa-03', 30), (13, 'bb-13', 
130)"""
+
+        sql """alter table ${tableName} drop partition key bucket(4, id)"""
+        sql """insert into ${tableName} values (4, 'aa-04', 40), (14, 'bb-14', 
140)"""
+
+        // Every data file must be evaluated with the partition spec that 
wrote it; applying the
+        // newest transform to old-spec files can silently prune valid rows or 
double-count keys.
+        order_qt_multi_spec_rows """
+            select id, code, metric from ${tableName} order by id
+        """
+        order_qt_multi_spec_static_filter """
+            select id, code
+            from ${tableName}
+            where id in (1, 3, 12, 14) and code >= 'aa-00'
+            order by id
+        """
+        order_qt_multi_spec_runtime_filter """
+            with filter_keys as (
+                select 2 as id
+                union all
+                select 3
+                union all
+                select 12
+                union all
+                select 14
+            )
+            select t.id, t.code, t.metric
+            from ${tableName} t
+            join filter_keys k on t.id = k.id
+            order by t.id
+        """
+        order_qt_multi_spec_grouped """
+            select substr(code, 1, 2) as code_prefix,
+                   count(*) as row_count,
+                   count(distinct id) as distinct_ids,
+                   sum(metric) as metric_sum
+            from ${tableName}
+            group by code_prefix
+            order by code_prefix
+        """
+        qt_multi_spec_distinct """
+            select count(*), count(distinct id), count(distinct code)
+            from ${tableName}
+        """
+        order_qt_multi_spec_metadata """
+            select spec_id, sum(record_count)
+            from ${tableName}\$partitions
+            group by spec_id
+            order by spec_id
+        """
+    } finally {
+        sql """drop database if exists ${catalogName}.${dbName} force"""
+        sql """drop catalog if exists ${catalogName}"""
+    }
+}
diff --git 
a/regression-test/suites/external_table_p0/iceberg/test_iceberg_schema_evolution_filter_binding.groovy
 
b/regression-test/suites/external_table_p0/iceberg/test_iceberg_schema_evolution_filter_binding.groovy
new file mode 100644
index 00000000000..1500b6702ae
--- /dev/null
+++ 
b/regression-test/suites/external_table_p0/iceberg/test_iceberg_schema_evolution_filter_binding.groovy
@@ -0,0 +1,119 @@
+// 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.
+
+suite("test_iceberg_schema_evolution_filter_binding", "p0,external") {
+    String enabled = context.config.otherConfigs.get("enableIcebergTest")
+    if (enabled == null || !enabled.equalsIgnoreCase("true")) {
+        logger.info("disable iceberg test.")
+        return
+    }
+
+    String catalogName = "test_iceberg_schema_evolution_filter_binding"
+    String dbName = "schema_evolution_filter_binding_db"
+    String tableName = "schema_evolution_filter_binding"
+    String restPort = context.config.otherConfigs.get("iceberg_rest_uri_port")
+    String minioPort = context.config.otherConfigs.get("iceberg_minio_port")
+    String externalEnvIp = context.config.otherConfigs.get("externalEnvIp")
+
+    sql """drop catalog if exists ${catalogName}"""
+    sql """
+        create catalog ${catalogName} properties (
+            'type' = 'iceberg',
+            'iceberg.catalog.type' = 'rest',
+            'uri' = 'http://${externalEnvIp}:${restPort}',
+            's3.access_key' = 'admin',
+            's3.secret_key' = 'password',
+            's3.endpoint' = 'http://${externalEnvIp}:${minioPort}',
+            's3.region' = 'us-east-1'
+        )
+    """
+
+    try {
+        sql """switch ${catalogName}"""
+        sql """drop database if exists ${dbName} force"""
+        sql """create database ${dbName}"""
+        sql """use ${dbName}"""
+        sql """set enable_fallback_to_original_planner = false"""
+        sql """set runtime_filter_mode = 'GLOBAL'"""
+
+        sql """
+            create table ${tableName} (
+                id int,
+                old_name string,
+                dropped_value string,
+                metric int
+            )
+        """
+        sql """
+            insert into ${tableName} values
+                (1, 'old-a', 'drop-a', 10),
+                (2, 'old-b', 'drop-b', 20),
+                (3, 'old-c', 'drop-c', 30)
+        """
+
+        // Warm the scan metadata before changing field names and types. 
Predicates must continue
+        // to bind by Iceberg field ID rather than by a cached ordinal from 
the old schema.
+        order_qt_schema_filter_warm """
+            select id, old_name, metric from ${tableName} order by id
+        """
+        sql """alter table ${tableName} rename column old_name new_name"""
+        sql """alter table ${tableName} drop column dropped_value"""
+        sql """alter table ${tableName} modify column metric bigint"""
+        sql """
+            insert into ${tableName} values
+                (4, 'new-d', 4000000000),
+                (5, 'new-e', 5000000000)
+        """
+
+        order_qt_schema_filter_static """
+            select id, new_name, metric
+            from ${tableName}
+            where new_name in ('old-b', 'new-d') and metric >= 20
+            order by id
+        """
+        order_qt_schema_filter_runtime """
+            with filter_keys as (
+                select 2 as id
+                union all
+                select 4
+            )
+            select t.id, t.new_name, t.metric
+            from ${tableName} t
+            join filter_keys k on t.id = k.id
+            order by t.id
+        """
+        qt_schema_filter_aggregate """
+            with filter_keys as (
+                select 'old-b' as new_name
+                union all
+                select 'new-e'
+            )
+            select count(*), sum(t.metric)
+            from ${tableName} t
+            join filter_keys k on t.new_name = k.new_name
+        """
+        order_qt_schema_filter_null_semantics """
+            select id, new_name
+            from ${tableName}
+            where new_name is not null and metric between 20 and 4000000000
+            order by id
+        """
+    } finally {
+        sql """drop database if exists ${catalogName}.${dbName} force"""
+        sql """drop catalog if exists ${catalogName}"""
+    }
+}
diff --git 
a/regression-test/suites/external_table_p0/iceberg/write/test_iceberg_failed_write_atomicity_retry.groovy
 
b/regression-test/suites/external_table_p0/iceberg/write/test_iceberg_failed_write_atomicity_retry.groovy
new file mode 100644
index 00000000000..8769295d991
--- /dev/null
+++ 
b/regression-test/suites/external_table_p0/iceberg/write/test_iceberg_failed_write_atomicity_retry.groovy
@@ -0,0 +1,107 @@
+// 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.
+
+suite("test_iceberg_failed_write_atomicity_retry", "p0,external") {
+    String enabled = context.config.otherConfigs.get("enableIcebergTest")
+    if (enabled == null || !enabled.equalsIgnoreCase("true")) {
+        logger.info("disable iceberg test.")
+        return
+    }
+
+    String catalogName = "test_iceberg_failed_write_atomicity_retry"
+    String dbName = "failed_write_atomicity_retry_db"
+    String tableName = "failed_write_atomicity_retry"
+    String restPort = context.config.otherConfigs.get("iceberg_rest_uri_port")
+    String minioPort = context.config.otherConfigs.get("iceberg_minio_port")
+    String externalEnvIp = context.config.otherConfigs.get("externalEnvIp")
+
+    sql """drop catalog if exists ${catalogName}"""
+    sql """
+        create catalog ${catalogName} properties (
+            'type' = 'iceberg',
+            'iceberg.catalog.type' = 'rest',
+            'uri' = 'http://${externalEnvIp}:${restPort}',
+            's3.access_key' = 'admin',
+            's3.secret_key' = 'password',
+            's3.endpoint' = 'http://${externalEnvIp}:${minioPort}',
+            's3.region' = 'us-east-1'
+        )
+    """
+
+    try {
+        sql """switch ${catalogName}"""
+        sql """drop database if exists ${dbName} force"""
+        sql """create database ${dbName}"""
+        sql """use ${dbName}"""
+
+        sql """
+            create table ${tableName} (
+                id int,
+                payload string
+            )
+        """
+        sql """insert into ${tableName} values (1, 'committed')"""
+        sql """set enable_strict_cast = true"""
+
+        long snapshotsBeforeFailure = (sql """
+            select count(*) from ${tableName}\$snapshots
+        """)[0][0] as long
+        long filesBeforeFailure = (sql """
+            select count(*) from ${tableName}\$files
+        """)[0][0] as long
+
+        // A pipeline failure after rows reach the Iceberg sink must not 
publish a partial snapshot.
+        // Retrying the corrected logical write must therefore make each row 
visible exactly once.
+        test {
+            sql """
+                insert into ${tableName}
+                select cast(if(number = 1, 'invalid-id', cast(number + 2 as 
string)) as int),
+                       concat('candidate-', number)
+                from numbers('number' = '3')
+            """
+            exception "can't cast to INT in strict mode"
+        }
+
+        assertEquals(snapshotsBeforeFailure, (sql """
+            select count(*) from ${tableName}\$snapshots
+        """)[0][0] as long)
+        assertEquals(filesBeforeFailure, (sql """
+            select count(*) from ${tableName}\$files
+        """)[0][0] as long)
+        order_qt_failed_write_state """
+            select id, payload from ${tableName} order by id
+        """
+
+        sql """
+            insert into ${tableName}
+            select number + 2, concat('candidate-', number)
+            from numbers('number' = '3')
+        """
+        assertEquals(snapshotsBeforeFailure + 1, (sql """
+            select count(*) from ${tableName}\$snapshots
+        """)[0][0] as long)
+        order_qt_failed_write_retry """
+            select id, payload, count(*)
+            from ${tableName}
+            group by id, payload
+            order by id
+        """
+    } finally {
+        sql """drop database if exists ${catalogName}.${dbName} force"""
+        sql """drop catalog if exists ${catalogName}"""
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to