Fix multi table joins error and add more integration tests

diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiIDsWithAttributesTableIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiIDsWithAttributesTableIT.java
index 1f414e4..110fc34 100644
--- a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiIDsWithAttributesTableIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBMultiIDsWithAttributesTableIT.java
@@ -32,7 +32,9 @@
 
 import java.sql.Connection;
 import java.sql.Statement;
+import java.util.Arrays;
 
+import static org.apache.iotdb.db.it.utils.TestUtils.tableAssertTestFail;
 import static org.apache.iotdb.db.it.utils.TestUtils.tableResultSetEqualTest;
 import static org.junit.Assert.fail;
 
@@ -86,15 +88,6 @@
         "insert into table0(device,level,time,num,bigNum,floatNum,str,bool,date) values('d2','l5',51536000000,15,3147483648,235.213,'watermelon',TRUE,'2023-01-01')"
       };
 
-  //  public static void main(String[] args) {
-  //    for (String sql : sql1) {
-  //      System.out.println(sql+";");
-  //    }
-  //    for (String sql : sql2) {
-  //      System.out.println(sql+";");
-  //    }
-  //  }
-
   private static final String[] sql3 =
       new String[] {
         "CREATE TABLE table1 (device string id, level string id, attr1 string attribute, attr2 string attribute, num int32 measurement, bigNum int64 measurement, "
@@ -120,6 +113,35 @@
         "insert into table1(time, device, level, attr1, attr2, num,bigNum,floatNum,str,bool) values(40, 'd11', 'l11', 'c', 'd', 3, 2947483648, 231.2121, 'coconut', FALSE)"
       };
 
+  private static final String[] sql4 =
+      new String[] {
+        "create table students(region STRING ID, student_id INT32 MEASUREMENT, name STRING MEASUREMENT, genders text MEASUREMENT, date_of_birth DATE MEASUREMENT)",
+        "create table teachers(region STRING ID, teacher_id INT32 MEASUREMENT, course_id INT32 MEASUREMENT, age INT32 MEASUREMENT)",
+        "create table courses(course_id STRING ID, course_name STRING MEASUREMENT, teacher_id INT32 MEASUREMENT)",
+        "create table grades(grade_id STRING ID, course_id INT32 MEASUREMENT, student_id INT32 MEASUREMENT, score INT32 MEASUREMENT)",
+        "insert into students(time,region,student_id,name,genders,date_of_birth) values"
+            + "(1,'haidian',1,'Lucy','女','2015-10-10'),(2,'haidian',2,'Jack','男','2015-09-24'),(3,'chaoyang',3,'Sam','男','2014-07-20'),(4,'chaoyang',4,'Lily','女','2015-03-28'),"
+            + "(5,'xicheng',5,'Helen','女','2016-01-22'),(6,'changping',6,'Nancy','女','2017-12-20'),(7,'changping',7,'Mike','男','2016-11-22'),(8,'shunyi',8,'Bob','男','2016-05-12')",
+        "insert into teachers(time,region,teacher_id,course_id,age) values"
+            + "(1,'haidian',1001,10000001,25),(2,'haidian',1002,10000002,26),(3,'chaoyang',1003,10000003,28),"
+            + "(4,'chaoyang',1004,10000004,27),(5,'xicheng',1005,10000005,26)",
+        "insert into courses(time,course_id,course_name,teacher_id) values"
+            + "(1,10000001,'数学',1001),(2,10000002,'语文',1002),(3,10000003,'英语',1003),"
+            + "(4,10000004,'体育',1004),(5,10000005,'历史',1005)",
+        "insert into grades(time,grade_id,course_id,student_id,score) values"
+            + "(1,1111,10000001,1,99),(2,1112,10000002,2,90),(3,1113,10000003,3,85),(4,1114,10000004,4,89),(5,1115,10000005,5,98),"
+            + "(6,1113,10000003,6,55),(7,1114,10000004,7,60),(8,1115,10000005,8,100),(9,1114,10000001,2,99),(10,1115,10000002,1,95)"
+      };
+
+  //  public static void main(String[] args) {
+  //    for (String sql : sql1) {
+  //      System.out.println(sql+";");
+  //    }
+  //    for (String sql : sql2) {
+  //      System.out.println(sql+";");
+  //    }
+  //  }
+
   String[] expectedHeader;
   String[] retArray;
   String sql;
@@ -144,15 +166,10 @@
   private static void insertData() {
     try (Connection connection = EnvFactory.getEnv().getTableConnection();
         Statement statement = connection.createStatement()) {
-
-      for (String sql : sql1) {
-        statement.execute(sql);
-      }
-      for (String sql : sql2) {
-        statement.execute(sql);
-      }
-      for (String sql : sql3) {
-        statement.execute(sql);
+      for (String[] sqlList : Arrays.asList(sql1, sql2, sql3, sql4)) {
+        for (String sql : sqlList) {
+          statement.execute(sql);
+        }
       }
     } catch (Exception e) {
       e.printStackTrace();
@@ -1579,9 +1596,9 @@
   // has filter
   @Test
   public void fullOuterJoinTest2() {
-    String[] expectedHeader =
+    expectedHeader =
         new String[] {"time", "device", "level", "t1_num_add", "device", "attr2", "num", "str"};
-    String[] retArray =
+    retArray =
         new String[] {
           "1970-01-01T00:00:00.000Z,null,null,null,d1,d,3,coconut,",
           "1970-01-01T00:00:00.000Z,null,null,null,d2,c,3,coconut,",
@@ -1636,6 +1653,43 @@
     tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE_NAME);
   }
 
+  @Test
+  public void fourTableJoinTest() {
+    expectedHeader =
+        new String[] {
+          "time", "s_id", "s_name", "s_birth", "t_id", "t_c_id", "c_name", "g_id", "score"
+        };
+    retArray =
+        new String[] {
+          "1970-01-01T00:00:00.001Z,1,Lucy,2015-10-10,1001,10000001,数学,1111,99,",
+          "1970-01-01T00:00:00.002Z,2,Jack,2015-09-24,1002,10000002,语文,1112,90,",
+          "1970-01-01T00:00:00.003Z,3,Sam,2014-07-20,1003,10000003,英语,1113,85,",
+          "1970-01-01T00:00:00.004Z,4,Lily,2015-03-28,1004,10000004,体育,1114,89,",
+        };
+    sql =
+        "select s.time,"
+            + "         s.student_id as s_id, s.name as s_name, s.date_of_birth as s_birth,"
+            + "         t.teacher_id as t_id, t.course_id as t_c_id,"
+            + "         c.course_name as c_name,"
+            + "         g.grade_id as g_id, g.score as score "
+            + "from students s, teachers t, courses c, grades g "
+            + "where s.time=t.time AND c.time=g.time AND s.time=c.time "
+            + "order by s.student_id, t.teacher_id, c.course_id,g.grade_id";
+    tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE_NAME);
+
+    sql =
+        "select s.region, s.name,"
+            + "           t.teacher_id,"
+            + "           c.course_name,"
+            + "           g.score "
+            + "from students s, teachers t, courses c, grades g "
+            + "where s.time=c.time and c.time=g.time";
+    tableAssertTestFail(
+        sql,
+        "701: Cross join is not supported in current version, each table must have at least one equiJoinClause",
+        DATABASE_NAME);
+  }
+
   public static String[] buildHeaders(int length) {
     String[] expectedHeader = new String[length];
     for (int i = 0; i < length; i++) {
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/JoinNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/JoinNode.java
index 0775075..3546c9a 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/JoinNode.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/JoinNode.java
@@ -44,6 +44,7 @@
 import static com.google.common.base.Preconditions.checkArgument;
 import static java.lang.String.format;
 import static java.util.Objects.requireNonNull;
+import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.JoinNode.JoinType.INNER;
 
 public class JoinNode extends TwoChildProcessNode {
 
@@ -292,6 +293,10 @@
     return spillable;
   }
 
+  public boolean isCrossJoin() {
+    return criteria.isEmpty() && !filter.isPresent() && joinType == INNER;
+  }
+
   @Override
   public String toString() {
     return "JoinNode-" + this.getPlanNodeId();
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
index f9d2d5e..2f78669 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
@@ -24,6 +24,7 @@
 import org.apache.iotdb.commons.partition.DataPartitionQueryParam;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.exception.sql.SemanticException;
 import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
 import org.apache.iotdb.db.queryengine.common.QueryId;
 import org.apache.iotdb.db.queryengine.metric.QueryPlanCostMetricSet;
@@ -640,6 +641,11 @@
                 node.isSpillable());
       }
 
+      if (((JoinNode) output).isCrossJoin()) {
+        throw new SemanticException(
+            "Cross join is not supported in current version, each table must have at least one equiJoinClause");
+      }
+
       JoinNode.EquiJoinClause joinCriteria = ((JoinNode) output).getCriteria().get(0);
       OrderingScheme leftOrderingScheme =
           new OrderingScheme(