#include <gtest/gtest.h>
#include <qast.h>
#include <sys/time.h>
#include <cassert>
#include <iostream>

#include "qast.h"
#include "taosmsg.h"
#include "tsdb.h"
#include "tskiplist.h"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"

typedef struct ResultObj {
  int32_t numOfResult;
  char *  resultName[64];
} ResultObj;

static void initSchema(SSchema *pSchema, int32_t numOfCols);

static void initSchema_binary(SSchema *schema, int32_t numOfCols);

static SSkipList *createSkipList(SSchema *pSchema, int32_t numOfTags);
static SSkipList *createSkipList_binary(SSchema *pSchema, int32_t numOfTags);

static void testQueryStr(SSchema *schema, int32_t numOfCols, char *sql, SSkipList *pSkipList, ResultObj *expectedVal);

static void dropMeter(SSkipList *pSkipList);

static void Right2LeftTest(SSchema *schema, int32_t numOfCols, SSkipList *pSkipList);

static void Left2RightTest(SSchema *schema, int32_t numOfCols, SSkipList *pSkipList);

static void IllegalExprTest(SSchema *schema, int32_t numOfCols, SSkipList *pSkipList);

static void Left2RightTest_binary(SSchema *schema, int32_t numOfCols, SSkipList *pSkipList);
static void Right2LeftTest_binary(SSchema *schema, int32_t numOfCols, SSkipList *pSkipList);

void setValue(ResultObj *pResult, int32_t num, char **val) {
  pResult->numOfResult = num;
  for (int32_t i = 0; i < num; ++i) {
    pResult->resultName[i] = val[i];
  }
}

static void initSchema_binary(SSchema *schema, int32_t numOfCols) {
  schema[0].type = TSDB_DATA_TYPE_BINARY;
  schema[0].bytes = 8;
  strcpy(schema[0].name, "a");

  schema[1].type = TSDB_DATA_TYPE_DOUBLE;
  schema[1].bytes = 8;
  strcpy(schema[1].name, "b");

  schema[2].type = TSDB_DATA_TYPE_INT;
  schema[2].bytes = 20;
  strcpy(schema[2].name, "c");

  schema[3].type = TSDB_DATA_TYPE_BIGINT;
  schema[3].bytes = 8;
  strcpy(schema[3].name, "d");

  schema[4].type = TSDB_DATA_TYPE_SMALLINT;
  schema[4].bytes = 2;
  strcpy(schema[4].name, "e");

  schema[5].type = TSDB_DATA_TYPE_TINYINT;
  schema[5].bytes = 1;
  strcpy(schema[5].name, "f");

  schema[6].type = TSDB_DATA_TYPE_FLOAT;
  schema[6].bytes = 4;
  strcpy(schema[6].name, "g");

  schema[7].type = TSDB_DATA_TYPE_BOOL;
  schema[7].bytes = 1;
  strcpy(schema[7].name, "h");
}

static void initSchema(SSchema *schema, int32_t numOfCols) {
  schema[0].type = TSDB_DATA_TYPE_INT;
  schema[0].bytes = 8;
  strcpy(schema[0].name, "a");

  schema[1].type = TSDB_DATA_TYPE_DOUBLE;
  schema[1].bytes = 8;
  strcpy(schema[1].name, "b");

  schema[2].type = TSDB_DATA_TYPE_BINARY;
  schema[2].bytes = 20;
  strcpy(schema[2].name, "c");

  schema[3].type = TSDB_DATA_TYPE_BIGINT;
  schema[3].bytes = 8;
  strcpy(schema[3].name, "d");

  schema[4].type = TSDB_DATA_TYPE_SMALLINT;
  schema[4].bytes = 2;
  strcpy(schema[4].name, "e");

  schema[5].type = TSDB_DATA_TYPE_TINYINT;
  schema[5].bytes = 1;
  strcpy(schema[5].name, "f");

  schema[6].type = TSDB_DATA_TYPE_FLOAT;
  schema[6].bytes = 4;
  strcpy(schema[6].name, "g");

  schema[7].type = TSDB_DATA_TYPE_BOOL;
  schema[7].bytes = 1;
  strcpy(schema[7].name, "h");
}

// static void addOneNode(SSchema *pSchema, int32_t tagsLen, SSkipList *pSkipList,
//                       char *meterId, int32_t a, double b, char *c, int64_t d, int16_t e, int8_t f, float g,
//                       bool h, int32_t numOfTags) {
//  STabObj *pMeter = calloc(1, sizeof(STabObj));
//  pMeter->numOfTags = numOfTags;
//  pMeter->pTagData = calloc(1, tagsLen + TSDB_METER_ID_LEN);
//  strcpy(pMeter->meterId, meterId);
//
//  char *tags = pMeter->pTagData + TSDB_METER_ID_LEN;
//  int32_t offset = 0;
//
//  *(int32_t *) tags = a;
//
//  offset += pSchema[0].bytes;
//  *(double *) (tags + offset) = b;
//
//  offset += pSchema[1].bytes;
//  memcpy(tags + offset, c, 3);
//
//  offset += pSchema[2].bytes;
//  *(int64_t *) (tags + offset) = d;
//
//  offset += pSchema[3].bytes;
//  *(int16_t *) (tags + offset) = e;
//
//  offset += pSchema[4].bytes;
//  *(int8_t *) (tags + offset) = f;
//
//  offset += pSchema[5].bytes;
//  *(float *) (tags + offset) = g;
//
//  offset += pSchema[6].bytes;
//  *(int8_t *) (tags + offset) = h ? 1 : 0;
//
//  SSkipListKey pKey = SSkipListCreateKey(pSchema[0].type, tags, pSchema[0].bytes);
//  SSkipListPut(pSkipList, pMeter, &pKey, 1);
//}
//
// static void addOneNode_binary(SSchema *pSchema, int32_t tagsLen, SSkipList *pSkipList,
//                              char *meterId, int32_t a, double b, char *c, int64_t d, int16_t e, int8_t f, float g,
//                              bool h, int32_t numOfTags) {
//  STabObj *pMeter = calloc(1, sizeof(STabObj));
//  pMeter->numOfTags = numOfTags;
//  pMeter->pTagData = calloc(1, tagsLen + TSDB_METER_ID_LEN);
//  strcpy(pMeter->meterId, meterId);
//
//  char *tags = pMeter->pTagData + TSDB_METER_ID_LEN;
//  int32_t offset = 0;
//  memcpy(tags, c, pSchema[0].bytes);
//
//  offset += pSchema[0].bytes;
//  *(double *) (tags + offset) = b;
//
//  offset += pSchema[1].bytes;
//  *(int32_t *) (tags + offset) = a;
//
//  offset += pSchema[2].bytes;
//  *(int64_t *) (tags + offset) = d;
//
//  offset += pSchema[3].bytes;
//  *(int16_t *) (tags + offset) = e;
//
//  offset += pSchema[4].bytes;
//  *(int8_t *) (tags + offset) = f;
//
//  offset += pSchema[5].bytes;
//  *(float *) (tags + offset) = g;
//
//  offset += pSchema[6].bytes;
//  *(int8_t *) (tags + offset) = h ? 1 : 0;
//
//  SSkipListKey pKey = SSkipListCreateKey(pSchema[0].type, tags, pSchema[0].bytes);
//  SSkipListPut(pSkipList, pMeter, &pKey, 1);
//  SSkipListDestroyKey(&pKey);
//}

// static void dropMeter(SSkipList *pSkipList) {
//  SSkipListNode **pRes = NULL;
//  int32_t num = SSkipListIterateList(pSkipList, &pRes, NULL, NULL);
//  for (int32_t i = 0; i < num; ++i) {
//    SSkipListNode *pNode = pRes[i];
//    STabObj *pMeter = (STabObj *) pNode->pData;
//    free(pMeter->pTagData);
//    free(pMeter);
//    pNode->pData = NULL;
//  }
//  free(pRes);
//}

// static SSkipList *createSkipList(SSchema *pSchema, int32_t numOfTags) {
//  int32_t tagsLen = 0;
//  for (int32_t i = 0; i < numOfTags; ++i) {
//    tagsLen += pSchema[i].bytes;
//  }
//
//  SSkipList *pSkipList = SSkipListCreate(10, pSchema[0].type, 4);
//
//  addOneNode(pSchema, tagsLen, pSkipList, "tm0\0", 0, 10.5, "abc", 1000, -10000, -20, 1.0, true, 8);
//  addOneNode(pSchema, tagsLen, pSkipList, "tm1\0", 1, 20.5, "def", 1100, -10500, -30, 2.0, false, 8);
//  addOneNode(pSchema, tagsLen, pSkipList, "tm2\0", 2, 30.5, "ghi", 1200, -11000, -40, 3.0, true, 8);
//  addOneNode(pSchema, tagsLen, pSkipList, "tm3\0", 3, 40.5, "jkl", 1300, -11500, -50, 4.0, false, 8);
//  addOneNode(pSchema, tagsLen, pSkipList, "tm4\0", 4, 50.5, "mno", 1400, -12000, -60, 5.0, true, 8);
//  addOneNode(pSchema, tagsLen, pSkipList, "tm5\0", 5, 60.5, "pqr", 1500, -12500, -70, 6.0, false, 8);
//  addOneNode(pSchema, tagsLen, pSkipList, "tm6\0", 6, 70.5, "stu", 1600, -13000, -80, 7.0, true, 8);
//
//  return pSkipList;
//}
//
// static SSkipList *createSkipList_binary(SSchema *pSchema, int32_t numOfTags) {
//  int32_t tagsLen = 0;
//  for (int32_t i = 0; i < numOfTags; ++i) {
//    tagsLen += pSchema[i].bytes;
//  }
//
//  SSkipList *pSkipList = SSkipListCreate(10, pSchema[0].type, 4);
//
//  addOneNode_binary(pSchema, tagsLen, pSkipList, "tm0\0", 0, 10.5, "abc", 1000, -10000, -20, 1.0, true, 8);
//  addOneNode_binary(pSchema, tagsLen, pSkipList, "tm1\0", 1, 20.5, "def", 1100, -10500, -30, 2.0, false, 8);
//  addOneNode_binary(pSchema, tagsLen, pSkipList, "tm2\0", 2, 30.5, "ghi", 1200, -11000, -40, 3.0, true, 8);
//  addOneNode_binary(pSchema, tagsLen, pSkipList, "tm3\0", 3, 40.5, "jkl", 1300, -11500, -50, 4.0, false, 8);
//  addOneNode_binary(pSchema, tagsLen, pSkipList, "tm4\0", 4, 50.5, "mno", 1400, -12000, -60, 5.0, true, 8);
//  addOneNode_binary(pSchema, tagsLen, pSkipList, "tm5\0", 5, 60.5, "pqr", 1500, -12500, -70, 6.0, false, 8);
//  addOneNode_binary(pSchema, tagsLen, pSkipList, "tm6\0", 6, 70.5, "stu", 1600, -13000, -80, 7.0, true, 8);
//
//  return pSkipList;
//}

static void testQueryStr(SSchema *schema, int32_t numOfCols, char *sql, SSkipList *pSkipList, ResultObj *pResult) {
  tExprNode *pExpr = NULL;
  tSQLBinaryExprFromString(&pExpr, schema, numOfCols, sql, strlen(sql));

  char    str[512] = {0};
  int32_t len = 0;
  if (pExpr == NULL) {
    printf("-----error in parse syntax:%s\n\n", sql);
    assert(pResult == NULL);
    return;
  }

  tSQLBinaryExprToString(pExpr, str, &len);
  printf("expr is: %s\n", str);

  SArray *result = NULL;
  //  tExprTreeTraverse(pExpr, pSkipList, result, SSkipListNodeFilterCallback, &result);
  //  printf("the result is:%lld\n", result.num);
  //
  //  bool findResult = false;
  //  for (int32_t i = 0; i < result.num; ++i) {
  //    STabObj *pm = (STabObj *)result.pRes[i];
  //    printf("meterid:%s,\t", pm->meterId);
  //
  //    for (int32_t j = 0; j < pResult->numOfResult; ++j) {
  //      if (strcmp(pm->meterId, pResult->resultName[j]) == 0) {
  //        findResult = true;
  //        break;
  //      }
  //    }
  //    assert(findResult == true);
  //    findResult = false;
  //  }

  printf("\n\n");
  tExprTreeDestroy(&pExpr, NULL);
}

static void Left2RightTest(SSchema *schema, int32_t numOfCols, SSkipList *pSkipList) {
  char str[256] = {0};

  char *t0[1] = {"tm0"};
  char *t1[1] = {"tm1"};
  char *sql = NULL;

  ResultObj res = {1, {"tm1"}};
  testQueryStr(schema, numOfCols, "a=1", pSkipList, &res);

  char *tt[1] = {"tm6"};
  setValue(&res, 1, tt);
  testQueryStr(schema, numOfCols, "a>=6", pSkipList, &res);

  setValue(&res, 1, t0);
  testQueryStr(schema, numOfCols, "b<=10.6", pSkipList, &res);

  strcpy(str, "c<>'pqr'");
  char *t2[6] = {"tm0", "tm1", "tm2", "tm3", "tm4", "tm6"};
  setValue(&res, 6, t2);
  testQueryStr(schema, numOfCols, str, pSkipList, &res);

  strcpy(str, "c='abc'");
  setValue(&res, 1, t0);
  testQueryStr(schema, numOfCols, str, pSkipList, &res);

  char *t3[6] = {"tm1", "tm2", "tm3", "tm4", "tm5", "tm6"};
  setValue(&res, 6, t3);
  testQueryStr(schema, numOfCols, "d>1050", pSkipList, &res);

  char *t4[3] = {"tm4", "tm5", "tm6"};
  setValue(&res, 3, t4);
  testQueryStr(schema, numOfCols, "g>4.5980765", pSkipList, &res);

  char *t5[4] = {"tm0", "tm2", "tm4", "tm6"};
  setValue(&res, 4, t5);
  testQueryStr(schema, numOfCols, "h=true", pSkipList, &res);

  char *t6[3] = {"tm1", "tm3", "tm5"};
  setValue(&res, 3, t6);
  testQueryStr(schema, numOfCols, "h=0", pSkipList, &res);

  sql = "(((b<40)))\0";
  char *t7[3] = {"tm0", "tm1", "tm2"};
  setValue(&res, 3, t7);
  testQueryStr(schema, numOfCols, sql, pSkipList, &res);

  sql = "((a=1) or (a=10)) or ((b=12))";
  setValue(&res, 1, t1);
  testQueryStr(schema, numOfCols, sql, pSkipList, &res);

  sql = "((((((a>0 and a<2))) or a=6) or a=3) or (b=50.5)) and h=0";
  char *t8[2] = {"tm1", "tm3"};
  setValue(&res, 2, t8);
  testQueryStr(schema, numOfCols, sql, pSkipList, &res);

  char *tf[1] = {"tm6"};
  setValue(&res, 1, tf);
  testQueryStr(schema, numOfCols, "e = -13000", pSkipList, &res);

  char *ft[5] = {"tm0", "tm1", "tm2", "tm3", "tm4"};
  setValue(&res, 5, ft);
  testQueryStr(schema, numOfCols, "f > -65", pSkipList, &res);
}

void Right2LeftTest(SSchema *schema, int32_t numOfCols, SSkipList *pSkipList) {
  ResultObj res = {1, {"tm1"}};
  testQueryStr(schema, numOfCols, "((1=a))", pSkipList, &res);

  char *t9[2] = {"tm0", "tm1"};
  setValue(&res, 2, t9);
  testQueryStr(schema, numOfCols, "1>=a", pSkipList, &res);

  char *t0[1] = {"tm0"};
  setValue(&res, 1, t0);
  testQueryStr(schema, numOfCols, "10.6>=b", pSkipList, &res);

  char *t10[3] = {"tm1", "tm3", "tm5"};
  setValue(&res, 3, t10);
  testQueryStr(schema, numOfCols, "0=h", pSkipList, &res);
}

static void IllegalExprTest(SSchema *schema, int32_t numOfCols, SSkipList *pSkipList) {
  testQueryStr(schema, numOfCols, "h=", pSkipList, NULL);
  testQueryStr(schema, numOfCols, "h<", pSkipList, NULL);
  testQueryStr(schema, numOfCols, "a=1 and ", pSkipList, NULL);
  testQueryStr(schema, numOfCols, "and or", pSkipList, NULL);
  testQueryStr(schema, numOfCols, "and a = 1 or", pSkipList, NULL);
  testQueryStr(schema, numOfCols, "(())", pSkipList, NULL);
  testQueryStr(schema, numOfCols, "(", pSkipList, NULL);
  testQueryStr(schema, numOfCols, "(a", pSkipList, NULL);
  testQueryStr(schema, numOfCols, "(a)", pSkipList, NULL);
  testQueryStr(schema, numOfCols, "())", pSkipList, NULL);
  testQueryStr(schema, numOfCols, "a===1", pSkipList, NULL);
  testQueryStr(schema, numOfCols, "a=1 and ", pSkipList, NULL);
}

static void Left2RightTest_binary(SSchema *schema, int32_t numOfCols, SSkipList *pSkipList) {
  char  str[256] = {0};
  char *sql = NULL;

  char *t0[1] = {"tm0"};
  char *t1[1] = {"tm1"};

  ResultObj res = {1, {"tm0"}};
  strcpy(str, "a='abc'");
  testQueryStr(schema, numOfCols, str, pSkipList, &res);

  char *tt[1] = {"tm6"};
  setValue(&res, 1, tt);
  testQueryStr(schema, numOfCols, "c>=6", pSkipList, &res);

  setValue(&res, 1, t0);
  testQueryStr(schema, numOfCols, "b<=10.6", pSkipList, &res);

  strcpy(str, "a<>'pqr'");
  char *t2[6] = {"tm0", "tm1", "tm2", "tm3", "tm4", "tm6"};
  setValue(&res, 6, t2);
  testQueryStr(schema, numOfCols, str, pSkipList, &res);

  strcpy(str, "a='abc'");
  setValue(&res, 1, t0);
  testQueryStr(schema, numOfCols, str, pSkipList, &res);

  char *t3[6] = {"tm1", "tm2", "tm3", "tm4", "tm5", "tm6"};
  setValue(&res, 6, t3);
  testQueryStr(schema, numOfCols, "d>1050", pSkipList, &res);

  char *t4[3] = {"tm4", "tm5", "tm6"};
  setValue(&res, 3, t4);
  testQueryStr(schema, numOfCols, "g>4.5980765", pSkipList, &res);

  char *t5[4] = {"tm0", "tm2", "tm4", "tm6"};
  setValue(&res, 4, t5);
  testQueryStr(schema, numOfCols, "h=true", pSkipList, &res);

  char *t6[3] = {"tm1", "tm3", "tm5"};
  setValue(&res, 3, t6);
  testQueryStr(schema, numOfCols, "h=0", pSkipList, &res);

  sql = "(((b<40)))\0";
  char *t7[3] = {"tm0", "tm1", "tm2"};
  setValue(&res, 3, t7);
  testQueryStr(schema, numOfCols, sql, pSkipList, &res);

  sql = "((c=1) or (c=10)) or ((b=12))\0";
  setValue(&res, 1, t1);
  testQueryStr(schema, numOfCols, sql, pSkipList, &res);

  sql = "((((((c>0 and c<2))) or c=6) or c=3) or (b=50.5)) and h=false\0";
  char *t8[2] = {"tm1", "tm3"};
  setValue(&res, 2, t8);
  testQueryStr(schema, numOfCols, sql, pSkipList, &res);
}

static void Right2LeftTest_binary(SSchema *schema, int32_t numOfCols, SSkipList *pSkipList) {
  char  str[256] = {0};
  char *sql = NULL;

  char *t0[1] = {"tm0"};
  char *t1[1] = {"tm1"};

  ResultObj res = {1, {"tm0"}};
  strcpy(str, "'abc'=a");
  testQueryStr(schema, numOfCols, str, pSkipList, &res);

  char *tt[1] = {"tm6"};
  setValue(&res, 1, tt);
  testQueryStr(schema, numOfCols, "6<=c", pSkipList, &res);

  setValue(&res, 1, t0);
  testQueryStr(schema, numOfCols, "10.6>=b", pSkipList, &res);

  strcpy(str, "'pqr'<>a");
  char *t2[6] = {"tm0", "tm1", "tm2", "tm3", "tm4", "tm6"};
  setValue(&res, 6, t2);
  testQueryStr(schema, numOfCols, str, pSkipList, &res);
}

namespace {
// two level expression tree
tExprNode *createExpr1() {
  auto *pLeft = (tExprNode*) calloc(1, sizeof(tExprNode));
  pLeft->nodeType = TSQL_NODE_COL;
  pLeft->pSchema = (SSchema*) calloc(1, sizeof(SSchema));
  
  strcpy(pLeft->pSchema->name, "col_a");
  pLeft->pSchema->type = TSDB_DATA_TYPE_INT;
  pLeft->pSchema->bytes = sizeof(int32_t);
  pLeft->pSchema->colId = 1;
  
  auto *pRight = (tExprNode*) calloc(1, sizeof(tExprNode));
  pRight->nodeType = TSQL_NODE_VALUE;
  pRight->pVal = (tVariant*) calloc(1, sizeof(tVariant));
  
  pRight->pVal->nType = TSDB_DATA_TYPE_INT;
  pRight->pVal->i64Key = 12;
  
  auto *pRoot = (tExprNode*) calloc(1, sizeof(tExprNode));
  pRoot->nodeType = TSQL_NODE_EXPR;
  
  pRoot->_node.optr = TSDB_RELATION_EQUAL;
  pRoot->_node.pLeft = pLeft;
  pRoot->_node.pRight = pRight;
  pRoot->_node.hasPK = true;
  
  return pRoot;
}

// thress level expression tree
tExprNode* createExpr2() {
  auto *pLeft2 = (tExprNode*) calloc(1, sizeof(tExprNode));
  pLeft2->nodeType = TSQL_NODE_COL;
  pLeft2->pSchema = (SSchema*) calloc(1, sizeof(SSchema));
  
  strcpy(pLeft2->pSchema->name, "col_a");
  pLeft2->pSchema->type = TSDB_DATA_TYPE_BINARY;
  pLeft2->pSchema->bytes = 20;
  pLeft2->pSchema->colId = 1;
  
  auto *pRight2 = (tExprNode*) calloc(1, sizeof(tExprNode));
  pRight2->nodeType = TSQL_NODE_VALUE;
  pRight2->pVal = (tVariant*) calloc(1, sizeof(tVariant));
  
  pRight2->pVal->nType = TSDB_DATA_TYPE_BINARY;
  const char* v = "hello world!";
  pRight2->pVal->pz = strdup(v);
  pRight2->pVal->nLen = strlen(v);
  
  auto *p1 = (tExprNode*) calloc(1, sizeof(tExprNode));
  p1->nodeType = TSQL_NODE_EXPR;
  
  p1->_node.optr = TSDB_RELATION_LIKE;
  p1->_node.pLeft = pLeft2;
  p1->_node.pRight = pRight2;
  p1->_node.hasPK = false;
  
  auto *pLeft1 = (tExprNode*) calloc(1, sizeof(tExprNode));
  pLeft1->nodeType = TSQL_NODE_COL;
  pLeft1->pSchema = (SSchema*) calloc(1, sizeof(SSchema));
  
  strcpy(pLeft1->pSchema->name, "col_b");
  pLeft1->pSchema->type = TSDB_DATA_TYPE_DOUBLE;
  pLeft1->pSchema->bytes = 8;
  pLeft1->pSchema->colId = 99;
  
  auto *pRight1 = (tExprNode*) calloc(1, sizeof(tExprNode));
  pRight1->nodeType = TSQL_NODE_VALUE;
  pRight1->pVal = (tVariant*) calloc(1, sizeof(tVariant));
  
  pRight1->pVal->nType = TSDB_DATA_TYPE_DOUBLE;
  pRight1->pVal->dKey = 91.99;
  
  auto *p2 = (tExprNode*) calloc(1, sizeof(tExprNode));
  p2->nodeType = TSQL_NODE_EXPR;
  
  p2->_node.optr = TSDB_RELATION_GREATER_EQUAL;
  p2->_node.pLeft = pLeft1;
  p2->_node.pRight = pRight1;
  p2->_node.hasPK = false;
  
  auto *pRoot = (tExprNode*) calloc(1, sizeof(tExprNode));
  pRoot->nodeType = TSQL_NODE_EXPR;
  
  pRoot->_node.optr = TSDB_RELATION_OR;
  pRoot->_node.pLeft = p1;
  pRoot->_node.pRight = p2;
  pRoot->_node.hasPK = true;
  return pRoot;
}

void exprSerializeTest1() {
  tExprNode* p1 = createExpr1();
  SBuffer buf = exprTreeToBinary(p1);
  
  size_t size = tbufTell(&buf);
  ASSERT_TRUE(size > 0);
  char* b = tbufGetData(&buf, false);
  
  tExprNode* p2 = exprTreeFromBinary(b, size);
  ASSERT_EQ(p1->nodeType, p2->nodeType);
  
  ASSERT_EQ(p2->_node.optr, p1->_node.optr);
  ASSERT_EQ(p2->_node.pLeft->nodeType, p1->_node.pLeft->nodeType);
  ASSERT_EQ(p2->_node.pRight->nodeType, p1->_node.pRight->nodeType);
  
  SSchema* s1 = p1->_node.pLeft->pSchema;
  SSchema* s2 = p2->_node.pLeft->pSchema;
  
  ASSERT_EQ(s2->colId, s1->colId);
  ASSERT_EQ(s2->type, s1->type);
  ASSERT_EQ(s2->bytes, s1->bytes);
  ASSERT_STRCASEEQ(s2->name, s1->name);
  
  tVariant* v1 = p1->_node.pRight->pVal;
  tVariant* v2 = p2->_node.pRight->pVal;
  
  ASSERT_EQ(v1->nType, v2->nType);
  ASSERT_EQ(v1->i64Key, v2->i64Key);
  ASSERT_EQ(p1->_node.hasPK, p2->_node.hasPK);
  
  tExprTreeDestroy(&p1, nullptr);
  tExprTreeDestroy(&p2, nullptr);
  
  tbufClose(&buf, false);
}

void exprSerializeTest2() {
  tExprNode* p1 = createExpr2();
  SBuffer buf = exprTreeToBinary(p1);
  
  size_t size = tbufTell(&buf);
  ASSERT_TRUE(size > 0);
  char* b = tbufGetData(&buf, false);
  
  tExprNode* p2 = exprTreeFromBinary(b, size);
  ASSERT_EQ(p1->nodeType, p2->nodeType);
  
  ASSERT_EQ(p2->_node.optr, p1->_node.optr);
  ASSERT_EQ(p2->_node.pLeft->nodeType, p1->_node.pLeft->nodeType);
  ASSERT_EQ(p2->_node.pRight->nodeType, p1->_node.pRight->nodeType);
  
  tExprNode* c1Left = p1->_node.pLeft;
  tExprNode* c2Left = p2->_node.pLeft;
  
  ASSERT_EQ(c1Left->nodeType, c2Left->nodeType);
  
  ASSERT_EQ(c2Left->nodeType, TSQL_NODE_EXPR);
  ASSERT_EQ(c2Left->_node.optr, TSDB_RELATION_LIKE);
  
  ASSERT_STRCASEEQ(c2Left->_node.pLeft->pSchema->name, "col_a");
  ASSERT_EQ(c2Left->_node.pRight->nodeType, TSQL_NODE_VALUE);
  
  ASSERT_STRCASEEQ(c2Left->_node.pRight->pVal->pz, "hello world!");
  
  tExprNode* c1Right = p1->_node.pRight;
  tExprNode* c2Right = p2->_node.pRight;
  
  ASSERT_EQ(c1Right->nodeType, c2Right->nodeType);
  ASSERT_EQ(c2Right->nodeType, TSQL_NODE_EXPR);
  ASSERT_EQ(c2Right->_node.optr, TSDB_RELATION_GREATER_EQUAL);
  ASSERT_EQ(c2Right->_node.pRight->pVal->dKey, 91.99);
  
  ASSERT_EQ(p2->_node.hasPK, true);
  
  tExprTreeDestroy(&p1, nullptr);
  tExprTreeDestroy(&p2, nullptr);

  tbufClose(&buf, false);
}
}  // namespace
TEST(testCase, astTest) {
  exprSerializeTest2();
}