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

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


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new 518c6a70714 branch:4.2: [fix](fe) Fix IS TRUE/ IS FALSE predicate null 
semantics #64696 (#68286)
518c6a70714 is described below

commit 518c6a70714dc9f3d11dabe02a1b263ec8cf8799
Author: morrySnow <[email protected]>
AuthorDate: Mon Sep 21 10:35:33 2026 +0800

    branch:4.2: [fix](fe) Fix IS TRUE/ IS FALSE predicate null semantics #64696 
(#68286)
    
    picked from #64696
---
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  9 +--
 .../nereids/rules/analysis/ExpressionAnalyzer.java | 21 +++++
 .../doris/nereids/trees/expressions/IsFalse.java   | 90 ++++++++++++++++++++++
 .../doris/nereids/trees/expressions/IsTrue.java    | 90 ++++++++++++++++++++++
 .../expressions/visitor/ExpressionVisitor.java     | 10 +++
 .../doris/nereids/parser/NereidsParserTest.java    | 20 +++++
 .../rules/analysis/ExpressionAnalyzerTest.java     | 37 +++++++++
 .../sql/test_compare_expression.out                |  2 +-
 .../sql_functions/test_is_true_false_predicate.out | 22 ++++++
 .../test_is_true_false_predicate.groovy            | 48 ++++++++++++
 10 files changed, 343 insertions(+), 6 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 07ae3bbf909..5f6c72d0a5a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -555,7 +555,9 @@ import 
org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
 import org.apache.doris.nereids.trees.expressions.InPredicate;
 import org.apache.doris.nereids.trees.expressions.InSubquery;
 import org.apache.doris.nereids.trees.expressions.IntegralDivide;
+import org.apache.doris.nereids.trees.expressions.IsFalse;
 import org.apache.doris.nereids.trees.expressions.IsNull;
+import org.apache.doris.nereids.trees.expressions.IsTrue;
 import org.apache.doris.nereids.trees.expressions.LessThan;
 import org.apache.doris.nereids.trees.expressions.LessThanEqual;
 import org.apache.doris.nereids.trees.expressions.Like;
@@ -1092,7 +1094,6 @@ import 
org.apache.doris.nereids.trees.plans.logical.LogicalUsingJoin;
 import org.apache.doris.nereids.types.AggStateType;
 import org.apache.doris.nereids.types.ArrayType;
 import org.apache.doris.nereids.types.BigIntType;
-import org.apache.doris.nereids.types.BooleanType;
 import org.apache.doris.nereids.types.DataType;
 import org.apache.doris.nereids.types.DateTimeType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
@@ -5126,12 +5127,10 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
                     outExpression = new IsNull(valueExpression);
                     break;
                 case DorisParser.TRUE:
-                    outExpression = new Cast(valueExpression,
-                            BooleanType.INSTANCE, true);
+                    outExpression = new IsTrue(valueExpression);
                     break;
                 case DorisParser.FALSE:
-                    outExpression = new Not(new Cast(valueExpression,
-                            BooleanType.INSTANCE, true));
+                    outExpression = new IsFalse(valueExpression);
                     break;
                 case DorisParser.MATCH:
                 case DorisParser.MATCH_ANY:
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java
index 8a3eade3f20..51777c0fd6d 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java
@@ -59,6 +59,9 @@ import 
org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
 import org.apache.doris.nereids.trees.expressions.InPredicate;
 import org.apache.doris.nereids.trees.expressions.InSubquery;
 import org.apache.doris.nereids.trees.expressions.IntegralDivide;
+import org.apache.doris.nereids.trees.expressions.IsFalse;
+import org.apache.doris.nereids.trees.expressions.IsNull;
+import org.apache.doris.nereids.trees.expressions.IsTrue;
 import org.apache.doris.nereids.trees.expressions.LessThanEqual;
 import org.apache.doris.nereids.trees.expressions.Match;
 import org.apache.doris.nereids.trees.expressions.Not;
@@ -857,6 +860,24 @@ public class ExpressionAnalyzer extends 
SubExprAnalyzer<ExpressionRewriteContext
         }
     }
 
+    @Override
+    public Expression visitIsTrue(IsTrue isTrue, ExpressionRewriteContext 
context) {
+        Expression child = isTrue.child().accept(this, context);
+        if (!child.getDataType().isBooleanType()) {
+            child = new Cast(child, BooleanType.INSTANCE);
+        }
+        return new And(child, new Not(new IsNull(child)));
+    }
+
+    @Override
+    public Expression visitIsFalse(IsFalse isFalse, ExpressionRewriteContext 
context) {
+        Expression child = isFalse.child().accept(this, context);
+        if (!child.getDataType().isBooleanType()) {
+            child = new Cast(child, BooleanType.INSTANCE);
+        }
+        return new And(new Not(child), new Not(new IsNull(child)));
+    }
+
     @Override
     public Expression visitInSubquery(InSubquery inSubquery, 
ExpressionRewriteContext context) {
         // analyze subquery
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IsFalse.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IsFalse.java
new file mode 100644
index 00000000000..fdd17c296fc
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IsFalse.java
@@ -0,0 +1,90 @@
+// 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.doris.nereids.trees.expressions;
+
+import org.apache.doris.nereids.exceptions.UnboundException;
+import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
+import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.BooleanType;
+import org.apache.doris.nereids.types.DataType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * expr is false predicate.
+ */
+public class IsFalse extends Expression implements UnaryExpression, 
AlwaysNotNullable {
+
+    public IsFalse(Expression e) {
+        super(ImmutableList.of(e));
+    }
+
+    private IsFalse(List<Expression> children) {
+        super(children);
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitIsFalse(this, context);
+    }
+
+    @Override
+    public IsFalse withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new IsFalse(children);
+    }
+
+    @Override
+    public String computeToSql() throws UnboundException {
+        return child().toSql() + " IS FALSE";
+    }
+
+    @Override
+    public String toString() {
+        return child().toString() + " IS FALSE";
+    }
+
+    @Override
+    public String toDigest() {
+        return child().toDigest() + " IS FALSE";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!super.equals(o)) {
+            return false;
+        }
+        IsFalse other = (IsFalse) o;
+        return Objects.equals(child(), other.child());
+    }
+
+    @Override
+    public int computeHashCode() {
+        return child().hashCode();
+    }
+
+    @Override
+    public DataType getDataType() throws UnboundException {
+        return BooleanType.INSTANCE;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IsTrue.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IsTrue.java
new file mode 100644
index 00000000000..366ab0056dc
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IsTrue.java
@@ -0,0 +1,90 @@
+// 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.doris.nereids.trees.expressions;
+
+import org.apache.doris.nereids.exceptions.UnboundException;
+import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
+import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.BooleanType;
+import org.apache.doris.nereids.types.DataType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * expr is true predicate.
+ */
+public class IsTrue extends Expression implements UnaryExpression, 
AlwaysNotNullable {
+
+    public IsTrue(Expression e) {
+        super(ImmutableList.of(e));
+    }
+
+    private IsTrue(List<Expression> children) {
+        super(children);
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitIsTrue(this, context);
+    }
+
+    @Override
+    public IsTrue withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new IsTrue(children);
+    }
+
+    @Override
+    public String computeToSql() throws UnboundException {
+        return child().toSql() + " IS TRUE";
+    }
+
+    @Override
+    public String toString() {
+        return child().toString() + " IS TRUE";
+    }
+
+    @Override
+    public String toDigest() {
+        return child().toDigest() + " IS TRUE";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!super.equals(o)) {
+            return false;
+        }
+        IsTrue other = (IsTrue) o;
+        return Objects.equals(child(), other.child());
+    }
+
+    @Override
+    public int computeHashCode() {
+        return child().hashCode();
+    }
+
+    @Override
+    public DataType getDataType() throws UnboundException {
+        return BooleanType.INSTANCE;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java
index cd4efe43d8f..f4343459c17 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java
@@ -52,7 +52,9 @@ import 
org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
 import org.apache.doris.nereids.trees.expressions.InPredicate;
 import org.apache.doris.nereids.trees.expressions.InSubquery;
 import org.apache.doris.nereids.trees.expressions.IntegralDivide;
+import org.apache.doris.nereids.trees.expressions.IsFalse;
 import org.apache.doris.nereids.trees.expressions.IsNull;
+import org.apache.doris.nereids.trees.expressions.IsTrue;
 import org.apache.doris.nereids.trees.expressions.LessThan;
 import org.apache.doris.nereids.trees.expressions.LessThanEqual;
 import org.apache.doris.nereids.trees.expressions.MarkJoinSlotReference;
@@ -449,6 +451,14 @@ public abstract class ExpressionVisitor<R, C>
         return visit(isNull, context);
     }
 
+    public R visitIsTrue(IsTrue isTrue, C context) {
+        return visit(isTrue, context);
+    }
+
+    public R visitIsFalse(IsFalse isFalse, C context) {
+        return visit(isFalse, context);
+    }
+
     public R visitInSubquery(InSubquery in, C context) {
         return visitSubqueryExpr(in, context);
     }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
index 9fb470b46ca..345d6715b5e 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
@@ -33,7 +33,9 @@ import org.apache.doris.nereids.glue.LogicalPlanAdapter;
 import org.apache.doris.nereids.trees.expressions.Cast;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.IsFalse;
 import org.apache.doris.nereids.trees.expressions.IsNull;
+import org.apache.doris.nereids.trees.expressions.IsTrue;
 import org.apache.doris.nereids.trees.expressions.Not;
 import org.apache.doris.nereids.trees.expressions.OrderExpression;
 import org.apache.doris.nereids.trees.expressions.functions.generator.Unnest;
@@ -1791,4 +1793,22 @@ public class NereidsParserTest extends ParserTestBase {
         Assertions.assertInstanceOf(Not.class, expression.child(0));
         Assertions.assertInstanceOf(IsNull.class, 
expression.child(0).child(0));
     }
+
+    @Test
+    public void testIsTrueAndIsFalseExpression() {
+        NereidsParser nereidsParser = new NereidsParser();
+        Expression expression = nereidsParser.parseExpression("X IS TRUE");
+        Assertions.assertInstanceOf(IsTrue.class, expression);
+
+        expression = nereidsParser.parseExpression("X IS FALSE");
+        Assertions.assertInstanceOf(IsFalse.class, expression);
+
+        expression = nereidsParser.parseExpression("X IS NOT TRUE");
+        Assertions.assertInstanceOf(Not.class, expression);
+        Assertions.assertInstanceOf(IsTrue.class, expression.child(0));
+
+        expression = nereidsParser.parseExpression("X IS NOT FALSE");
+        Assertions.assertInstanceOf(Not.class, expression);
+        Assertions.assertInstanceOf(IsFalse.class, expression.child(0));
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzerTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzerTest.java
index 052325cac35..6c70a37aa0c 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzerTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzerTest.java
@@ -21,14 +21,21 @@ import org.apache.doris.nereids.analyzer.Scope;
 import org.apache.doris.nereids.analyzer.UnboundFunction;
 import org.apache.doris.nereids.analyzer.UnboundSlot;
 import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.And;
 import org.apache.doris.nereids.trees.expressions.BoundStar;
+import org.apache.doris.nereids.trees.expressions.Cast;
 import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.IsFalse;
+import org.apache.doris.nereids.trees.expressions.IsNull;
+import org.apache.doris.nereids.trees.expressions.IsTrue;
+import org.apache.doris.nereids.trees.expressions.Not;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
 import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
 import org.apache.doris.nereids.types.BigIntType;
+import org.apache.doris.nereids.types.BooleanType;
 
 import com.google.common.collect.ImmutableList;
 import org.junit.jupiter.api.Assertions;
@@ -96,4 +103,34 @@ public class ExpressionAnalyzerTest {
         );
         Assertions.assertEquals(expectedResult, result);
     }
+
+    @Test
+    public void testAnalyzeIsTrueAndIsFalse() {
+        ExpressionAnalyzer analyzer = new ExpressionAnalyzer(null, new 
Scope(ImmutableList.of()),
+                null, true, true);
+        SlotReference slot = new SlotReference(new ExprId(1), "c1", 
BigIntType.INSTANCE, true, ImmutableList.of());
+
+        Expression isTrue = analyzer.analyze(new IsTrue(slot));
+        Assertions.assertInstanceOf(And.class, isTrue);
+        Assertions.assertInstanceOf(Cast.class, isTrue.child(0));
+        Assertions.assertEquals(BooleanType.INSTANCE, 
isTrue.child(0).getDataType());
+        Assertions.assertInstanceOf(Not.class, isTrue.child(1));
+        Assertions.assertInstanceOf(IsNull.class, isTrue.child(1).child(0));
+
+        Expression isFalse = analyzer.analyze(new IsFalse(slot));
+        Assertions.assertInstanceOf(And.class, isFalse);
+        Assertions.assertInstanceOf(Not.class, isFalse.child(0));
+        Assertions.assertInstanceOf(Cast.class, isFalse.child(0).child(0));
+        Assertions.assertEquals(BooleanType.INSTANCE, 
isFalse.child(0).child(0).getDataType());
+        Assertions.assertInstanceOf(Not.class, isFalse.child(1));
+        Assertions.assertInstanceOf(IsNull.class, isFalse.child(1).child(0));
+
+        Expression isNotTrue = analyzer.analyze(new Not(new IsTrue(slot)));
+        Assertions.assertInstanceOf(Not.class, isNotTrue);
+        Assertions.assertInstanceOf(And.class, isNotTrue.child(0));
+
+        Expression isNotFalse = analyzer.analyze(new Not(new IsFalse(slot)));
+        Assertions.assertInstanceOf(Not.class, isNotFalse);
+        Assertions.assertInstanceOf(And.class, isNotFalse.child(0));
+    }
 }
diff --git 
a/regression-test/data/nereids_syntax_p0/sql/test_compare_expression.out 
b/regression-test/data/nereids_syntax_p0/sql/test_compare_expression.out
index 7f4f2d231be..8ee4c5c164e 100644
--- a/regression-test/data/nereids_syntax_p0/sql/test_compare_expression.out
+++ b/regression-test/data/nereids_syntax_p0/sql/test_compare_expression.out
@@ -45,5 +45,5 @@ true  false   true    true
 true   false   false   true
 
 -- !test_compare_expression_16 --
-\N     \N      \N      \N
+false  true    false   true
 
diff --git 
a/regression-test/data/query_p0/sql_functions/test_is_true_false_predicate.out 
b/regression-test/data/query_p0/sql_functions/test_is_true_false_predicate.out
new file mode 100644
index 00000000000..4b6a4767299
--- /dev/null
+++ 
b/regression-test/data/query_p0/sql_functions/test_is_true_false_predicate.out
@@ -0,0 +1,22 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !is_true --
+1
+
+-- !is_false --
+2
+
+-- !is_not_true --
+2
+3
+4
+
+-- !is_not_false --
+1
+3
+4
+
+-- !truth_table --
+1      true    true    false   false   true    true    true    false   false   
true
+2      false   false   true    true    false   false   false   true    true    
false
+3      \N      false   false   true    true    abc     false   false   true    
true
+4      \N      false   false   true    true    \N      false   false   true    
true
diff --git 
a/regression-test/suites/query_p0/sql_functions/test_is_true_false_predicate.groovy
 
b/regression-test/suites/query_p0/sql_functions/test_is_true_false_predicate.groovy
new file mode 100644
index 00000000000..4ad05100d42
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/test_is_true_false_predicate.groovy
@@ -0,0 +1,48 @@
+// 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_is_true_false_predicate", "arrow_flight_sql") {
+    sql """drop table if exists test_is_true_false_predicate;"""
+    sql """
+        create table test_is_true_false_predicate (
+            id int null,
+            b boolean null,
+            c string null
+        )
+        duplicate key (id)
+        distributed by hash(id) buckets 1
+        properties("replication_num" = "1");
+        """
+    sql """insert into test_is_true_false_predicate values (1, true, 'true'), 
(2, false, 'false'), (3, null, 'abc'), (4, null, null);"""
+
+    order_qt_is_true """
+        select id from test_is_true_false_predicate where b is true order by 
id;
+        """
+    order_qt_is_false """
+        select id from test_is_true_false_predicate where b is false order by 
id;
+        """
+    order_qt_is_not_true """
+        select id from test_is_true_false_predicate where b is not true order 
by id;
+        """
+    order_qt_is_not_false """
+        select id from test_is_true_false_predicate where b is not false order 
by id;
+        """
+    order_qt_truth_table """
+        select id, b, b is true, b is false, b is not true, b is not false, c, 
c is true, c is false, c is not true, c is not false
+        from test_is_true_false_predicate order by id;
+        """
+}


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

Reply via email to